vagrant-libvirt 0.0.41 → 0.0.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.github/issue_template.md +37 -0
  4. data/.gitignore +21 -0
  5. data/.travis.yml +24 -0
  6. data/Gemfile +26 -0
  7. data/LICENSE +22 -0
  8. data/README.md +1380 -0
  9. data/Rakefile +8 -0
  10. data/example_box/README.md +29 -0
  11. data/example_box/Vagrantfile +60 -0
  12. data/example_box/metadata.json +5 -0
  13. data/lib/vagrant-libvirt.rb +29 -0
  14. data/lib/vagrant-libvirt/action.rb +370 -0
  15. data/lib/vagrant-libvirt/action/create_domain.rb +322 -0
  16. data/lib/vagrant-libvirt/action/create_domain_volume.rb +87 -0
  17. data/lib/vagrant-libvirt/action/create_network_interfaces.rb +302 -0
  18. data/lib/vagrant-libvirt/action/create_networks.rb +361 -0
  19. data/lib/vagrant-libvirt/action/destroy_domain.rb +83 -0
  20. data/lib/vagrant-libvirt/action/destroy_networks.rb +95 -0
  21. data/lib/vagrant-libvirt/action/forward_ports.rb +227 -0
  22. data/lib/vagrant-libvirt/action/halt_domain.rb +41 -0
  23. data/lib/vagrant-libvirt/action/handle_box_image.rb +156 -0
  24. data/lib/vagrant-libvirt/action/handle_storage_pool.rb +57 -0
  25. data/lib/vagrant-libvirt/action/is_created.rb +18 -0
  26. data/lib/vagrant-libvirt/action/is_running.rb +21 -0
  27. data/lib/vagrant-libvirt/action/is_suspended.rb +42 -0
  28. data/lib/vagrant-libvirt/action/message_already_created.rb +16 -0
  29. data/lib/vagrant-libvirt/action/message_not_created.rb +16 -0
  30. data/lib/vagrant-libvirt/action/message_not_running.rb +16 -0
  31. data/lib/vagrant-libvirt/action/message_not_suspended.rb +16 -0
  32. data/lib/vagrant-libvirt/action/message_will_not_destroy.rb +17 -0
  33. data/lib/vagrant-libvirt/action/package_domain.rb +105 -0
  34. data/lib/vagrant-libvirt/action/prepare_nfs_settings.rb +94 -0
  35. data/lib/vagrant-libvirt/action/prepare_nfs_valid_ids.rb +17 -0
  36. data/lib/vagrant-libvirt/action/prune_nfs_exports.rb +27 -0
  37. data/lib/vagrant-libvirt/action/read_mac_addresses.rb +40 -0
  38. data/lib/vagrant-libvirt/action/remove_libvirt_image.rb +20 -0
  39. data/lib/vagrant-libvirt/action/remove_stale_volume.rb +50 -0
  40. data/lib/vagrant-libvirt/action/resume_domain.rb +34 -0
  41. data/lib/vagrant-libvirt/action/set_boot_order.rb +109 -0
  42. data/lib/vagrant-libvirt/action/set_name_of_domain.rb +64 -0
  43. data/lib/vagrant-libvirt/action/share_folders.rb +71 -0
  44. data/lib/vagrant-libvirt/action/start_domain.rb +307 -0
  45. data/lib/vagrant-libvirt/action/suspend_domain.rb +40 -0
  46. data/lib/vagrant-libvirt/action/wait_till_up.rb +109 -0
  47. data/lib/vagrant-libvirt/cap/mount_p9.rb +42 -0
  48. data/lib/vagrant-libvirt/cap/nic_mac_addresses.rb +17 -0
  49. data/lib/vagrant-libvirt/cap/synced_folder.rb +113 -0
  50. data/lib/vagrant-libvirt/config.rb +746 -0
  51. data/lib/vagrant-libvirt/driver.rb +118 -0
  52. data/lib/vagrant-libvirt/errors.rb +153 -0
  53. data/lib/vagrant-libvirt/plugin.rb +92 -0
  54. data/lib/vagrant-libvirt/provider.rb +130 -0
  55. data/lib/vagrant-libvirt/templates/default_storage_pool.xml.erb +13 -0
  56. data/lib/vagrant-libvirt/templates/domain.xml.erb +244 -0
  57. data/lib/vagrant-libvirt/templates/private_network.xml.erb +42 -0
  58. data/lib/vagrant-libvirt/templates/public_interface.xml.erb +26 -0
  59. data/lib/vagrant-libvirt/util.rb +11 -0
  60. data/lib/vagrant-libvirt/util/collection.rb +19 -0
  61. data/lib/vagrant-libvirt/util/erb_template.rb +22 -0
  62. data/lib/vagrant-libvirt/util/error_codes.rb +100 -0
  63. data/lib/vagrant-libvirt/util/network_util.rb +151 -0
  64. data/lib/vagrant-libvirt/util/timer.rb +17 -0
  65. data/lib/vagrant-libvirt/version.rb +5 -0
  66. data/locales/en.yml +162 -0
  67. data/spec/spec_helper.rb +9 -0
  68. data/spec/support/environment_helper.rb +46 -0
  69. data/spec/support/libvirt_context.rb +30 -0
  70. data/spec/support/sharedcontext.rb +34 -0
  71. data/spec/unit/action/destroy_domain_spec.rb +97 -0
  72. data/spec/unit/action/set_name_of_domain_spec.rb +21 -0
  73. data/spec/unit/action/wait_till_up_spec.rb +127 -0
  74. data/spec/unit/config_spec.rb +113 -0
  75. data/spec/unit/templates/domain_all_settings.xml +137 -0
  76. data/spec/unit/templates/domain_defaults.xml +46 -0
  77. data/spec/unit/templates/domain_spec.rb +84 -0
  78. data/tools/create_box.sh +130 -0
  79. data/tools/prepare_redhat_for_box.sh +119 -0
  80. data/vagrant-libvirt.gemspec +54 -0
  81. metadata +93 -3
