Adding SEV support (#1664)

Secure Encryption Virtualization is supported by libvirt and this
change adds support for vagrant-libvirt to enable it.

It requires a UEFI base box and needs a combination of options to be
configured for it to work.

Co-authored-by: PELLET Norman <norman.pellet@csem.ch>
Co-authored-by: MUNTANÉ CALVO Enric <emc@csem.ch>
Co-authored-by: Darragh Bailey <daragh.bailey@gmail.com>
Closes: #1372
This commit is contained in:
Darragh Bailey
2022-11-10 12:22:37 +00:00
committed by GitHub
parent 4fe53477b2
commit 980db1049a
29 changed files with 455 additions and 24 deletions

View File

@@ -149,6 +149,14 @@ end
{:cpus => "2-3", :memory => "4096"}
]
```
* `launchsecurity` - Configure Secure Encryption Virtualization for the guest, requires additional components to be configured to work, see [examples](./examples.html#secure-encryption-virtualization). For more information look at [libvirt documentation](https://libvirt.org/kbase/launch_security_sev.html).
```
libvirt.launchsecurity :type => 'sev', :cbitpos => 47, :reducedPhysBits => 1, :policy => "0x0003"
```
* `memtune` - Configure the memtune settings for the guest, primarily exposed to facilitate enabling Secure Encryption Virtualization. Note that when configuring `hard_limit` that the value is in kB as opposed to `libvirt.memory` which is in Mb. Additionally it must be set to be higher than `libvirt.memory`, see [libvirt documentation](https://libvirt.org/kbase/launch_security_sev.html) for details on why.
```
libvirt.memtune :type => "hard_limit", :value => 2500000 # Note here the value in kB (not in Mb)
```
* `loader` - Sets path to custom UEFI loader.
* `kernel` - To launch the guest with a kernel residing on host filesystems.
Equivalent to qemu `-kernel`.

View File

@@ -477,6 +477,66 @@ Vagrant.configure("2") do |config|
end
```
## Secure Encryption Virtualization (SEV)
Secure Encryption Virtualization is supported by libvirt and by the vagrant-libvirt provider but comes with several requirements.
This mode has only been tested with q35 types of machines, so you'll need an UEFI boot
```ruby
Vagrant.configure("2") do |config|
config.vm.provider :libvirt do |libvirt|
libvirt.loader = "/usr/share/OVMF/OVMF_CODE.fd"
libvirt.nvram = "/path/to/ovmf/OVMF_VARS.fd"
libvirt.machine_type = 'pc-q35-focal'
end
end
```
Read the libvirt documentaiton to understand what OVMF is and how to use it.
Next, you'll want to call the following methods:
```ruby
Vagrant.configure("2") do |config|
config.vm.provider :libvirt do |libvirt|
libvirt.launchsecurity :type => 'sev', :cbitpos => 47, :reducedPhysBits => 1, :policy => "0x0003"
libvirt.memtune :type => "hard_limit", :value => 2500000 # Note here the value in kB (not in Mb)
end
end
```
Note that the value provided in the memtune `hard_limit` is in Kb by default. It should be higher than the
one given in `libvirt.memory` (which is in Mb, by the way) by some amount (again, check out the [https://libvirt.org/kbase/launch_security_sev.html](documentation)) to understand why.
It is also necessary to explicitly define the memballoon for it to accept the iommu flag.
```ruby
Vagrant.configure("2") do |config|
config.vm.provider :libvirt do |libvirt|
libvirt.memballoon_enabled = true
libvirt.memballoon_model = 'virtio'
libvirt.memballoon_pci_bus = '0x07'
libvirt.memballoon_pci_slot = '0x00'
end
end
```
And finally, because the iommu flag has to be passed to the networks, you also need to set it explicitly:
```ruby
Vagrant.configure("2") do |config|
config.vm.provider :libvirt do |libvirt|
# Management network only (the NAT'ed network provided by Vagrant)
libvirt.management_network_driver_iommu = true
end
# Example in defining a bridge
config.vm.network :public_network, :dev => "br0", :bridge => "br0", :mode => "bridge", :type => "bridge", :driver_iommu => true # <== Note here the additional flag
end
```
Don't forget that you'll need an UEFI base box.
## Libvirt communication channels
For certain functionality to be available within a guest, a private

View File

@@ -38,6 +38,7 @@ module VagrantPlugins
@features_hyperv = config.features_hyperv
@clock_offset = config.clock_offset
@clock_timers = config.clock_timers
@launchsecurity_data = config.launchsecurity_data
@shares = config.shares
@cpu_mode = config.cpu_mode
@cpu_model = config.cpu_model
@@ -52,6 +53,8 @@ module VagrantPlugins
@nested = config.nested
@memory_size = config.memory.to_i * 1024
@memory_backing = config.memory_backing
@memtunes = config.memtunes
@management_network_mac = config.management_network_mac
@domain_volume_cache = config.volume_cache || 'default'
@kernel = config.kernel
@@ -240,6 +243,10 @@ module VagrantPlugins
@memory_backing.each do |backing|
env[:ui].info(" -- Memory Backing: #{backing[:name]}: #{backing[:config].map { |k,v| "#{k}='#{v}'"}.join(' ')}")
end
@memtunes.each do |type, options|
env[:ui].info(" -- Memory Tuning: #{type}: #{options[:config].map { |k,v| "#{k}='#{v}'"}.join(' ')}, value: #{options[:value]}")
end
unless @shares.nil?
env[:ui].info(" -- Shares: #{@shares}")
end
@@ -311,6 +318,10 @@ module VagrantPlugins
env[:ui].info(" -- Disks: #{_disks_print(@disks)}")
end
if not @launchsecurity_data.nil?
env[:ui].info(" -- Launch security: #{@launchsecurity_data.map { |k, v| "#{k.to_s}=#{v}" }.join(", ")}")
end
@disks.each do |disk|
msg = " -- Disk(#{disk[:device]}): #{disk[:absolute_path]}"
msg += ' Shared' if disk[:shareable]

View File

@@ -76,6 +76,7 @@ module VagrantPlugins
@mac = iface_configuration.fetch(:mac, false)
@model_type = iface_configuration.fetch(:model_type, @nic_model_type)
@driver_name = iface_configuration.fetch(:driver_name, false)
@driver_iommu = iface_configuration.fetch(:driver_iommu, false )
@driver_queues = iface_configuration.fetch(:driver_queues, false)
@device_name = iface_configuration.fetch(:iface_name, false)
@mtu = iface_configuration.fetch(:mtu, nil)
@@ -84,12 +85,15 @@ module VagrantPlugins
template_name = 'interface'
@type = nil
@udp_tunnel = nil
@logger.debug("Interface configuration: #{iface_configuration}")
# Configuration for public interfaces which use the macvtap driver
if iface_configuration[:iface_type] == :public_network
@device = iface_configuration.fetch(:dev, 'eth0')
@mode = iface_configuration.fetch(:mode, 'bridge')
@type = iface_configuration.fetch(:type, 'direct')
@model_type = iface_configuration.fetch(:model_type, @nic_model_type)
@driver_iommu = iface_configuration.fetch(:driver_iommu, false )
@driver_name = iface_configuration.fetch(:driver_name, false)
@driver_queues = iface_configuration.fetch(:driver_queues, false)
@portgroup = iface_configuration.fetch(:portgroup, nil)
@@ -124,13 +128,14 @@ module VagrantPlugins
}
@tunnel_type = iface_configuration.fetch(:model_type, @nic_model_type)
@driver_name = iface_configuration.fetch(:driver_name, false)
@driver_iommu = iface_configuration.fetch(:driver_iommu, false )
@driver_queues = iface_configuration.fetch(:driver_queues, false)
template_name = 'tunnel_interface'
@logger.info("Setting up #{@type} tunnel interface using #{@tunnel_ip} port #{@tunnel_port}")
end
message = "Creating network interface eth#{@iface_number}"
message += " connected to network #{@network_name}."
message += " connected to network #{@network_name} based on template #{template_name}."
if @mac
@mac = @mac.scan(/(\h{2})/).join(':')
message += " Using MAC address: #{@mac}"
@@ -141,7 +146,9 @@ module VagrantPlugins
# FIXME: all options for network driver should be hash from Vagrantfile
driver_options = {}
driver_options[:name] = @driver_name if @driver_name
driver_options[:iommu] = @driver_iommu ? "on" : "off"
driver_options[:queues] = @driver_queues if @driver_queues
@udp_tunnel ||= {}
xml = if template_name == 'interface' or
template_name == 'tunnel_interface'
@@ -259,12 +266,15 @@ module VagrantPlugins
xml.source(source_options) do
xml.local(udp_tunnel) if type == 'udp'
end
@logger.debug "Driver options: #{driver_options}"
xml.mac(address: mac) if mac
xml.target(dev: target_dev_name(device_name, type, iface_number))
xml.alias(name: "net#{iface_number}")
xml.model(type: model_type.to_s)
xml.mtu(size: Integer(mtu)) if mtu
xml.driver(driver_options)
xml.driver(**driver_options)
xml.address(type: 'pci', bus: pci_bus, slot: pci_slot) if pci_bus and pci_slot
end
end.to_xml(

View File

@@ -194,6 +194,68 @@ module VagrantPlugins
end
end
# Launch security
launchSecurity = REXML::XPath.first(xml_descr, '/domain/launchSecurity')
unless config.launchsecurity_data.nil?
if launchSecurity.nil?
@logger.debug "Launch security has been added"
launchSecurity = REXML::Element.new('launchSecurity', REXML::XPath.first(xml_descr, '/domain'))
descr_changed = true
end
if launchSecurity.attributes['type'] != config.launchsecurity_data[:type]
launchSecurity.attributes['type'] = config.launchsecurity_data[:type]
descr_changed = true
end
[:cbitpos, :policy, :reducedPhysBits].each do |setting|
setting_value = config.launchsecurity_data[setting]
element = REXML::XPath.first(launchSecurity, setting.to_s)
if !setting_value.nil?
if element.nil?
element = launchSecurity.add_element(setting.to_s)
descr_changed = true
end
if element.text != setting_value
@logger.debug "launchSecurity #{setting.to_s} config changed"
element.text = setting_value
descr_changed = true
end
else
if !element.nil?
launchSecurity.delete_element(setting.to_s)
descr_changed = true
end
end
end
controllers = REXML::XPath.each( xml_descr, '/domain/devices/controller')
memballoon = REXML::XPath.each( xml_descr, '/domain/devices/memballoon')
[controllers, memballoon].lazy.flat_map(&:lazy).each do |controller|
driver_node = REXML::XPath.first(controller, 'driver')
driver_node = controller.add_element('driver') if driver_node.nil?
descr_changed = true if driver_node.attributes['iommu'] != 'on'
driver_node.attributes['iommu'] = 'on'
end
else
unless launchSecurity.nil?
@logger.debug "Launch security to be deleted"
descr_changed = true
launchSecurity.parent.delete_element(launchSecurity)
end
REXML::XPath.each( xml_descr, '/domain/devices/controller') do | controller |
driver_node = REXML::XPath.first(controller, 'driver')
if !driver_node.nil?
descr_changed = true if driver_node.attributes['iommu']
driver_node.attributes.delete('iommu')
end
end
end
# Graphics
graphics = REXML::XPath.first(xml_descr, '/domain/devices/graphics')
if config.graphics_type != 'none'

View File

@@ -67,6 +67,7 @@ module VagrantPlugins
attr_accessor :management_network_domain
attr_accessor :management_network_mtu
attr_accessor :management_network_keep
attr_accessor :management_network_driver_iommu
# System connection information
attr_accessor :system_uri
@@ -81,6 +82,7 @@ module VagrantPlugins
attr_accessor :memory
attr_accessor :nodeset
attr_accessor :memory_backing
attr_accessor :memtunes
attr_accessor :channel
attr_accessor :cpus
attr_accessor :cpuset
@@ -95,6 +97,7 @@ module VagrantPlugins
attr_accessor :features_hyperv
attr_accessor :clock_offset
attr_accessor :clock_timers
attr_accessor :launchsecurity_data
attr_accessor :numa_nodes
attr_accessor :loader
attr_accessor :nvram
@@ -243,6 +246,7 @@ module VagrantPlugins
@management_network_domain = UNSET_VALUE
@management_network_mtu = UNSET_VALUE
@management_network_keep = UNSET_VALUE
@management_network_driver_iommu = UNSET_VALUE
# System connection information
@system_uri = UNSET_VALUE
@@ -254,6 +258,7 @@ module VagrantPlugins
@memory = UNSET_VALUE
@nodeset = UNSET_VALUE
@memory_backing = UNSET_VALUE
@memtunes = {}
@cpus = UNSET_VALUE
@cpuset = UNSET_VALUE
@cpu_mode = UNSET_VALUE
@@ -267,6 +272,7 @@ module VagrantPlugins
@features_hyperv = UNSET_VALUE
@clock_offset = UNSET_VALUE
@clock_timers = []
@launchsecurity_data = UNSET_VALUE
@numa_nodes = UNSET_VALUE
@loader = UNSET_VALUE
@nvram = UNSET_VALUE
@@ -528,6 +534,37 @@ module VagrantPlugins
config: config)
end
def memtune(config={})
if config[:type].nil?
raise "Missing memtune type"
end
unless ['hard_limit', 'soft_limit', 'swap_hard_limit'].include? config[:type]
raise "Memtune type '#{config[:type]}' not allowed (hard_limit, soft_limit, swap_hard_limit are allowed)"
end
if config[:value].nil?
raise "Missing memtune value"
end
opts = config[:options] || {}
opts[:unit] = opts[:unit] || "KiB"
@memtunes[config[:type]] = { value: config[:value], config: opts }
end
def launchsecurity(options = {})
if options.fetch(:type) != 'sev'
raise "Launch security type only supports SEV. Explicitly set 'sev' as a type"
end
@launchsecurity_data = {}
@launchsecurity_data[:type] = options[:type]
@launchsecurity_data[:cbitpos] = options[:cbitpos] || 47
@launchsecurity_data[:reducedPhysBits] = options[:reducedPhysBits] || 1
@launchsecurity_data[:policy] = options[:policy] || "0x0003"
end
def input(options = {})
if options[:type].nil? || options[:bus].nil?
raise 'Input type AND bus must be specified'
@@ -916,6 +953,7 @@ module VagrantPlugins
@management_network_domain = nil if @management_network_domain == UNSET_VALUE
@management_network_mtu = nil if @management_network_mtu == UNSET_VALUE
@management_network_keep = false if @management_network_keep == UNSET_VALUE
@management_network_driver_iommu = false if @management_network_driver_iommu == UNSET_VALUE
# Domain specific settings.
@title = '' if @title == UNSET_VALUE
@@ -954,6 +992,7 @@ module VagrantPlugins
@features_hyperv = [] if @features_hyperv == UNSET_VALUE
@clock_offset = 'utc' if @clock_offset == UNSET_VALUE
@clock_timers = [] if @clock_timers == UNSET_VALUE
@launchsecurity_data = nil if @launchsecurity_data == UNSET_VALUE
@numa_nodes = @numa_nodes == UNSET_VALUE ? nil : _generate_numa
@loader = nil if @loader == UNSET_VALUE
@nvram = nil if @nvram == UNSET_VALUE
@@ -1224,6 +1263,8 @@ module VagrantPlugins
c += other.floppies
result.floppies = c
result.memtunes = memtunes.merge(other.memtunes)
result.disk_driver_opts = disk_driver_opts.merge(other.disk_driver_opts)
result.inputs = inputs != UNSET_VALUE ? inputs.dup + (other.inputs != UNSET_VALUE ? other.inputs : []) : other.inputs

View File

@@ -45,6 +45,13 @@
<<%= backing[:name] %> <%= backing[:config].map { |k,v| "#{k}='#{v}'"}.join(' ') %>/>
<%- end -%>
</memoryBacking>
<%- end -%>
<%- unless @memtunes.empty? -%>
<memtune>
<%- @memtunes.each do |name, options| -%>
<<%= name %> <%= options[:config].map { |k,v| "#{k}='#{v}'"}.join(' ') %>><%= options[:value] %></<%= name %>>
<%- end -%>
</memtune>
<%- end%>
<%- if !@cpu_affinity.empty? || @shares -%>
<cputune>
@@ -229,7 +236,11 @@
</channel>
<%- end -%>
<%- @inputs.each do |input| -%>
<input type='<%= input[:type] %>' bus='<%= input[:bus] %>'/>
<input type='<%= input[:type] %>' bus='<%= input[:bus] %>'>
<%- unless @launchsecurity_data.nil? -%>
<driver iommu='on' />
<%- end -%>
</input>
<%- end -%>
<%- if !@sound_type.nil? -%>
<%# Sound device-%>
@@ -265,6 +276,9 @@
<%- if @rng[:model] == "random"%>
<rng model='virtio'>
<backend model='random'>/dev/random</backend>
<%- unless @launchsecurity_data.nil? -%>
<driver iommu='on' />
<%- end -%>
</rng>
<%- end -%>
<%-
@@ -338,18 +352,32 @@
<%- end -%>
<%- if not @usbctl_dev.empty? -%>
<%# USB Controller -%>
<controller type='usb' model='<%= @usbctl_dev[:model] %>' <%= "ports=\"#{@usbctl_dev[:ports]}\" " if @usbctl_dev[:ports] %>/>
<controller type='usb' model='<%= @usbctl_dev[:model] %>' <%= "ports=\"#{@usbctl_dev[:ports]}\" " if @usbctl_dev[:ports] %>>
<%- unless @launchsecurity_data.nil? -%>
<driver iommu='on' />
<%- end -%>
</controller>
<%- end -%>
<%- unless @memballoon_enabled.nil? -%>
<%- if @memballoon_enabled -%>
<memballoon model='<%= @memballoon_model %>'>
<address type='pci' domain='0x0000' bus='<%= @memballoon_pci_bus %>' slot='<%= @memballoon_pci_slot %>' function='0x0'/>
<%- unless @launchsecurity_data.nil? -%>
<driver iommu='on' />
<%- end -%>
</memballoon>
<%- else -%>
<memballoon model='none'/>
<%- end -%>
<%- end -%>
</devices>
<%- unless @launchsecurity_data.nil? -%>
<launchSecurity type='<%= @launchsecurity_data[:type] %>'>
<cbitpos><%= @launchsecurity_data[:cbitpos] %></cbitpos>
<reducedPhysBits><%= @launchsecurity_data[:reducedPhysBits] %></reducedPhysBits>
<policy><%= @launchsecurity_data[:policy] %></policy>
</launchSecurity>
<%- end -%>
<%- if not @qemu_args.empty? or not @qemu_env.empty? -%>
<qemu:commandline>
<%- @qemu_args.each do |arg| -%>

View File

@@ -12,12 +12,15 @@
<% end %>
<model type='<%=@model_type%>'/>
<% if @driver_name and @driver_queues %>
<driver name='<%=@driver_name%>' queues='<%=@driver_queues%>'/>
<driver <% if @driver_iommu %> iommu="on" <% end %> name='<%=@driver_name%>' queues='<%=@driver_queues%>'/>
<% elsif @driver_queues %>
<driver queues='<%=@driver_queues%>'/>
<driver <% if @driver_iommu %> iommu="on" <% end %> queues='<%=@driver_queues%>'/>
<% elsif @driver_name %>
<driver name='<%=@driver_name%>'/>
<driver <% if @driver_iommu %> iommu="on" <% end %> name='<%=@driver_name%>'/>
<% elsif @driver_iommu %>
<driver iommu='on' />
<% end %>
<% if @ovs %>
<virtualport type='openvswitch'>
<% if @ovs_interfaceid %>

View File

@@ -33,6 +33,7 @@ module VagrantPlugins
management_network_domain = env[:machine].provider_config.management_network_domain
management_network_mtu = env[:machine].provider_config.management_network_mtu
management_network_keep = env[:machine].provider_config.management_network_keep
management_network_driver_iommu = env[:machine].provider_config.management_network_driver_iommu
logger.info "Using #{management_network_name} at #{management_network_address} as the management network #{management_network_mode} is the mode"
begin
@@ -74,7 +75,7 @@ module VagrantPlugins
}
end
management_network_options[:driver_iommu] = management_network_driver_iommu
unless management_network_mac.nil?
management_network_options[:mac] = management_network_mac

View File

@@ -73,6 +73,10 @@ RSpec.configure do |config|
# don't run acceptance tests by default
config.filter_run_excluding :acceptance => true
config.expect_with :rspec do |c|
c.max_formatted_output_length = 2000 if c.respond_to?("max_formatted_output_length=")
end
end
begin

View File

@@ -185,6 +185,38 @@ describe VagrantPlugins::ProviderLibvirt::Action::CreateDomain do
end
end
context 'launchSecurity' do
let(:vagrantfile_providerconfig) do
<<-EOF
libvirt.launchsecurity :type => 'sev', :cbitpos => 47, :reducedPhysBits => 1, :policy => "0x0003"
EOF
end
it 'should emit the settings to the ui' do
expect(ui).to receive(:info).with(/ -- Launch security: type=sev, cbitpos=47, reducedPhysBits=1, policy=0x0003/)
expect(servers).to receive(:create).and_return(machine)
expect(subject.call(env)).to be_nil
end
end
context 'memtunes' do
let(:vagrantfile_providerconfig) do
<<-EOF
libvirt.memtune :type => 'hard_limit', :value => 250000
libvirt.memtune :type => 'soft_limit', :value => 200000
EOF
end
it 'should emit the settings to the ui' do
expect(ui).to receive(:info).with(/ -- Memory Tuning: hard_limit: unit='KiB', value: 250000/)
expect(ui).to receive(:info).with(/ -- Memory Tuning: soft_limit: unit='KiB', value: 200000/)
expect(servers).to receive(:create).and_return(machine)
expect(subject.call(env)).to be_nil
end
end
context 'sysinfo' do
let(:domain_xml_file) { 'sysinfo.xml' }
let(:vagrantfile_providerconfig) do

View File

@@ -41,7 +41,8 @@
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<input type='mouse' bus='ps2'>
</input>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>

View File

@@ -35,7 +35,8 @@
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<input type='mouse' bus='ps2'>
</input>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>

View File

@@ -35,7 +35,8 @@
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<input type='mouse' bus='ps2'>
</input>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>

View File

@@ -58,7 +58,8 @@
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<input type='mouse' bus='ps2'>
</input>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>

View File

@@ -41,7 +41,8 @@
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<input type='mouse' bus='ps2'>
</input>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>

View File

@@ -41,7 +41,8 @@
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<input type='mouse' bus='ps2'>
</input>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>

View File

@@ -4,6 +4,7 @@ require_relative '../../spec_helper'
require 'vagrant-libvirt/errors'
require 'vagrant-libvirt/action/start_domain'
require 'vagrant-libvirt/util/unindent'
describe VagrantPlugins::ProviderLibvirt::Action::StartDomain do
subject { described_class.new(app, env) }
@@ -226,6 +227,108 @@ describe VagrantPlugins::ProviderLibvirt::Action::StartDomain do
end
end
context 'launchSecurity' do
let(:updated_domain_xml_new_launch_security) {
new_xml = domain_xml.dup
new_xml.gsub!(
/<\/devices>/,
<<-EOF.unindent.rstrip
</devices>
<launchSecurity type='sev'>
<cbitpos>47</cbitpos>
<reducedPhysBits>1</reducedPhysBits>
<policy>0x0003</policy>
</launchSecurity>
EOF
)
new_xml
}
it 'should create if not already set' do
machine.provider_config.launchsecurity_data = {:type => 'sev', :cbitpos => 47, :reducedPhysBits => 1, :policy => "0x0003"}
expect(ui).to_not receive(:warn)
expect(connection).to receive(:define_domain).and_return(libvirt_domain)
expect(libvirt_domain).to receive(:xml_desc).and_return(domain_xml, updated_domain_xml_new_launch_security)
expect(libvirt_domain).to receive(:autostart=)
expect(domain).to receive(:start)
expect(subject.call(env)).to be_nil
end
context 'already exists' do
let(:domain_xml_launch_security) { updated_domain_xml_new_launch_security }
let(:updated_domain_xml_launch_security) {
new_xml = domain_xml_launch_security.dup
new_xml.gsub!(/<cbitpos>47/, '<cbitpos>48')
new_xml.gsub!(/<reducedPhysBits>1/, '<reducedPhysBits>2')
new_xml.gsub!(/<policy>0x0003/, '<policy>0x0004')
new_xml
}
it 'should update all settings' do
machine.provider_config.launchsecurity_data = {:type => 'sev', :cbitpos => 48, :reducedPhysBits => 2, :policy => "0x0004"}
expect(ui).to_not receive(:warn)
expect(connection).to receive(:define_domain).and_return(libvirt_domain)
expect(libvirt_domain).to receive(:xml_desc).and_return(domain_xml_launch_security, updated_domain_xml_launch_security)
expect(libvirt_domain).to receive(:autostart=)
expect(domain).to receive(:start)
expect(subject.call(env)).to be_nil
end
it 'should remove if disabled' do
machine.provider_config.launchsecurity_data = nil
expect(ui).to_not receive(:warn)
expect(connection).to receive(:define_domain).and_return(libvirt_domain)
expect(libvirt_domain).to receive(:xml_desc).and_return(domain_xml_launch_security, domain_xml)
expect(libvirt_domain).to receive(:autostart=)
expect(domain).to receive(:start)
expect(subject.call(env)).to be_nil
end
context 'with controllers' do
# makes domain_xml contain 2 controllers and memballoon
# which should mean that launchsecurity element exists, but without
# iommu set on controllers
let(:test_file) { 'existing.xml' }
let(:updated_domain_xml_launch_security_controllers) {
new_xml = updated_domain_xml_new_launch_security.dup
new_xml.gsub!(
/<controller type='pci' index='0' model='pci-root'\/>/,
"<controller type='pci' index='0' model='pci-root'><driver iommu='on'/></controller>",
)
new_xml.gsub!(
/(<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'\/>)/,
'\1<driver iommu="on"/>',
)
# memballoon
new_xml.gsub!(
/(<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'\/>)/,
'\1<driver iommu="on"/>',
)
new_xml
}
it 'should set driver iommu on all controllers' do
machine.provider_config.launchsecurity_data = {:type => 'sev', :cbitpos => 47, :reducedPhysBits => 1, :policy => "0x0003"}
expect(ui).to_not receive(:warn)
expect(connection).to receive(:define_domain).and_return(libvirt_domain)
expect(libvirt_domain).to receive(:xml_desc).and_return(domain_xml_launch_security, updated_domain_xml_launch_security_controllers)
expect(libvirt_domain).to receive(:autostart=)
expect(domain).to receive(:start)
expect(subject.call(env)).to be_nil
end
end
end
end
context 'graphics' do
context 'autoport not disabled' do
let(:test_file) { 'existing.xml' }

View File

@@ -704,6 +704,31 @@ describe VagrantPlugins::ProviderLibvirt::Config do
end
end
describe '#launchsecurity' do
it 'should reject invalid type' do
expect { subject.launchsecurity(:type => 'bad') }.to raise_error("Launch security type only supports SEV. Explicitly set 'sev' as a type")
end
it 'should save when valid' do
expect(subject.launchsecurity(:type => 'sev', :cbitpos => 47, :reducedPhysBits => 1, :policy => "0x0003")).to be_truthy
end
end
describe '#memtune' do
it 'should raise an exception without type' do
expect { subject.memtune(:value => 250000) }.to raise_error('Missing memtune type')
end
it 'should raise an exception if type unrecognized' do
expect { subject.memtune(:type => 'limit', :value => 250000) }.to raise_error('Memtune type \'limit\' not allowed (hard_limit, soft_limit, swap_hard_limit are allowed)')
end
it 'should accept multiple calls' do
expect(subject.memtune(:type => 'hard_limit', :value => 250000)).to be_truthy
expect(subject.memtune(:type => 'soft_limit', :value => 200000)).to be_truthy
end
end
def assert_invalid
subject.finalize!
errors = subject.validate(machine).values.first
@@ -1045,6 +1070,27 @@ describe VagrantPlugins::ProviderLibvirt::Config do
subject { one.merge(two) }
context 'memtunes' do
it 'should merge where type is different' do
one.memtune(type: 'hard_limit', value: '250000')
two.memtune(type: 'soft_limit', value: '200000')
subject.finalize!
expect(subject.memtunes).to eq({
'hard_limit' => {value: '250000', config: {unit: 'KiB'}},
'soft_limit' => {value: '200000', config: {unit: 'KiB'}},
})
end
it 'should override where type is the same' do
one.memtune(type: 'hard_limit', value: '250000')
two.memtune(type: 'hard_limit', value: '200000')
subject.finalize!
expect(subject.memtunes).to eq({
'hard_limit' => {value: '200000', config: {unit: 'KiB'}},
})
end
end
context 'storage' do
context 'with disks' do
context 'assigned specific devices' do

View File

@@ -13,6 +13,10 @@
<numatune>
<memory nodeset='1-4,^3,6'/>
</numatune>
<memtune>
<hard_limit unit='KiB'>250000</hard_limit>
<soft_limit unit='KiB'>200000</soft_limit>
</memtune>
<cputune>
<vcpupin vcpu="0" cpuset="0" />
<shares>1024</shares>
@@ -107,7 +111,8 @@
<source path='/tmp/foo'/>
<target type='guestfwd' address='192.0.2.42' port='4242'/>
</channel>
<input type='mouse' bus='ps2'/>
<input type='mouse' bus='ps2'>
</input>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'>
<gl enable='yes'/>
</graphics>
@@ -154,7 +159,8 @@
<device path='/dev/tpm0'/>
</backend>
</tpm>
<controller type='usb' model='nec-xhci' ports="4" />
<controller type='usb' model='nec-xhci' ports="4" >
</controller>
</devices>
<qemu:commandline>
<qemu:arg value='-device'/>

View File

@@ -31,7 +31,8 @@
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<input type='mouse' bus='ps2'>
</input>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>

View File

@@ -29,7 +29,8 @@
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<input type='mouse' bus='ps2'>
</input>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>

View File

@@ -29,7 +29,8 @@
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<input type='mouse' bus='ps2'>
</input>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>

View File

@@ -36,7 +36,8 @@
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<input type='mouse' bus='ps2'>
</input>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>

View File

@@ -36,7 +36,8 @@
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<input type='mouse' bus='ps2'>
</input>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>

View File

@@ -122,7 +122,8 @@
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<input type='mouse' bus='ps2'>
</input>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>

View File

@@ -67,6 +67,8 @@ describe 'templates/domain' do
domain.clock_timer(name: 't2', track: 'b', tickpolicy: 'c', frequency: 'd', mode: 'e', present: 'yes')
domain.hyperv_feature(name: 'spinlocks', state: 'on', retries: '4096')
domain.cputopology(sockets: '1', cores: '3', threads: '2')
domain.memtune(type: 'hard_limit', value: '250000')
domain.memtune(type: 'soft_limit', value: '200000')
domain.cpuaffinitiy(0 => '0')
domain.machine_type = 'pc-compatible'
domain.machine_arch = 'x86_64'

View File

@@ -29,7 +29,8 @@
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<input type='mouse' bus='ps2'>
</input>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>

View File

@@ -29,7 +29,8 @@
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<input type='mouse' bus='ps2'>
</input>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>