Skip to content

Commit

Permalink
Use equivalent-xml to compare xml (vagrant-libvirt#1564)
Browse files Browse the repository at this point in the history
Using diffy alone is insufficient to spot when the XML returned by
libvirt for the changes applied is equivalent but formatted differently.

When libvirt returns slightly differently formatted XML corresponding to
what changes were actually applied, it can result in an error being
assumed to have happened when the update actually worked as expected.

Equivalent-xml handles detecting XML documents as being identical,
therefore switching to using it instead of diffy should produce more
reliable results, while retaining diffy for better formatted diffs
should an issue occur is useful.

Fixes: vagrant-libvirt#1556
  • Loading branch information
electrofelix authored and mmguero committed Aug 29, 2022
1 parent 9f31650 commit d7de5e4
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 14 deletions.
34 changes: 20 additions & 14 deletions lib/vagrant-libvirt/action/start_domain.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# frozen_string_literal: true

require 'diffy'
require 'log4r'

require 'equivalent-xml'
require 'rexml/document'

module VagrantPlugins
Expand Down Expand Up @@ -425,24 +426,29 @@ def call(env)
end

begin
proposed = Nokogiri::XML(new_xml, &:noblanks)
# need to check whether the updated XML contains all the changes requested

# This normalizes the attribute order to be consistent across both XML docs to eliminate differences
# for subsequent comparison by diffy
updated_xml_descr = REXML::Document.new(libvirt_domain.xml_desc(1))
updated_xml = String.new
updated_xml_descr.write(updated_xml)
# This normalizes the attribute order to be consistent across both XML docs to
# eliminate differences for subsequent comparison by diffy
applied_xml_descr = REXML::Document.new(libvirt_domain.xml_desc(1))
applied_xml = String.new
applied_xml_descr.write(applied_xml)

proposed = Nokogiri::XML(new_xml, &:noblanks)
applied = Nokogiri::XML(applied_xml, &:noblanks)

updated = Nokogiri::XML(updated_xml, &:noblanks)
if !EquivalentXml.equivalent?(proposed, applied)
require 'diffy'

pretty_proposed = StringIO.new
pretty_updated = StringIO.new
proposed.write_xml_to(pretty_proposed, indent: 2)
updated.write_xml_to(pretty_updated, indent: 2)
# pretty print the XML as even though there can be additional changes,
# the output with diffy appears to be clearer
pretty_proposed = StringIO.new
pretty_applied = StringIO.new
proposed.write_xml_to(pretty_proposed, indent: 2)
applied.write_xml_to(pretty_applied, indent: 2)

diff = Diffy::Diff.new(pretty_proposed.string, pretty_updated.string, :context => 3).to_s(:text)
diff = Diffy::Diff.new(pretty_proposed.string, pretty_applied.string, :context => 3).to_s(:text)

unless diff.empty?
error_msg = "Libvirt failed to fully update the domain with the specified XML. Result differs from requested:\n" +
"--- requested\n+++ result\n#{diff}\n" +
"Typically this means there is a bug in the XML being sent, please log an issue"
Expand Down
25 changes: 25 additions & 0 deletions spec/unit/action/start_domain_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,31 @@
expect(subject.call(env)).to be_nil
end

context 'when xml is formatted differently' do
let(:test_file) { 'default_with_different_formatting.xml' }
let(:updated_domain_xml) {
new_xml = domain_xml.dup
new_xml.gsub!(/<cpu .*<\/cpu>/m, '<cpu check="none" mode="host-passthrough"/>')
new_xml
}
let(:vagrantfile_providerconfig) do
<<-EOF
libvirt.cpu_mode = "host-passthrough"
EOF
end

it 'should correctly detect the domain was updated' do
expect(ui).to_not receive(:error)
expect(libvirt_domain).to receive(:autostart=)
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(domain).to receive(:start)

expect(subject.call(env)).to be_nil
end

end

context 'when any setting changed' do
let(:vagrantfile_providerconfig) do
<<-EOF
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<domain xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0' type=''>
<name/>
<title/>
<description/>
<uuid/>
<memory/>
<vcpu>1</vcpu>
<cpu check="none" mode="host-model">


</cpu>
<os>
<type>hvm</type>
<kernel/>
<initrd/>
<cmdline/>
</os>
<features>
<acpi/>
<apic/>
<pae/>
</features>
<clock offset='utc'/>
<devices>
<serial type='pty'>
<target port='0'/>
</serial>
<console type='pty'>
<target port='0'/>
</console>
<input bus='ps2' type='mouse'/>
<graphics autoport='yes' keymap='en-us' listen='127.0.0.1' port='-1' type='vnc'/>
<video>
<model heads='1' type='cirrus' vram='16384'/>
</video>
</devices>
</domain>
1 change: 1 addition & 0 deletions vagrant-libvirt.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Gem::Specification.new do |s|
s.add_runtime_dependency 'fog-libvirt', '>= 0.6.0'
s.add_runtime_dependency 'fog-core', '~> 2'
s.add_runtime_dependency 'rexml'
s.add_runtime_dependency 'equivalent-xml'
s.add_runtime_dependency 'diffy'

# Make sure to allow use of the same version as Vagrant by being less specific
Expand Down

0 comments on commit d7de5e4

Please sign in to comment.