@@ -0,0 +1,20 @@
1
+ require 'log4r'
2
+
3
+ module VagrantPlugins
4
+ module ProviderLibvirt
5
+ module Action
6
+ class RemoveLibvirtImage
7
+ def initialize(app, _env)
8
+ @logger = Log4r::Logger.new('vagrant_libvirt::action::remove_libvirt_image')
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ env[:ui].info('Vagrant-libvirt plugin removed box only from you LOCAL ~/.vagrant/boxes directory')
14
+ env[:ui].info('From libvirt storage pool you have to delete image manually(virsh, virt-manager or by any other tool)')
15
+ @app.call(env)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,50 @@
1
+ require 'log4r'
2
+ # require 'log4r/yamlconfigurator'
3
+
4
+ module VagrantPlugins
5
+ module ProviderLibvirt
6
+ module Action
7
+ class RemoveStaleVolume
8
+ def initialize(app, _env)
9
+ # log4r_config= YAML.load_file(File.join(File.dirname(__FILE__),"log4r.yaml"))
10
+ # log_cfg = Log4r::YamlConfigurator
11
+ # log_cfg.decode_yaml( log4r_config['log4r_config'] )
12
+
13
+ @logger = Log4r::Logger.new('vagrant_libvirt::action::remove_stale_volume')
14
+ @app = app
15
+ end
16
+
17
+ def call(env)
18
+ # Remove stale server volume
19
+ env[:ui].info(I18n.t('vagrant_libvirt.remove_stale_volume'))
20
+
21
+ config = env[:machine].provider_config
22
+ # Check for storage pool, where box image should be created
23
+ fog_pool = ProviderLibvirt::Util::Collection.find_matching(
24
+ env[:machine].provider.driver.connection.pools.all, config.storage_pool_name
25
+ )
26
+ @logger.debug("**** Pool #{fog_pool.name}")
27
+
28
+ # This is name of newly created image for vm.
29
+ name = "#{env[:domain_name]}.img"
30
+ @logger.debug("**** Volume name #{name}")
31
+
32
+ # remove root storage
33
+ box_volume = ProviderLibvirt::Util::Collection.find_matching(
34
+ env[:machine].provider.driver.connection.volumes.all, name
35
+ )
36
+ if box_volume && box_volume.pool_name == fog_pool.name
37
+ @logger.info("Deleting volume #{box_volume.key}")
38
+ box_volume.destroy
39
+ env[:result] = box_volume
40
+ else
41
+ env[:result] = nil
42
+ end
43
+
44
+ # Continue the middleware chain.
45
+ @app.call(env)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,34 @@
1
+ require 'log4r'
2
+
3
+ module VagrantPlugins
4
+ module ProviderLibvirt
5
+ module Action
6
+ # Resume suspended domain.
7
+ class ResumeDomain
8
+ def initialize(app, _env)
9
+ @logger = Log4r::Logger.new('vagrant_libvirt::action::resume_domain')
10
+ @app = app
11
+ end
12
+
13
+ def call(env)
14
+ env[:ui].info(I18n.t('vagrant_libvirt.resuming_domain'))
15
+
16
+ domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
17
+ raise Errors::NoDomainError if domain.nil?
18
+
19
+ libvirt_domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid(env[:machine].id)
20
+ config = env[:machine].provider_config
21
+ if config.suspend_mode == 'managedsave'
22
+ domain.start
23
+ else
24
+ domain.resume
25
+ end
26
+
27
+ @logger.info("Machine #{env[:machine].id} is resumed.")
28
+
29
+ @app.call(env)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,109 @@
1
+ require 'log4r'
2
+ require 'nokogiri'
3
+
4
+ module VagrantPlugins
5
+ module ProviderLibvirt
6
+ module Action
7
+ # boot order useful for pxe in discovery workflow
8
+ class SetBootOrder
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new('vagrant_libvirt::action::set_boot_order')
12
+ config = env[:machine].provider_config
13
+ @boot_order = config.boot_order
14
+ end
15
+
16
+ def call(env)
17
+ # Get domain first
18
+ begin
19
+ domain = env[:machine].provider
20
+ .driver
21
+ .connection
22
+ .client
23
+ .lookup_domain_by_uuid(
24
+ env[:machine].id.to_s
25
+ )
26
+ rescue => e
27
+ raise Errors::NoDomainError,
28
+ error_message: e.message
29
+ end
30
+
31
+ # Only execute specific boot ordering if this is defined
32
+ # in the Vagrant file
33
+ if @boot_order.count >= 1
34
+
35
+ # If a domain is initially defined with no box or disk or
36
+ # with an explicit boot order, libvirt adds <boot dev="foo">
37
+ # This conflicts with an explicit boot_order configuration,
38
+ # so we need to remove it from the domain xml and feed it back.
39
+ # Also see https://bugzilla.redhat.com/show_bug.cgi?id=1248514
40
+ # as to why we have to do this after all devices have been defined.
41
+ xml = Nokogiri::XML(domain.xml_desc)
42
+ xml.search('/domain/os/boot').each(&:remove)
43
+
44
+ # Parse the XML and find each defined drive and network interfacee
45
+ hd = xml.search("/domain/devices/disk[@device='disk']")
46
+ cdrom = xml.search("/domain/devices/disk[@device='cdrom']")
47
+ # implemented only for 1 network
48
+ nets = @boot_order.flat_map do |x|
49
+ x.class == Hash ? x : nil
50
+ end.compact
51
+ raise 'Defined only for 1 network for boot' if nets.size > 1
52
+ network = search_network(nets, xml)
53
+
54
+ # Generate an array per device group and a flattened
55
+ # array from all of those
56
+ devices = { 'hd' => hd,
57
+ 'cdrom' => cdrom,
58
+ 'network' => network }
59
+
60
+ final_boot_order = final_boot_order(@boot_order, devices)
61
+ # Loop over the entire defined boot order array and
62
+ # create boot order entries in the domain XML
63
+ final_boot_order.each_with_index do |node, index|
64
+ boot = "<boot order='#{index + 1}'/>"
65
+ node.add_child(boot)
66
+ logger_msg(node, index)
67
+ end
68
+
69
+ # Finally redefine the domain XML through libvirt
70
+ # to apply the boot ordering
71
+ env[:machine].provider
72
+ .driver
73
+ .connection
74
+ .client
75
+ .define_domain_xml(xml.to_s)
76
+ end
77
+
78
+ @app.call(env)
79
+ end
80
+
81
+ def final_boot_order(boot_order, devices)
82
+ boot_order.flat_map do |category|
83
+ devices[category.class == Hash ? category.keys.first : category]
84
+ end
85
+ end
86
+
87
+ def search_network(nets, xml)
88
+ str = '/domain/devices/interface'
89
+ str += "[(@type='network' or @type='udp' or @type='bridge')"
90
+ unless nets.empty?
91
+ str += " and source[@network='#{nets.first['network']}']"
92
+ end
93
+ str += ']'
94
+ @logger.debug(str)
95
+ xml.search(str)
96
+ end
97
+
98
+ def logger_msg(node, index)
99
+ name = if node.name == 'disk'
100
+ node['device']
101
+ elsif node.name == 'interface'
102
+ node.name
103
+ end
104
+ @logger.debug "Setting #{name} to boot index #{index + 1}"
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,64 @@
1
+ require 'securerandom'
2
+ module VagrantPlugins
3
+ module ProviderLibvirt
4
+ module Action
5
+ # Setup name for domain and domain volumes.
6
+ class SetNameOfDomain
7
+ def initialize(app, _env)
8
+ @logger = Log4r::Logger.new('vagrant_libvirt::action::set_name_of_domain')
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ env[:domain_name] = build_domain_name(env)
14
+
15
+ begin
16
+ @logger.info("Looking for domain #{env[:domain_name]} through list " \
17
+ "#{env[:machine].provider.driver.connection.servers.all}")
18
+ # Check if the domain name is not already taken
19
+
20
+ domain = ProviderLibvirt::Util::Collection.find_matching(
21
+ env[:machine].provider.driver.connection.servers.all, env[:domain_name]
22
+ )
23
+ rescue Fog::Errors::Error => e
24
+ @logger.info(e.to_s)
25
+ domain = nil
26
+ end
27
+
28
+ @logger.info("Looking for domain #{env[:domain_name]}")
29
+
30
+ unless domain.nil?
31
+ raise ProviderLibvirt::Errors::DomainNameExists,
32
+ domain_name: env[:domain_name]
33
+ end
34
+
35
+ @app.call(env)
36
+ end
37
+
38
+ # build domain name
39
+ # random_hostname option avoids
40
+ # `domain about to create is already taken`
41
+ # parsable and sortable by epoch time
42
+ # @example
43
+ # development-centos-6-chef-11_1404488971_3b7a569e2fd7c554b852
44
+ # @return [String] libvirt domain name
45
+ def build_domain_name(env)
46
+ config = env[:machine].provider_config
47
+ domain_name =
48
+ if config.default_prefix.nil?
49
+ env[:root_path].basename.to_s.dup.concat('_')
50
+ elsif config.default_prefix.empty?
51
+ # don't have any prefix, not even "_"
52
+ ''
53
+ else
54
+ config.default_prefix.to_s.dup.concat('_')
55
+ end
56
+ domain_name << env[:machine].name.to_s
57
+ domain_name.gsub!(/[^-a-z0-9_\.]/i, '')
58
+ domain_name << "_#{Time.now.utc.to_i}_#{SecureRandom.hex(10)}" if config.random_hostname
59
+ domain_name
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,71 @@
1
+ require 'pathname'
2
+
3
+ require 'log4r'
4
+
5
+ module VagrantPlugins
6
+ module ProviderLibvirt
7
+ module Action
8
+ class ShareFolders
9
+ def initialize(app, _env)
10
+ @logger = Log4r::Logger.new('vagrant::action::vm::share_folders')
11
+ @app = app
12
+ end
13
+
14
+ def call(env)
15
+ @env = env
16
+
17
+ prepare_folders
18
+ create_metadata
19
+
20
+ @app.call(env)
21
+ end
22
+
23
+ # This method returns an actual list of shared
24
+ # folders to create and their proper path.
25
+ def shared_folders
26
+ {}.tap do |result|
27
+ @env[:machine].config.vm.synced_folders.each do |id, data|
28
+ # Ignore NFS shared folders
29
+ next if !data[:type] == :nfs
30
+
31
+ # This to prevent overwriting the actual shared folders data
32
+ result[id] = data.dup
33
+ end
34
+ end
35
+ end
36
+
37
+ # Prepares the shared folders by verifying they exist and creating them
38
+ # if they don't.
39
+ def prepare_folders
40
+ shared_folders.each do |_id, options|
41
+ hostpath = Pathname.new(options[:hostpath]).expand_path(@env[:root_path])
42
+
43
+ next unless !hostpath.directory? && options[:create]
44
+ # Host path doesn't exist, so let's create it.
45
+ @logger.debug("Host path doesn't exist, creating: #{hostpath}")
46
+
47
+ begin
48
+ hostpath.mkpath
49
+ rescue Errno::EACCES
50
+ raise Vagrant::Errors::SharedFolderCreateFailed,
51
+ path: hostpath.to_s
52
+ end
53
+ end
54
+ end
55
+
56
+ def create_metadata
57
+ @env[:ui].info I18n.t('vagrant.actions.vm.share_folders.creating')
58
+
59
+ folders = []
60
+ shared_folders.each do |id, data|
61
+ folders << {
62
+ name: id,
63
+ hostpath: File.expand_path(data[:hostpath], @env[:root_path]),
64
+ transient: data[:transient]
65
+ }
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,307 @@
1
+ require 'log4r'
2
+ require 'rexml/document'
3
+
4
+ module VagrantPlugins
5
+ module ProviderLibvirt
6
+ module Action
7
+ # Just start the domain.
8
+ class StartDomain
9
+ def initialize(app, _env)
10
+ @logger = Log4r::Logger.new('vagrant_libvirt::action::start_domain')
11
+ @app = app
12
+ end
13
+
14
+ def call(env)
15
+ env[:ui].info(I18n.t('vagrant_libvirt.starting_domain'))
16
+
17
+ domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
18
+ raise Errors::NoDomainError if domain.nil?
19
+ config = env[:machine].provider_config
20
+
21
+ begin
22
+ # update domain settings on change.
23
+
24
+ libvirt_domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid(env[:machine].id)
25
+
26
+ # libvirt API doesn't support modifying memory on NUMA enabled CPUs
27
+ # http://libvirt.org/git/?p=libvirt.git;a=commit;h=d174394105cf00ed266bf729ddf461c21637c736
28
+ if config.numa_nodes == nil
29
+ if config.memory.to_i * 1024 != libvirt_domain.max_memory
30
+ libvirt_domain.max_memory = config.memory.to_i * 1024
31
+ libvirt_domain.memory = libvirt_domain.max_memory
32
+ end
33
+ end
34
+ begin
35
+ # XML definition manipulation
36
+ descr = libvirt_domain.xml_desc(1)
37
+ xml_descr = REXML::Document.new descr
38
+ descr_changed = false
39
+
40
+ # additional disk bus
41
+ config.disks.each do |disk|
42
+ device = disk[:device]
43
+ bus = disk[:bus]
44
+ REXML::XPath.each(xml_descr, '/domain/devices/disk[@device="disk"]/target[@dev="' + device + '"]') do |disk_target|
45
+ next unless disk_target.attributes['bus'] != bus
46
+ descr_changed = true
47
+ disk_target.attributes['bus'] = bus
48
+ disk_target.parent.delete_element("#{disk_target.parent.xpath}/address")
49
+ end
50
+ end
51
+
52
+ # disk_bus
53
+ REXML::XPath.each(xml_descr, '/domain/devices/disk[@device="disk"]/target[@dev="vda"]') do |disk_target|
54
+ next unless disk_target.attributes['bus'] != config.disk_bus
55
+ descr_changed = true
56
+ disk_target.attributes['bus'] = config.disk_bus
57
+ disk_target.parent.delete_element("#{disk_target.parent.xpath}/address")
58
+ end
59
+
60
+ # Iterface type
61
+ unless config.nic_model_type.nil?
62
+ REXML::XPath.each(xml_descr, '/domain/devices/interface/model') do |iface_model|
63
+ if iface_model.attributes['type'] != config.nic_model_type
64
+ descr_changed = true
65
+ iface_model.attributes['type'] = config.nic_model_type
66
+ end
67
+ end
68
+ end
69
+
70
+ # vCpu count
71
+ if config.cpus.to_i != libvirt_domain.vcpus.length
72
+ descr_changed = true
73
+ REXML::XPath.first(xml_descr, '/domain/vcpu').text = config.cpus
74
+ end
75
+
76
+ # cpu_mode
77
+ cpu = REXML::XPath.first(xml_descr, '/domain/cpu')
78
+ if cpu.nil?
79
+ descr_changed = true
80
+ cpu = REXML::Element.new('cpu', REXML::XPath.first(xml_descr, '/domain'))
81
+ cpu.attributes['mode'] = config.cpu_mode
82
+ else
83
+ if cpu.attributes['mode'] != config.cpu_mode
84
+ descr_changed = true
85
+ cpu.attributes['mode'] = config.cpu_mode
86
+ end
87
+ end
88
+
89
+ if config.cpu_mode != 'host-passthrough'
90
+ cpu_model = REXML::XPath.first(xml_descr, '/domain/cpu/model')
91
+ if cpu_model.nil?
92
+ descr_changed = true
93
+ cpu_model = REXML::Element.new('model', REXML::XPath.first(xml_descr, '/domain/cpu'))
94
+ cpu_model.attributes['fallback'] = 'allow'
95
+ cpu_model.text = config.cpu_model
96
+ else
97
+ if cpu_model.text != config.cpu_model
98
+ descr_changed = true
99
+ cpu_model.text = config.cpu_model
100
+ end
101
+ if cpu_model.attributes['fallback'] != config.cpu_fallback
102
+ descr_changed = true
103
+ cpu_model.attributes['fallback'] = config.cpu_fallback
104
+ end
105
+ end
106
+ vmx_feature = REXML::XPath.first(xml_descr, '/domain/cpu/feature[@name="vmx"]')
107
+ svm_feature = REXML::XPath.first(xml_descr, '/domain/cpu/feature[@name="svm"]')
108
+ if config.nested
109
+ if vmx_feature.nil?
110
+ descr_changed = true
111
+ vmx_feature = REXML::Element.new('feature', REXML::XPath.first(xml_descr, '/domain/cpu'))
112
+ vmx_feature.attributes['policy'] = 'optional'
113
+ vmx_feature.attributes['name'] = 'vmx'
114
+ end
115
+ if svm_feature.nil?
116
+ descr_changed = true
117
+ svm_feature = REXML::Element.new('feature', REXML::XPath.first(xml_descr, '/domain/cpu'))
118
+ svm_feature.attributes['policy'] = 'optional'
119
+ svm_feature.attributes['name'] = 'svm'
120
+ end
121
+ else
122
+ unless vmx_feature.nil?
123
+ descr_changed = true
124
+ cpu.delete_element(vmx_feature)
125
+ end
126
+ unless svm_feature.nil?
127
+ descr_changed = true
128
+ cpu.delete_element(svm_feature)
129
+ end
130
+ end
131
+ elsif config.numa_nodes == nil
132
+ unless cpu.elements.to_a.empty?
133
+ descr_changed = true
134
+ cpu.elements.each do |elem|
135
+ cpu.delete_element(elem)
136
+ end
137
+ end
138
+ end
139
+
140
+ # Graphics
141
+ graphics = REXML::XPath.first(xml_descr, '/domain/devices/graphics')
142
+ if config.graphics_type != 'none'
143
+ if graphics.nil?
144
+ descr_changed = true
145
+ graphics = REXML::Element.new('graphics', REXML::XPath.first(xml_descr, '/domain/devices'))
146
+ end
147
+ if graphics.attributes['type'] != config.graphics_type
148
+ descr_changed = true
149
+ graphics.attributes['type'] = config.graphics_type
150
+ end
151
+ if graphics.attributes['listen'] != config.graphics_ip
152
+ descr_changed = true
153
+ graphics.attributes['listen'] = config.graphics_ip
154
+ graphics.delete_element('//listen')
155
+ end
156
+ if graphics.attributes['autoport'] != config.graphics_autoport
157
+ descr_changed = true
158
+ graphics.attributes['autoport'] = config.graphics_autoport
159
+ if config.graphics_autoport == 'no'
160
+ graphics.attributes['port'] = config.graphics_port
161
+ end
162
+ end
163
+ if graphics.attributes['keymap'] != config.keymap
164
+ descr_changed = true
165
+ graphics.attributes['keymap'] = config.keymap
166
+ end
167
+ if graphics.attributes['passwd'] != config.graphics_passwd
168
+ descr_changed = true
169
+ if config.graphics_passwd.nil?
170
+ graphics.attributes.delete 'passwd'
171
+ else
172
+ graphics.attributes['passwd'] = config.graphics_passwd
173
+ end
174
+ end
175
+ else
176
+ # graphics_type = none, remove entire element
177
+ graphics.parent.delete_element(graphics) unless graphics.nil?
178
+ end
179
+
180
+ # TPM
181
+ if config.tpm_path
182
+ raise Errors::FogCreateServerError, 'The TPM Path must be fully qualified' unless config.tpm_path[0].chr == '/'
183
+
184
+ tpm = REXML::XPath.first(xml_descr, '/domain/devices/tpm')
185
+ if tpm.nil?
186
+ descr_changed = true
187
+ tpm = REXML::Element.new('tpm', REXML::XPath.first(xml_descr, '/domain/devices/tpm/model'))
188
+ tpm.attributes['model'] = config.tpm_model
189
+ tpm_backend_type = tpm.add_element('backend')
190
+ tpm_backend_type.attributes['type'] = config.tpm_type
191
+ tpm_device_path = tpm_backend_type.add_element('device')
192
+ tpm_device_path.attributes['path'] = config.tpm_path
193
+ else
194
+ if tpm.attributes['model'] != config.tpm_model
195
+ descr_changed = true
196
+ tpm.attributes['model'] = config.tpm_model
197
+ end
198
+ if tpm.elements['backend'].attributes['type'] != config.tpm_type
199
+ descr_changed = true
200
+ tpm.elements['backend'].attributes['type'] = config.tpm_type
201
+ end
202
+ if tpm.elements['backend'].elements['device'].attributes['path'] != config.tpm_path
203
+ descr_changed = true
204
+ tpm.elements['backend'].elements['device'].attributes['path'] = config.tpm_path
205
+ end
206
+ end
207
+ end
208
+
209
+ # Video device
210
+ video = REXML::XPath.first(xml_descr, '/domain/devices/video')
211
+ if !video.nil? && (config.graphics_type == 'none')
212
+ # graphics_type = none, video devices are removed since there is no possible output
213
+ descr_changed = true
214
+ video.parent.delete_element(video)
215
+ else
216
+ video_model = REXML::XPath.first(xml_descr, '/domain/devices/video/model')
217
+ if video_model.nil?
218
+ video_model = REXML::Element.new('model', REXML::XPath.first(xml_descr, '/domain/devices/video'))
219
+ video_model.attributes['type'] = config.video_type
220
+ video_model.attributes['vram'] = config.video_vram
221
+ else
222
+ if video_model.attributes['type'] != config.video_type || video_model.attributes['vram'] != config.video_vram
223
+ descr_changed = true
224
+ video_model.attributes['type'] = config.video_type
225
+ video_model.attributes['vram'] = config.video_vram
226
+ end
227
+ end
228
+ end
229
+
230
+ # Sound device
231
+ if config.sound_type
232
+ sound = REXML::XPath.first(xml_descr,'/domain/devices/sound/model')
233
+ end
234
+
235
+
236
+ # dtb
237
+ if config.dtb
238
+ dtb = REXML::XPath.first(xml_descr, '/domain/os/dtb')
239
+ if dtb.nil?
240
+ descr_changed = true
241
+ dtb = REXML::Element.new('dtb', REXML::XPath.first(xml_descr, '/domain/os'))
242
+ dtb.text = config.dtb
243
+ else
244
+ if dtb.text != config.dtb
245
+ descr_changed = true
246
+ dtb.text = config.dtb
247
+ end
248
+ end
249
+ end
250
+
251
+ # kernel and initrd
252
+ if config.kernel
253
+ kernel = REXML::XPath.first(xml_descr, '/domain/os/kernel')
254
+ if kernel.nil?
255
+ descr_changed = true
256
+ kernel = REXML::Element.new('kernel', REXML::XPath.first(xml_descr, '/domain/os'))
257
+ kernel.text = config.kernel
258
+ else
259
+ if kernel.text != config.kernel
260
+ descr_changed = true
261
+ kernel.text = config.kernel
262
+ end
263
+ end
264
+ end
265
+ if config.initrd
266
+ initrd = REXML::XPath.first(xml_descr, '/domain/os/initrd')
267
+ if initrd.nil?
268
+ descr_changed = true
269
+ initrd = REXML::Element.new('initrd', REXML::XPath.first(xml_descr, '/domain/os'))
270
+ initrd.text = config.initrd
271
+ else
272
+ if initrd.text != config.initrd
273
+ descr_changed = true
274
+ initrd.text = config.initrd
275
+ end
276
+ end
277
+ end
278
+
279
+ # Apply
280
+ if descr_changed
281
+ begin
282
+ libvirt_domain.undefine
283
+ new_descr = ''
284
+ xml_descr.write new_descr
285
+ server = env[:machine].provider.driver.connection.servers.create(xml: new_descr)
286
+ rescue Fog::Errors::Error => e
287
+ server = env[:machine].provider.driver.connection.servers.create(xml: descr)
288
+ raise Errors::FogCreateServerError, error_message: e.message
289
+ end
290
+ end
291
+ rescue => e
292
+ env[:ui].error("Error when updating domain settings: #{e.message}")
293
+ end
294
+ # Autostart with host if enabled in Vagrantfile
295
+ libvirt_domain.autostart = config.autostart
296
+ # Actually start the domain
297
+ domain.start
298
+ rescue => e
299
+ raise Errors::FogError, message: e.message
300
+ end
301
+
302
+ @app.call(env)
303
+ end
304
+ end
305
+ end
306
+ end
307
+ end