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.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.github/issue_template.md +37 -0
- data/.gitignore +21 -0
- data/.travis.yml +24 -0
- data/Gemfile +26 -0
- data/LICENSE +22 -0
- data/README.md +1380 -0
- data/Rakefile +8 -0
- data/example_box/README.md +29 -0
- data/example_box/Vagrantfile +60 -0
- data/example_box/metadata.json +5 -0
- data/lib/vagrant-libvirt.rb +29 -0
- data/lib/vagrant-libvirt/action.rb +370 -0
- data/lib/vagrant-libvirt/action/create_domain.rb +322 -0
- data/lib/vagrant-libvirt/action/create_domain_volume.rb +87 -0
- data/lib/vagrant-libvirt/action/create_network_interfaces.rb +302 -0
- data/lib/vagrant-libvirt/action/create_networks.rb +361 -0
- data/lib/vagrant-libvirt/action/destroy_domain.rb +83 -0
- data/lib/vagrant-libvirt/action/destroy_networks.rb +95 -0
- data/lib/vagrant-libvirt/action/forward_ports.rb +227 -0
- data/lib/vagrant-libvirt/action/halt_domain.rb +41 -0
- data/lib/vagrant-libvirt/action/handle_box_image.rb +156 -0
- data/lib/vagrant-libvirt/action/handle_storage_pool.rb +57 -0
- data/lib/vagrant-libvirt/action/is_created.rb +18 -0
- data/lib/vagrant-libvirt/action/is_running.rb +21 -0
- data/lib/vagrant-libvirt/action/is_suspended.rb +42 -0
- data/lib/vagrant-libvirt/action/message_already_created.rb +16 -0
- data/lib/vagrant-libvirt/action/message_not_created.rb +16 -0
- data/lib/vagrant-libvirt/action/message_not_running.rb +16 -0
- data/lib/vagrant-libvirt/action/message_not_suspended.rb +16 -0
- data/lib/vagrant-libvirt/action/message_will_not_destroy.rb +17 -0
- data/lib/vagrant-libvirt/action/package_domain.rb +105 -0
- data/lib/vagrant-libvirt/action/prepare_nfs_settings.rb +94 -0
- data/lib/vagrant-libvirt/action/prepare_nfs_valid_ids.rb +17 -0
- data/lib/vagrant-libvirt/action/prune_nfs_exports.rb +27 -0
- data/lib/vagrant-libvirt/action/read_mac_addresses.rb +40 -0
- data/lib/vagrant-libvirt/action/remove_libvirt_image.rb +20 -0
- data/lib/vagrant-libvirt/action/remove_stale_volume.rb +50 -0
- data/lib/vagrant-libvirt/action/resume_domain.rb +34 -0
- data/lib/vagrant-libvirt/action/set_boot_order.rb +109 -0
- data/lib/vagrant-libvirt/action/set_name_of_domain.rb +64 -0
- data/lib/vagrant-libvirt/action/share_folders.rb +71 -0
- data/lib/vagrant-libvirt/action/start_domain.rb +307 -0
- data/lib/vagrant-libvirt/action/suspend_domain.rb +40 -0
- data/lib/vagrant-libvirt/action/wait_till_up.rb +109 -0
- data/lib/vagrant-libvirt/cap/mount_p9.rb +42 -0
- data/lib/vagrant-libvirt/cap/nic_mac_addresses.rb +17 -0
- data/lib/vagrant-libvirt/cap/synced_folder.rb +113 -0
- data/lib/vagrant-libvirt/config.rb +746 -0
- data/lib/vagrant-libvirt/driver.rb +118 -0
- data/lib/vagrant-libvirt/errors.rb +153 -0
- data/lib/vagrant-libvirt/plugin.rb +92 -0
- data/lib/vagrant-libvirt/provider.rb +130 -0
- data/lib/vagrant-libvirt/templates/default_storage_pool.xml.erb +13 -0
- data/lib/vagrant-libvirt/templates/domain.xml.erb +244 -0
- data/lib/vagrant-libvirt/templates/private_network.xml.erb +42 -0
- data/lib/vagrant-libvirt/templates/public_interface.xml.erb +26 -0
- data/lib/vagrant-libvirt/util.rb +11 -0
- data/lib/vagrant-libvirt/util/collection.rb +19 -0
- data/lib/vagrant-libvirt/util/erb_template.rb +22 -0
- data/lib/vagrant-libvirt/util/error_codes.rb +100 -0
- data/lib/vagrant-libvirt/util/network_util.rb +151 -0
- data/lib/vagrant-libvirt/util/timer.rb +17 -0
- data/lib/vagrant-libvirt/version.rb +5 -0
- data/locales/en.yml +162 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/support/environment_helper.rb +46 -0
- data/spec/support/libvirt_context.rb +30 -0
- data/spec/support/sharedcontext.rb +34 -0
- data/spec/unit/action/destroy_domain_spec.rb +97 -0
- data/spec/unit/action/set_name_of_domain_spec.rb +21 -0
- data/spec/unit/action/wait_till_up_spec.rb +127 -0
- data/spec/unit/config_spec.rb +113 -0
- data/spec/unit/templates/domain_all_settings.xml +137 -0
- data/spec/unit/templates/domain_defaults.xml +46 -0
- data/spec/unit/templates/domain_spec.rb +84 -0
- data/tools/create_box.sh +130 -0
- data/tools/prepare_redhat_for_box.sh +119 -0
- data/vagrant-libvirt.gemspec +54 -0
- metadata +93 -3
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'log4r'
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module ProviderLibvirt
|
5
|
+
module Action
|
6
|
+
# Suspend domain.
|
7
|
+
class SuspendDomain
|
8
|
+
def initialize(app, _env)
|
9
|
+
@logger = Log4r::Logger.new('vagrant_libvirt::action::suspend_domain')
|
10
|
+
@app = app
|
11
|
+
end
|
12
|
+
|
13
|
+
# make pause
|
14
|
+
def call(env)
|
15
|
+
env[:ui].info(I18n.t('vagrant_libvirt.suspending_domain'))
|
16
|
+
|
17
|
+
domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
|
18
|
+
raise Errors::NoDomainError if domain.nil?
|
19
|
+
|
20
|
+
config = env[:machine].provider_config
|
21
|
+
if config.suspend_mode == 'managedsave'
|
22
|
+
libvirt_domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid(env[:machine].id)
|
23
|
+
begin
|
24
|
+
libvirt_domain.managed_save
|
25
|
+
rescue => e
|
26
|
+
env[:ui].error("Error doing a managed save for domain. It may have entered a paused state.
|
27
|
+
Check the output of `virsh managedsave DOMAIN_NAME --verbose` on the VM host, error: #{e.message}")
|
28
|
+
end
|
29
|
+
else
|
30
|
+
domain.suspend
|
31
|
+
end
|
32
|
+
|
33
|
+
@logger.info("Machine #{env[:machine].id} is suspended ")
|
34
|
+
|
35
|
+
@app.call(env)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'log4r'
|
2
|
+
require 'vagrant-libvirt/errors'
|
3
|
+
require 'vagrant-libvirt/util/timer'
|
4
|
+
require 'vagrant/util/retryable'
|
5
|
+
|
6
|
+
module VagrantPlugins
|
7
|
+
module ProviderLibvirt
|
8
|
+
module Action
|
9
|
+
# Wait till domain is started, till it obtains an IP address and is
|
10
|
+
# accessible via ssh.
|
11
|
+
class WaitTillUp
|
12
|
+
include Vagrant::Util::Retryable
|
13
|
+
|
14
|
+
def initialize(app, _env)
|
15
|
+
@logger = Log4r::Logger.new('vagrant_libvirt::action::wait_till_up')
|
16
|
+
@app = app
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
# Initialize metrics if they haven't been
|
21
|
+
env[:metrics] ||= {}
|
22
|
+
|
23
|
+
# Get domain object
|
24
|
+
domain = env[:machine].provider.driver.get_domain(env[:machine].id.to_s)
|
25
|
+
if domain.nil?
|
26
|
+
raise Errors::NoDomainError,
|
27
|
+
error_message: "Domain #{env[:machine].id} not found"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Wait for domain to obtain an ip address. Ip address is searched
|
31
|
+
# from arp table, either localy or remotely via ssh, if libvirt
|
32
|
+
# connection was done via ssh.
|
33
|
+
env[:ip_address] = nil
|
34
|
+
env[:metrics]['instance_ip_time'] = Util::Timer.time do
|
35
|
+
@logger.debug("Searching for IP for MAC address: #{domain.mac}")
|
36
|
+
env[:ui].info(I18n.t('vagrant_libvirt.waiting_for_ip'))
|
37
|
+
retryable(on: Fog::Errors::TimeoutError, tries: 300) do
|
38
|
+
# If we're interrupted don't worry about waiting
|
39
|
+
return terminate(env) if env[:interrupted]
|
40
|
+
|
41
|
+
# Wait for domain to obtain an ip address
|
42
|
+
domain.wait_for(2) do
|
43
|
+
addresses.each_pair do |_type, ip|
|
44
|
+
env[:ip_address] = ip[0] unless ip[0].nil?
|
45
|
+
end
|
46
|
+
!env[:ip_address].nil?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
@logger.info("Got IP address #{env[:ip_address]}")
|
51
|
+
@logger.info("Time for getting IP: #{env[:metrics]['instance_ip_time']}")
|
52
|
+
|
53
|
+
# Machine has ip address assigned, now wait till we are able to
|
54
|
+
# connect via ssh.
|
55
|
+
env[:metrics]['instance_ssh_time'] = Util::Timer.time do
|
56
|
+
env[:ui].info(I18n.t('vagrant_libvirt.waiting_for_ssh'))
|
57
|
+
retryable(on: Fog::Errors::TimeoutError, tries: 60) do
|
58
|
+
# If we're interrupted don't worry about waiting
|
59
|
+
next if env[:interrupted]
|
60
|
+
|
61
|
+
# Wait till we are able to connect via ssh.
|
62
|
+
loop do
|
63
|
+
# If we're interrupted then just back out
|
64
|
+
break if env[:interrupted]
|
65
|
+
break if env[:machine].communicate.ready?
|
66
|
+
sleep 2
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
# if interrupted above, just terminate immediately
|
71
|
+
return terminate(env) if env[:interrupted]
|
72
|
+
@logger.info("Time for SSH ready: #{env[:metrics]['instance_ssh_time']}")
|
73
|
+
|
74
|
+
# Booted and ready for use.
|
75
|
+
# env[:ui].info(I18n.t("vagrant_libvirt.ready"))
|
76
|
+
|
77
|
+
@app.call(env)
|
78
|
+
end
|
79
|
+
|
80
|
+
def recover(env)
|
81
|
+
return if env['vagrant.error'].is_a?(Vagrant::Errors::VagrantError)
|
82
|
+
|
83
|
+
# Undo the import
|
84
|
+
terminate(env)
|
85
|
+
end
|
86
|
+
|
87
|
+
def terminate(env)
|
88
|
+
if env[:machine].provider.state.id != :not_created
|
89
|
+
# If we're not supposed to destroy on error then just return
|
90
|
+
return unless env[:destroy_on_error]
|
91
|
+
|
92
|
+
if env[:halt_on_error]
|
93
|
+
halt_env = env.dup
|
94
|
+
halt_env.delete(:interrupted)
|
95
|
+
halt_env[:config_validate] = false
|
96
|
+
env[:action_runner].run(Action.action_halt, halt_env)
|
97
|
+
else
|
98
|
+
destroy_env = env.dup
|
99
|
+
destroy_env.delete(:interrupted)
|
100
|
+
destroy_env[:config_validate] = false
|
101
|
+
destroy_env[:force_confirm_destroy] = true
|
102
|
+
env[:action_runner].run(Action.action_destroy, destroy_env)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'vagrant/util/retryable'
|
3
|
+
|
4
|
+
module VagrantPlugins
|
5
|
+
module ProviderLibvirt
|
6
|
+
module Cap
|
7
|
+
class MountP9
|
8
|
+
extend Vagrant::Util::Retryable
|
9
|
+
|
10
|
+
def self.mount_p9_shared_folder(machine, folders)
|
11
|
+
folders.each do |_name, opts|
|
12
|
+
# Expand the guest path so we can handle things like "~/vagrant"
|
13
|
+
expanded_guest_path = machine.guest.capability(
|
14
|
+
:shell_expand_guest_path, opts[:guestpath]
|
15
|
+
)
|
16
|
+
|
17
|
+
# Do the actual creating and mounting
|
18
|
+
machine.communicate.sudo("mkdir -p #{expanded_guest_path}")
|
19
|
+
|
20
|
+
# Mount
|
21
|
+
mount_tag = Digest::MD5.new.update(opts[:hostpath]).to_s[0, 31]
|
22
|
+
|
23
|
+
mount_opts = '-o trans=virtio'
|
24
|
+
mount_opts += ",access=#{opts[:owner]}" if opts[:owner]
|
25
|
+
mount_opts += ",version=#{opts[:version]}" if opts[:version]
|
26
|
+
mount_opts += ",#{opts[:mount_opts]}" if opts[:mount_opts]
|
27
|
+
|
28
|
+
mount_command = "mount -t 9p #{mount_opts} '#{mount_tag}' #{expanded_guest_path}"
|
29
|
+
retryable(on: Vagrant::Errors::LinuxMountFailed,
|
30
|
+
tries: 5,
|
31
|
+
sleep: 3) do
|
32
|
+
machine.communicate.sudo('modprobe 9p')
|
33
|
+
machine.communicate.sudo('modprobe 9pnet_virtio')
|
34
|
+
machine.communicate.sudo(mount_command,
|
35
|
+
error_class: Vagrant::Errors::LinuxMountFailed)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module ProviderLibvirt
|
3
|
+
module Cap
|
4
|
+
class NicMacAddresses
|
5
|
+
def self.nic_mac_addresses(machine)
|
6
|
+
# Vagrant expects a Hash with an index starting at 1 as key
|
7
|
+
# and the mac as uppercase string without colons as value
|
8
|
+
nic_macs = {}
|
9
|
+
machine.provider.mac_addresses.each do |index, mac|
|
10
|
+
nic_macs[index + 1] = mac.upcase.delete(':')
|
11
|
+
end
|
12
|
+
nic_macs
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'log4r'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'digest/md5'
|
5
|
+
|
6
|
+
require 'vagrant/util/subprocess'
|
7
|
+
require 'vagrant/errors'
|
8
|
+
require 'vagrant-libvirt/errors'
|
9
|
+
# require_relative "helper"
|
10
|
+
|
11
|
+
module VagrantPlugins
|
12
|
+
module SyncedFolder9p
|
13
|
+
class SyncedFolder < Vagrant.plugin('2', :synced_folder)
|
14
|
+
include Vagrant::Util
|
15
|
+
include VagrantPlugins::ProviderLibvirt::Util::ErbTemplate
|
16
|
+
|
17
|
+
def initialize(*args)
|
18
|
+
super
|
19
|
+
@logger = Log4r::Logger.new('vagrant_libvirt::synced_folders::9p')
|
20
|
+
end
|
21
|
+
|
22
|
+
def usable?(machine, _raise_error = false)
|
23
|
+
# bail now if not using libvirt since checking version would throw error
|
24
|
+
return false unless machine.provider_name == :libvirt
|
25
|
+
|
26
|
+
# <filesystem/> support in device attach/detach introduced in 1.2.2
|
27
|
+
# version number format is major * 1,000,000 + minor * 1,000 + release
|
28
|
+
libvirt_version = machine.provider.driver.connection.client.libversion
|
29
|
+
libvirt_version >= 1_002_002
|
30
|
+
end
|
31
|
+
|
32
|
+
def prepare(machine, folders, _opts)
|
33
|
+
raise Vagrant::Errors::Error('No libvirt connection') if machine.provider.driver.connection.nil?
|
34
|
+
@conn = machine.provider.driver.connection.client
|
35
|
+
|
36
|
+
begin
|
37
|
+
# loop through folders
|
38
|
+
folders.each do |id, folder_opts|
|
39
|
+
folder_opts.merge!(target: id,
|
40
|
+
accessmode: 'passthrough',
|
41
|
+
mount: true,
|
42
|
+
readonly: nil) { |_k, ov, _nv| ov }
|
43
|
+
|
44
|
+
mount_tag = Digest::MD5.new.update(folder_opts[:hostpath]).to_s[0, 31]
|
45
|
+
folder_opts[:mount_tag] = mount_tag
|
46
|
+
|
47
|
+
machine.ui.info "================\nMachine id: #{machine.id}\nShould be mounting folders\n #{id}, opts: #{folder_opts}"
|
48
|
+
|
49
|
+
#xml = to_xml('filesystem', folder_opts)
|
50
|
+
xml = Nokogiri::XML::Builder.new do |xml|
|
51
|
+
xml.filesystem(type: 'mount', accessmode: folder_opts[:accessmode]) do
|
52
|
+
xml.driver(type: 'path', wrpolicy: 'immediate')
|
53
|
+
xml.source(dir: folder_opts[:hostpath])
|
54
|
+
xml.target(dir: mount_tag)
|
55
|
+
xml.readonly unless folder_opts[:readonly].nil?
|
56
|
+
end
|
57
|
+
end.to_xml(
|
58
|
+
save_with: Nokogiri::XML::Node::SaveOptions::NO_DECLARATION |
|
59
|
+
Nokogiri::XML::Node::SaveOptions::NO_EMPTY_TAGS |
|
60
|
+
Nokogiri::XML::Node::SaveOptions::FORMAT
|
61
|
+
)
|
62
|
+
# puts "<<<<< XML:\n #{xml}\n >>>>>"
|
63
|
+
@conn.lookup_domain_by_uuid(machine.id).attach_device(xml, 0)
|
64
|
+
end
|
65
|
+
rescue => e
|
66
|
+
machine.ui.error("could not attach device because: #{e}")
|
67
|
+
raise VagrantPlugins::ProviderLibvirt::Errors::AttachDeviceError,
|
68
|
+
error_message: e.message
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# TODO: once up, mount folders
|
73
|
+
def enable(machine, folders, _opts)
|
74
|
+
# Go through each folder and mount
|
75
|
+
machine.ui.info('mounting p9 share in guest')
|
76
|
+
# Only mount folders that have a guest path specified.
|
77
|
+
mount_folders = {}
|
78
|
+
folders.each do |id, opts|
|
79
|
+
next unless opts[:mount] && opts[:guestpath] && !opts[:guestpath].empty?
|
80
|
+
mount_folders[id] = opts.dup
|
81
|
+
# merge common options if not given
|
82
|
+
mount_folders[id].merge!(version: '9p2000.L') { |_k, ov, _nv| ov }
|
83
|
+
end
|
84
|
+
# Mount the actual folder
|
85
|
+
machine.guest.capability(
|
86
|
+
:mount_p9_shared_folder, mount_folders
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
def cleanup(machine, _opts)
|
91
|
+
if machine.provider.driver.connection.nil?
|
92
|
+
raise Vagrant::Errors::Error('No libvirt connection')
|
93
|
+
end
|
94
|
+
@conn = machine.provider.driver.connection.client
|
95
|
+
begin
|
96
|
+
if machine.id && machine.id != ''
|
97
|
+
dom = @conn.lookup_domain_by_uuid(machine.id)
|
98
|
+
Nokogiri::XML(dom.xml_desc).xpath(
|
99
|
+
'/domain/devices/filesystem'
|
100
|
+
).each do |xml|
|
101
|
+
dom.detach_device(xml.to_s)
|
102
|
+
machine.ui.info 'Cleaned up shared folders'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
rescue => e
|
106
|
+
machine.ui.error("could not detach device because: #{e}")
|
107
|
+
raise VagrantPlugins::ProviderLibvirt::Errors::DetachDeviceError,
|
108
|
+
error_message: e.message
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,746 @@
|
|
1
|
+
require 'vagrant'
|
2
|
+
|
3
|
+
class Numeric
|
4
|
+
Alphabet = ('a'..'z').to_a
|
5
|
+
def vdev
|
6
|
+
s = ''
|
7
|
+
q = self
|
8
|
+
(q, r = (q - 1).divmod(26)) && s.prepend(Alphabet[r]) until q.zero?
|
9
|
+
'vd' + s
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module VagrantPlugins
|
14
|
+
module ProviderLibvirt
|
15
|
+
class Config < Vagrant.plugin('2', :config)
|
16
|
+
# manually specify URI
|
17
|
+
# will supercede most other options if provided
|
18
|
+
attr_accessor :uri
|
19
|
+
|
20
|
+
# A hypervisor name to access via Libvirt.
|
21
|
+
attr_accessor :driver
|
22
|
+
|
23
|
+
# The name of the server, where libvirtd is running.
|
24
|
+
attr_accessor :host
|
25
|
+
|
26
|
+
# If use ssh tunnel to connect to Libvirt.
|
27
|
+
attr_accessor :connect_via_ssh
|
28
|
+
# Path towards the libvirt socket
|
29
|
+
attr_accessor :socket
|
30
|
+
|
31
|
+
# The username to access Libvirt.
|
32
|
+
attr_accessor :username
|
33
|
+
|
34
|
+
# Password for Libvirt connection.
|
35
|
+
attr_accessor :password
|
36
|
+
|
37
|
+
# ID SSH key file
|
38
|
+
attr_accessor :id_ssh_key_file
|
39
|
+
|
40
|
+
# Libvirt storage pool name, where box image and instance snapshots will
|
41
|
+
# be stored.
|
42
|
+
attr_accessor :storage_pool_name
|
43
|
+
|
44
|
+
# Turn on to prevent hostname conflicts
|
45
|
+
attr_accessor :random_hostname
|
46
|
+
|
47
|
+
# Libvirt default network
|
48
|
+
attr_accessor :management_network_name
|
49
|
+
attr_accessor :management_network_address
|
50
|
+
attr_accessor :management_network_mode
|
51
|
+
attr_accessor :management_network_mac
|
52
|
+
attr_accessor :management_network_guest_ipv6
|
53
|
+
attr_accessor :management_network_autostart
|
54
|
+
attr_accessor :management_network_pci_bus
|
55
|
+
attr_accessor :management_network_pci_slot
|
56
|
+
|
57
|
+
# Default host prefix (alternative to use project folder name)
|
58
|
+
attr_accessor :default_prefix
|
59
|
+
|
60
|
+
# Domain specific settings used while creating new domain.
|
61
|
+
attr_accessor :uuid
|
62
|
+
attr_accessor :memory
|
63
|
+
attr_accessor :memory_backing
|
64
|
+
attr_accessor :channel
|
65
|
+
attr_accessor :cpus
|
66
|
+
attr_accessor :cpu_mode
|
67
|
+
attr_accessor :cpu_model
|
68
|
+
attr_accessor :cpu_fallback
|
69
|
+
attr_accessor :cpu_features
|
70
|
+
attr_accessor :cpu_topology
|
71
|
+
attr_accessor :features
|
72
|
+
attr_accessor :numa_nodes
|
73
|
+
attr_accessor :loader
|
74
|
+
attr_accessor :boot_order
|
75
|
+
attr_accessor :machine_type
|
76
|
+
attr_accessor :machine_arch
|
77
|
+
attr_accessor :machine_virtual_size
|
78
|
+
attr_accessor :disk_bus
|
79
|
+
attr_accessor :disk_device
|
80
|
+
attr_accessor :nic_model_type
|
81
|
+
attr_accessor :nested
|
82
|
+
attr_accessor :volume_cache
|
83
|
+
attr_accessor :kernel
|
84
|
+
attr_accessor :cmd_line
|
85
|
+
attr_accessor :initrd
|
86
|
+
attr_accessor :dtb
|
87
|
+
attr_accessor :emulator_path
|
88
|
+
attr_accessor :graphics_type
|
89
|
+
attr_accessor :graphics_autoport
|
90
|
+
attr_accessor :graphics_port
|
91
|
+
attr_accessor :graphics_passwd
|
92
|
+
attr_accessor :graphics_ip
|
93
|
+
attr_accessor :video_type
|
94
|
+
attr_accessor :video_vram
|
95
|
+
attr_accessor :keymap
|
96
|
+
attr_accessor :kvm_hidden
|
97
|
+
attr_accessor :sound_type
|
98
|
+
|
99
|
+
# Sets the information for connecting to a host TPM device
|
100
|
+
# Only supports socket-based TPMs
|
101
|
+
attr_accessor :tpm_model
|
102
|
+
attr_accessor :tpm_type
|
103
|
+
attr_accessor :tpm_path
|
104
|
+
|
105
|
+
# Sets the max number of NICs that can be created
|
106
|
+
# Default set to 8. Don't change the default unless you know
|
107
|
+
# what are doing
|
108
|
+
attr_accessor :nic_adapter_count
|
109
|
+
|
110
|
+
# Storage
|
111
|
+
attr_accessor :disks
|
112
|
+
attr_accessor :cdroms
|
113
|
+
|
114
|
+
# Inputs
|
115
|
+
attr_accessor :inputs
|
116
|
+
|
117
|
+
# Channels
|
118
|
+
attr_accessor :channels
|
119
|
+
|
120
|
+
# PCI device passthrough
|
121
|
+
attr_accessor :pcis
|
122
|
+
|
123
|
+
# Random number device passthrough
|
124
|
+
attr_accessor :rng
|
125
|
+
|
126
|
+
# Watchdog device
|
127
|
+
attr_accessor :watchdog_dev
|
128
|
+
|
129
|
+
# USB device passthrough
|
130
|
+
attr_accessor :usbs
|
131
|
+
|
132
|
+
# Redirected devices
|
133
|
+
attr_accessor :redirdevs
|
134
|
+
attr_accessor :redirfilters
|
135
|
+
|
136
|
+
# smartcard device
|
137
|
+
attr_accessor :smartcard_dev
|
138
|
+
|
139
|
+
# Suspend mode
|
140
|
+
attr_accessor :suspend_mode
|
141
|
+
|
142
|
+
# Autostart
|
143
|
+
attr_accessor :autostart
|
144
|
+
|
145
|
+
# Attach mgmt network
|
146
|
+
attr_accessor :mgmt_attach
|
147
|
+
|
148
|
+
# Additional qemuargs arguments
|
149
|
+
attr_accessor :qemu_args
|
150
|
+
|
151
|
+
def initialize
|
152
|
+
@uri = UNSET_VALUE
|
153
|
+
@driver = UNSET_VALUE
|
154
|
+
@host = UNSET_VALUE
|
155
|
+
@connect_via_ssh = UNSET_VALUE
|
156
|
+
@username = UNSET_VALUE
|
157
|
+
@password = UNSET_VALUE
|
158
|
+
@id_ssh_key_file = UNSET_VALUE
|
159
|
+
@storage_pool_name = UNSET_VALUE
|
160
|
+
@random_hostname = UNSET_VALUE
|
161
|
+
@management_network_name = UNSET_VALUE
|
162
|
+
@management_network_address = UNSET_VALUE
|
163
|
+
@management_network_mode = UNSET_VALUE
|
164
|
+
@management_network_mac = UNSET_VALUE
|
165
|
+
@management_network_guest_ipv6 = UNSET_VALUE
|
166
|
+
@management_network_autostart = UNSET_VALUE
|
167
|
+
@management_network_pci_slot = UNSET_VALUE
|
168
|
+
@management_network_pci_bus = UNSET_VALUE
|
169
|
+
|
170
|
+
# Domain specific settings.
|
171
|
+
@uuid = UNSET_VALUE
|
172
|
+
@memory = UNSET_VALUE
|
173
|
+
@memory_backing = UNSET_VALUE
|
174
|
+
@cpus = UNSET_VALUE
|
175
|
+
@cpu_mode = UNSET_VALUE
|
176
|
+
@cpu_model = UNSET_VALUE
|
177
|
+
@cpu_fallback = UNSET_VALUE
|
178
|
+
@cpu_features = UNSET_VALUE
|
179
|
+
@cpu_topology = UNSET_VALUE
|
180
|
+
@features = UNSET_VALUE
|
181
|
+
@numa_nodes = UNSET_VALUE
|
182
|
+
@loader = UNSET_VALUE
|
183
|
+
@machine_type = UNSET_VALUE
|
184
|
+
@machine_arch = UNSET_VALUE
|
185
|
+
@machine_virtual_size = UNSET_VALUE
|
186
|
+
@disk_bus = UNSET_VALUE
|
187
|
+
@disk_device = UNSET_VALUE
|
188
|
+
@nic_model_type = UNSET_VALUE
|
189
|
+
@nested = UNSET_VALUE
|
190
|
+
@volume_cache = UNSET_VALUE
|
191
|
+
@kernel = UNSET_VALUE
|
192
|
+
@initrd = UNSET_VALUE
|
193
|
+
@dtb = UNSET_VALUE
|
194
|
+
@cmd_line = UNSET_VALUE
|
195
|
+
@emulator_path = UNSET_VALUE
|
196
|
+
@graphics_type = UNSET_VALUE
|
197
|
+
@graphics_autoport = UNSET_VALUE
|
198
|
+
@graphics_port = UNSET_VALUE
|
199
|
+
@graphics_ip = UNSET_VALUE
|
200
|
+
@graphics_passwd = UNSET_VALUE
|
201
|
+
@video_type = UNSET_VALUE
|
202
|
+
@video_vram = UNSET_VALUE
|
203
|
+
@sound_type = UNSET_VALUE
|
204
|
+
@keymap = UNSET_VALUE
|
205
|
+
@kvm_hidden = UNSET_VALUE
|
206
|
+
|
207
|
+
@tpm_model = UNSET_VALUE
|
208
|
+
@tpm_type = UNSET_VALUE
|
209
|
+
@tpm_path = UNSET_VALUE
|
210
|
+
|
211
|
+
@nic_adapter_count = UNSET_VALUE
|
212
|
+
|
213
|
+
# Boot order
|
214
|
+
@boot_order = []
|
215
|
+
# Storage
|
216
|
+
@disks = []
|
217
|
+
@cdroms = []
|
218
|
+
|
219
|
+
# Inputs
|
220
|
+
@inputs = UNSET_VALUE
|
221
|
+
|
222
|
+
# Channels
|
223
|
+
@channels = UNSET_VALUE
|
224
|
+
|
225
|
+
# PCI device passthrough
|
226
|
+
@pcis = UNSET_VALUE
|
227
|
+
|
228
|
+
# Random number device passthrough
|
229
|
+
@rng = UNSET_VALUE
|
230
|
+
|
231
|
+
# Watchdog device
|
232
|
+
@watchdog_dev = UNSET_VALUE
|
233
|
+
|
234
|
+
# USB device passthrough
|
235
|
+
@usbs = UNSET_VALUE
|
236
|
+
|
237
|
+
# Redirected devices
|
238
|
+
@redirdevs = UNSET_VALUE
|
239
|
+
@redirfilters = UNSET_VALUE
|
240
|
+
|
241
|
+
# smartcard device
|
242
|
+
@smartcard_dev = UNSET_VALUE
|
243
|
+
|
244
|
+
# Suspend mode
|
245
|
+
@suspend_mode = UNSET_VALUE
|
246
|
+
|
247
|
+
# Autostart
|
248
|
+
@autostart = UNSET_VALUE
|
249
|
+
|
250
|
+
# Attach mgmt network
|
251
|
+
@mgmt_attach = UNSET_VALUE
|
252
|
+
|
253
|
+
@qemu_args = []
|
254
|
+
end
|
255
|
+
|
256
|
+
def boot(device)
|
257
|
+
@boot_order << device # append
|
258
|
+
end
|
259
|
+
|
260
|
+
def _get_device(disks)
|
261
|
+
# skip existing devices and also the first one (vda)
|
262
|
+
exist = disks.collect { |x| x[:device] } + [1.vdev.to_s]
|
263
|
+
skip = 1 # we're 1 based, not 0 based...
|
264
|
+
loop do
|
265
|
+
dev = skip.vdev # get lettered device
|
266
|
+
return dev unless exist.include?(dev)
|
267
|
+
skip += 1
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def _get_cdrom_dev(cdroms)
|
272
|
+
exist = Hash[cdroms.collect { |x| [x[:dev], true] }]
|
273
|
+
# hda - hdc
|
274
|
+
curr = 'a'.ord
|
275
|
+
while curr <= 'd'.ord
|
276
|
+
dev = 'hd' + curr.chr
|
277
|
+
if exist[dev]
|
278
|
+
curr += 1
|
279
|
+
next
|
280
|
+
else
|
281
|
+
return dev
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# is it better to raise our own error, or let libvirt cause the exception?
|
286
|
+
raise 'Only four cdroms may be attached at a time'
|
287
|
+
end
|
288
|
+
|
289
|
+
def _generate_numa
|
290
|
+
@numa_nodes.collect { |x|
|
291
|
+
# Perform some validation of cpu values
|
292
|
+
unless x[:cpus] =~ /^\d+-\d+$/
|
293
|
+
raise 'numa_nodes[:cpus] must be in format "integer-integer"'
|
294
|
+
end
|
295
|
+
|
296
|
+
# Convert to KiB
|
297
|
+
x[:memory] = x[:memory].to_i * 1024
|
298
|
+
}
|
299
|
+
|
300
|
+
# Grab the value of the last @numa_nodes[:cpus] and verify @cpus matches
|
301
|
+
# Note: [:cpus] is zero based and @cpus is not, so we need to +1
|
302
|
+
last_cpu = @numa_nodes.last[:cpus]
|
303
|
+
last_cpu = last_cpu.scan(/\d+$/)[0]
|
304
|
+
last_cpu = last_cpu.to_i + 1
|
305
|
+
|
306
|
+
if @cpus != last_cpu.to_i
|
307
|
+
raise 'The total number of numa_nodes[:cpus] must equal config.cpus'
|
308
|
+
end
|
309
|
+
|
310
|
+
@numa_nodes
|
311
|
+
end
|
312
|
+
|
313
|
+
def cpu_feature(options = {})
|
314
|
+
if options[:name].nil? || options[:policy].nil?
|
315
|
+
raise 'CPU Feature name AND policy must be specified'
|
316
|
+
end
|
317
|
+
|
318
|
+
@cpu_features = [] if @cpu_features == UNSET_VALUE
|
319
|
+
|
320
|
+
@cpu_features.push(name: options[:name],
|
321
|
+
policy: options[:policy])
|
322
|
+
end
|
323
|
+
|
324
|
+
def cputopology(options = {})
|
325
|
+
if options[:sockets].nil? || options[:cores].nil? || options[:threads].nil?
|
326
|
+
raise 'CPU topology must have all of sockets, cores and threads specified'
|
327
|
+
end
|
328
|
+
|
329
|
+
if @cpu_topology == UNSET_VALUE
|
330
|
+
@cpu_topology = {}
|
331
|
+
end
|
332
|
+
|
333
|
+
@cpu_topology[:sockets] = options[:sockets]
|
334
|
+
@cpu_topology[:cores] = options[:cores]
|
335
|
+
@cpu_topology[:threads] = options[:threads]
|
336
|
+
end
|
337
|
+
|
338
|
+
def memorybacking(option, config = {})
|
339
|
+
case option
|
340
|
+
when :source
|
341
|
+
raise 'Source type must be specified' if config[:type].nil?
|
342
|
+
when :access
|
343
|
+
raise 'Access mode must be specified' if config[:mode].nil?
|
344
|
+
when :allocation
|
345
|
+
raise 'Allocation mode must be specified' if config[:mode].nil?
|
346
|
+
end
|
347
|
+
|
348
|
+
@memory_backing = [] if @memory_backing == UNSET_VALUE
|
349
|
+
@memory_backing.push(name: option,
|
350
|
+
config: config)
|
351
|
+
end
|
352
|
+
|
353
|
+
def input(options = {})
|
354
|
+
if options[:type].nil? || options[:bus].nil?
|
355
|
+
raise 'Input type AND bus must be specified'
|
356
|
+
end
|
357
|
+
|
358
|
+
@inputs = [] if @inputs == UNSET_VALUE
|
359
|
+
|
360
|
+
@inputs.push(type: options[:type],
|
361
|
+
bus: options[:bus])
|
362
|
+
end
|
363
|
+
|
364
|
+
def channel(options = {})
|
365
|
+
if options[:type].nil?
|
366
|
+
raise 'Channel type must be specified.'
|
367
|
+
elsif options[:type] == 'unix' && options[:target_type] == 'guestfwd'
|
368
|
+
# Guest forwarding requires a target (ip address) and a port
|
369
|
+
if options[:target_address].nil? || options[:target_port].nil? ||
|
370
|
+
options[:source_path].nil?
|
371
|
+
raise 'guestfwd requires target_address, target_port and source_path'
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
@channels = [] if @channels == UNSET_VALUE
|
376
|
+
|
377
|
+
@channels.push(type: options[:type],
|
378
|
+
source_mode: options[:source_mode],
|
379
|
+
source_path: options[:source_path],
|
380
|
+
target_address: options[:target_address],
|
381
|
+
target_name: options[:target_name],
|
382
|
+
target_port: options[:target_port],
|
383
|
+
target_type: options[:target_type])
|
384
|
+
end
|
385
|
+
|
386
|
+
def random(options = {})
|
387
|
+
if !options[:model].nil? && options[:model] != 'random'
|
388
|
+
raise 'The only supported rng backend is "random".'
|
389
|
+
end
|
390
|
+
|
391
|
+
@rng = {} if @rng == UNSET_VALUE
|
392
|
+
|
393
|
+
@rng[:model] = options[:model]
|
394
|
+
end
|
395
|
+
|
396
|
+
def pci(options = {})
|
397
|
+
if options[:bus].nil? || options[:slot].nil? || options[:function].nil?
|
398
|
+
raise 'Bus AND slot AND function must be specified. Check `lspci` for that numbers.'
|
399
|
+
end
|
400
|
+
|
401
|
+
@pcis = [] if @pcis == UNSET_VALUE
|
402
|
+
|
403
|
+
@pcis.push(bus: options[:bus],
|
404
|
+
slot: options[:slot],
|
405
|
+
function: options[:function])
|
406
|
+
end
|
407
|
+
|
408
|
+
def watchdog(options = {})
|
409
|
+
if options[:model].nil?
|
410
|
+
raise 'Model must be specified.'
|
411
|
+
end
|
412
|
+
|
413
|
+
if @watchdog_dev == UNSET_VALUE
|
414
|
+
@watchdog_dev = {}
|
415
|
+
end
|
416
|
+
|
417
|
+
@watchdog_dev[:model] = options[:model]
|
418
|
+
@watchdog_dev[:action] = options[:action] || 'reset'
|
419
|
+
end
|
420
|
+
|
421
|
+
|
422
|
+
def usb(options = {})
|
423
|
+
if (options[:bus].nil? || options[:device].nil?) && options[:vendor].nil? && options[:product].nil?
|
424
|
+
raise 'Bus and device and/or vendor and/or product must be specified. Check `lsusb` for these.'
|
425
|
+
end
|
426
|
+
|
427
|
+
@usbs = [] if @usbs == UNSET_VALUE
|
428
|
+
|
429
|
+
@usbs.push(bus: options[:bus],
|
430
|
+
device: options[:device],
|
431
|
+
vendor: options[:vendor],
|
432
|
+
product: options[:product],
|
433
|
+
startupPolicy: options[:startupPolicy])
|
434
|
+
end
|
435
|
+
|
436
|
+
def redirdev(options = {})
|
437
|
+
raise 'Type must be specified.' if options[:type].nil?
|
438
|
+
|
439
|
+
@redirdevs = [] if @redirdevs == UNSET_VALUE
|
440
|
+
|
441
|
+
@redirdevs.push(type: options[:type])
|
442
|
+
end
|
443
|
+
|
444
|
+
def redirfilter(options = {})
|
445
|
+
raise 'Option allow must be specified.' if options[:allow].nil?
|
446
|
+
|
447
|
+
@redirfilters = [] if @redirfilters == UNSET_VALUE
|
448
|
+
|
449
|
+
@redirfilters.push(class: options[:class] || -1,
|
450
|
+
vendor: options[:class] || -1,
|
451
|
+
product: options[:class] || -1,
|
452
|
+
version: options[:class] || -1,
|
453
|
+
allow: options[:allow])
|
454
|
+
end
|
455
|
+
|
456
|
+
def smartcard(options = {})
|
457
|
+
if options[:mode].nil?
|
458
|
+
raise 'Option mode must be specified.'
|
459
|
+
elsif options[:mode] != 'passthrough'
|
460
|
+
raise 'Currently only passthrough mode is supported!'
|
461
|
+
elsif options[:type] == 'tcp' && (options[:source_mode].nil? || options[:source_host].nil? || options[:source_service].nil?)
|
462
|
+
raise 'If using type "tcp", option "source_mode", "source_host" and "source_service" must be specified.'
|
463
|
+
end
|
464
|
+
|
465
|
+
if @smartcard_dev == UNSET_VALUE
|
466
|
+
@smartcard_dev = {}
|
467
|
+
end
|
468
|
+
|
469
|
+
@smartcard_dev[:mode] = options[:mode]
|
470
|
+
@smartcard_dev[:type] = options[:type] || 'spicevmc'
|
471
|
+
@smartcard_dev[:source_mode] = options[:source_mode] if @smartcard_dev[:type] == 'tcp'
|
472
|
+
@smartcard_dev[:source_host] = options[:source_host] if @smartcard_dev[:type] == 'tcp'
|
473
|
+
@smartcard_dev[:source_service] = options[:source_service] if @smartcard_dev[:type] == 'tcp'
|
474
|
+
end
|
475
|
+
|
476
|
+
# NOTE: this will run twice for each time it's needed- keep it idempotent
|
477
|
+
def storage(storage_type, options = {})
|
478
|
+
if storage_type == :file
|
479
|
+
if options[:device] == :cdrom
|
480
|
+
_handle_cdrom_storage(options)
|
481
|
+
else
|
482
|
+
_handle_disk_storage(options)
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
def _handle_cdrom_storage(options = {})
|
488
|
+
# <disk type="file" device="cdrom">
|
489
|
+
# <source file="/home/user/virtio-win-0.1-100.iso"/>
|
490
|
+
# <target dev="hdc"/>
|
491
|
+
# <readonly/>
|
492
|
+
# <address type='drive' controller='0' bus='1' target='0' unit='0'/>
|
493
|
+
# </disk>
|
494
|
+
#
|
495
|
+
# note the target dev will need to be changed with each cdrom drive (hdc, hdd, etc),
|
496
|
+
# as will the address unit number (unit=0, unit=1, etc)
|
497
|
+
|
498
|
+
options = {
|
499
|
+
bus: 'ide',
|
500
|
+
path: nil
|
501
|
+
}.merge(options)
|
502
|
+
|
503
|
+
cdrom = {
|
504
|
+
dev: options[:dev],
|
505
|
+
bus: options[:bus],
|
506
|
+
path: options[:path]
|
507
|
+
}
|
508
|
+
|
509
|
+
@cdroms << cdrom
|
510
|
+
end
|
511
|
+
|
512
|
+
def _handle_disk_storage(options = {})
|
513
|
+
options = {
|
514
|
+
type: 'qcow2',
|
515
|
+
size: '10G', # matches the fog default
|
516
|
+
path: nil,
|
517
|
+
bus: 'virtio'
|
518
|
+
}.merge(options)
|
519
|
+
|
520
|
+
disk = {
|
521
|
+
device: options[:device],
|
522
|
+
type: options[:type],
|
523
|
+
size: options[:size],
|
524
|
+
path: options[:path],
|
525
|
+
bus: options[:bus],
|
526
|
+
cache: options[:cache] || 'default',
|
527
|
+
allow_existing: options[:allow_existing],
|
528
|
+
shareable: options[:shareable],
|
529
|
+
serial: options[:serial]
|
530
|
+
}
|
531
|
+
|
532
|
+
@disks << disk # append
|
533
|
+
end
|
534
|
+
|
535
|
+
def qemuargs(options = {})
|
536
|
+
@qemu_args << options if options[:value]
|
537
|
+
end
|
538
|
+
|
539
|
+
# code to generate URI from a config moved out of the connect action
|
540
|
+
def _generate_uri
|
541
|
+
# builds the libvirt connection URI from the given driver config
|
542
|
+
# Setup connection uri.
|
543
|
+
uri = @driver.dup
|
544
|
+
virt_path = case uri
|
545
|
+
when 'qemu', 'openvz', 'uml', 'phyp', 'parallels', 'kvm'
|
546
|
+
'/system'
|
547
|
+
when '@en', 'esx'
|
548
|
+
'/'
|
549
|
+
when 'vbox', 'vmwarews', 'hyperv'
|
550
|
+
'/session'
|
551
|
+
else
|
552
|
+
raise "Require specify driver #{uri}"
|
553
|
+
end
|
554
|
+
if uri == 'kvm'
|
555
|
+
uri = 'qemu' # use qemu uri for kvm domain type
|
556
|
+
end
|
557
|
+
|
558
|
+
if @connect_via_ssh
|
559
|
+
uri << '+ssh://'
|
560
|
+
uri << @username + '@' if @username
|
561
|
+
|
562
|
+
uri << if @host
|
563
|
+
@host
|
564
|
+
else
|
565
|
+
'localhost'
|
566
|
+
end
|
567
|
+
else
|
568
|
+
uri << '://'
|
569
|
+
uri << @host if @host
|
570
|
+
end
|
571
|
+
|
572
|
+
uri << virt_path
|
573
|
+
uri << '?no_verify=1'
|
574
|
+
|
575
|
+
if @id_ssh_key_file
|
576
|
+
# set ssh key for access to libvirt host
|
577
|
+
uri << "\&keyfile="
|
578
|
+
# if no slash, prepend $HOME/.ssh/
|
579
|
+
@id_ssh_key_file.prepend("#{`echo ${HOME}`.chomp}/.ssh/") if @id_ssh_key_file !~ /\A\//
|
580
|
+
uri << @id_ssh_key_file
|
581
|
+
end
|
582
|
+
# set path to libvirt socket
|
583
|
+
uri << "\&socket=" + @socket if @socket
|
584
|
+
uri
|
585
|
+
end
|
586
|
+
|
587
|
+
def finalize!
|
588
|
+
@driver = 'kvm' if @driver == UNSET_VALUE
|
589
|
+
@host = nil if @host == UNSET_VALUE
|
590
|
+
@connect_via_ssh = false if @connect_via_ssh == UNSET_VALUE
|
591
|
+
@username = nil if @username == UNSET_VALUE
|
592
|
+
@password = nil if @password == UNSET_VALUE
|
593
|
+
@id_ssh_key_file = 'id_rsa' if @id_ssh_key_file == UNSET_VALUE
|
594
|
+
@storage_pool_name = 'default' if @storage_pool_name == UNSET_VALUE
|
595
|
+
@random_hostname = false if @random_hostname == UNSET_VALUE
|
596
|
+
@management_network_name = 'vagrant-libvirt' if @management_network_name == UNSET_VALUE
|
597
|
+
@management_network_address = '192.168.121.0/24' if @management_network_address == UNSET_VALUE
|
598
|
+
@management_network_mode = 'nat' if @management_network_mode == UNSET_VALUE
|
599
|
+
@management_network_mac = nil if @management_network_mac == UNSET_VALUE
|
600
|
+
@management_network_guest_ipv6 = 'yes' if @management_network_guest_ipv6 == UNSET_VALUE
|
601
|
+
@management_network_autostart = false if @management_network_autostart == UNSET_VALUE
|
602
|
+
@management_network_pci_bus = nil if @management_network_pci_bus == UNSET_VALUE
|
603
|
+
@management_network_pci_slot = nil if @management_network_pci_slot == UNSET_VALUE
|
604
|
+
|
605
|
+
# generate a URI if none is supplied
|
606
|
+
@uri = _generate_uri if @uri == UNSET_VALUE
|
607
|
+
|
608
|
+
# Domain specific settings.
|
609
|
+
@uuid = '' if @uuid == UNSET_VALUE
|
610
|
+
@memory = 512 if @memory == UNSET_VALUE
|
611
|
+
@memory_backing = [] if @memory_backing == UNSET_VALUE
|
612
|
+
@cpus = 1 if @cpus == UNSET_VALUE
|
613
|
+
@cpu_mode = 'host-model' if @cpu_mode == UNSET_VALUE
|
614
|
+
@cpu_model = if (@cpu_model == UNSET_VALUE) && (@cpu_mode == 'custom')
|
615
|
+
'qemu64'
|
616
|
+
elsif @cpu_mode != 'custom'
|
617
|
+
''
|
618
|
+
end
|
619
|
+
@cpu_topology = {} if @cpu_topology == UNSET_VALUE
|
620
|
+
@cpu_fallback = 'allow' if @cpu_fallback == UNSET_VALUE
|
621
|
+
@cpu_features = [] if @cpu_features == UNSET_VALUE
|
622
|
+
@features = ['acpi','apic','pae'] if @features == UNSET_VALUE
|
623
|
+
@numa_nodes = @numa_nodes == UNSET_VALUE ? nil : _generate_numa
|
624
|
+
@loader = nil if @loader == UNSET_VALUE
|
625
|
+
@machine_type = nil if @machine_type == UNSET_VALUE
|
626
|
+
@machine_arch = nil if @machine_arch == UNSET_VALUE
|
627
|
+
@machine_virtual_size = nil if @machine_virtual_size == UNSET_VALUE
|
628
|
+
@disk_bus = 'virtio' if @disk_bus == UNSET_VALUE
|
629
|
+
@disk_device = 'vda' if @disk_device == UNSET_VALUE
|
630
|
+
@nic_model_type = nil if @nic_model_type == UNSET_VALUE
|
631
|
+
@nested = false if @nested == UNSET_VALUE
|
632
|
+
@volume_cache = 'default' if @volume_cache == UNSET_VALUE
|
633
|
+
@kernel = nil if @kernel == UNSET_VALUE
|
634
|
+
@cmd_line = '' if @cmd_line == UNSET_VALUE
|
635
|
+
@initrd = '' if @initrd == UNSET_VALUE
|
636
|
+
@dtb = nil if @dtb == UNSET_VALUE
|
637
|
+
@graphics_type = 'vnc' if @graphics_type == UNSET_VALUE
|
638
|
+
@graphics_autoport = 'yes' if @graphics_port == UNSET_VALUE
|
639
|
+
@graphics_autoport = 'no' if @graphics_port != UNSET_VALUE
|
640
|
+
if (@graphics_type != 'vnc' && @graphics_type != 'spice') ||
|
641
|
+
@graphics_passwd == UNSET_VALUE
|
642
|
+
@graphics_passwd = nil
|
643
|
+
end
|
644
|
+
@graphics_port = -1 if @graphics_port == UNSET_VALUE
|
645
|
+
@graphics_ip = '127.0.0.1' if @graphics_ip == UNSET_VALUE
|
646
|
+
@video_type = 'cirrus' if @video_type == UNSET_VALUE
|
647
|
+
@video_vram = 9216 if @video_vram == UNSET_VALUE
|
648
|
+
@sound_type = nil if @sound_type == UNSET_VALUE
|
649
|
+
@keymap = 'en-us' if @keymap == UNSET_VALUE
|
650
|
+
@kvm_hidden = false if @kvm_hidden == UNSET_VALUE
|
651
|
+
@tpm_model = 'tpm-tis' if @tpm_model == UNSET_VALUE
|
652
|
+
@tpm_type = 'passthrough' if @tpm_type == UNSET_VALUE
|
653
|
+
@tpm_path = nil if @tpm_path == UNSET_VALUE
|
654
|
+
@nic_adapter_count = 8 if @nic_adapter_count == UNSET_VALUE
|
655
|
+
@emulator_path = nil if @emulator_path == UNSET_VALUE
|
656
|
+
|
657
|
+
# Boot order
|
658
|
+
@boot_order = [] if @boot_order == UNSET_VALUE
|
659
|
+
|
660
|
+
# Storage
|
661
|
+
@disks = [] if @disks == UNSET_VALUE
|
662
|
+
@disks.map! do |disk|
|
663
|
+
disk[:device] = _get_device(@disks) if disk[:device].nil?
|
664
|
+
disk
|
665
|
+
end
|
666
|
+
@cdroms = [] if @cdroms == UNSET_VALUE
|
667
|
+
@cdroms.map! do |cdrom|
|
668
|
+
cdrom[:dev] = _get_cdrom_dev(@cdroms) if cdrom[:dev].nil?
|
669
|
+
cdrom
|
670
|
+
end
|
671
|
+
|
672
|
+
# Inputs
|
673
|
+
@inputs = [{ type: 'mouse', bus: 'ps2' }] if @inputs == UNSET_VALUE
|
674
|
+
|
675
|
+
# Channels
|
676
|
+
@channels = [] if @channels == UNSET_VALUE
|
677
|
+
|
678
|
+
# PCI device passthrough
|
679
|
+
@pcis = [] if @pcis == UNSET_VALUE
|
680
|
+
|
681
|
+
# Random number generator passthrough
|
682
|
+
@rng = {} if @rng == UNSET_VALUE
|
683
|
+
|
684
|
+
# Watchdog device
|
685
|
+
@watchdog_dev = {} if @watchdog_dev == UNSET_VALUE
|
686
|
+
|
687
|
+
# USB device passthrough
|
688
|
+
@usbs = [] if @usbs == UNSET_VALUE
|
689
|
+
|
690
|
+
# Redirected devices
|
691
|
+
@redirdevs = [] if @redirdevs == UNSET_VALUE
|
692
|
+
@redirfilters = [] if @redirfilters == UNSET_VALUE
|
693
|
+
|
694
|
+
# smartcard device
|
695
|
+
@smartcard_dev = {} if @smartcard_dev == UNSET_VALUE
|
696
|
+
|
697
|
+
# Suspend mode
|
698
|
+
@suspend_mode = 'pause' if @suspend_mode == UNSET_VALUE
|
699
|
+
|
700
|
+
# Autostart
|
701
|
+
@autostart = false if @autostart == UNSET_VALUE
|
702
|
+
|
703
|
+
# Attach mgmt network
|
704
|
+
@mgmt_attach = true if @mgmt_attach == UNSET_VALUE
|
705
|
+
|
706
|
+
@qemu_args = [] if @qemu_args == UNSET_VALUE
|
707
|
+
end
|
708
|
+
|
709
|
+
def validate(machine)
|
710
|
+
errors = _detected_errors
|
711
|
+
|
712
|
+
machine.provider_config.disks.each do |disk|
|
713
|
+
if disk[:path] && (disk[:path][0] == '/')
|
714
|
+
errors << "absolute volume paths like '#{disk[:path]}' not yet supported"
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
machine.config.vm.networks.each do |_type, opts|
|
719
|
+
if opts[:mac]
|
720
|
+
opts[:mac].downcase!
|
721
|
+
if opts[:mac] =~ /\A([0-9a-f]{12})\z/
|
722
|
+
opts[:mac] = opts[:mac].scan(/../).join(':')
|
723
|
+
end
|
724
|
+
unless opts[:mac] =~ /\A([0-9a-f]{2}:){5}([0-9a-f]{2})\z/
|
725
|
+
errors << "Configured NIC MAC '#{opts[:mac]}' is not in 'xx:xx:xx:xx:xx:xx' or 'xxxxxxxxxxxx' format"
|
726
|
+
end
|
727
|
+
end
|
728
|
+
end
|
729
|
+
|
730
|
+
{ 'Libvirt Provider' => errors }
|
731
|
+
end
|
732
|
+
|
733
|
+
def merge(other)
|
734
|
+
super.tap do |result|
|
735
|
+
c = disks.dup
|
736
|
+
c += other.disks
|
737
|
+
result.disks = c
|
738
|
+
|
739
|
+
c = cdroms.dup
|
740
|
+
c += other.cdroms
|
741
|
+
result.cdroms = c
|
742
|
+
end
|
743
|
+
end
|
744
|
+
end
|
745
|
+
end
|
746
|
+
end
|