vagrant-libvirt 0.0.41 → 0.0.42

Sign up to get free protection for your applications and to get access to all the features.
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