Skip to content
This repository has been archived by the owner on Jun 19, 2020. It is now read-only.

Commit

Permalink
(maint) Add FreeBSD disks and partitions facts
Browse files Browse the repository at this point in the history
Storage on FreeBSD is managed by GEOM, which expose the current storage
topology as an XML tree through a sysctl.  Get this XML tree to build
relevant facts.
  • Loading branch information
smortex committed Jun 11, 2020
1 parent 137b291 commit 168332f
Show file tree
Hide file tree
Showing 7 changed files with 600 additions and 0 deletions.
13 changes: 13 additions & 0 deletions lib/facts/freebsd/disks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Facts
module Freebsd
class Disks
FACT_NAME = 'disks'

def call_the_resolver
disks = Facter::Resolvers::Freebsd::Geom.resolve(:disks)

Facter::ResolvedFact.new(FACT_NAME, disks)
end
end
end
end
13 changes: 13 additions & 0 deletions lib/facts/freebsd/partitions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Facts
module Freebsd
class Partitions
FACT_NAME = 'partitions'

def call_the_resolver
partitions = Facter::Resolvers::Freebsd::Geom.resolve(:partitions)

Facter::ResolvedFact.new(FACT_NAME, partitions)
end
end
end
end
109 changes: 109 additions & 0 deletions lib/resolvers/freebsd/geom_resolver.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# frozen_string_literal: true

module Facter
module Resolvers
module Freebsd
class Geom < BaseResolver
@semaphore = Mutex.new
@fact_list ||= {}

class << self
private

def post_resolve(fact_name)
@fact_list.fetch(fact_name) { read_facts(fact_name) }
end

def read_facts(fact_name)
require_relative 'ffi/ffi_helper'
require 'rexml/document'

read_disks
read_partitions

@fact_list[fact_name]
end

def read_disks
@fact_list[:disks] = {}

each_geom_class_provider('DISK') do |provider|
name = provider.get_text('./name').value

@fact_list[:disks][name] = %i[read_model read_serial_number read_size].map do |x|
send(x, provider)
end.inject(:merge)
end
end

def read_partitions
@fact_list[:partitions] = {}

each_geom_class_provider('PART') do |provider|
name = provider.get_text('./name').value

@fact_list[:partitions][name] = %i[read_partlabel read_partuuid read_size].map do |x|
send(x, provider)
end.inject(:merge)
end
end

def each_geom_class_provider(geom_class_name, &block)
REXML::XPath.each(geom_topology, "/mesh/class[name/text() = '#{geom_class_name}']/geom/provider", &block)
end

def read_size(provider)
res = {}
return res unless (mediasize = provider.get_text('mediasize'))

mediasize = Integer(mediasize.value)

res[:size_bytes] = mediasize
res[:size] = Facter::FactsUtils::UnitConverter.bytes_to_human_readable(mediasize)
res
end

def read_model(provider)
res = {}
return res unless (model = provider.get_text('./config/descr'))

res[:model] = model.value
res
end

def read_partlabel(provider)
res = {}
return res unless (rawuuid = provider.get_text('./config/label'))

res[:partlabel] = rawuuid.value
res
end

def read_partuuid(provider)
res = {}
return res unless (rawuuid = provider.get_text('./config/rawuuid'))

res[:partuuid] = rawuuid.value
res
end

def read_serial_number(provider)
res = {}
return res unless (serial_number = provider.get_text('./config/ident'))

res[:serial_number] = serial_number.value
res
end

def geom_topology
@geom_topology ||= REXML::Document.new(geom_confxml)
end

def geom_confxml
Facter::Freebsd::FfiHelper.sysctl_by_name(:string, 'kern.geom.confxml')
end
end
end
end
end
end
35 changes: 35 additions & 0 deletions spec/facter/facts/freebsd/disks_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

describe Facts::Freebsd::Disks do
subject(:fact) { Facts::Freebsd::Disks.new }

let(:disk) do
{
'disks' => {
'sda' => {
'model' => 'Virtual disk',
'size' => '20.00 GiB',
'size_bytes' => 21_474_836_480,
'vendor' => 'VMware'
}
}
}
end

describe '#call_the_resolver' do
before do
allow(Facter::Resolvers::Freebsd::Geom).to receive(:resolve).with(:disks).and_return(disk)
end

it 'calls Facter::Resolvers::Freebsd::Disk' do
fact.call_the_resolver
expect(Facter::Resolvers::Freebsd::Geom).to have_received(:resolve).with(:disks)
end

it 'returns resolved fact with name disk and value' do
expect(fact.call_the_resolver)
.to be_an_instance_of(Facter::ResolvedFact)
.and have_attributes(name: 'disks', value: disk)
end
end
end
33 changes: 33 additions & 0 deletions spec/facter/facts/freebsd/partitions_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

describe Facts::Freebsd::Partitions do
subject(:fact) { Facts::Freebsd::Partitions.new }

let(:partitions) do
{
'ada0p1' => {
'partlabel' => 'gptboot0',
'partuuid' => '503d3458-c135-11e8-bd11-7d7cd061b26f',
'size' => '512.00 KiB',
'size_bytes' => 524288
}
}
end

describe '#call_the_resolver' do
before do
allow(Facter::Resolvers::Freebsd::Geom).to receive(:resolve).with(:partitions).and_return(partitions)
end

it 'calls Facter::Resolvers::Freebsd::Partitions' do
fact.call_the_resolver
expect(Facter::Resolvers::Freebsd::Geom).to have_received(:resolve).with(:partitions)
end

it 'returns resolved fact with name partitions and value' do
expect(fact.call_the_resolver)
.to be_an_instance_of(Facter::ResolvedFact)
.and have_attributes(name: 'partitions', value: partitions)
end
end
end
59 changes: 59 additions & 0 deletions spec/facter/resolvers/freebsd/geom_resolver_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# frozen_string_literal: true

describe Facter::Resolvers::Freebsd::Geom do
describe '#resolve' do
subject(:resolver) { Facter::Resolvers::Freebsd::Geom }

before do
allow(Facter::Freebsd::FfiHelper).to receive(:sysctl_by_name)
.with(:string, 'kern.geom.confxml')
.and_return(load_fixture('kern.geom.confxml').read)
end

describe 'disks resolution' do
let(:expected_output) do
{
'ada0' => {
model: 'Samsung SSD 850 PRO 512GB',
serial_number: 'S250NXAG959927J',
size: '476.94 GiB',
size_bytes: 512110190592
}
}
end

it 'returns disks fact' do
expect(resolver.resolve(:disks)).to eql(expected_output)
end
end

describe 'partitions resolution' do
let(:expected_output) do
{
'ada0p1' => {
partlabel: 'gptboot0',
partuuid: '503d3458-c135-11e8-bd11-7d7cd061b26f',
size: '512.00 KiB',
size_bytes: 524288
},
'ada0p2' => {
partlabel: 'swap0',
partuuid: '5048d40d-c135-11e8-bd11-7d7cd061b26f',
size: '2.00 GiB',
size_bytes: 2147483648
},
'ada0p3' => {
partlabel: 'zfs0',
partuuid: '504f1547-c135-11e8-bd11-7d7cd061b26f',
size: '474.94 GiB',
size_bytes: 509961306112
}
}
end

it 'returns partitions fact' do
expect(resolver.resolve(:partitions)).to eql(expected_output)
end
end
end
end

0 comments on commit 168332f

Please sign in to comment.