Skip to content

Commit

Permalink
Merge pull request #2040 from oanatmaria/FACT-2745
Browse files Browse the repository at this point in the history
(FACT-2745) Add Linux xen fact
  • Loading branch information
Bogdan Irimie authored Aug 21, 2020
2 parents 0a03f68 + 106c066 commit 877591f
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 0 deletions.
28 changes: 28 additions & 0 deletions lib/facter/facts/linux/xen.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

module Facts
module Linux
class Xen
FACT_NAME = 'xen'
ALIASES = 'xendomains'

def call_the_resolver
xen_type = check_virt_what || check_xen
return Facter::ResolvedFact.new(FACT_NAME, nil) if !xen_type || xen_type != 'xen0'

domains = Facter::Resolvers::Xen.resolve(:domains) || []

[Facter::ResolvedFact.new(FACT_NAME, { domains: domains }),
Facter::ResolvedFact.new(ALIASES, domains.entries.join(','), :legacy)]
end

def check_virt_what
Facter::Resolvers::VirtWhat.resolve(:vm)
end

def check_xen
Facter::Resolvers::Xen.resolve(:vm)
end
end
end
end
28 changes: 28 additions & 0 deletions lib/facter/resolvers/xen.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ class Xen < BaseResolver
@fact_list ||= {}

XEN_PATH = '/proc/xen/capabilities'
XEN_TOOLSTACK = '/usr/lib/xen-common/bin/xen-toolstack'
XEN_COMMANDS = ['/usr/sbin/xl', '/usr/sbin/xm'].freeze

class << self
private
Expand All @@ -18,6 +20,7 @@ def post_resolve(fact_name)
def detect_xen(fact_name)
@fact_list[:vm] = detect_xen_type
@fact_list[:privileged] = privileged?(@fact_list[:vm])
@fact_list[:domains] = detect_domains

@fact_list[fact_name]
end
Expand All @@ -33,6 +36,31 @@ def privileged?(xen_type)
content = Util::FileHelper.safe_read(XEN_PATH, nil)
content&.strip == 'control_d' || xen_type == 'xen0'
end

def detect_domains
domains = []
xen_command = find_command
return unless xen_command

output = Facter::Core::Execution.execute("#{xen_command} list", logger: log)
return if output.empty?

output.each_line do |line|
next if line =~ /Domain-0|Name/

domain = line.match(/^([^\s]*)\s/)
domain = domain&.captures&.first
domains << domain if domain
end

domains
end

def find_command
return XEN_TOOLSTACK if File.exist?(XEN_TOOLSTACK)

XEN_COMMANDS.each { |command| return command if File.exist?(command) }
end
end
end
end
Expand Down
117 changes: 117 additions & 0 deletions spec/facter/facts/linux/xen_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# frozen_string_literal: true

describe Facts::Linux::Xen do
describe '#call_the_resolver' do
subject(:fact) { Facts::Linux::Xen.new }

before do
allow(Facter::Resolvers::VirtWhat).to receive(:resolve).with(:vm).and_return(vm)
allow(Facter::Resolvers::Xen).to receive(:resolve).with(:domains).and_return(domains)
end

context 'when is xen privileged' do
let(:vm) { 'xen0' }
let(:domains) { %w[domain1 domain2] }

it 'calls Facter::Resolvers::VirtWhat' do
fact.call_the_resolver
expect(Facter::Resolvers::VirtWhat).to have_received(:resolve).with(:vm)
end

it 'calls Facter::Resolvers::Xen with :domains' do
fact.call_the_resolver
expect(Facter::Resolvers::Xen).to have_received(:resolve).with(:domains)
end

it 'returns xen fact' do
expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
contain_exactly(an_object_having_attributes(name: 'xen', value: { 'domains' => domains }),
an_object_having_attributes(name: 'xendomains',
value: domains.entries.join(','), type: :legacy))
end
end

context 'when is xen privileged but there are no domains' do
let(:vm) { nil }
let(:domains) { [] }

before do
allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return('xen0')
end

it 'calls Facter::Resolvers::VirtWhat' do
fact.call_the_resolver
expect(Facter::Resolvers::VirtWhat).to have_received(:resolve).with(:vm)
end

it 'calls Facter::Resolvers::Xen with :vm' do
fact.call_the_resolver
expect(Facter::Resolvers::Xen).to have_received(:resolve).with(:vm)
end

