diff --git a/lib/ohai/mixin/oci_metadata.rb b/lib/ohai/mixin/oci_metadata.rb index c65c57546..263dee2cf 100644 --- a/lib/ohai/mixin/oci_metadata.rb +++ b/lib/ohai/mixin/oci_metadata.rb @@ -28,6 +28,39 @@ module OCIMetadata OCI_METADATA_URL = "/opc/v2" CHASSIS_ASSET_TAG_FILE = "/sys/devices/virtual/dmi/id/chassis_asset_tag" + # Get the chassis asset tag from DMI information + # On Linux: reads from sysfs + # On Windows: queries WMI Win32_SystemEnclosure + def chassis_asset_tag + if RUBY_PLATFORM =~ /mswin|mingw|windows/ + get_chassis_asset_tag_windows + else + get_chassis_asset_tag_linux + end + end + + # Read chassis asset tag from Linux sysfs + def get_chassis_asset_tag_linux + return unless ::File.exist?(CHASSIS_ASSET_TAG_FILE) + + ::File.read(CHASSIS_ASSET_TAG_FILE).strip + rescue => e + logger.debug("Mixin OciMetadata: Failed to read chassis asset tag from #{CHASSIS_ASSET_TAG_FILE}: #{e}") + nil + end + + # Read chassis asset tag from Windows WMI + def get_chassis_asset_tag_windows + require "wmi-lite/wmi" unless defined?(WmiLite::Wmi) + + wmi = WmiLite::Wmi.new + enclosure = wmi.first_of("Win32_SystemEnclosure") + enclosure&.[]("SMBIOSAssetTag") + rescue => e + logger.debug("Mixin OciMetadata: Failed to read chassis asset tag from WMI: #{e}") + nil + end + # fetch the meta content with a timeout and the required header def http_get(uri) conn = Net::HTTP.start(OCI_METADATA_ADDR) @@ -51,7 +84,7 @@ def fetch_metadata(metadata = "instance") end json_data else - logger.warn("Mixin OciMetadata: Received response code #{response.code} requesting metadata") + logger.debug("Mixin OciMetadata: Received response code #{response.code} requesting #{metadata}") nil end end diff --git a/lib/ohai/plugins/oci.rb b/lib/ohai/plugins/oci.rb index 04e83ba50..6e342ad11 100644 --- a/lib/ohai/plugins/oci.rb +++ b/lib/ohai/plugins/oci.rb @@ -44,24 +44,22 @@ end def oci_chassis_asset_tag? - has_oci_chassis_asset_tag = false - if file_exist?(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE) - file_open(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE).each do |line| - next unless /OracleCloud.com/.match?(line) - - logger.trace("Plugin oci: Found OracleCloud.com chassis_asset_tag used by oci.") - has_oci_chassis_asset_tag = true - break - end + asset_tag = chassis_asset_tag + return false if asset_tag.nil? || asset_tag.empty? + + if /OracleCloud.com/.match?(asset_tag) + logger.trace("Plugin oci: Found OracleCloud.com chassis_asset_tag used by oci.") + true + else + false end - has_oci_chassis_asset_tag end def parse_metadata - return nil unless can_socket_connect?(Ohai::Mixin::OCIMetadata::OCI_METADATA_ADDR, 80) + return unless can_socket_connect?(Ohai::Mixin::OCIMetadata::OCI_METADATA_ADDR, 80) instance_data = fetch_metadata("instance") - return nil if instance_data.nil? + return if instance_data.nil? metadata = Mash.new metadata["compute"] = Mash.new diff --git a/spec/unit/mixin/oci_metadata_spec.rb b/spec/unit/mixin/oci_metadata_spec.rb index 4a922253d..90af46f39 100644 --- a/spec/unit/mixin/oci_metadata_spec.rb +++ b/spec/unit/mixin/oci_metadata_spec.rb @@ -49,7 +49,7 @@ http_mock = double("http", { code: "404" }) allow(mixin).to receive(:http_get).and_return(http_mock) - expect(mixin.logger).to receive(:warn) + expect(mixin.logger).to receive(:debug) vals = mixin.fetch_metadata expect(vals).to eq(nil) end @@ -63,4 +63,88 @@ expect(vals).to eq({ "foo" => "bar" }) end end + + describe "#chassis_asset_tag" do + context "on Windows platform" do + before do + stub_const("RUBY_PLATFORM", "mswin") + end + + it "calls get_chassis_asset_tag_windows" do + expect(mixin).to receive(:get_chassis_asset_tag_windows).and_return("test-asset-tag") + expect(mixin.chassis_asset_tag).to eq("test-asset-tag") + end + end + + context "on non-Windows platform" do + before do + stub_const("RUBY_PLATFORM", "linux") + end + + it "calls get_chassis_asset_tag_linux" do + expect(mixin).to receive(:get_chassis_asset_tag_linux).and_return("test-asset-tag") + expect(mixin.chassis_asset_tag).to eq("test-asset-tag") + end + end + end + + describe "#get_chassis_asset_tag_linux" do + let(:chassis_file) { Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE } + + it "returns asset tag when file exists and is readable" do + allow(::File).to receive(:exist?).with(chassis_file).and_return(true) + allow(::File).to receive(:read).with(chassis_file).and_return(" OracleCloud.com \n") + + expect(mixin.get_chassis_asset_tag_linux).to eq("OracleCloud.com") + end + + it "returns nil when file does not exist" do + allow(::File).to receive(:exist?).with(chassis_file).and_return(false) + + expect(mixin.get_chassis_asset_tag_linux).to be_nil + end + + it "returns nil when file read fails" do + allow(::File).to receive(:exist?).with(chassis_file).and_return(true) + allow(::File).to receive(:read).with(chassis_file).and_raise(Errno::EACCES) + + expect(mixin.logger).to receive(:debug).with(/Failed to read chassis asset tag/) + expect(mixin.get_chassis_asset_tag_linux).to be_nil + end + end + + describe "#get_chassis_asset_tag_windows" do + let(:wmi_mock) { double("WmiLite::Wmi") } + let(:enclosure_mock) { { "SMBIOSAssetTag" => "OracleCloud.com" } } + + before do + allow(mixin).to receive(:require) + stub_const("WmiLite::Wmi", double(new: wmi_mock)) + end + + it "returns asset tag from WMI when available" do + allow(wmi_mock).to receive(:first_of).with("Win32_SystemEnclosure").and_return(enclosure_mock) + + expect(mixin.get_chassis_asset_tag_windows).to eq("OracleCloud.com") + end + + it "returns nil when WMI query returns nil" do + allow(wmi_mock).to receive(:first_of).with("Win32_SystemEnclosure").and_return(nil) + + expect(mixin.get_chassis_asset_tag_windows).to be_nil + end + + it "returns nil when WMI query fails" do + allow(wmi_mock).to receive(:first_of).with("Win32_SystemEnclosure").and_raise(StandardError.new("WMI error")) + + expect(mixin.logger).to receive(:debug).with(/Failed to read chassis asset tag from WMI/) + expect(mixin.get_chassis_asset_tag_windows).to be_nil + end + + it "returns nil when SMBIOSAssetTag is not present" do + allow(wmi_mock).to receive(:first_of).with("Win32_SystemEnclosure").and_return({}) + + expect(mixin.get_chassis_asset_tag_windows).to be_nil + end + end end diff --git a/spec/unit/plugins/oci_spec.rb b/spec/unit/plugins/oci_spec.rb index 537373837..562de466a 100644 --- a/spec/unit/plugins/oci_spec.rb +++ b/spec/unit/plugins/oci_spec.rb @@ -129,11 +129,7 @@ describe "without oci hint file not in OCI" do before do allow(plugin).to receive(:hint?).with("oci").and_return(false) - allow(plugin).to receive(:file_exist?).with(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE).and_return(true) - @double_file = double(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE) - allow(@double_file).to receive(:each) - .and_yield("") - allow(plugin).to receive(:file_open).with(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE).and_return(@double_file) + allow(plugin).to receive(:chassis_asset_tag).and_return("") end it_behaves_like "!oci" @@ -142,11 +138,7 @@ describe "without oci hint file in OCI" do before do allow(plugin).to receive(:hint?).with("oci").and_return(false) - allow(plugin).to receive(:file_exist?).with(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE).and_return(true) - @double_file = double(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE) - allow(@double_file).to receive(:each) - .and_yield("OracleCloud.com") - allow(plugin).to receive(:file_open).with(Ohai::Mixin::OCIMetadata::CHASSIS_ASSET_TAG_FILE).and_return(@double_file) + allow(plugin).to receive(:chassis_asset_tag).and_return("OracleCloud.com") end it_behaves_like "oci" @@ -195,4 +187,31 @@ expect(plugin[:oci][:metadata][:network][:interface][0][:privateIp]).to eq("10.0.3.6") end end + + describe "#oci_chassis_asset_tag?" do + it "returns true when chassis asset tag contains OracleCloud.com" do + allow(plugin).to receive(:chassis_asset_tag).and_return("OracleCloud.com") + expect(plugin.oci_chassis_asset_tag?).to be true + end + + it "returns false when chassis asset tag is nil" do + allow(plugin).to receive(:chassis_asset_tag).and_return(nil) + expect(plugin.oci_chassis_asset_tag?).to be false + end + + it "returns false when chassis asset tag is empty" do + allow(plugin).to receive(:chassis_asset_tag).and_return("") + expect(plugin.oci_chassis_asset_tag?).to be false + end + + it "returns false when chassis asset tag does not contain OracleCloud.com" do + allow(plugin).to receive(:chassis_asset_tag).and_return("SomeOtherTag") + expect(plugin.oci_chassis_asset_tag?).to be false + end + + it "returns true when chassis asset tag contains OracleCloud.com with extra content" do + allow(plugin).to receive(:chassis_asset_tag).and_return("prefix OracleCloud.com suffix") + expect(plugin.oci_chassis_asset_tag?).to be true + end + end end