add sysinfo support (#1500)

For testing certain scenarios with vagrant-libvirt, need in the guest system a
value for the systems serial number in the DMI/SMBIOS system information.
The domain https://libvirt.org/formatdomain.html#smbios-system-information
format of libvirt allows to specify those values.

While adding `-smbios type=1,serial=$serial_value` to the `qemuargs` parameter
of the libvirt provider is already able to achieve this, a dedicated provider config
value adds native support from the `Vagrantfile` layering system. For example,
in the .box included Vagrantfile a random serial number can be enforced by
adding the following:

require 'securerandom'
Vagrant.configure("2") do |config|
  config.vm.provider :libvirt do |libvirt|
    libvirt.dmi_system_serial = SecureRandom.alphanumeric(8).upcase
  end
end

Then in an instance specific Vagrantfile this value can be overwritten by adding:

Vagrant.configure("2") do |config|
  config.vm.provider :libvirt do |libvirt|
    libvirt.dmi_system_serial = "ABCDEFGH"
  end
end

Co-authored-by: Nils Ballmann <nils.ballmann.ext@siemens.com>
Co-authored-by: Darragh Bailey <daragh.bailey@gmail.com>
This commit is contained in:
Nils Ballmann
2022-08-16 18:44:11 +02:00
committed by GitHub
parent 77e53a2f53
commit 63d265d9ca
11 changed files with 444 additions and 6 deletions

View File

@@ -170,6 +170,70 @@ describe VagrantPlugins::ProviderLibvirt::Action::CreateDomain do
expect(subject.call(env)).to be_nil
end
end
context 'sysinfo' do
let(:domain_xml_file) { 'sysinfo.xml' }
let(:vagrantfile_providerconfig) do
<<-EOF
libvirt.sysinfo = {
'bios': {
'vendor': 'Test Vendor',
'version': '',
},
'system': {
'manufacturer': 'Test Manufacturer',
'version': '0.1.0',
'serial': '',
},
'base board': {
'manufacturer': 'Test Manufacturer',
'version': '',
},
'chassis': {
'manufacturer': 'Test Manufacturer',
'serial': 'AABBCCDDEE',
'asset': '',
},
'oem strings': [
'app1: string1',
'app1: string2',
'app2: string1',
'app2: string2',
'',
'',
],
}
EOF
end
it 'should populate sysinfo as expected' do
expect(servers).to receive(:create).with(xml: domain_xml).and_return(machine)
expect(subject.call(env)).to be_nil
end
context 'with block of empty entries' do
let(:domain_xml_file) { 'sysinfo_only_required.xml' }
let(:vagrantfile_providerconfig) do
<<-EOF
libvirt.sysinfo = {
'bios': {
'vendor': 'Test Vendor',
},
'system': {
'serial': '',
},
}
EOF
end
it 'should skip outputting the surrounding tags' do
expect(servers).to receive(:create).with(xml: domain_xml).and_return(machine)
expect(subject.call(env)).to be_nil
end
end
end
end
context 'connection => qemu:///session' do

View File

@@ -0,0 +1,66 @@
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
<name>vagrant-test_default</name>
<title></title>
<description>Source: /rootpath/Vagrantfile</description>
<uuid></uuid>
<memory>524288</memory>
<vcpu>1</vcpu>
<cpu mode='host-model'>
<model fallback='allow'></model>
</cpu>
<os>
<type>hvm</type>
<kernel></kernel>
<initrd></initrd>
<cmdline></cmdline>
<smbios mode='sysinfo'/>
</os>
<sysinfo type='smbios'>
<bios>
<entry name='vendor'>Test Vendor</entry>
</bios>
<system>
<entry name='manufacturer'>Test Manufacturer</entry>
<entry name='version'>0.1.0</entry>
</system>
<baseBoard>
<entry name='manufacturer'>Test Manufacturer</entry>
</baseBoard>
<chassis>
<entry name='manufacturer'>Test Manufacturer</entry>
<entry name='serial'>AABBCCDDEE</entry>
</chassis>
<oemStrings>
<entry>app1: string1</entry>
<entry>app1: string2</entry>
<entry>app2: string1</entry>
<entry>app2: string2</entry>
</oemStrings>
</sysinfo>
<features>
<acpi/>
<apic/>
<pae/>
</features>
<clock offset='utc'>
</clock>
<devices>
<disk type='file' device='disk'>
<alias name='ua-box-volume-0'/>
<driver name='qemu' type='qcow2' cache='default'/>
<source file='/var/lib/libvirt/images/vagrant-test_default.img'/>
<target dev='vda' bus='virtio'/>
</disk>
<serial type='pty'>
<target port='0'/>
</serial>
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>
</video>
</devices>
</domain>

View File

@@ -0,0 +1,49 @@
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
<name>vagrant-test_default</name>
<title></title>
<description>Source: /rootpath/Vagrantfile</description>
<uuid></uuid>
<memory>524288</memory>
<vcpu>1</vcpu>
<cpu mode='host-model'>
<model fallback='allow'></model>
</cpu>
<os>
<type>hvm</type>
<kernel></kernel>
<initrd></initrd>
<cmdline></cmdline>
<smbios mode='sysinfo'/>
</os>
<sysinfo type='smbios'>
<bios>
<entry name='vendor'>Test Vendor</entry>
</bios>
</sysinfo>
<features>
<acpi/>
<apic/>
<pae/>
</features>
<clock offset='utc'>
</clock>
<devices>
<disk type='file' device='disk'>
<alias name='ua-box-volume-0'/>
<driver name='qemu' type='qcow2' cache='default'/>
<source file='/var/lib/libvirt/images/vagrant-test_default.img'/>
<target dev='vda' bus='virtio'/>
</disk>
<serial type='pty'>
<target port='0'/>
</serial>
<console type='pty'>
<target port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' keymap='en-us'/>
<video>
<model type='cirrus' vram='16384' heads='1'/>
</video>
</devices>
</domain>

View File

@@ -577,24 +577,28 @@ describe VagrantPlugins::ProviderLibvirt::Config do
def assert_invalid
subject.finalize!
errors = subject.validate(machine)
raise "No errors: #{errors.inspect}" if errors.values.all?(&:empty?)
errors = subject.validate(machine).values.first
expect(errors).to_not be_empty
errors
end
def assert_valid
subject.finalize!
errors = subject.validate(machine)
raise "Errors: #{errors.inspect}" unless errors.values.all?(&:empty?)
errors = subject.validate(machine).values.first
expect(errors).to be_empty
end
describe '#validate' do
before do
allow(machine).to receive(:provider_config).and_return(subject)
allow(machine).to receive(:ui).and_return(ui)
end
it 'is valid with defaults' do
assert_valid
end
context 'with disks defined' do
before { expect(machine).to receive(:provider_config).and_return(subject).at_least(:once) }
it 'is valid if relative path used for disk' do
subject.storage :file, path: '../path/to/file.qcow2'
assert_valid
@@ -709,6 +713,60 @@ describe VagrantPlugins::ProviderLibvirt::Config do
assert_valid
end
end
context 'with sysinfo defined' do
context 'when invalid block name provided' do
it 'should be invalid' do
subject.sysinfo = {'bad bios': {'vendor': 'some vendor'}}
errors = assert_invalid
expect(errors).to include(match(/invalid sysinfo element 'bad bios';/))
end
end
context 'when invalid element name provided' do
it 'should be invalid' do
subject.sysinfo = {'bios': {'bad vendor': 'some vendor'}}
errors = assert_invalid
expect(errors).to include(match(/'sysinfo.bios' does not support entry name 'bad vendor'/))
end
end
context 'when empty element value provided' do
it 'should succeed with a warning' do
expect(ui).to receive(:warn).with(/Libvirt Provider: sysinfo.bios.vendor is nil or empty/)
subject.sysinfo = {'bios': {'vendor': ''}}
assert_valid
end
end
context 'when handling "oem strings"' do
it 'should succeed' do
subject.sysinfo = {'oem strings': ['string 1']}
assert_valid
end
context 'when empty entries' do
it 'should succeed with a warning' do
expect(ui).to receive(:warn).with(/Libvirt Provider: 'sysinfo.oem strings' contains an empty/)
subject.sysinfo = {'oem strings': ['']}
assert_valid
end
end
context 'when non string passed' do
it 'should be invalid' do
subject.sysinfo = {'oem strings': [true]}
assert_invalid
end
end
end
end
end
describe '#merge' do
@@ -772,5 +830,27 @@ describe VagrantPlugins::ProviderLibvirt::Config do
include(name: 'hpet'))
end
end
context 'sysinfo' do
it 'should merge' do
one.sysinfo = {
'bios' => {'vendor': 'Some Vendor'},
'system' => {'manufacturer': 'some manufacturer'},
'oem strings' => ['string 1'],
}
two.sysinfo = {
'bios' => {'vendor': 'Another Vendor'},
'system' => {'serial': 'AABBCCDDEE'},
'oem strings' => ['string 2'],
}
subject.finalize!
expect(subject.sysinfo).to eq(
'bios' => {'vendor': 'Another Vendor'},
'system' => {'manufacturer': 'some manufacturer', 'serial': 'AABBCCDDEE'},
'oem strings' => ['string 1', 'string 2'],
)
end
end
end
end

