require 'log4r' require 'rexml/document' module VagrantPlugins module ProviderLibvirt module Action # Just start the domain. class StartDomain def initialize(app, env) @logger = Log4r::Logger.new("vagrant_libvirt::action::start_domain") @app = app end def call(env) env[:ui].info(I18n.t("vagrant_libvirt.starting_domain")) domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s) raise Errors::NoDomainError if domain == nil config = env[:machine].provider_config begin # update domain settings on change. libvirt_domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid(env[:machine].id) if config.memory*1024 != libvirt_domain.max_memory libvirt_domain.max_memory = config.memory*1024 libvirt_domain.memory = libvirt_domain.max_memory end begin # XML definition manipulation descr = libvirt_domain.xml_desc(1) xml_descr = REXML::Document.new descr descr_changed = false # additional disk bus config.disks.each {|disk| device = disk[:device] bus = disk[:bus] REXML::XPath.each(xml_descr,'/domain/devices/disk[@device="disk"]/target[@dev="'+device+'"]') {|disk_target| if disk_target.attributes['bus'] != bus descr_changed = true disk_target.attributes['bus'] = bus disk_target.parent.delete_element("#{disk_target.parent.xpath}/address") end } } # disk_bus REXML::XPath.each(xml_descr,'/domain/devices/disk[@device="disk"]/target[@dev="vda"]') {|disk_target| if disk_target.attributes['bus'] != config.disk_bus descr_changed = true disk_target.attributes['bus'] = config.disk_bus disk_target.parent.delete_element("#{disk_target.parent.xpath}/address") end } # Iterface type REXML::XPath.each(xml_descr,'/domain/devices/interface/model') {|iface_model| if iface_model.attributes['type'] != config.nic_model_type descr_changed = true iface_model.attributes['type'] = config.nic_model_type end } # vCpu count if config.cpus.to_i != libvirt_domain.vcpus.length descr_changed = true REXML::XPath.first(xml_descr,'/domain/vcpu').text = config.cpus end # cpu_mode cpu = REXML::XPath.first(xml_descr,'/domain/cpu') if cpu.nil? descr_changed = true cpu = REXML::Element.new('cpu', REXML::XPath.first(xml_descr,'/domain')) cpu.attributes['mode'] = config.cpu_mode else if cpu.attributes['mode'] != config.cpu_mode descr_changed = true cpu.attributes['mode'] = config.cpu_mode end end if config.cpu_mode != 'host-passthrough' cpu_model = REXML::XPath.first(xml_descr,'/domain/cpu/model') if cpu_model.nil? descr_changed = true cpu_model = REXML::Element.new('model', REXML::XPath.first(xml_descr,'/domain/cpu')) cpu_model.attributes['fallback'] = 'allow' cpu_model.text = 'qemu64' end vmx_feature = REXML::XPath.first(xml_descr,'/domain/cpu/feature[@name="vmx"]') svm_feature = REXML::XPath.first(xml_descr,'/domain/cpu/feature[@name="svm"]') if config.nested if vmx_feature.nil? descr_changed = true vmx_feature = REXML::Element.new('feature', REXML::XPath.first(xml_descr,'/domain/cpu')) vmx_feature.attributes['policy'] = 'optional' vmx_feature.attributes['name'] = 'vmx' end if svm_feature.nil? descr_changed = true svm_feature = REXML::Element.new('feature', REXML::XPath.first(xml_descr,'/domain/cpu')) svm_feature.attributes['policy'] = 'optional' svm_feature.attributes['name'] = 'svm' end else if !vmx_feature.nil? descr_changed = true cpu.delete_element(vmx_feature) end if !svm_feature.nil? descr_changed = true cpu.delete_element(svm_feature) end end else if cpu.elements.to_a.length > 0 descr_changed = true cpu.elements.each {|elem| cpu.delete_element(elem) } end end # Graphics graphics = REXML::XPath.first(xml_descr,'/domain/devices/graphics') if graphics.attributes['type'] != config.graphics_type descr_changed = true graphics.attributes['type'] = config.graphics_type end if graphics.attributes['listen'] != config.graphics_ip descr_changed = true graphics.attributes['listen'] = config.graphics_ip graphics.delete_element('//listen') end if graphics.attributes['autoport'] != config.graphics_autoport descr_changed = true graphics.attributes['autoport'] = config.graphics_autoport if config.graphics_autoport == 'no' graphics.attributes['port'] = config.graphics_port end end if graphics.attributes['keymap'] != config.keymap descr_changed = true graphics.attributes['keymap'] = config.keymap end if graphics.attributes['passwd'] != config.graphics_passwd descr_changed = true if config.graphics_passwd.nil? graphics.attributes.delete 'passwd' else graphics.attributes['passwd'] = config.graphics_passwd end end # Video device video = REXML::XPath.first(xml_descr,'/domain/devices/video/model') if video.attributes['type'] != config.video_type || video.attributes['vram'] != config.video_vram descr_changed = true video.attributes.each_attribute {|attr| video.attributes.delete attr} video.attributes['type'] = config.video_type video.attributes['vram'] = config.video_vram end # Apply if descr_changed begin libvirt_domain.undefine new_descr = "" xml_descr.write new_descr server = env[:machine].provider.driver.connection.servers.create(xml: new_descr) rescue Fog::Errors::Error => e server = env[:machine].provider.driver.connection.servers.create(xml: descr) raise Errors::FogCreateServerError, error_message: e.message end end rescue => e env[:ui].error("Error when updating domain settings: #{e.message}") end # Actually start the domain domain.start rescue => e raise Errors::FogError, :message => e.message end @app.call(env) end end end end end