it 'calls Facter::Resolvers::Xen with :domains' do
fact.call_the_resolver
expect(Facter::Resolvers::Xen).to have_received(:resolve).with(:domains)
end

it 'returns xen fact' do
expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
contain_exactly(an_object_having_attributes(name: 'xen', value: { 'domains' => domains }),
an_object_having_attributes(name: 'xendomains',
value: domains.entries.join(','), type: :legacy))
end
end

context 'when is xen privileged but domains are nil' do
let(:vm) { nil }
let(:domains) { nil }
let(:result) { [] }

before do
allow(Facter::Resolvers::Xen).to receive(:resolve).with(:vm).and_return('xen0')
end

it 'calls Facter::Resolvers::VirtWhat' do
fact.call_the_resolver
expect(Facter::Resolvers::VirtWhat).to have_received(:resolve).with(:vm)
end

it 'calls Facter::Resolvers::Xen with :vm' do
fact.call_the_resolver
expect(Facter::Resolvers::Xen).to have_received(:resolve).with(:vm)
end

it 'calls Facter::Resolvers::Xen with :domains' do
fact.call_the_resolver
expect(Facter::Resolvers::Xen).to have_received(:resolve).with(:domains)
end

it 'returns xen fact' do
expect(fact.call_the_resolver).to be_an_instance_of(Array).and \
contain_exactly(an_object_having_attributes(name: 'xen', value: { 'domains' => result }),
an_object_having_attributes(name: 'xendomains',
value: result.entries.join(','), type: :legacy))
end
end

context 'when is xen unprivileged' do
let(:vm) { 'xenhvm' }
let(:domains) { nil }

it 'calls Facter::Resolvers::VirtWhat' do
fact.call_the_resolver
expect(Facter::Resolvers::VirtWhat).to have_received(:resolve).with(:vm)
end

it 'does not call Facter::Resolvers::Xen with :domains' do
fact.call_the_resolver
expect(Facter::Resolvers::Xen).not_to have_received(:resolve).with(:domains)
end

it 'returns nil fact' do
expect(fact.call_the_resolver).to be_an_instance_of(Facter::ResolvedFact).and \
have_attributes(name: 'xen', value: domains)
end
end
end
end
21 changes: 21 additions & 0 deletions spec/facter/resolvers/xen_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,29 @@

let(:proc_xen_file) { false }
let(:xvda1_file) { false }
let(:log_spy) { instance_spy(Facter::Log) }
let(:domains) { '' }

before do
xen_resolver.instance_variable_set(:@log, log_spy)
allow(File).to receive(:exist?).with('/dev/xen/evtchn').and_return(evtchn_file)
allow(File).to receive(:exist?).with('/proc/xen').and_return(proc_xen_file)
allow(File).to receive(:exist?).with('/dev/xvda1').and_return(xvda1_file)
allow(File).to receive(:exist?).with('/usr/lib/xen-common/bin/xen-toolstack').and_return(false)
allow(File).to receive(:exist?).with('/usr/sbin/xl').and_return(false)
allow(File).to receive(:exist?).with('/usr/sbin/xm').and_return(true)
allow(Facter::Core::Execution).to receive(:execute).with('/usr/sbin/xm list', logger: log_spy).and_return(domains)

xen_resolver.invalidate_cache
end

after do
xen_resolver.invalidate_cache
end

context 'when xen is privileged' do
context 'when /dev/xen/evtchn exists' do
let(:domains) { load_fixture('xen_domains').read }
let(:evtchn_file) { true }

it 'returns xen0' do
Expand All @@ -28,6 +41,10 @@
it 'does not check other files' do
expect(File).not_to have_received(:exist?).with('/proc/xen')
end

it 'returns domains' do
expect(xen_resolver.resolve(:domains)).to eq(%w[win linux])
end
end

context 'when /dev/xen/evtchn does not exist' do
Expand Down Expand Up @@ -64,5 +81,9 @@
it 'detects xen as unprivileged' do
expect(xen_resolver.resolve(:privileged)).to be_falsey
end

it 'does not detect domains' do
expect(xen_resolver.resolve(:domains)).to be_nil
end
end
end
4 changes: 4 additions & 0 deletions spec/fixtures/xen_domains
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Name ID Mem VCPUs State Time(s)
Domain-0 0 750 4 r----- 11794.3
win 1 1019 1 r----- 0.3
linux 2 2048 2 r----- 5624.2

0 comments on commit 877591f

Please sign in to comment.