View File

@@ -23,7 +23,16 @@
<kernel></kernel>
<initrd></initrd>
<cmdline></cmdline>
<smbios mode='sysinfo'/>
</os>
<sysinfo type='smbios'>
<system>
<entry name='serial'>AAAAAAAA</entry>
</system>
<oemStrings>
<entry>AAAAAAAA</entry>
</oemStrings>
</sysinfo>
<features>
<acpi/>
<apic/>

View File

@@ -16,6 +16,13 @@ describe 'templates/domain' do
def initialize
super
@domain_volumes = []
@sysinfo_blocks = {
'bios' => {:section => "BIOS", :xml => "bios"},
'system' => {:section => "System", :xml => "system"},
'base board' => {:section => "Base Board", :xml => "baseBoard"},
'chassis' => {:section => "Chassis", :xml => "chassis"},
'oem strings' => {:section => "OEM Strings", :xml => "oemStrings"},
}
end
def finalize!
@@ -100,6 +107,15 @@ describe 'templates/domain' do
domain.smartcard(mode: 'passthrough')
domain.tpm_path = '/dev/tpm0'
domain.sysinfo = {
'system' => {
'serial' => 'AAAAAAAA',
},
'oem strings' => [
'AAAAAAAA',
],
}
domain.qemuargs(value: '-device')
domain.qemuargs(value: 'dummy-device')