Files
vagrant-libvirt/lib/vagrant-libvirt/config.rb
Darragh Bailey a16c3f7898 Move proxy_command to config and support templating (#1226)
Migrate the proxy_command specification to the config and add support
for user override template to be used for edge cases. Moving it to the
config allows mistakes in the interpolation to be caught before the
machine is brought up.

Note this uses a more restrictive replacement to avoid requiring
escaping of '%' or '$'.

Issue #921 already partially resolved thanks to @ElArtista, this
completes the fix by allowing users to override as needed.

Fixes: #921
2021-03-21 15:19:53 +00:00

1057 lines
35 KiB
Ruby

require 'vagrant'
class Numeric
Alphabet = ('a'..'z').to_a
def vdev
s = ''
q = self
(q, r = (q - 1).divmod(26)) && s.prepend(Alphabet[r]) until q.zero?
'vd' + s
end
end
module VagrantPlugins
module ProviderLibvirt
class Config < Vagrant.plugin('2', :config)
# manually specify URI
# will supercede most other options if provided
attr_accessor :uri
# A hypervisor name to access via Libvirt.
attr_accessor :driver
# The name of the server, where Libvirtd is running.
attr_accessor :host
# If use ssh tunnel to connect to Libvirt.
attr_accessor :connect_via_ssh
# Path towards the Libvirt socket
attr_accessor :socket
# The username to access Libvirt.
attr_accessor :username
# Password for Libvirt connection.
attr_accessor :password
# ID SSH key file
attr_accessor :id_ssh_key_file
attr_accessor :proxy_command
# Libvirt storage pool name, where box image and instance snapshots will
# be stored.
attr_accessor :storage_pool_name
attr_accessor :storage_pool_path
# Libvirt storage pool where the base image snapshot shall be stored
attr_accessor :snapshot_pool_name
# Turn on to prevent hostname conflicts
attr_accessor :random_hostname
# Libvirt default network
attr_accessor :management_network_device
attr_accessor :management_network_name
attr_accessor :management_network_address
attr_accessor :management_network_mode
attr_accessor :management_network_mac
attr_accessor :management_network_guest_ipv6
attr_accessor :management_network_autostart
attr_accessor :management_network_pci_bus
attr_accessor :management_network_pci_slot
attr_accessor :management_network_domain
# System connection information
attr_accessor :system_uri
# Default host prefix (alternative to use project folder name)
attr_accessor :default_prefix
# Domain specific settings used while creating new domain.
attr_accessor :title
attr_accessor :description
attr_accessor :uuid
attr_accessor :memory
attr_accessor :nodeset
attr_accessor :memory_backing
attr_accessor :channel
attr_accessor :cpus
attr_accessor :cpuset
attr_accessor :cpu_mode
attr_accessor :cpu_model
attr_accessor :cpu_fallback
attr_accessor :cpu_features
attr_accessor :cpu_topology
attr_accessor :shares
attr_accessor :features
attr_accessor :features_hyperv
attr_accessor :clock_offset
attr_accessor :clock_timers
attr_accessor :numa_nodes
attr_accessor :loader
attr_accessor :nvram
attr_accessor :boot_order
attr_accessor :machine_type
attr_accessor :machine_arch
attr_accessor :machine_virtual_size
attr_accessor :disk_bus
attr_accessor :disk_device
attr_accessor :disk_driver_opts
attr_accessor :nic_model_type
attr_accessor :nested
attr_accessor :volume_cache # deprecated, kept for backwards compatibility; use disk_driver
attr_accessor :kernel
attr_accessor :cmd_line
attr_accessor :initrd
attr_accessor :dtb
attr_accessor :emulator_path
attr_accessor :graphics_type
attr_accessor :graphics_autoport
attr_accessor :graphics_port
attr_accessor :graphics_passwd
attr_accessor :graphics_ip
attr_accessor :video_type
attr_accessor :video_vram
attr_accessor :keymap
attr_accessor :kvm_hidden
attr_accessor :sound_type
# Sets the information for connecting to a host TPM device
# Only supports socket-based TPMs
attr_accessor :tpm_model
attr_accessor :tpm_type
attr_accessor :tpm_path
attr_accessor :tpm_version
# Configure the memballoon
attr_accessor :memballoon_enabled
attr_accessor :memballoon_model
attr_accessor :memballoon_pci_bus
attr_accessor :memballoon_pci_slot
# Sets the max number of NICs that can be created
# Default set to 8. Don't change the default unless you know
# what are doing
attr_accessor :nic_adapter_count
# Storage
attr_accessor :disks
attr_accessor :cdroms
# Inputs
attr_accessor :inputs
# Channels
attr_accessor :channels
# PCI device passthrough
attr_accessor :pcis
# Random number device passthrough
attr_accessor :rng
# Watchdog device
attr_accessor :watchdog_dev
# USB controller
attr_accessor :usbctl_dev
# USB device passthrough
attr_accessor :usbs
# Redirected devices
attr_accessor :redirdevs
attr_accessor :redirfilters
# smartcard device
attr_accessor :smartcard_dev
# Suspend mode
attr_accessor :suspend_mode
# Autostart
attr_accessor :autostart
# Attach mgmt network
attr_accessor :mgmt_attach
# Additional qemuargs arguments
attr_accessor :qemu_args
# Additional qemuenv arguments
attr_accessor :qemu_env
# Use QEMU session instead of system
attr_accessor :qemu_use_session
def initialize
@uri = UNSET_VALUE
@driver = UNSET_VALUE
@host = UNSET_VALUE
@connect_via_ssh = UNSET_VALUE
@username = UNSET_VALUE
@password = UNSET_VALUE
@id_ssh_key_file = UNSET_VALUE
@socket = UNSET_VALUE
@proxy_command = UNSET_VALUE
@storage_pool_name = UNSET_VALUE
@snapshot_pool_name = UNSET_VALUE
@random_hostname = UNSET_VALUE
@management_network_device = UNSET_VALUE
@management_network_name = UNSET_VALUE
@management_network_address = UNSET_VALUE
@management_network_mode = UNSET_VALUE
@management_network_mac = UNSET_VALUE
@management_network_guest_ipv6 = UNSET_VALUE
@management_network_autostart = UNSET_VALUE
@management_network_pci_slot = UNSET_VALUE
@management_network_pci_bus = UNSET_VALUE
@management_network_domain = UNSET_VALUE
# System connection information
@system_uri = UNSET_VALUE
# Domain specific settings.
@title = UNSET_VALUE
@description = UNSET_VALUE
@uuid = UNSET_VALUE
@memory = UNSET_VALUE
@nodeset = UNSET_VALUE
@memory_backing = UNSET_VALUE
@cpus = UNSET_VALUE
@cpuset = UNSET_VALUE
@cpu_mode = UNSET_VALUE
@cpu_model = UNSET_VALUE
@cpu_fallback = UNSET_VALUE
@cpu_features = UNSET_VALUE
@cpu_topology = UNSET_VALUE
@shares = UNSET_VALUE
@features = UNSET_VALUE
@features_hyperv = UNSET_VALUE
@clock_offset = UNSET_VALUE
@clock_timers = []
@numa_nodes = UNSET_VALUE
@loader = UNSET_VALUE
@nvram = UNSET_VALUE
@machine_type = UNSET_VALUE
@machine_arch = UNSET_VALUE
@machine_virtual_size = UNSET_VALUE
@disk_bus = UNSET_VALUE
@disk_device = UNSET_VALUE
@disk_driver_opts = {}
@nic_model_type = UNSET_VALUE
@nested = UNSET_VALUE
@volume_cache = UNSET_VALUE
@kernel = UNSET_VALUE
@initrd = UNSET_VALUE
@dtb = UNSET_VALUE
@cmd_line = UNSET_VALUE
@emulator_path = UNSET_VALUE
@graphics_type = UNSET_VALUE
@graphics_autoport = UNSET_VALUE
@graphics_port = UNSET_VALUE
@graphics_ip = UNSET_VALUE
@graphics_passwd = UNSET_VALUE
@video_type = UNSET_VALUE
@video_vram = UNSET_VALUE
@sound_type = UNSET_VALUE
@keymap = UNSET_VALUE
@kvm_hidden = UNSET_VALUE
@tpm_model = UNSET_VALUE
@tpm_type = UNSET_VALUE
@tpm_path = UNSET_VALUE
@tpm_version = UNSET_VALUE
@memballoon_enabled = UNSET_VALUE
@memballoon_model = UNSET_VALUE
@memballoon_pci_bus = UNSET_VALUE
@memballoon_pci_slot = UNSET_VALUE
@nic_adapter_count = UNSET_VALUE
# Boot order
@boot_order = []
# Storage
@disks = []
@cdroms = []
# Inputs
@inputs = UNSET_VALUE
# Channels
@channels = UNSET_VALUE
# PCI device passthrough
@pcis = UNSET_VALUE
# Random number device passthrough
@rng = UNSET_VALUE
# Watchdog device
@watchdog_dev = UNSET_VALUE
# USB controller
@usbctl_dev = UNSET_VALUE
# USB device passthrough
@usbs = UNSET_VALUE
# Redirected devices
@redirdevs = UNSET_VALUE
@redirfilters = UNSET_VALUE
# smartcard device
@smartcard_dev = UNSET_VALUE
# Suspend mode
@suspend_mode = UNSET_VALUE
# Autostart
@autostart = UNSET_VALUE
# Attach mgmt network
@mgmt_attach = UNSET_VALUE
# Additional QEMU commandline arguments
@qemu_args = UNSET_VALUE
# Additional QEMU commandline environment variables
@qemu_env = UNSET_VALUE
@qemu_use_session = UNSET_VALUE
end
def boot(device)
@boot_order << device # append
end
def _get_device(disks)
# skip existing devices and also the first one (vda)
exist = disks.collect { |x| x[:device] } + [1.vdev.to_s]
skip = 1 # we're 1 based, not 0 based...
loop do
dev = skip.vdev # get lettered device
return dev unless exist.include?(dev)
skip += 1
end
end
def _get_cdrom_dev(cdroms)
exist = Hash[cdroms.collect { |x| [x[:dev], true] }]
# hda - hdc
curr = 'a'.ord
while curr <= 'd'.ord
dev = 'hd' + curr.chr
if exist[dev]
curr += 1
next
else
return dev
end
end
# is it better to raise our own error, or let Libvirt cause the exception?
raise 'Only four cdroms may be attached at a time'
end
def _generate_numa
@numa_nodes.collect { |x|
# Perform some validation of cpu values
unless x[:cpus] =~ /^\d+-\d+$/
raise 'numa_nodes[:cpus] must be in format "integer-integer"'
end
# Convert to KiB
x[:memory] = x[:memory].to_i * 1024
}
# Grab the value of the last @numa_nodes[:cpus] and verify @cpus matches
# Note: [:cpus] is zero based and @cpus is not, so we need to +1
last_cpu = @numa_nodes.last[:cpus]
last_cpu = last_cpu.scan(/\d+$/)[0]
last_cpu = last_cpu.to_i + 1
if @cpus != last_cpu.to_i
raise 'The total number of numa_nodes[:cpus] must equal config.cpus'
end
@numa_nodes
end
def cpu_feature(options = {})
if options[:name].nil? || options[:policy].nil?
raise 'CPU Feature name AND policy must be specified'
end
@cpu_features = [] if @cpu_features == UNSET_VALUE
@cpu_features.push(name: options[:name],
policy: options[:policy])
end
def hyperv_feature(options = {})
if options[:name].nil? || options[:state].nil?
raise 'Feature name AND state must be specified'
end
@features_hyperv = [] if @features_hyperv == UNSET_VALUE
@features_hyperv.push(name: options[:name],
state: options[:state])
end
def clock_timer(options = {})
if options[:name].nil?
raise 'Clock timer name must be specified'
end
options.each do |key, value|
case key
when :name, :track, :tickpolicy, :frequency, :mode, :present
if value.nil?
raise "Value of timer option #{key} is nil"
end
else
raise "Unknown clock timer option: #{key}"
end
end
@clock_timers.push(options.dup)
end
def cputopology(options = {})
if options[:sockets].nil? || options[:cores].nil? || options[:threads].nil?
raise 'CPU topology must have all of sockets, cores and threads specified'
end
if @cpu_topology == UNSET_VALUE
@cpu_topology = {}
end
@cpu_topology[:sockets] = options[:sockets]
@cpu_topology[:cores] = options[:cores]
@cpu_topology[:threads] = options[:threads]
end
def memorybacking(option, config = {})
case option
when :source
raise 'Source type must be specified' if config[:type].nil?
when :access
raise 'Access mode must be specified' if config[:mode].nil?
when :allocation
raise 'Allocation mode must be specified' if config[:mode].nil?
end
@memory_backing = [] if @memory_backing == UNSET_VALUE
@memory_backing.push(name: option,
config: config)
end
def input(options = {})
if options[:type].nil? || options[:bus].nil?
raise 'Input type AND bus must be specified'
end
@inputs = [] if @inputs == UNSET_VALUE
@inputs.push(type: options[:type],
bus: options[:bus])
end
def channel(options = {})
if options[:type].nil?
raise 'Channel type must be specified.'
elsif options[:type] == 'unix' && options[:target_type] == 'guestfwd'
# Guest forwarding requires a target (ip address) and a port
if options[:target_address].nil? || options[:target_port].nil? ||
options[:source_path].nil?
raise 'guestfwd requires target_address, target_port and source_path'
end
end
@channels = [] if @channels == UNSET_VALUE
@channels.push(type: options[:type],
source_mode: options[:source_mode],
source_path: options[:source_path],
target_address: options[:target_address],
target_name: options[:target_name],
target_port: options[:target_port],
target_type: options[:target_type])
end
def random(options = {})
if !options[:model].nil? && options[:model] != 'random'
raise 'The only supported rng backend is "random".'
end
@rng = {} if @rng == UNSET_VALUE
@rng[:model] = options[:model]
end
def pci(options = {})
if options[:bus].nil? || options[:slot].nil? || options[:function].nil?
raise 'Bus AND slot AND function must be specified. Check `lspci` for that numbers.'
end
@pcis = [] if @pcis == UNSET_VALUE
if options[:domain].nil?
pci_domain = '0x0000'
else
pci_domain = options[:domain]
end
@pcis.push(domain: pci_domain,
bus: options[:bus],
slot: options[:slot],
function: options[:function])
end
def watchdog(options = {})
if options[:model].nil?
raise 'Model must be specified.'
end
if @watchdog_dev == UNSET_VALUE
@watchdog_dev = {}
end
@watchdog_dev[:model] = options[:model]
@watchdog_dev[:action] = options[:action] || 'reset'
end
def usb_controller(options = {})
if options[:model].nil?
raise 'USB controller model must be specified.'
end
if @usbctl_dev == UNSET_VALUE
@usbctl_dev = {}
end
@usbctl_dev[:model] = options[:model]
@usbctl_dev[:ports] = options[:ports]
end
def usb(options = {})
if (options[:bus].nil? || options[:device].nil?) && options[:vendor].nil? && options[:product].nil?
raise 'Bus and device and/or vendor and/or product must be specified. Check `lsusb` for these.'
end
@usbs = [] if @usbs == UNSET_VALUE
@usbs.push(bus: options[:bus],
device: options[:device],
vendor: options[:vendor],
product: options[:product],
startupPolicy: options[:startupPolicy])
end
def redirdev(options = {})
raise 'Type must be specified.' if options[:type].nil?
@redirdevs = [] if @redirdevs == UNSET_VALUE
@redirdevs.push(type: options[:type])
end
def redirfilter(options = {})
raise 'Option allow must be specified.' if options[:allow].nil?
@redirfilters = [] if @redirfilters == UNSET_VALUE
@redirfilters.push(class: options[:class] || -1,
vendor: options[:vendor] || -1,
product: options[:product] || -1,
version: options[:version] || -1,
allow: options[:allow])
end
def smartcard(options = {})
if options[:mode].nil?
raise 'Option mode must be specified.'
elsif options[:mode] != 'passthrough'
raise 'Currently only passthrough mode is supported!'
elsif options[:type] == 'tcp' && (options[:source_mode].nil? || options[:source_host].nil? || options[:source_service].nil?)
raise 'If using type "tcp", option "source_mode", "source_host" and "source_service" must be specified.'
end
if @smartcard_dev == UNSET_VALUE
@smartcard_dev = {}
end
@smartcard_dev[:mode] = options[:mode]
@smartcard_dev[:type] = options[:type] || 'spicevmc'
@smartcard_dev[:source_mode] = options[:source_mode] if @smartcard_dev[:type] == 'tcp'
@smartcard_dev[:source_host] = options[:source_host] if @smartcard_dev[:type] == 'tcp'
@smartcard_dev[:source_service] = options[:source_service] if @smartcard_dev[:type] == 'tcp'
end
# Disk driver options for primary disk
def disk_driver(options = {})
supported_opts = [:cache, :io, :copy_on_read, :discard, :detect_zeroes]
@disk_driver_opts = options.select { |k,_| supported_opts.include? k }
end
# NOTE: this will run twice for each time it's needed- keep it idempotent
def storage(storage_type, options = {})
if storage_type == :file
if options[:device] == :cdrom
_handle_cdrom_storage(options)
else
_handle_disk_storage(options)
end
end
end
def _handle_cdrom_storage(options = {})
# <disk type="file" device="cdrom">
# <source file="/home/user/virtio-win-0.1-100.iso"/>
# <target dev="hdc"/>
# <readonly/>
# <address type='drive' controller='0' bus='1' target='0' unit='0'/>
# </disk>
#
# note the target dev will need to be changed with each cdrom drive (hdc, hdd, etc),
# as will the address unit number (unit=0, unit=1, etc)
options = {
bus: 'ide',
path: nil
}.merge(options)
cdrom = {
dev: options[:dev],
bus: options[:bus],
path: options[:path]
}
@cdroms << cdrom
end
def _handle_disk_storage(options = {})
options = {
type: 'qcow2',
size: '10G', # matches the fog default
path: nil,
bus: 'virtio'
}.merge(options)
disk = {
device: options[:device],
type: options[:type],
size: options[:size],
path: options[:path],
bus: options[:bus],
cache: options[:cache] || 'default',
allow_existing: options[:allow_existing],
shareable: options[:shareable],
serial: options[:serial],
io: options[:io],
copy_on_read: options[:copy_on_read],
discard: options[:discard],
detect_zeroes: options[:detect_zeroes],
pool: options[:pool], # overrides storage_pool setting for additional disks
wwn: options[:wwn],
}
@disks << disk # append
end
def qemuargs(options = {})
@qemu_args = [] if @qemu_args == UNSET_VALUE
@qemu_args << options if options[:value]
end
def qemuenv(options = {})
@qemu_env = {} if @qemu_env == UNSET_VALUE
@qemu_env.merge!(options)
end
def _default_uri
# Determine if any settings except driver provided explicitly, if not
# and the LIBVIRT_DEFAULT_URI var is set, use that.
#
# Skipping driver because that may be set on individual boxes rather
# than by the user.
if [
@connect_via_ssh, @host, @username, @password,
@id_ssh_key_file, @qemu_use_session, @socket,
].none?{ |v| v != UNSET_VALUE }
if ENV.fetch('LIBVIRT_DEFAULT_URI', '') != ""
@uri = ENV['LIBVIRT_DEFAULT_URI']
end
end
end
# code to generate URI from from either the LIBVIRT_URI environment
# variable or a config moved out of the connect action
def _generate_uri(qemu_use_session)
# builds the Libvirt connection URI from the given driver config
# Setup connection uri.
uri = @driver.dup
virt_path = case uri
when 'qemu', 'kvm'
qemu_use_session ? '/session' : '/system'
when 'openvz', 'uml', 'phyp', 'parallels'
'/system'
when '@en', 'esx'
'/'
when 'vbox', 'vmwarews', 'hyperv'
'/session'
else
raise "Require specify driver #{uri}"
end
if uri == 'kvm'
uri = 'qemu' # use QEMU uri for KVM domain type
end
# turn on ssh if an ssh key file is explicitly provided
if @connect_via_ssh == UNSET_VALUE && @id_ssh_key_file && @id_ssh_key_file != UNSET_VALUE
@connect_via_ssh = true
end
params = {}
if @connect_via_ssh == true
finalize_id_ssh_key_file
uri << '+ssh://'
uri << @username + '@' if @username && @username != UNSET_VALUE
uri << ( @host && @host != UNSET_VALUE ? @host : 'localhost' )
params['no_verify'] = '1'
params['keyfile'] = @id_ssh_key_file if @id_ssh_key_file
else
uri << '://'
uri << @host if @host && @host != UNSET_VALUE
end
uri << virt_path
# set path to Libvirt socket
params['socket'] = @socket if @socket
uri << "?" + params.map{|pair| pair.join('=')}.join('&') if !params.empty?
uri
end
def _parse_uri(uri)
begin
URI.parse(uri)
rescue
raise "@uri set to invalid uri '#{uri}'"
end
end
def finalize!
_default_uri if @uri == UNSET_VALUE
# settings which _generate_uri
@driver = 'kvm' if @driver == UNSET_VALUE
@password = nil if @password == UNSET_VALUE
@socket = nil if @socket == UNSET_VALUE
# If uri isn't set then let's build one from various sources.
# Default to passing false for qemu_use_session if it's not set.
if @uri == UNSET_VALUE
@uri = _generate_uri(@qemu_use_session == UNSET_VALUE ? false : @qemu_use_session)
end
finalize_from_uri
finalize_proxy_command
@storage_pool_name = 'default' if @storage_pool_name == UNSET_VALUE
@snapshot_pool_name = @storage_pool_name if @snapshot_pool_name == UNSET_VALUE
@storage_pool_path = nil if @storage_pool_path == UNSET_VALUE
@random_hostname = false if @random_hostname == UNSET_VALUE
@management_network_device = 'virbr0' if @management_network_device == UNSET_VALUE
@management_network_name = 'vagrant-libvirt' if @management_network_name == UNSET_VALUE
@management_network_address = '192.168.121.0/24' if @management_network_address == UNSET_VALUE
@management_network_mode = 'nat' if @management_network_mode == UNSET_VALUE
@management_network_mac = nil if @management_network_mac == UNSET_VALUE
@management_network_guest_ipv6 = 'yes' if @management_network_guest_ipv6 == UNSET_VALUE
@management_network_autostart = false if @management_network_autostart == UNSET_VALUE
@management_network_pci_bus = nil if @management_network_pci_bus == UNSET_VALUE
@management_network_pci_slot = nil if @management_network_pci_slot == UNSET_VALUE
@management_network_domain = nil if @management_network_domain == UNSET_VALUE
@system_uri = 'qemu:///system' if @system_uri == UNSET_VALUE
# Domain specific settings.
@title = '' if @title == UNSET_VALUE
@description = '' if @description == UNSET_VALUE
@uuid = '' if @uuid == UNSET_VALUE
@memory = 512 if @memory == UNSET_VALUE
@nodeset = nil if @nodeset == UNSET_VALUE
@memory_backing = [] if @memory_backing == UNSET_VALUE
@cpus = 1 if @cpus == UNSET_VALUE
@cpuset = nil if @cpuset == UNSET_VALUE
@cpu_mode = 'host-model' if @cpu_mode == UNSET_VALUE
@cpu_model = if (@cpu_model == UNSET_VALUE) && (@cpu_mode == 'custom')
'qemu64'
elsif @cpu_mode != 'custom'
''
else
@cpu_model
end
@cpu_topology = {} if @cpu_topology == UNSET_VALUE
@cpu_fallback = 'allow' if @cpu_fallback == UNSET_VALUE
@cpu_features = [] if @cpu_features == UNSET_VALUE
@shares = nil if @shares == UNSET_VALUE
@features = ['acpi','apic','pae'] if @features == UNSET_VALUE
@features_hyperv = [] if @features_hyperv == UNSET_VALUE
@clock_offset = 'utc' if @clock_offset == UNSET_VALUE
@clock_timers = [] if @clock_timers == UNSET_VALUE
@numa_nodes = @numa_nodes == UNSET_VALUE ? nil : _generate_numa
@loader = nil if @loader == UNSET_VALUE
@nvram = nil if @nvram == UNSET_VALUE
@machine_type = nil if @machine_type == UNSET_VALUE
@machine_arch = nil if @machine_arch == UNSET_VALUE
@machine_virtual_size = nil if @machine_virtual_size == UNSET_VALUE
@disk_bus = 'virtio' if @disk_bus == UNSET_VALUE
@disk_device = 'vda' if @disk_device == UNSET_VALUE
@disk_driver_opts = {} if @disk_driver_opts == UNSET_VALUE
@nic_model_type = nil if @nic_model_type == UNSET_VALUE
@nested = false if @nested == UNSET_VALUE
@volume_cache = nil if @volume_cache == UNSET_VALUE
@kernel = nil if @kernel == UNSET_VALUE
@cmd_line = '' if @cmd_line == UNSET_VALUE
@initrd = '' if @initrd == UNSET_VALUE
@dtb = nil if @dtb == UNSET_VALUE
@graphics_type = 'vnc' if @graphics_type == UNSET_VALUE
@graphics_autoport = 'yes' if @graphics_port == UNSET_VALUE
@graphics_autoport = 'no' if @graphics_port != UNSET_VALUE
if (@graphics_type != 'vnc' && @graphics_type != 'spice') ||
@graphics_passwd == UNSET_VALUE
@graphics_passwd = nil
end
@graphics_port = -1 if @graphics_port == UNSET_VALUE
@graphics_ip = '127.0.0.1' if @graphics_ip == UNSET_VALUE
@video_type = 'cirrus' if @video_type == UNSET_VALUE
@video_vram = 9216 if @video_vram == UNSET_VALUE
@sound_type = nil if @sound_type == UNSET_VALUE
@keymap = 'en-us' if @keymap == UNSET_VALUE
@kvm_hidden = false if @kvm_hidden == UNSET_VALUE
@tpm_model = 'tpm-tis' if @tpm_model == UNSET_VALUE
@tpm_type = 'passthrough' if @tpm_type == UNSET_VALUE
@tpm_path = nil if @tpm_path == UNSET_VALUE
@tpm_version = nil if @tpm_version == UNSET_VALUE
@memballoon_enabled = nil if @memballoon_enabled == UNSET_VALUE
@memballoon_model = 'virtio' if @memballoon_model == UNSET_VALUE
@memballoon_pci_bus = '0x00' if @memballoon_pci_bus == UNSET_VALUE
@memballoon_pci_slot = '0x0f' if @memballoon_pci_slot == UNSET_VALUE
@nic_adapter_count = 8 if @nic_adapter_count == UNSET_VALUE
@emulator_path = nil if @emulator_path == UNSET_VALUE
# Boot order
@boot_order = [] if @boot_order == UNSET_VALUE
# Storage
@disks = [] if @disks == UNSET_VALUE
@disks.map! do |disk|
disk[:device] = _get_device(@disks) if disk[:device].nil?
disk
end
@cdroms = [] if @cdroms == UNSET_VALUE
@cdroms.map! do |cdrom|
cdrom[:dev] = _get_cdrom_dev(@cdroms) if cdrom[:dev].nil?
cdrom
end
# Inputs
@inputs = [{ type: 'mouse', bus: 'ps2' }] if @inputs == UNSET_VALUE
# Channels
@channels = [] if @channels == UNSET_VALUE
# PCI device passthrough
@pcis = [] if @pcis == UNSET_VALUE
# Random number generator passthrough
@rng = {} if @rng == UNSET_VALUE
# Watchdog device
@watchdog_dev = {} if @watchdog_dev == UNSET_VALUE
# USB controller
@usbctl_dev = {} if @usbctl_dev == UNSET_VALUE
# USB device passthrough
@usbs = [] if @usbs == UNSET_VALUE
# Redirected devices
@redirdevs = [] if @redirdevs == UNSET_VALUE
@redirfilters = [] if @redirfilters == UNSET_VALUE
# smartcard device
@smartcard_dev = {} if @smartcard_dev == UNSET_VALUE
# Suspend mode
@suspend_mode = 'pause' if @suspend_mode == UNSET_VALUE
# Autostart
@autostart = false if @autostart == UNSET_VALUE
# Attach mgmt network
@mgmt_attach = true if @mgmt_attach == UNSET_VALUE
# Additional QEMU commandline arguments
@qemu_args = [] if @qemu_args == UNSET_VALUE
# Additional QEMU commandline environment variables
@qemu_env = {} if @qemu_env == UNSET_VALUE
end
def validate(machine)
errors = _detected_errors
# The @uri and @qemu_use_session should not conflict
uri = _parse_uri(@uri)
if (uri.scheme.start_with? "qemu") && (uri.path.include? "session")
if @qemu_use_session != true
errors << "the URI and qemu_use_session configuration conflict: uri:'#{@uri}' qemu_use_session:'#{@qemu_use_session}'"
end
end
machine.provider_config.disks.each do |disk|
if disk[:path] && (disk[:path][0] == '/')
errors << "absolute volume paths like '#{disk[:path]}' not yet supported"
end
end
machine.config.vm.networks.each do |_type, opts|
if opts[:mac]
opts[:mac].downcase!
if opts[:mac] =~ /\A([0-9a-f]{12})\z/
opts[:mac] = opts[:mac].scan(/../).join(':')
end
unless opts[:mac] =~ /\A([0-9a-f]{2}:){5}([0-9a-f]{2})\z/
errors << "Configured NIC MAC '#{opts[:mac]}' is not in 'xx:xx:xx:xx:xx:xx' or 'xxxxxxxxxxxx' format"
end
end
end
if !machine.provider_config.volume_cache.nil? and machine.provider_config.volume_cache != UNSET_VALUE
machine.ui.warn("Libvirt Provider: volume_cache is deprecated. Use disk_driver :cache => '#{machine.provider_config.volume_cache}' instead.")
if !machine.provider_config.disk_driver_opts.empty?
machine.ui.warn("Libvirt Provider: volume_cache has no effect when disk_driver is defined.")
end
end
{ 'Libvirt Provider' => errors }
end
def merge(other)
super.tap do |result|
c = disks.dup
c += other.disks
result.disks = c
c = cdroms.dup
c += other.cdroms
result.cdroms = c
result.disk_driver_opts = disk_driver_opts.merge(other.disk_driver_opts)
c = clock_timers.dup
c += other.clock_timers
result.clock_timers = c
c = qemu_env != UNSET_VALUE ? qemu_env.dup : {}
c.merge!(other.qemu_env) if other.qemu_env != UNSET_VALUE
result.qemu_env = c
end
end
private
def finalize_from_uri
# Parse uri to extract individual components
uri = _parse_uri(@uri)
# only set @connect_via_ssh if not explicitly to avoid overriding
# and allow an error to occur if the @uri and @connect_via_ssh disagree
@connect_via_ssh = uri.scheme.include? "ssh" if @connect_via_ssh == UNSET_VALUE
# Set qemu_use_session based on the URI if it wasn't set by the user
if @qemu_use_session == UNSET_VALUE
if (uri.scheme.start_with? "qemu") && (uri.path.include? "session")
@qemu_use_session = true
else
@qemu_use_session = false
end
end
# Extract host and username values from uri if provided, otherwise nil
@host = uri.host
@username = uri.user
finalize_id_ssh_key_file
end
def resolve_ssh_key_file(key_file)
# set ssh key for access to Libvirt host
# if no slash, prepend $HOME/.ssh/
key_file.prepend("#{ENV['HOME']}/.ssh/") if key_file && key_file !~ /\A\//
key_file
end
def finalize_id_ssh_key_file
# resolve based on the following roles
# 1) if @connect_via_ssh is set to true, and id_ssh_key_file not current set,
# set default if the file exists
# 2) if supplied the key name, attempt to expand based on user home
# 3) otherwise set to nil
if @connect_via_ssh == true && @id_ssh_key_file == UNSET_VALUE
# set default if using ssh while allowing a user using nil to disable this
id_ssh_key_file = resolve_ssh_key_file('id_rsa')
id_ssh_key_file = nil if !File.file?(id_ssh_key_file)
elsif @id_ssh_key_file != UNSET_VALUE
id_ssh_key_file = resolve_ssh_key_file(@id_ssh_key_file)
else
id_ssh_key_file = nil
end
@id_ssh_key_file = id_ssh_key_file
end
def finalize_proxy_command
if @connect_via_ssh
if @proxy_command == UNSET_VALUE
proxy_command = "ssh '#{@host}' "
proxy_command << "-l '#{@username}' " if @username
proxy_command << "-i '#{@id_ssh_key_file}' " if @id_ssh_key_file
proxy_command << '-W %h:%p'
else
inputs = { host: @host }
inputs[:username] = @username if @username
inputs[:id_ssh_key_file] = @id_ssh_key_file if @id_ssh_key_file
proxy_command = @proxy_command
# avoid needing to escape '%' symbols
inputs.each do |key, value|
proxy_command.gsub!("{#{key}}", value)
end
end
@proxy_command = proxy_command
else
@proxy_command = nil
end
end
end
end
end