diff --git a/lib/facter/ipmitool.rb b/lib/facter/ipmitool.rb new file mode 100755 index 0000000..c1a830e --- /dev/null +++ b/lib/facter/ipmitool.rb @@ -0,0 +1,48 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +Facter.add(:ipmitool, type: :aggregate) do + # https://puppet.com/docs/puppet/latest/fact_overview.html + confine kernel: 'Linux' + confine is_virtual: false + # TODO: consider confining based on which + # this has the side affect that the ipmitool fact and ipmitool_mc_info facts would be Nil + # if ipmi is not present instead of the curent values of + # ipmitool: {"fru"=>{}, "mc_info"=>{"IPMI_Puppet_Service_Recommend"=>"stopped"}} + # ipmitool_mc_info: {"IPMI_Puppet_Service_Recommend"=>"stopped"} + # confine do + # Facter::Util::Resolution.which('ipmitool') + # end + + ipmitool_present = Facter::Util::Resolution.which('ipmitool') + chunk(:fru) do + retval = { fru: {} } + if ipmitool_present + ipmitool_output = Facter::Util::Resolution.exec('ipmitool fru print 0 2>/dev/null') + ipmitool_output.each_line do |line| + next unless line.include?(':') + + info = line.split(':', 2) + next if info[1].strip.empty? + + key = info[0].strip.tr("\s", '_').downcase + retval[:fru][key] = info[1].strip + end + end + retval + end + + chunk(:mc_info) do + retval = { mc_info: { 'IPMI_Puppet_Service_Recommend' => 'stopped' } } + if ipmitool_present + ipmitool_output = Facter::Util::Resolution.exec('ipmitool mc info 2>/dev/null') + + ipmitool_output.each_line do |line| + info = line.split(':') + retval[:mc_info][info[0].strip] = info[1].strip if info.length == 2 && (info[1].strip != '') + end + retval[:mc_info]['IPMI_Puppet_Service_Recommend'] = 'running' if retval[:mc_info].fetch('Device Available', 'no') == 'yes' + end + retval + end +end diff --git a/lib/facter/ipmitool_mc_info.rb b/lib/facter/ipmitool_mc_info.rb index 1bf93be..4ae5a58 100755 --- a/lib/facter/ipmitool_mc_info.rb +++ b/lib/facter/ipmitool_mc_info.rb @@ -4,21 +4,8 @@ Facter.add(:ipmitool_mc_info) do # https://puppet.com/docs/puppet/latest/fact_overview.html confine kernel: 'Linux' - - retval = {} - retval['IPMI_Puppet_Service_Recommend'] = 'stopped' - - if Facter::Util::Resolution.which('ipmitool') - ipmitool_output = Facter::Util::Resolution.exec('ipmitool mc info 2>/dev/null') - - ipmitool_output.each_line do |line| - info = line.split(':') - retval[info[0].strip] = info[1].strip if info.length == 2 && (info[1].strip != '') - end - retval['IPMI_Puppet_Service_Recommend'] = 'running' if retval.fetch('Device Available', 'no') == 'yes' - end - setcode do - retval + ipmitool_value = Facter.value('ipmitool') + ipmitool_value.nil? ? { 'IPMI_Puppet_Service_Recommend' => 'stopped' } : ipmitool_value[:mc_info] end end diff --git a/spec/unit/facter/ipmitool.rb b/spec/unit/facter/ipmitool.rb new file mode 100644 index 0000000..237b8fc --- /dev/null +++ b/spec/unit/facter/ipmitool.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'facter' + +describe 'ipmitool', type: :fact do + subject(:fact) { Facter.fact(:ipmitool) } + + before do + # perform any action that should be run before every test + Facter.clear + Facter.fact(:kernel).stubs(:value).returns('Linux') + Facter.fact(:is_virtual).stubs(:value).returns(false) + end + + let(:fru_output) do + <<~OUTPUT + Board Mfg Date : Tue Mar 3 21:43:00 2015 + Board Mfg : DELL + Board Product : PowerEdge R220 + Board Serial : 000000 + Board Part Number : 0DRXF5A04 + Product Manufacturer : DELL + Product Name : Test + Product Extra : 000000 + OUTPUT + end + let(:mc_output) do + <<~SAMPLE + Device ID : 32 + Device Revision : 1 + Firmware Revision : 2.49 + IPMI Version : 2.0 + Manufacturer ID : 10876 + Manufacturer Name : Supermicro + Product ID : 43707 (0xaabb) + Product Name : Unknown (0xAABB) + Device Available : yes + Provides Device SDRs : no + Additional Device Support : + Sensor Device + SDR Repository Device + SEL Device + FRU Inventory Device + IPMB Event Receiver + IPMB Event Generator + Chassis Device + Aux Firmware Rev Info : + 0x00 + 0x00 + 0x00 + 0x00 + SAMPLE + end + + context 'with no ipmitool' do + before do + Facter::Util::Resolution.expects(:which).at_least(1).with('ipmitool').returns(nil) + Facter::Util::Resolution.expects(:exec).with('ipmitool mc info 2>/dev/null').never + Facter::Util::Resolution.expects(:exec).with('ipmitool fru print 0 2>/dev/null').never + end + + it do + expect(fact.value).to eq({ 'fru' => {}, 'mc_info' => { 'IPMI_Puppet_Service_Recommend' => 'stopped' } }) + end + end + + context 'with detailed output' do + before do + Facter::Util::Resolution.expects(:which).with('ipmitool').returns(true) + Facter::Util::Resolution.expects(:exec).with('ipmitool mc info 2>/dev/null').returns(mc_output) + Facter::Util::Resolution.expects(:exec).with('ipmitool fru print 0 2>/dev/null').returns(fru_output) + end + + it do + expect(fact.value).to eq( + { + 'mc_info' => { + 'Device ID' => '32', + 'Device Revision' => '1', + 'Firmware Revision' => '2.49', + 'IPMI Version' => '2.0', + 'IPMI_Puppet_Service_Recommend' => 'running', + 'Manufacturer ID' => '10876', + 'Manufacturer Name' => 'Supermicro', + 'Product ID' => '43707 (0xaabb)', + 'Product Name' => 'Unknown (0xAABB)', + 'Device Available' => 'yes', + 'Provides Device SDRs' => 'no', + }, + 'fru' => { + 'board_mfg_date' => 'Tue Mar 3 21:43:00 2015', + 'board_mfg' => 'DELL', + 'board_product' => 'PowerEdge R220', + 'board_serial' => '000000', + 'board_part_number' => '0DRXF5A04', + 'product_manufacturer' => 'DELL', + 'product_name' => 'Test', + 'product_extra' => '000000', + } + } + ) + end + end +end diff --git a/spec/unit/facter/ipmitool_mc_info_spec.rb b/spec/unit/facter/ipmitool_mc_info_spec.rb index 71288a9..70a6aa8 100644 --- a/spec/unit/facter/ipmitool_mc_info_spec.rb +++ b/spec/unit/facter/ipmitool_mc_info_spec.rb @@ -10,40 +10,12 @@ before do # perform any action that should be run before every test Facter.clear + Facter.fact(:kernel).stubs(:value).returns('Linux') end - let(:detailed_output) do - <<~SAMPLE - Device ID : 32 - Device Revision : 1 - Firmware Revision : 2.49 - IPMI Version : 2.0 - Manufacturer ID : 10876 - Manufacturer Name : Supermicro - Product ID : 43707 (0xaabb) - Product Name : Unknown (0xAABB) - Device Available : yes - Provides Device SDRs : no - Additional Device Support : - Sensor Device - SDR Repository Device - SEL Device - FRU Inventory Device - IPMB Event Receiver - IPMB Event Generator - Chassis Device - Aux Firmware Rev Info : - 0x00 - 0x00 - 0x00 - 0x00 - SAMPLE - end - - context 'with no ipmitool' do + context 'with no ipmitool fact' do before do - Facter::Util::Resolution.expects(:which).at_least(1).with('ipmitool').returns(nil) - Facter::Util::Resolution.expects(:exec).with('ipmitool mc info 2>/dev/null').never + Facter.fact(:ipmitool).stubs(:value).returns(nil) end it do @@ -53,8 +25,24 @@ context 'with detailed output' do before do - Facter::Util::Resolution.expects(:which).with('ipmitool').returns('ipmitool') - Facter::Util::Resolution.expects(:exec).with('ipmitool mc info 2>/dev/null').returns(detailed_output) + Facter.fact(:ipmitool).stubs(:value).returns( + { + fru: {}, + mc_info: { + 'Device ID' => '32', + 'Device Revision' => '1', + 'Firmware Revision' => '2.49', + 'IPMI Version' => '2.0', + 'IPMI_Puppet_Service_Recommend' => 'running', + 'Manufacturer ID' => '10876', + 'Manufacturer Name' => 'Supermicro', + 'Product ID' => '43707 (0xaabb)', + 'Product Name' => 'Unknown (0xAABB)', + 'Device Available' => 'yes', + 'Provides Device SDRs' => 'no', + } + } + ) end it do