diff options
author | Tim Smith <tsmith@chef.io> | 2019-12-06 14:05:21 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-12-06 14:05:21 -0800 |
commit | 1d9343d1761a5a57a63ac0b325dd3efda9b2c577 (patch) | |
tree | 6b3f5cd56bad0b3982c1a3e932aeee46d95b0985 | |
parent | e243f2f3d1aa14b81f927300c2555dedc1372b82 (diff) | |
parent | fe528cbb0f5fda0466a36c9a94bbf112ad82c278 (diff) | |
download | ohai-1d9343d1761a5a57a63ac0b325dd3efda9b2c577.tar.gz |
Merge pull request #1267 from jaymzh/windows_fs2
[filesystem] Convert windows to filesystem2
-rw-r--r-- | lib/ohai/plugins/filesystem.rb | 166 | ||||
-rw-r--r-- | lib/ohai/plugins/windows/filesystem.rb | 147 | ||||
-rw-r--r-- | spec/unit/plugins/windows/filesystem_spec.rb | 146 |
3 files changed, 283 insertions, 176 deletions
diff --git a/lib/ohai/plugins/filesystem.rb b/lib/ohai/plugins/filesystem.rb index 32ddb607..e2671635 100644 --- a/lib/ohai/plugins/filesystem.rb +++ b/lib/ohai/plugins/filesystem.rb @@ -107,6 +107,14 @@ Ohai.plugin(:Filesystem) do view end + def generate_deprecated_windows_view(fs) + view = generate_mountpoint_view(fs) + view.each do |mp, entry| + view[mp].delete("devices") + end + view + end + def generate_deprecated_solaris_view(fs, old_zfs) view = generate_deprecated_view(fs) old_zfs.each do |fsname, attributes| @@ -153,6 +161,143 @@ Ohai.plugin(:Filesystem) do logger.warn("Plugin Filesystem: #{bin} binary is not available. Some data will not be available.") end + ### Windows specific methods BEGINS + # Volume encryption or decryption status + # + # @see https://docs.microsoft.com/en-us/windows/desktop/SecProv/getconversionstatus-win32-encryptablevolume#parameters + # + CONVERSION_STATUS ||= %w{ + FullyDecrypted FullyEncrypted EncryptionInProgress + DecryptionInProgress EncryptionPaused DecryptionPaused + }.freeze + + # Returns a Mash loaded with logical details + # + # Uses Win32_LogicalDisk and logical_properties to return encryption details of volumes. + # + # Returns an empty Mash in case of any WMI exception. + # + # @see https://docs.microsoft.com/en-us/windows/desktop/CIMWin32Prov/win32-logicaldisk + # + # @return [Mash] + # + def logical_info + wmi = WmiLite::Wmi.new("Root\\CIMV2") + + # Note: we should really be parsing Win32_Volume and Win32_Mapped drive. + disks = wmi.instances_of("Win32_LogicalDisk") + logical_properties(disks) + rescue WmiLite::WmiException + Ohai::Log.debug("Unable to access Win32_LogicalDisk. Skipping logical details") + Mash.new + end + + # Returns a Mash loaded with encryption details + # + # Uses Win32_EncryptableVolume and encryption_properties to return encryption details of volumes. + # + # Returns an empty Mash in case of any WMI exception. + # + # @note We are fetching Encryption Status only as of now + # + # @see https://msdn.microsoft.com/en-us/library/windows/desktop/aa376483(v=vs.85).aspx + # + # @return [Mash] + # + def encryptable_info + wmi = WmiLite::Wmi.new("Root\\CIMV2\\Security\\MicrosoftVolumeEncryption") + disks = wmi.instances_of("Win32_EncryptableVolume") + encryption_properties(disks) + rescue WmiLite::WmiException + Ohai::Log.debug("Unable to access Win32_EncryptableVolume. Skipping encryptable details") + Mash.new + end + + # Refines and calculates logical properties out of given instances. + # + # Note that :device here is the same as Volume name and there for compatibility with other OSes. + # + # @param [WmiLite::Wmi::Instance] disks + # + # @return [Mash] Each drive containing following properties: + # + # * :kb_size (Integer) + # * :kb_available (Integer) + # * :kb_used (Integer) + # * :percent_used (Integer) + # * :mount (String) + # * :fs_type (String) + # * :volume_name (String) + # * :device (String) + # + def logical_properties(disks) + properties = Mash.new + disks.each do |disk| + property = Mash.new + # In windows the closet thing we have to a device is the volume_name + # and the "mountpoint" is the drive letter... + device = disk["volume_name"].to_s.downcase + mount = disk["deviceid"] + property[:kb_size] = disk["size"] ? disk["size"].to_i / 1000 : 0 + property[:kb_available] = disk["freespace"].to_i / 1000 + property[:kb_used] = property[:kb_size] - property[:kb_available] + property[:percent_used] = (property[:kb_size] == 0 ? 0 : (property[:kb_used] * 100 / property[:kb_size])) + property[:mount] = mount + property[:fs_type] = disk["filesystem"].to_s.downcase + property[:volume_name] = device + property[:device] = device + + key = "#{device},#{mount}" + properties[key] = property + end + properties + end + + # Refines and calculates encryption properties out of given instances + # + # @param [WmiLite::Wmi::Instance] disks + # + # @return [Mash] Each drive containing following properties: + # + # * :encryption_status (String) + # + def encryption_properties(disks) + properties = Mash.new + disks.each do |disk| + property = Mash.new + property[:encryption_status] = disk["conversionstatus"] ? CONVERSION_STATUS[disk["conversionstatus"]] : "" + key = disk["driveletter"] + properties[key] = property + end + properties + end + + # Merges all the various properties of filesystems + # + # @param [Array<Mash>] disks_info + # Array of the Mashes containing disk properties + # + # @return [Mash] + # + def merge_info(logical_info, encryption_info) + fs = Mash.new + + encryption_keys_used = Set.new + logical_info.each do |key, info| + if encryption_info[info["mount"]] + encryption_keys_used.add(info["mount"]) + fs[key] = info.merge(encryption_info[info["mount"]]) + else + fs[key] = info.dup + end + end + left_enc = encryption_info.reject { |x| encryption_keys_used.include?(x) } + left_enc.each do |key, info| + fs[",#{key}"] = info + end + fs + end + collect_data(:linux) do fs = Mash.new @@ -599,4 +744,25 @@ Ohai.plugin(:Filesystem) do filesystem generate_deprecated_view(fs) filesystem2 fs_data end + + collect_data(:windows) do + require "wmi-lite/wmi" + require "ohai/mash" + + fs = merge_info(logical_info, encryptable_info) + + by_pair = fs + by_device = generate_device_view(fs) + by_mountpoint = generate_mountpoint_view(fs) + + fs_data = Mash.new + fs_data["by_device"] = by_device + fs_data["by_mountpoint"] = by_mountpoint + fs_data["by_pair"] = by_pair + + # Set the filesystem data - Windows didn't do the conversion when everyone + # else did, so 15 will have both be the new API and 16 will drop the old API + filesystem generate_deprecated_windows_view(fs) + filesystem2 fs_data + end end diff --git a/lib/ohai/plugins/windows/filesystem.rb b/lib/ohai/plugins/windows/filesystem.rb deleted file mode 100644 index e637cf54..00000000 --- a/lib/ohai/plugins/windows/filesystem.rb +++ /dev/null @@ -1,147 +0,0 @@ -# -# Author:: James Gartrell (<jgartrel@gmail.com>) -# Copyright:: Copyright (c) 2009-2016 Chef Software, Inc. -# License:: Apache License, Version 2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -Ohai.plugin(:Filesystem) do - provides "filesystem" - - # Volume encryption or decryption status - # - # @see https://docs.microsoft.com/en-us/windows/desktop/SecProv/getconversionstatus-win32-encryptablevolume#parameters - # - CONVERSION_STATUS ||= %w{FullyDecrypted FullyEncrypted EncryptionInProgress - DecryptionInProgress EncryptionPaused DecryptionPaused}.freeze - - # Returns a Mash loaded with logical details - # - # Uses Win32_LogicalDisk and logical_properties to return encryption details of volumes. - # - # Returns an empty Mash in case of any WMI exception. - # - # @see https://docs.microsoft.com/en-us/windows/desktop/CIMWin32Prov/win32-logicaldisk - # - # @return [Mash] - # - def logical_info - wmi = WmiLite::Wmi.new("Root\\CIMV2") - - # Note: we should really be parsing Win32_Volume and Win32_Mapped drive. - disks = wmi.instances_of("Win32_LogicalDisk") - logical_properties(disks) - rescue WmiLite::WmiException - Ohai::Log.debug("Unable to access Win32_LogicalDisk. Skipping logical details") - Mash.new - end - - # Returns a Mash loaded with encryption details - # - # Uses Win32_EncryptableVolume and encryption_properties to return encryption details of volumes. - # - # Returns an empty Mash in case of any WMI exception. - # - # @note We are fetching Encryption Status only as of now - # - # @see https://msdn.microsoft.com/en-us/library/windows/desktop/aa376483(v=vs.85).aspx - # - # @return [Mash] - # - def encryptable_info - wmi = WmiLite::Wmi.new("Root\\CIMV2\\Security\\MicrosoftVolumeEncryption") - disks = wmi.instances_of("Win32_EncryptableVolume") - encryption_properties(disks) - rescue WmiLite::WmiException - Ohai::Log.debug("Unable to access Win32_EncryptableVolume. Skipping encryptable details") - Mash.new - end - - # Refines and calculates logical properties out of given instances - # - # @param [WmiLite::Wmi::Instance] disks - # - # @return [Mash] Each drive containing following properties: - # - # * :kb_size (Integer) - # * :kb_available (Integer) - # * :kb_used (Integer) - # * :percent_used (Integer) - # * :mount (String) - # * :fs_type (String) - # * :volume_name (String) - # - def logical_properties(disks) - properties = Mash.new - disks.each do |disk| - property = Mash.new - drive = disk["deviceid"] - property[:kb_size] = disk["size"].to_i / 1000 - property[:kb_available] = disk["freespace"].to_i / 1000 - property[:kb_used] = property[:kb_size] - property[:kb_available] - property[:percent_used] = (property[:kb_size] == 0 ? 0 : (property[:kb_used] * 100 / property[:kb_size])) - property[:mount] = disk["name"] - property[:fs_type] = disk["filesystem"].to_s.downcase - property[:volume_name] = disk["volumename"].to_s.downcase - properties[drive] = property - end - properties - end - - # Refines and calculates encryption properties out of given instances - # - # @param [WmiLite::Wmi::Instance] disks - # - # @return [Mash] Each drive containing following properties: - # - # * :encryption_status (String) - # - def encryption_properties(disks) - properties = Mash.new - disks.each do |disk| - drive = disk["driveletter"] - property = Mash.new - property[:encryption_status] = disk["conversionstatus"] ? CONVERSION_STATUS[disk["conversionstatus"]] : "" - properties[drive] = property - end - properties - end - - # Merges all the various properties of filesystems - # - # @param [Array<Mash>] disks_info - # Array of the Mashes containing disk properties - # - # @return [Mash] - # - def merge_info(disks_info) - fs = Mash.new - disks_info.each do |info| - info.each do |disk, data| - if fs[disk] - fs[disk].merge!(data) - else - fs[disk] = data.dup - end - end - end - fs - end - - collect_data(:windows) do - require "wmi-lite/wmi" - filesystem merge_info([logical_info, - encryptable_info]) - end -end diff --git a/spec/unit/plugins/windows/filesystem_spec.rb b/spec/unit/plugins/windows/filesystem_spec.rb index 0d6c0632..62996897 100644 --- a/spec/unit/plugins/windows/filesystem_spec.rb +++ b/spec/unit/plugins/windows/filesystem_spec.rb @@ -20,7 +20,7 @@ require "spec_helper" require "wmi-lite/wmi" describe Ohai::System, "Windows Filesystem Plugin", :windows_only do - let(:plugin) { get_plugin("windows/filesystem") } + let(:plugin) { get_plugin("filesystem") } let(:success) { true } @@ -70,9 +70,97 @@ describe Ohai::System, "Windows Filesystem Plugin", :windows_only do allow(plugin).to receive(:collect_os).and_return(:windows) end + describe "the plugin" do + context "when there are no volume names" do + before do + allow(plugin).to receive(:logical_info).and_return(plugin.logical_properties(logical_disks_instances)) + allow(plugin).to receive(:encryptable_info).and_return(plugin.encryption_properties(encryptable_volume_instances)) + plugin.run + end + + it "returns space information" do + { + "kb_size" => 10000, + "kb_available" => 100, + "kb_used" => 9900, + "percent_used" => 99, + }.each do |k, v| + expect(plugin[:filesystem]["C:"][k]).to eq(v) + expect(plugin[:filesystem]["D:"][k]).to eq(v) + expect(plugin[:filesystem2]["by_pair"][",C:"][k]).to eq(v) + expect(plugin[:filesystem2]["by_pair"][",D:"][k]).to eq(v) + end + end + + it "returns disk information" do + { + "fs_type" => "ntfs", + "volume_name" => "", + "encryption_status" => "FullyDecrypted", + }.each do |k, v| + expect(plugin[:filesystem]["C:"][k]).to eq(v) + expect(plugin[:filesystem2]["by_pair"][",C:"][k]).to eq(v) + end + + { + "fs_type" => "fat32", + "volume_name" => "", + "encryption_status" => "EncryptionInProgress", + }.each do |k, v| + expect(plugin[:filesystem]["D:"][k]).to eq(v) + expect(plugin[:filesystem2]["by_pair"][",D:"][k]).to eq(v) + end + end + end + + context "when there are volume names" do + before do + ldi = logical_disks_instances + ldi.each_with_index { |d, i| d["volume_name"] = "Volume #{i}" } + allow(plugin).to receive(:logical_info).and_return(plugin.logical_properties(ldi)) + allow(plugin).to receive(:encryptable_info).and_return(plugin.encryption_properties(encryptable_volume_instances)) + plugin.run + end + + it "returns space information" do + { + "kb_size" => 10000, + "kb_available" => 100, + "kb_used" => 9900, + "percent_used" => 99, + }.each do |k, v| + expect(plugin[:filesystem]["C:"][k]).to eq(v) + expect(plugin[:filesystem]["D:"][k]).to eq(v) + expect(plugin[:filesystem2]["by_pair"]["volume 0,C:"][k]).to eq(v) + expect(plugin[:filesystem2]["by_pair"]["volume 1,D:"][k]).to eq(v) + end + end + + it "returns disk information" do + { + "fs_type" => "ntfs", + "volume_name" => "volume 0", + "encryption_status" => "FullyDecrypted", + }.each do |k, v| + expect(plugin[:filesystem]["C:"][k]).to eq(v) + expect(plugin[:filesystem2]["by_pair"]["volume 0,C:"][k]).to eq(v) + end + + { + "fs_type" => "fat32", + "volume_name" => "volume 1", + "encryption_status" => "EncryptionInProgress", + }.each do |k, v| + expect(plugin[:filesystem]["D:"][k]).to eq(v) + expect(plugin[:filesystem2]["by_pair"]["volume 1,D:"][k]).to eq(v) + end + end + end + end + describe "#logical_properties" do let(:disks) { logical_disks_instances } - let(:logical_props) { %i{kb_size kb_available kb_used percent_used mount fs_type volume_name} } + let(:logical_props) { %i{kb_size kb_available kb_used percent_used mount fs_type volume_name device} } it "Returns a mash" do expect(plugin.logical_properties(disks)).to be_a(Mash) @@ -85,23 +173,23 @@ describe Ohai::System, "Windows Filesystem Plugin", :windows_only do it "Returns properties without values when there is no disk information" do data = plugin.logical_properties([{}]) - expect(data[nil].symbolize_keys.keys).to eq(logical_props) - expect(data[nil]["kb_used"]).to eq(0) - expect(data[nil]["fs_type"]).to be_empty + expect(data[","].symbolize_keys.keys).to eq(logical_props) + expect(data[","]["kb_used"]).to eq(0) + expect(data[","]["fs_type"]).to be_empty end it "Refines required logical properties out of given instance" do data = plugin.logical_properties(disks) - expect(data["C:"].symbolize_keys.keys).to eq(logical_props) - expect(data["D:"].symbolize_keys.keys).to eq(logical_props) + expect(data[",C:"].symbolize_keys.keys).to eq(logical_props) + expect(data[",D:"].symbolize_keys.keys).to eq(logical_props) end it "Calculates logical properties out of given instance" do data = plugin.logical_properties(disks) - expect(data["C:"]["kb_used"]).to eq(data["D:"]["kb_used"]).and eq(9900) - expect(data["C:"]["percent_used"]).to eq(data["D:"]["percent_used"]).and eq(99) - expect(data["C:"]["fs_type"]).to eq("ntfs") - expect(data["D:"]["fs_type"]).to eq("fat32") + expect(data[",C:"]["kb_used"]).to eq(data[",D:"]["kb_used"]).and eq(9900) + expect(data[",C:"]["percent_used"]).to eq(data[",D:"]["percent_used"]).and eq(99) + expect(data[",C:"]["fs_type"]).to eq("ntfs") + expect(data[",D:"]["fs_type"]).to eq("fat32") end end @@ -194,33 +282,33 @@ describe Ohai::System, "Windows Filesystem Plugin", :windows_only do end describe "#merge_info" do - let(:info1) do - { "drive1" => { "x" => 10, "y" => "test1" }, - "drive2" => { "x" => 20, "z" => "test2" } } + let(:logical_info) do + { "dev1,drive1" => { "mount" => "drive1", "x" => 10, "y" => "test1" }, + "dev2,drive2" => { "mount" => "drive2", "x" => 20, "z" => "test2" }, + "dev2,drive3" => { "mount" => "drive3", "x" => 20, "z" => "test3" } } end - let(:info2) do + let(:encryption_info) do { "drive1" => { "k" => 10, "l" => "test1" }, "drive2" => { "l" => 20, "m" => "test2" } } end - let(:info3) { { "drive1" => { "o" => 10, "p" => "test1" } } } - let(:info4) { { "drive2" => { "q" => 10, "r" => "test1" } } } - - it "Returns an empty mash when no info is passed" do - expect(plugin.merge_info([])).to be_a(Mash) - expect(plugin.merge_info([])).to be_empty - end + let(:logical_info2) { { ",drive1" => { "mount" => "drive1", "o" => 10, "p" => "test1" } } } + let(:encryption_info2) { { "drive2" => { "q" => 10, "r" => "test1" } } } it "Merges all the various properties of filesystems" do - expect(plugin.merge_info([info1, info2, info3, info4])) - .to eq("drive1" => { "x" => 10, "y" => "test1", "k" => 10, "l" => "test1", "o" => 10, "p" => "test1" }, - "drive2" => { "x" => 20, "z" => "test2", "l" => 20, "m" => "test2", "q" => 10, "r" => "test1" }) + expect(plugin.merge_info(logical_info, encryption_info)).to eq( + "dev1,drive1" => { "mount" => "drive1", "x" => 10, "y" => "test1", "k" => 10, "l" => "test1" }, + "dev2,drive2" => { "mount" => "drive2", "x" => 20, "z" => "test2", "l" => 20, "m" => "test2" }, + "dev2,drive3" => { "mount" => "drive3", "x" => 20, "z" => "test3" } + ) end it "Does not affect any core information after processing" do - expect(plugin.merge_info([info3, info4])).to eq("drive1" => { "o" => 10, "p" => "test1" }, - "drive2" => { "q" => 10, "r" => "test1" }) - expect(info3).to eq("drive1" => { "o" => 10, "p" => "test1" }) - expect(info4).to eq("drive2" => { "q" => 10, "r" => "test1" }) + expect(plugin.merge_info(logical_info2, encryption_info2)).to eq( + ",drive1" => { "mount" => "drive1", "o" => 10, "p" => "test1" }, + ",drive2" => { "q" => 10, "r" => "test1" } + ) + expect(logical_info2).to eq(",drive1" => { "mount" => "drive1", "o" => 10, "p" => "test1" }) + expect(encryption_info2).to eq("drive2" => { "q" => 10, "r" => "test1" }) end end end |