New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add sysinfo support #1500
add sysinfo support #1500
Conversation
Just noticed: I forgot to add a section to the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As these are only set for create, and will not be updated on subsequent starts, can we presume that changing this after first boot should not be supported?
I'm thinking it's probably a bad idea to change the serial, but just wanted to check in case you use case needed to consider preserving the serial to use across subsequent halts and bring back up.
Is being able to update after creation something you would need to be able to do for debugging or is it always going to to cause more issues than would be useful?
Yes, as we try to replicate "real" HW, we don't need to update the serial number between a I'm okay with an "update" in between a |
<%- if @dmi_system_serial -%> | ||
<sysinfo type='smbios'> | ||
<system> | ||
<entry name='serial'><%= @dmi_system_serial %></entry> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this and at https://libvirt.org/formatdomain.html#smbios-system-information I think might need to come up with a way to avoid needing a new config attribute for each entry that would be useful.
It feels like would want to support being able to configure using something like the following:
require 'securerandom'
Vagrant.configure("2") do |config|
config.vm.provider :libvirt do |libvirt|
libvirt.sysinfo :section => 'system', :serial => SecureRandom.alphanumeric(8).upcase
end
end
Could try to keep this relatively simple by just having it store in a hash and then expand in the template based on the keys and nested items:
<%- unless @sysinfo.empty? -%>
<smbios mode='sysinfo'/>
<% end -%>
</os>
<%- unless @sysinfo.empty? -%>
<sysinfo type='smbios'>
<%- @sysinfo.each |section, entries| do -%>
<<%= section %>>
<%- @entries.each |name, value| do -%>
<entry name='<%= name %>'><%= value %></entry>
<% end -%>
</<%= section %>>
<% end -%>
</sysinfo>
<% end -%>
That should provide the ability to control most of sysinfo, and we can work out in the future potentially what ones to allow modification for subsequent boots when adding support to the start domain, for now leave it as all only being settable on create.
Can also add validation in the future for what sections are known to be valid, though might want to be soft errors where the user is warned rather than aborting if there is a mismatch.
Does this seem reasonable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nearly forgot, one possible improvement would be to maintain an internal list of recognized entries that can automatically be assigned a corresponding section if there is concern about making the end user interface as simple as possible.
e.g. support the following:
require 'securerandom'
Vagrant.configure("2") do |config|
config.vm.provider :libvirt do |libvirt|
libvirt.sysinfo :serial => SecureRandom.alphanumeric(8).upcase
end
end
Where the function:
def sysinfo(options = {})
entry_sections = {
:serial => :system,
}
section = options.del(:section)
entry = Hash[*options.first]
raise "Section required for sysinfo entry #{entry.keys.first}" if section.nil? and entry_sections.fetch(entry.keys.first).nil?
sysinfo_options[section] ||= {}
sysinfo_options[section].merge!(entry)
end
Possibly it might make it more robust to support explicit entry name and value with section being optional to avoid collisions and store as a hash:
Vagrant.configure("2") do |config|
config.vm.provider :libvirt do |libvirt|
libvirt.sysinfo :name => 'serial', :value => SecureRandom.alphanumeric(8).upcase
end
end
Either way I think need to avoid needing one config option per sysinfo entry as more are added.
@nsballmann hoping to do a release in a couple of days so if you're happy with my suggested changes to facilitate the addition of other sysinfo settings and can update the PR should be able to have it released in the next week |
@electrofelix To incorporate those parts is on my agenda for a few days now. I just haven't found the time to do so. I wanted to do sth. similar like you are proposing when I was implementing it. But being fairly new to Vagrant/Ruby syntax (and being more of a Python guy) I'm just unaware of the available options (and their syntax), so I chose to take the path I can walk on (essentially frankensteining the But with a release next week looming, I can prioritize this and get it done today or tomorrow. |
Hm... I just noticed: Maybe it's better at the beginning of |
@electrofelix Please review. :) |
I still need to add some basic unit tests. |
I don't understand the errors I get with the tests. Please help. |
Sorry, had hoped to get to review it today, hopefully tomorrow |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the main thing is about moving the implicit validation to the config object and making it more explicit validation. And whether to add an entry to the merge function to handle merging correctly.
Other than that there are some tricks to condensing various bits here and there, but those aren't too important.
@sysinfo = {} | ||
@sysinfo_blocks = { | ||
'bios' => [ 'vendor', 'version', 'date', 'release' ], | ||
'system' => [ 'manufacturer', 'product', 'version', 'serial', 'uuid', 'sku', 'family' ], | ||
'base_board'=> [ 'manufacturer', 'product', 'version', 'serial', 'asset', 'location' ], | ||
'chassis'=> [ 'manufacturer', 'version', 'serial', 'asset', 'sku' ] | ||
} | ||
@sysinfo_blocks.each do |block_name, valid_entries| | ||
if config.sysinfo.has_key?(block_name) and not config.sysinfo[block_name].empty? | ||
has_valid_entries = false | ||
valid_entries.each do |entry_name| | ||
if config.sysinfo[block_name].has_key?(entry_name) | ||
has_valid_entries = true | ||
end | ||
end | ||
if has_valid_entries | ||
@sysinfo[block_name] = {} | ||
end | ||
valid_entries.each do |entry_name| | ||
@sysinfo[block_name][entry_name] = config.sysinfo[block_name][entry_name] | ||
end | ||
end | ||
end | ||
if config.sysinfo.has_key?('oem_strings') and not config.sysinfo[:oem_strings].empty? | ||
@sysinfo['oem_strings'] = config.sysinfo['oem_strings'] | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like effectively validation which should really be done in the config object if it is considered a good idea to filter out invalid entries, and also consider whether to either emit a warning or trigger an error.
Otherwise typos as someone is writing a Vagrantfile cannot be detected by the vagrant validate
command and mistakes only get picked up when the machine has been brought up.
I appreciate there is probably more churn than you were expecting, so I'm happy to help move stuff around if that's alright with you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I addressed this in 31a13ba.
unless @sysinfo.empty? | ||
env[:ui].info(" -- Sysinfo:") | ||
|
||
if @sysinfo.has_key?("bios") | ||
env[:ui].info(" -- BIOS:") | ||
@sysinfo[:bios].each do |key, value| | ||
env[:ui].info(" -> #{key}: #{value}") | ||
end | ||
end | ||
|
||
if @sysinfo.has_key?("system") | ||
env[:ui].info(" -- System:") | ||
@sysinfo[:system].each do |key, value| | ||
env[:ui].info(" -> #{key}: #{value}") | ||
end | ||
end | ||
|
||
if @sysinfo.has_key?("base_board") | ||
env[:ui].info(" -- Base Board:") | ||
@sysinfo[:base_board].each do |key, value| | ||
env[:ui].info(" -> #{key}: #{value}") | ||
end | ||
end | ||
|
||
if @sysinfo.has_key?("chassis") | ||
env[:ui].info(" -- Chassis:") | ||
@sysinfo[:chassis].each do |key, value| | ||
env[:ui].info(" -> #{key}: #{value}") | ||
end | ||
end | ||
|
||
if @sysinfo.has_key?("oem_strings") | ||
env[:ui].info(" -- OEM Strings:") | ||
@sysinfo[:oem_strings].each do |value| | ||
env[:ui].info(" -> #{value}") | ||
end | ||
end | ||
end | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can be condensed a bit:
unless @sysinfo.empty?
env[:ui].info(" -- Sysinfo:")
sections = [
[:bios, "BIOS"],
[:system, "System"],
[:base_board, "Base Board"],
[:oem_strings, "OEM Strings"],
]
sections.each do |section_key, description|
section = @sysinfo.fetch(section_key, [])
unless section.empty?
env[:ui].info(" -- #{description}:")
section.each do |key, value|
env[:ui].info(" -> #{key}: #{value}")
end
end
end
lib/vagrant-libvirt/config.rb
Outdated
@@ -901,6 +906,7 @@ def finalize! | |||
@tpm_type = 'passthrough' if @tpm_type == UNSET_VALUE | |||
@tpm_path = nil if @tpm_path == UNSET_VALUE | |||
@tpm_version = nil if @tpm_version == UNSET_VALUE | |||
@sysinfo = {} if @sysinfo == UNSET_VALUE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given the hash is being exposed directly it means that users have no concept of what can and cannot be set in the hash, adding bad values will just be ignored but there is nothing to give them any helpful suggestions or alert them when what has been inputted will be ignored.
This comes back to the implicit validation that is currently in the create domain. I think better to move that to this class and make one of two choices:
- provide a wrapper that performs validation on hash at input
- move the code confirming valid sections and entries to the validation function to run after the various Vagrantfile's are merged (I'm leaning towards this and happy to help move the code if this makes sense to you)
Probably will also want to add an entry to the merge function to ensure any sysinfo settings provided by the box Vagrantfile are deep merged with the project Vagrantfile sysinfo settings. I think it can be done via the following as we know that there are only two levels and they are hashes so there is no need to use a very complex deep merge:
result.sysinfo = sysinfo.merge(other.sysinfo) { |_k, x, y| x.merge(y) }
Though it's probably worth checking if that is desirable behaviour. I'm guessing it's likely better than requiring needing to provide a complete sysinfo hash to override a single entry?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<%- unless @sysinfo.empty? -%> | ||
<sysinfo type='smbios'> | ||
<%- if @sysinfo.has_key?('bios') -%> | ||
<bios> | ||
<%- @sysinfo[:bios].each do |key, value| -%> | ||
<entry name='<%= key %>'><%= value %></entry> | ||
<% end -%> | ||
</bios> | ||
<% end -%> | ||
<%- if @sysinfo.has_key?('system') -%> | ||
<system> | ||
<%- @sysinfo[:system].each do |key, value| -%> | ||
<entry name='<%= key %>'><%= value %></entry> | ||
<% end -%> | ||
</system> | ||
<% end -%> | ||
<%- if @sysinfo.has_key?('base_board') -%> | ||
<baseBoard> | ||
<%- @sysinfo[:base_board].each do |key, value| -%> | ||
<entry name='<%= key %>'><%= value %></entry> | ||
<% end -%> | ||
</baseBoard> | ||
<% end -%> | ||
<%- if @sysinfo.has_key?('chassis') -%> | ||
<chassis> | ||
<%- @sysinfo[:chassis].each do |key, value| -%> | ||
<entry name='<%= key %>'><%= value %></entry> | ||
<% end -%> | ||
</chassis> | ||
<% end -%> | ||
<%- if @sysinfo.has_key?('oem_strings') -%> | ||
<oemStrings> | ||
<%- @sysinfo[:oem_strings].each do |value| -%> | ||
<entry><%= value %></entry> | ||
<% end -%> | ||
</oemStrings> | ||
<% end -%> | ||
</sysinfo> | ||
<% end -%> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if there is some restriction with erb and string vs symbols conversion, I'd suggest using the same rather than switching between when you check the key and then attempt to access it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assumed they are the same and just found out they are not, especially when used as key in a hash. Now I do understand your comment. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I addressed this in 8e12797.
I think the renderer can't handle the nested hashes... I think we have to rely on a flatter hierarchy. |
I'm starting to not like Ruby. I will stop here. If somebody wants to pick up the pieces, feel free. I'm done. |
@nsballmann I'll look to get it over the finish line over the next few days |
@nsballmann sorry to say the problem is my fault rather than ruby. The domain template test is one I wrote very early on before I really had any appreciation for how to write tests for vagrant plugins and it shows in the size of the foot canon I created. I looked at this a couple of times over the last week and didn't understand what the issue was, just assumed it was something to do with mixing symbols and strings and a typo. It's actually caused by the test code using the config object as a proxy for what happens in the create domain action: vagrant-libvirt/spec/unit/templates/domain_spec.rb Lines 11 to 24 in 66e0fa7
I had completely forgotten that this worked by assuming the instance variable names would remain the same between both. I'll rework the test and create domain action over the following days to fix this so it's a lot more sane, by moving to a helper that can be used both by the tests and the create domain action rather than trying to have a kludge that relies on the config object. The problem you were running into was that the contents of
Since that is what is defined on the config object, except that is not what is defined in create domain nor the structure that is expected by the template. |
@electrofelix Ah okay... that makes sense. Rebasing today probably didn't include your changes, yet. Please notify me when you're done, then I'll rebase. :) |
No impact, I've been tossing up whether to use a class delegating to OpenStruct to wrap the template rendering so that it can be re-used from both the create domain action and the tests, vs using a proper model that is included in both the config and a template helper class so that there is a single place for lots of the attributes to be defined instead of 3 or 4 which definitely contributed to making this much harder for you to contribute. I'll look to try and update your PR in the next couple of days, once I've thought a little more and decided whether it needs the underlying problem tackled first to unpick the mess or it'll remain reasonably easy to sort after landing this. |
So I also managed to remind myself of one of the glaring annoying issues around config validation, in that it's not possible to warn users about things being skipped outside of the validation function. This means need to retain the original sysinfo in the config object instead of filtering during I've also run into some fun where lots of strings as keys are getting converted to symbols without it being exactly clear what is happening. Possibly frozen strings are causing a few issues here, but the solution appears to be just to ensure lots of use of I may move some stuff around again at a later time when possibly switching to model for the domain pieces to allow reuse between config, create domain and tests for both, for now it should work with the additional tests covering all areas. This was definitely far more troublesome that I originally thought when asking you to extend to cover all of the sysinfo parts, sorry about that. |
@nsballmann I'll try and summarize what way I've gone with the changes, let me know what you think, as I'd like to get some feedback based on how you hope to use it as there is no guarantee I've got it right.
|
This reverts commit 0481d68.
This reverts commit 6eda303.
This reverts commit 83c21d6.
avoid defining the valid sysinfo block details as an attribute on the config class to prevent confusion about what should appear with the template helper. refactor the validation to be more strict on bad block names or entries with specific supported names, retaining the warning on empty entries being skipped.
@nsballmann did you get a chance to have a look to see if my tweaks made sense? Or do they cause any issues? Sorry for the delay in following up, I was trying to line up a change to migrate the docs to GitHub pages |
I'm sorry, I haven't had the time, yet. I will have a look next week. |
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>
For testing certain scenarios with vagrant-libvirt, we need in the guest system a value for the systems serial number in the DMI/SMBIOS system information. The domain format of libvirt allows to specify those values.
This PR adds support to do that.
While adding
-smbios type=1,serial=$serial_value
to theqemuargs
parameter of the libvirt provider is already able to achieve this, a dedicated provider config value adds native support from theVagrantfile
layering system. For example, in the.box
included~/.vagrant.d/boxes/${box_name}/${box_version}/libvirt/Vagrantfile
a random serial number can be enforced by adding the following:Then in an instance specific
~/workspaces/vagrant/box_with_serial_ABCDEFGH/Vagrantfile
this value can be overwritten by adding:In the PR this is implemented in a (hopefully) purely optional fashion.