mirror of
https://github.com/vagrant-libvirt/vagrant-libvirt.git
synced 2025-02-25 18:55:27 -06:00
Avoid setting cpu element on unsupported architectures (#1633)
The CPU element to manage the mode, model, features (including nested), is only available on some architectures. To allow this plugin to generate XML valid for other architectures such as RISC-V, the CPU element needs to be optional and only enabled when the architecture specified supports it. Include checks in the validation section to help prevent the setting of an unsupported architecture with any of the CPU features that require the CPU element to be available. Fixes: #1538
This commit is contained in:
@@ -87,6 +87,7 @@ module VagrantPlugins
|
||||
end
|
||||
|
||||
# cpu_mode
|
||||
if !config.cpu_mode.nil?
|
||||
cpu = REXML::XPath.first(xml_descr, '/domain/cpu')
|
||||
if cpu.nil?
|
||||
@logger.debug "cpu_mode updated from not set to '#{config.cpu_mode}'"
|
||||
@@ -108,7 +109,7 @@ module VagrantPlugins
|
||||
@logger.debug "cpu_model updated from not set to '#{config.cpu_model}'"
|
||||
descr_changed = true
|
||||
cpu_model = REXML::Element.new('model', REXML::XPath.first(xml_descr, '/domain/cpu'))
|
||||
cpu_model.attributes['fallback'] = 'allow'
|
||||
cpu_model.attributes['fallback'] = config.cpu_fallback
|
||||
cpu_model.text = config.cpu_model
|
||||
end
|
||||
else
|
||||
@@ -161,6 +162,9 @@ module VagrantPlugins
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
xml_descr.delete_element('/domain/cpu')
|
||||
end
|
||||
|
||||
# Clock
|
||||
clock = REXML::XPath.first(xml_descr, '/domain/clock')
|
||||
|
||||
@@ -208,6 +208,14 @@ module VagrantPlugins
|
||||
# internal helper attributes
|
||||
attr_accessor :host_device_exclude_prefixes
|
||||
|
||||
# list of architectures that support cpu based on https://github.com/libvirt/libvirt/tree/master/src/cpu
|
||||
ARCH_SUPPORT_CPU = [
|
||||
'aarch64', 'armv6l', 'armv7b', 'armv7l',
|
||||
'i686', 'x86_64',
|
||||
'ppc64', 'ppc64le',
|
||||
's390', 's390x',
|
||||
]
|
||||
|
||||
def initialize
|
||||
@uri = UNSET_VALUE
|
||||
@driver = UNSET_VALUE
|
||||
@@ -913,12 +921,23 @@ module VagrantPlugins
|
||||
@title = '' if @title == UNSET_VALUE
|
||||
@description = '' if @description == UNSET_VALUE
|
||||
@uuid = '' if @uuid == UNSET_VALUE
|
||||
@machine_type = nil if @machine_type == UNSET_VALUE
|
||||
@machine_arch = nil if @machine_arch == 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_mode = if @cpu_mode == UNSET_VALUE
|
||||
# only some architectures support the cpu element
|
||||
if @machine_arch.nil? || ARCH_SUPPORT_CPU.include?(@machine_arch.downcase)
|
||||
'host-model'
|
||||
else
|
||||
nil
|
||||
end
|
||||
else
|
||||
@cpu_mode
|
||||
end
|
||||
@cpu_model = if (@cpu_model == UNSET_VALUE) && (@cpu_mode == 'custom')
|
||||
'qemu64'
|
||||
elsif @cpu_mode != 'custom'
|
||||
@@ -938,8 +957,6 @@ module VagrantPlugins
|
||||
@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_device = @disk_bus == 'scsi' ? 'sda' : 'vda' if @disk_device == UNSET_VALUE
|
||||
@disk_bus = @disk_device.start_with?('sd') ? 'scsi' : 'virtio' if @disk_bus == UNSET_VALUE
|
||||
@@ -1071,6 +1088,22 @@ module VagrantPlugins
|
||||
def validate(machine)
|
||||
errors = _detected_errors
|
||||
|
||||
unless @machine_arch.nil? || ARCH_SUPPORT_CPU.include?(@machine_arch.downcase)
|
||||
unsupported = [:cpu_mode, :cpu_model, :nested, :cpu_features, :cpu_topology, :numa_nodes]
|
||||
cpu_support_required_by = unsupported.select { |x|
|
||||
value = instance_variable_get("@#{x.to_s}")
|
||||
next if value.nil? # not set
|
||||
is_bool = !!value == value
|
||||
next if is_bool && !value # boolean and set to false
|
||||
next if !is_bool && value.empty? # not boolean, but empty '', [], {}
|
||||
true
|
||||
}
|
||||
|
||||
unless cpu_support_required_by.empty?
|
||||
errors << "Architecture #{@machine_arch} does not support /domain/cpu XML, which is required when setting the config options #{cpu_support_required_by.join(", ")}"
|
||||
end
|
||||
end
|
||||
|
||||
# technically this shouldn't occur, but ensure that if somehow it does, it gets rejected.
|
||||
if @cpu_mode == 'host-passthrough' && @cpu_model != ''
|
||||
errors << "cannot set cpu_model with cpu_mode of 'host-passthrough'. leave model unset or switch mode."
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<uuid><%= @uuid %></uuid>
|
||||
<memory><%= @memory_size %></memory>
|
||||
<vcpu<% if @cpuset %> cpuset='<%= @cpuset %>'<% end %>><%= @cpus %></vcpu>
|
||||
<%- unless @cpu_mode.nil? -%>
|
||||
<cpu mode='<%= @cpu_mode %>'>
|
||||
<%- if @cpu_mode != 'host-passthrough' -%>
|
||||
<model fallback='<%= @cpu_fallback %>'><% if @cpu_mode == 'custom' %><%= @cpu_model %><% end %></model>
|
||||
@@ -32,6 +33,7 @@
|
||||
</numa>
|
||||
<%- end -%>
|
||||
</cpu>
|
||||
<%- end -%>
|
||||
<%- if @nodeset -%>
|
||||
<numatune>
|
||||
<memory nodeset='<%= @nodeset %>'/>
|
||||
|
||||
@@ -161,6 +161,71 @@ describe VagrantPlugins::ProviderLibvirt::Action::StartDomain do
|
||||
end
|
||||
end
|
||||
|
||||
context 'cpu' do
|
||||
let(:test_file) { 'existing.xml' }
|
||||
let(:updated_domain_xml) {
|
||||
new_xml = domain_xml.dup
|
||||
new_xml.gsub!(
|
||||
/<cpu .*\/>/,
|
||||
<<-EOF
|
||||
<cpu check='partial' mode='custom'>
|
||||
<model fallback='allow'>Haswell</model>
|
||||
<feature name='vmx' policy='optional'/>
|
||||
<feature name='svm' policy='optional'/>
|
||||
</cpu>
|
||||
EOF
|
||||
)
|
||||
new_xml
|
||||
}
|
||||
let(:vagrantfile_providerconfig) {
|
||||
<<-EOF
|
||||
libvirt.cpu_mode = 'custom'
|
||||
libvirt.cpu_model = 'Haswell'
|
||||
libvirt.nested = true
|
||||
EOF
|
||||
}
|
||||
|
||||
it 'should set cpu related settings when changed' do
|
||||
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)
|
||||
expect(libvirt_domain).to receive(:autostart=)
|
||||
expect(domain).to receive(:start)
|
||||
|
||||
expect(subject.call(env)).to be_nil
|
||||
end
|
||||
|
||||
let(:domain_xml_no_cpu) {
|
||||
new_xml = domain_xml.dup
|
||||
new_xml.gsub!(/<cpu .*\/>/, '')
|
||||
new_xml
|
||||
}
|
||||
let(:updated_domain_xml_new_cpu) {
|
||||
new_xml = domain_xml.dup
|
||||
new_xml.gsub!(
|
||||
/<cpu .*\/>/,
|
||||
<<-EOF
|
||||
<cpu mode='custom'>
|
||||
<model fallback='allow'>Haswell</model>
|
||||
<feature name='vmx' policy='optional'/>
|
||||
<feature name='svm' policy='optional'/>
|
||||
</cpu>
|
||||
EOF
|
||||
)
|
||||
new_xml
|
||||
}
|
||||
|
||||
it 'should add cpu settings if not already present' do
|
||||
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_no_cpu, updated_domain_xml_new_cpu)
|
||||
expect(libvirt_domain).to receive(:autostart=)
|
||||
expect(domain).to receive(:start)
|
||||
|
||||
expect(subject.call(env)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'graphics' do
|
||||
context 'autoport not disabled' do
|
||||
let(:test_file) { 'existing.xml' }
|
||||
|
||||
@@ -676,6 +676,33 @@ describe VagrantPlugins::ProviderLibvirt::Config do
|
||||
expect(subject.channels).to match([a_hash_including({:target_name => 'com.redhat.spice.0'})])
|
||||
end
|
||||
end
|
||||
|
||||
context '@machine_arch and @cpu_*' do
|
||||
context 'should set @cpu_mode based on @machine_arch support' do
|
||||
# it's possible when this is unset that the host arch should be read
|
||||
it 'should default to host-model if machine_arch unset' do
|
||||
subject.finalize!
|
||||
|
||||
expect(subject.cpu_mode).to eq('host-model')
|
||||
end
|
||||
|
||||
it 'should default to host-model if supported' do
|
||||
subject.machine_arch = 'aarch64'
|
||||
|
||||
subject.finalize!
|
||||
|
||||
expect(subject.cpu_mode).to eq('host-model')
|
||||
end
|
||||
|
||||
it 'should default to nil if unsupported' do
|
||||
subject.machine_arch = 'ppc'
|
||||
|
||||
subject.finalize!
|
||||
|
||||
expect(subject.cpu_mode).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def assert_invalid
|
||||
@@ -804,6 +831,32 @@ describe VagrantPlugins::ProviderLibvirt::Config do
|
||||
end
|
||||
end
|
||||
|
||||
context '@machine_arch and @cpu_*' do
|
||||
it 'should be valid if cpu_* settings and no arch set' do
|
||||
subject.cpu_mode = 'host-passthrough'
|
||||
subject.nested = true
|
||||
|
||||
assert_valid
|
||||
end
|
||||
|
||||
it 'should be valid if cpu_* settings and supported' do
|
||||
subject.machine_arch = 'aarch64'
|
||||
subject.cpu_mode = 'host-passthrough'
|
||||
subject.nested = true
|
||||
|
||||
assert_valid
|
||||
end
|
||||
|
||||
it 'should flag settings invalid if unsupported' do
|
||||
subject.machine_arch = 'ppc'
|
||||
subject.cpu_mode = 'host-passthrough'
|
||||
subject.nested = true
|
||||
|
||||
errors = assert_invalid
|
||||
expect(errors).to include(match(/Architecture ppc does not support .* cpu_mode, nested/))
|
||||
end
|
||||
end
|
||||
|
||||
context 'with cpu_mode and cpu_model defined' do
|
||||
it 'should discard model if mode is passthrough' do
|
||||
subject.cpu_mode = 'host-passthrough'
|
||||
|
||||
Reference in New Issue
Block a user