ohai 18.2.6 → 19.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +28 -28
  3. data/LICENSE +201 -201
  4. data/bin/ohai +25 -25
  5. data/lib/ohai/application.rb +189 -189
  6. data/lib/ohai/common/dmi.rb +167 -167
  7. data/lib/ohai/config.rb +51 -51
  8. data/lib/ohai/dsl/plugin/versionvii.rb +203 -203
  9. data/lib/ohai/dsl/plugin.rb +232 -232
  10. data/lib/ohai/dsl.rb +22 -22
  11. data/lib/ohai/exception.rb +36 -36
  12. data/lib/ohai/hints.rb +68 -68
  13. data/lib/ohai/loader.rb +178 -178
  14. data/lib/ohai/log.rb +34 -34
  15. data/lib/ohai/mash.rb +22 -22
  16. data/lib/ohai/mixin/alibaba_metadata.rb +83 -83
  17. data/lib/ohai/mixin/azure_metadata.rb +105 -105
  18. data/lib/ohai/mixin/chef_utils_wiring.rb +52 -52
  19. data/lib/ohai/mixin/command.rb +4 -4
  20. data/lib/ohai/mixin/constant_helper.rb +55 -55
  21. data/lib/ohai/mixin/dmi_decode.rb +54 -54
  22. data/lib/ohai/mixin/do_metadata.rb +48 -48
  23. data/lib/ohai/mixin/ec2_metadata.rb +264 -270
  24. data/lib/ohai/mixin/gce_metadata.rb +79 -79
  25. data/lib/ohai/mixin/http_helper.rb +64 -64
  26. data/lib/ohai/mixin/json_helper.rb +36 -36
  27. data/lib/ohai/mixin/network_helper.rb +92 -92
  28. data/lib/ohai/mixin/oci_metadata.rb +60 -60
  29. data/lib/ohai/mixin/os.rb +128 -128
  30. data/lib/ohai/mixin/scaleway_metadata.rb +51 -51
  31. data/lib/ohai/mixin/seconds_to_human.rb +52 -52
  32. data/lib/ohai/mixin/shell_out.rb +51 -51
  33. data/lib/ohai/mixin/softlayer_metadata.rb +74 -74
  34. data/lib/ohai/mixin/string.rb +31 -31
  35. data/lib/ohai/mixin/train_helpers.rb +36 -36
  36. data/lib/ohai/mixin/which.rb +39 -39
  37. data/lib/ohai/plugin_config.rb +47 -47
  38. data/lib/ohai/plugins/aix/kernel.rb +50 -50
  39. data/lib/ohai/plugins/aix/memory.rb +37 -37
  40. data/lib/ohai/plugins/aix/network.rb +142 -142
  41. data/lib/ohai/plugins/aix/platform.rb +30 -30
  42. data/lib/ohai/plugins/aix/uptime.rb +54 -54
  43. data/lib/ohai/plugins/aix/virtualization.rb +154 -154
  44. data/lib/ohai/plugins/alibaba.rb +72 -72
  45. data/lib/ohai/plugins/azure.rb +154 -154
  46. data/lib/ohai/plugins/bsd/virtualization.rb +121 -121
  47. data/lib/ohai/plugins/c.rb +178 -178
  48. data/lib/ohai/plugins/chef.rb +50 -50
  49. data/lib/ohai/plugins/cloud.rb +379 -379
  50. data/lib/ohai/plugins/command.rb +26 -26
  51. data/lib/ohai/plugins/cpu.rb +642 -642
  52. data/lib/ohai/plugins/darwin/hardware.rb +99 -99
  53. data/lib/ohai/plugins/darwin/memory.rb +62 -62
  54. data/lib/ohai/plugins/darwin/network.rb +207 -207
  55. data/lib/ohai/plugins/darwin/platform.rb +40 -40
  56. data/lib/ohai/plugins/darwin/virtualization.rb +104 -104
  57. data/lib/ohai/plugins/digital_ocean.rb +67 -67
  58. data/lib/ohai/plugins/dmi.rb +134 -134
  59. data/lib/ohai/plugins/docker.rb +58 -58
  60. data/lib/ohai/plugins/dragonflybsd/memory.rb +60 -60
  61. data/lib/ohai/plugins/dragonflybsd/network.rb +128 -128
  62. data/lib/ohai/plugins/dragonflybsd/platform.rb +28 -28
  63. data/lib/ohai/plugins/ec2.rb +148 -148
  64. data/lib/ohai/plugins/elixir.rb +36 -36
  65. data/lib/ohai/plugins/erlang.rb +60 -60
  66. data/lib/ohai/plugins/eucalyptus.rb +86 -86
  67. data/lib/ohai/plugins/filesystem.rb +753 -753
  68. data/lib/ohai/plugins/fips.rb +36 -36
  69. data/lib/ohai/plugins/freebsd/memory.rb +60 -60
  70. data/lib/ohai/plugins/freebsd/network.rb +128 -128
  71. data/lib/ohai/plugins/freebsd/platform.rb +28 -28
  72. data/lib/ohai/plugins/gce.rb +89 -89
  73. data/lib/ohai/plugins/go.rb +34 -34
  74. data/lib/ohai/plugins/groovy.rb +38 -38
  75. data/lib/ohai/plugins/grub2.rb +40 -40
  76. data/lib/ohai/plugins/habitat.rb +73 -73
  77. data/lib/ohai/plugins/haskell.rb +96 -96
  78. data/lib/ohai/plugins/hostname.rb +133 -133
  79. data/lib/ohai/plugins/init_package.rb +26 -26
  80. data/lib/ohai/plugins/java.rb +78 -78
  81. data/lib/ohai/plugins/kernel.rb +292 -292
  82. data/lib/ohai/plugins/keys.rb +27 -27
  83. data/lib/ohai/plugins/languages.rb +26 -26
  84. data/lib/ohai/plugins/libvirt.rb +114 -114
  85. data/lib/ohai/plugins/linode.rb +73 -73
  86. data/lib/ohai/plugins/linux/block_device.rb +48 -48
  87. data/lib/ohai/plugins/linux/hostnamectl.rb +34 -34
  88. data/lib/ohai/plugins/linux/interrupts.rb +84 -83
  89. data/lib/ohai/plugins/linux/ipc.rb +52 -52
  90. data/lib/ohai/plugins/linux/livepatch.rb +38 -38
  91. data/lib/ohai/plugins/linux/lsb.rb +46 -46
  92. data/lib/ohai/plugins/linux/lspci.rb +80 -80
  93. data/lib/ohai/plugins/linux/machineid.rb +36 -36
  94. data/lib/ohai/plugins/linux/mdadm.rb +120 -120
  95. data/lib/ohai/plugins/linux/memory.rb +106 -106
  96. data/lib/ohai/plugins/linux/network.rb +879 -879
  97. data/lib/ohai/plugins/linux/os_release.rb +38 -38
  98. data/lib/ohai/plugins/linux/platform.rb +314 -314
  99. data/lib/ohai/plugins/linux/selinux.rb +69 -69
  100. data/lib/ohai/plugins/linux/sessions.rb +54 -54
  101. data/lib/ohai/plugins/linux/sysctl.rb +39 -39
  102. data/lib/ohai/plugins/linux/systemd_paths.rb +36 -36
  103. data/lib/ohai/plugins/linux/tc.rb +61 -61
  104. data/lib/ohai/plugins/linux/virtualization.rb +300 -300
  105. data/lib/ohai/plugins/lua.rb +39 -39
  106. data/lib/ohai/plugins/mono.rb +50 -50
  107. data/lib/ohai/plugins/netbsd/memory.rb +99 -99
  108. data/lib/ohai/plugins/netbsd/network.rb +122 -122
  109. data/lib/ohai/plugins/netbsd/platform.rb +28 -28
  110. data/lib/ohai/plugins/network.rb +186 -186
  111. data/lib/ohai/plugins/nodejs.rb +40 -40
  112. data/lib/ohai/plugins/oci.rb +94 -94
  113. data/lib/ohai/plugins/ohai.rb +29 -29
  114. data/lib/ohai/plugins/ohai_time.rb +26 -26
  115. data/lib/ohai/plugins/openbsd/memory.rb +99 -99
  116. data/lib/ohai/plugins/openbsd/network.rb +122 -122
  117. data/lib/ohai/plugins/openbsd/platform.rb +28 -28
  118. data/lib/ohai/plugins/openstack.rb +84 -84
  119. data/lib/ohai/plugins/os.rb +55 -55
  120. data/lib/ohai/plugins/packages.rb +234 -234
  121. data/lib/ohai/plugins/passwd.rb +104 -104
  122. data/lib/ohai/plugins/perl.rb +45 -45
  123. data/lib/ohai/plugins/php.rb +52 -52
  124. data/lib/ohai/plugins/platform.rb +41 -41
  125. data/lib/ohai/plugins/powershell.rb +82 -82
  126. data/lib/ohai/plugins/ps.rb +35 -35
  127. data/lib/ohai/plugins/python.rb +43 -43
  128. data/lib/ohai/plugins/rackspace.rb +177 -177
  129. data/lib/ohai/plugins/root_group.rb +41 -41
  130. data/lib/ohai/plugins/rpm.rb +121 -121
  131. data/lib/ohai/plugins/ruby.rb +66 -66
  132. data/lib/ohai/plugins/rust.rb +34 -34
  133. data/lib/ohai/plugins/scala.rb +38 -38
  134. data/lib/ohai/plugins/scaleway.rb +58 -58
  135. data/lib/ohai/plugins/scsi.rb +52 -52
  136. data/lib/ohai/plugins/shard.rb +142 -142
  137. data/lib/ohai/plugins/shells.rb +32 -32
  138. data/lib/ohai/plugins/softlayer.rb +48 -48
  139. data/lib/ohai/plugins/solaris2/dmi.rb +191 -191
  140. data/lib/ohai/plugins/solaris2/memory.rb +32 -32
  141. data/lib/ohai/plugins/solaris2/network.rb +192 -192
  142. data/lib/ohai/plugins/solaris2/platform.rb +58 -58
  143. data/lib/ohai/plugins/solaris2/virtualization.rb +90 -90
  144. data/lib/ohai/plugins/ssh_host_key.rb +84 -84
  145. data/lib/ohai/plugins/sysconf.rb +46 -46
  146. data/lib/ohai/plugins/timezone.rb +45 -45
  147. data/lib/ohai/plugins/train.rb +35 -35
  148. data/lib/ohai/plugins/uptime.rb +95 -95
  149. data/lib/ohai/plugins/virtualbox.rb +197 -197
  150. data/lib/ohai/plugins/vmware.rb +109 -109
  151. data/lib/ohai/plugins/windows/dmi.rb +95 -95
  152. data/lib/ohai/plugins/windows/drivers.rb +52 -52
  153. data/lib/ohai/plugins/windows/memory.rb +39 -39
  154. data/lib/ohai/plugins/windows/network.rb +222 -222
  155. data/lib/ohai/plugins/windows/platform.rb +34 -34
  156. data/lib/ohai/plugins/windows/system_enclosure.rb +29 -29
  157. data/lib/ohai/plugins/windows/virtualization.rb +45 -45
  158. data/lib/ohai/plugins/zpools.rb +94 -94
  159. data/lib/ohai/provides_map.rb +208 -208
  160. data/lib/ohai/runner.rb +128 -126
  161. data/lib/ohai/system.rb +258 -258
  162. data/lib/ohai/train_transport.rb +29 -29
  163. data/lib/ohai/util/file_helper.rb +6 -6
  164. data/lib/ohai/util/ip_helper.rb +56 -56
  165. data/lib/ohai/util/win32.rb +47 -47
  166. data/lib/ohai/version.rb +23 -23
  167. data/lib/ohai.rb +23 -23
  168. data/ohai.gemspec +35 -35
  169. metadata +9 -15
@@ -1,753 +1,753 @@
1
- #
2
- # Author:: Phil Dibowitz <phil@ipom.com>
3
- # Author:: Adam Jacob <adam@chef.io>
4
- # Author:: Kurt Yoder (ktyopscode@yoderhome.com)
5
- # Author:: Deepali Jagtap (<deepali.jagtap@clogeny.com>)
6
- # Author:: Prabhu Das (<prabhu.das@clogeny.com>)
7
- # Author:: Isa Farnik (<isa@chef.io>)
8
- # Author:: James Gartrell (<jgartrel@gmail.com>)
9
- # Copyright:: Copyright (c) Chef Software Inc.
10
- # Copyright:: Copyright (c) 2015 Facebook, Inc.
11
- # License:: Apache License, Version 2.0
12
- #
13
- # Licensed under the Apache License, Version 2.0 (the "License");
14
- # you may not use this file except in compliance with the License.
15
- # You may obtain a copy of the License at
16
- #
17
- # http://www.apache.org/licenses/LICENSE-2.0
18
- #
19
- # Unless required by applicable law or agreed to in writing, software
20
- # distributed under the License is distributed on an "AS IS" BASIS,
21
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
- # See the License for the specific language governing permissions and
23
- # limitations under the License.
24
- #
25
-
26
- Ohai.plugin(:Filesystem) do
27
- provides "filesystem".freeze
28
-
29
- def find_device(name)
30
- %w{/dev /dev/mapper}.each do |dir|
31
- path = File.join(dir, name)
32
- return path if file_exist?(path)
33
- end
34
- name
35
- end
36
-
37
- def parse_line(line, cmdtype)
38
- case cmdtype
39
- when "lsblk"
40
- regex = /NAME="(\S+).*?" UUID="(\S*)" LABEL="(\S*)" FSTYPE="(\S*)"/
41
- if line =~ regex
42
- dev = $1
43
- dev = find_device(dev) unless dev.start_with?("/")
44
- uuid = $2
45
- label = $3
46
- fs_type = $4
47
- return { dev: dev, uuid: uuid, label: label, fs_type: fs_type }
48
- end
49
- when "blkid"
50
- bits = line.split
51
- dev = bits.shift.split(":")[0]
52
- f = { dev: dev }
53
- bits.each do |keyval|
54
- if keyval =~ /(\S+)="(\S+)"/
55
- key = $1.downcase.to_sym
56
- key = :fs_type if key == :type
57
- f[key] = $2
58
- end
59
- end
60
- return f
61
- end
62
- nil
63
- end
64
-
65
- def generate_device_view(fs)
66
- view = {}
67
- fs.each_value do |entry|
68
- view[entry[:device]] ||= Mash.new
69
- entry.each do |key, val|
70
- next if %w{device mount}.include?(key)
71
-
72
- view[entry[:device]][key] = val
73
- end
74
- view[entry[:device]][:mounts] ||= []
75
- if entry[:mount]
76
- view[entry[:device]][:mounts] << entry[:mount]
77
- end
78
- end
79
- view
80
- end
81
-
82
- def generate_mountpoint_view(fs)
83
- view = {}
84
- fs.each_value do |entry|
85
- next unless entry[:mount]
86
-
87
- view[entry[:mount]] ||= Mash.new
88
- entry.each do |key, val|
89
- next if %w{mount device}.include?(key)
90
-
91
- view[entry[:mount]][key] = val
92
- end
93
- view[entry[:mount]][:devices] ||= []
94
- if entry[:device]
95
- view[entry[:mount]][:devices] << entry[:device]
96
- end
97
- end
98
- view
99
- end
100
-
101
- def parse_common_df(out)
102
- fs = {}
103
- out.each_line do |line|
104
- case line
105
- when /^Filesystem\s+1024-blocks/
106
- next
107
- when /^(.+?)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\%)\s+(.+)$/
108
- key = "#{$1},#{$6}"
109
- fs[key] = Mash.new
110
- fs[key][:device] = $1
111
- fs[key][:kb_size] = $2
112
- fs[key][:kb_used] = $3
113
- fs[key][:kb_available] = $4
114
- fs[key][:percent_used] = $5
115
- fs[key][:mount] = $6
116
- end
117
- end
118
- fs
119
- end
120
-
121
- def run_with_check(bin, &block)
122
- yield
123
- rescue Ohai::Exceptions::Exec => e
124
- unless Ohai.config[:plugin][:filesystem][:allow_partial_data]
125
- raise e
126
- end
127
-
128
- logger.warn("Plugin Filesystem: #{bin} binary is not available. Some data will not be available.")
129
- end
130
-
131
- ### Windows specific methods BEGINS
132
- # Drive types
133
- DRIVE_TYPE ||= %w{unknown no_root_dir removable local network cd ram}.freeze
134
-
135
- # Volume encryption or decryption status
136
- #
137
- # @see https://docs.microsoft.com/en-us/windows/desktop/SecProv/getconversionstatus-win32-encryptablevolume#parameters
138
- #
139
- CONVERSION_STATUS ||= %w{
140
- FullyDecrypted FullyEncrypted EncryptionInProgress
141
- DecryptionInProgress EncryptionPaused DecryptionPaused
142
- }.freeze
143
-
144
- # Returns a Mash loaded with logical details
145
- #
146
- # Uses Win32_LogicalDisk and logical_properties to return general details of volumes.
147
- #
148
- # Returns an empty Mash in case of any WMI exception.
149
- #
150
- # @see https://docs.microsoft.com/en-us/windows/desktop/CIMWin32Prov/win32-logicaldisk
151
- #
152
- # @return [Mash]
153
- #
154
- def logical_info
155
- wmi = WmiLite::Wmi.new("Root\\CIMV2")
156
-
157
- # TODO: We should really be parsing Win32_Volume and Win32_MountPoint.
158
- disks = wmi.instances_of("Win32_LogicalDisk")
159
- logical_properties(disks)
160
- rescue WmiLite::WmiException
161
- Ohai::Log.debug("Unable to access Win32_LogicalDisk. Skipping logical details")
162
- Mash.new
163
- end
164
-
165
- # Returns a Mash loaded with encryption details
166
- #
167
- # Uses Win32_EncryptableVolume and encryption_properties to return encryption details of volumes.
168
- #
169
- # Returns an empty Mash in case of any WMI exception.
170
- #
171
- # @note We are fetching Encryption Status only as of now
172
- #
173
- # @see https://msdn.microsoft.com/en-us/library/windows/desktop/aa376483(v=vs.85).aspx
174
- #
175
- # @return [Mash]
176
- #
177
- def encryptable_info
178
- wmi = WmiLite::Wmi.new("Root\\CIMV2\\Security\\MicrosoftVolumeEncryption")
179
- disks = wmi.instances_of("Win32_EncryptableVolume")
180
- encryption_properties(disks)
181
- rescue WmiLite::WmiException
182
- Ohai::Log.debug("Unable to access Win32_EncryptableVolume. Skipping encryptable details")
183
- Mash.new
184
- end
185
-
186
- # Refines and calculates logical properties out of given instances.
187
- #
188
- # Note that :device here is the same as Volume name and there for compatibility with other OSes.
189
- #
190
- # @param [WmiLite::Wmi::Instance] disks
191
- #
192
- # @return [Mash] Each drive containing following properties:
193
- #
194
- # * :kb_size (Integer)
195
- # * :kb_available (Integer)
196
- # * :kb_used (Integer)
197
- # * :percent_used (Integer)
198
- # * :mount (String)
199
- # * :fs_type (String)
200
- # * :volume_name (String)
201
- # * :device (String)
202
- #
203
- def logical_properties(disks)
204
- properties = Mash.new
205
- disks.each do |disk|
206
- property = Mash.new
207
- # In windows the closest thing we have to a device is the volume name
208
- # and the "mountpoint" is the drive letter...
209
- device = disk["volumename"].to_s.downcase
210
- mount = disk["deviceid"]
211
- property[:kb_size] = disk["size"] ? disk["size"].to_i / 1000 : 0
212
- property[:kb_available] = disk["freespace"].to_i / 1000
213
- property[:kb_used] = property[:kb_size] - property[:kb_available]
214
- property[:percent_used] = (property[:kb_size] == 0 ? 0 : (property[:kb_used] * 100 / property[:kb_size]))
215
- property[:mount] = mount
216
- property[:fs_type] = disk["filesystem"].to_s.downcase
217
- property[:drive_type] = disk["drivetype"].to_i
218
- property[:drive_type_string] = DRIVE_TYPE[disk["drivetype"].to_i]
219
- property[:drive_type_human] = disk["description"].to_s
220
- property[:volume_name] = disk["volumename"].to_s
221
- property[:device] = device
222
-
223
- key = "#{device},#{mount}"
224
- properties[key] = property
225
- end
226
- properties
227
- end
228
-
229
- # Refines and calculates encryption properties out of given instances
230
- #
231
- # @param [WmiLite::Wmi::Instance] disks
232
- #
233
- # @return [Mash] Each drive containing following properties:
234
- #
235
- # * :encryption_status (String)
236
- #
237
- def encryption_properties(disks)
238
- properties = Mash.new
239
- disks.each do |disk|
240
- property = Mash.new
241
- property[:encryption_status] = disk["conversionstatus"] ? CONVERSION_STATUS[disk["conversionstatus"]] : ""
242
- key = disk["driveletter"]
243
- properties[key] = property
244
- end
245
- properties
246
- end
247
-
248
- # Merges all the various properties of filesystems
249
- #
250
- # @param [Array<Mash>] disks_info
251
- # Array of the Mashes containing disk properties
252
- #
253
- # @return [Mash]
254
- #
255
- def merge_info(logical_info, encryption_info)
256
- fs = Mash.new
257
-
258
- encryption_keys_used = Set.new
259
- logical_info.each do |key, info|
260
- if encryption_info[info["mount"]]
261
- encryption_keys_used.add(info["mount"])
262
- fs[key] = info.merge(encryption_info[info["mount"]])
263
- else
264
- fs[key] = info.dup
265
- end
266
- end
267
- left_enc = encryption_info.reject { |x| encryption_keys_used.include?(x) }
268
- left_enc.each do |key, info|
269
- fs[",#{key}"] = info
270
- end
271
- fs
272
- end
273
-
274
- def collect_btrfs_data(entry)
275
- btrfs = Mash.new
276
- if entry[:fs_type] == "btrfs" && entry["uuid"]
277
- uuid = entry["uuid"]
278
- alloc = "/sys/fs/btrfs/#{uuid}/allocation"
279
- if Dir.exist?(alloc)
280
- %w{data metadata system}.each do |bg_type|
281
- dir = "#{alloc}/#{bg_type}"
282
- %w{single dup}.each do |raid|
283
- if file_exist?("#{dir}/#{raid}")
284
- btrfs["raid"] = raid
285
- end
286
- end
287
- logger.trace("Plugin Filesystem: reading btrfs allocation files at #{dir}")
288
- btrfs["allocation"] ||= Mash.new
289
- btrfs["allocation"][bg_type] ||= Mash.new
290
- %w{total_bytes bytes_used}.each do |field|
291
- bytes = file_read("#{dir}/#{field}").chomp.to_i
292
- btrfs["allocation"][bg_type][field] = "#{bytes}"
293
- end
294
- end
295
- end
296
- end
297
- btrfs
298
- end
299
-
300
- collect_data(:linux) do
301
- fs = Mash.new
302
-
303
- # Grab filesystem data from df
304
- run_with_check("df") do
305
- fs.merge!(parse_common_df(shell_out("df -P").stdout))
306
-
307
- # Grab filesystem inode data from df
308
- shell_out("df -iP").stdout.each_line do |line|
309
- case line
310
- when /^Filesystem\s+Inodes/
311
- next
312
- when /^(.+?)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\%)\s+(.+)$/
313
- key = "#{$1},#{$6}"
314
- fs[key] ||= Mash.new
315
- fs[key][:device] = $1
316
- fs[key][:total_inodes] = $2
317
- fs[key][:inodes_used] = $3
318
- fs[key][:inodes_available] = $4
319
- fs[key][:inodes_percent_used] = $5
320
- fs[key][:mount] = $6
321
- end
322
- end
323
- end
324
-
325
- # Grab mount information from /bin/mount
326
- run_with_check("mount") do
327
- shell_out("mount").stdout.each_line do |line|
328
- if line =~ /^(.+?) on (.+?) type (.+?) \((.+?)\)$/
329
- key = "#{$1},#{$2}"
330
- fs[key] ||= Mash.new
331
- fs[key][:device] = $1
332
- fs[key][:mount] = $2
333
- fs[key][:fs_type] = $3
334
- fs[key][:mount_options] = $4.split(",")
335
- end
336
- end
337
- end
338
-
339
- # We used to try to decide if we wanted to run lsblk or blkid
340
- # but they each have a variety of cases were they fail to report
341
- # data. For example, there are a variety of cases where lsblk won't
342
- # report unmounted filesystems, but blkid will. And vise-versa. Sweet.
343
- # So for reliability, we'll run both, if we have them.
344
-
345
- lsblk = which("lsblk")
346
- blkid = which("blkid")
347
- cmds = []
348
- # These should be in order of preference... first writer wins.
349
- if lsblk
350
- cmds << "#{lsblk} -n -P -o NAME,UUID,LABEL,FSTYPE"
351
- end
352
- if blkid
353
- cmds << blkid
354
- end
355
-
356
- cmds.each do |cmd|
357
- cmdtype = File.basename(cmd.split.first)
358
- # setting the timeout here for `lsblk` and `blkid` commands to 60
359
- # this is to allow machines with large amounts of attached LUNs
360
- # to respond back to the command successfully
361
- run_with_check(cmdtype) do
362
- shell_out(cmd, timeout: 60).stdout.each_line do |line|
363
- parsed = parse_line(line, cmdtype)
364
- next if parsed.nil?
365
-
366
- # lsblk lists each device once, so we need to update all entries
367
- # in the hash that are related to this device
368
- keys_to_update = []
369
- fs.each_key do |key|
370
- keys_to_update << key if key.start_with?("#{parsed[:dev]},")
371
- end
372
-
373
- if keys_to_update.empty?
374
- key = "#{parsed[:dev]},"
375
- fs[key] = Mash.new
376
- fs[key][:device] = parsed[:dev]
377
- keys_to_update << key
378
- end
379
-
380
- keys_to_update.each do |k|
381
- %i{fs_type uuid label}.each do |subkey|
382
- if parsed[subkey] && !parsed[subkey].empty?
383
- fs[k][subkey] = parsed[subkey]
384
- end
385
- end
386
- end
387
- end
388
- end
389
- end
390
-
391
- # Grab any missing mount information from /proc/mounts
392
- if file_exist?("/proc/mounts")
393
- mounts = ""
394
- # Due to https://tickets.opscode.com/browse/OHAI-196
395
- # we have to non-block read dev files. Ew.
396
- f = file_open("/proc/mounts")
397
- loop do
398
- data = f.read_nonblock(4096)
399
- mounts << data
400
- # We should just catch EOFError, but the kernel had a period of
401
- # bugginess with reading virtual files, so we're being extra
402
- # cautious here, catching all exceptions, and then we'll read
403
- # whatever data we might have
404
- rescue Exception
405
- break
406
- end
407
- f.close
408
-
409
- mounts.each_line do |line|
410
- if line =~ /^(\S+) (\S+) (\S+) (\S+) \S+ \S+$/
411
- key = "#{$1},#{$2}"
412
- next if fs.key?(key)
413
-
414
- fs[key] = Mash.new
415
- fs[key][:device] = $1
416
- fs[key][:mount] = $2
417
- fs[key][:fs_type] = $3
418
- fs[key][:mount_options] = $4.split(",")
419
- end
420
- end
421
- end
422
-
423
- fs.each do |key, entry|
424
- if entry[:fs_type] == "btrfs"
425
- fs[key][:btrfs] = collect_btrfs_data(entry)
426
- end
427
- end
428
-
429
- by_pair = fs
430
- by_device = generate_device_view(fs)
431
- by_mountpoint = generate_mountpoint_view(fs)
432
-
433
- fs_data = Mash.new
434
- fs_data["by_device"] = by_device
435
- fs_data["by_mountpoint"] = by_mountpoint
436
- fs_data["by_pair"] = by_pair
437
-
438
- # Set the filesystem data
439
- filesystem fs_data
440
- end
441
-
442
- collect_data(:freebsd, :openbsd, :netbsd, :dragonflybsd) do
443
- fs = Mash.new
444
-
445
- # Grab filesystem data from df
446
- run_with_check("df") do
447
- so = shell_out("df")
448
- fs.merge!(parse_common_df(so.stdout))
449
-
450
- so = shell_out("df -iP")
451
- so.stdout.lines do |line|
452
- case line
453
- when /^Filesystem/ # skip the header
454
- next
455
- when /^(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\%\s+(\d+)\s+(\d+)\s+(\d+)%\s+(\S+)$/
456
- key = "#{$1},#{$9}"
457
- fs[key] ||= Mash.new
458
- fs[key][:device] = $1
459
- fs[key][:inodes_used] = $6
460
- fs[key][:inodes_available] = $7
461
- fs[key][:total_inodes] = ($6.to_i + $7.to_i).to_s
462
- fs[key][:inodes_percent_used] = $8
463
- fs[key][:mount] = $9
464
- end
465
- end
466
- end
467
-
468
- # Grab mount information from mount
469
- run_with_check("mount") do
470
- so = shell_out("mount -l")
471
- so.stdout.lines do |line|
472
- if line =~ /^(.+?) on (.+?) \((.+?), (.+?)\)$/
473
- key = "#{$1},#{$2}"
474
- fs[key] ||= Mash.new
475
- fs[key][:device] = $1
476
- fs[key][:mount] = $2
477
- fs[key][:fs_type] = $3
478
- fs[key][:mount_options] = $4.split(/,\s*/)
479
- end
480
- end
481
- end
482
-
483
- # create views
484
- by_pair = fs
485
- by_device = generate_device_view(fs)
486
- by_mountpoint = generate_mountpoint_view(fs)
487
-
488
- fs_data = Mash.new
489
- fs_data["by_device"] = by_device
490
- fs_data["by_mountpoint"] = by_mountpoint
491
- fs_data["by_pair"] = by_pair
492
-
493
- filesystem fs_data
494
- end
495
-
496
- collect_data(:darwin) do
497
- fs = Mash.new
498
- block_size = 0
499
- # on new versions of OSX, -i is default, on old versions it's not, so
500
- # specifying it gets consistent output
501
- run_with_check("df") do
502
- so = shell_out("df -i")
503
- so.stdout.each_line do |line|
504
- case line
505
- when /^Filesystem\s+(\d+)-/
506
- block_size = $1.to_i
507
- next
508
- when /^(.+?)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\%)\s+(\d+)\s+(\d+)\s+(\d+%)\s+(.+)$/
509
- key = "#{$1},#{$9}"
510
- fs[key] = Mash.new
511
- fs[key][:block_size] = block_size
512
- fs[key][:device] = $1
513
- fs[key][:kb_size] = ($2.to_i / (1024 / block_size)).to_s
514
- fs[key][:kb_used] = ($3.to_i / (1024 / block_size)).to_s
515
- fs[key][:kb_available] = ($4.to_i / (1024 / block_size)).to_s
516
- fs[key][:percent_used] = $5
517
- fs[key][:inodes_used] = $6
518
- fs[key][:inodes_available] = $7
519
- fs[key][:total_inodes] = ($6.to_i + $7.to_i).to_s
520
- fs[key][:inodes_percent_used] = $8
521
- fs[key][:mount] = $9
522
- end
523
- end
524
- end
525
-
526
- run_with_check("mount") do
527
- so = shell_out("mount")
528
- so.stdout.lines do |line|
529
- if line =~ /^(.+?) on (.+?) \((.+?), (.+?)\)$/
530
- key = "#{$1},#{$2}"
531
- fs[key] ||= Mash.new
532
- fs[key][:mount] = $2
533
- fs[key][:fs_type] = $3
534
- fs[key][:mount_options] = $4.split(/,\s*/)
535
- end
536
- end
537
- end
538
-
539
- by_pair = fs
540
- by_device = generate_device_view(fs)
541
- by_mountpoint = generate_mountpoint_view(fs)
542
-
543
- fs_data = Mash.new
544
- fs_data["by_device"] = by_device
545
- fs_data["by_mountpoint"] = by_mountpoint
546
- fs_data["by_pair"] = by_pair
547
-
548
- filesystem fs_data
549
- end
550
-
551
- collect_data(:solaris2) do
552
- fs = Mash.new
553
-
554
- # Grab filesystem data from df
555
- run_with_check("df") do
556
- so = shell_out("df -Pka")
557
- fs.merge!(parse_common_df(so.stdout))
558
-
559
- # Grab file system type from df (must be done separately)
560
- so = shell_out("df -na")
561
- so.stdout.lines do |line|
562
- next unless line =~ /^(.+?)\s*: (\S+)\s*$/
563
-
564
- mount = $1
565
- fs.each do |key, fs_attributes|
566
- next unless fs_attributes[:mount] == mount
567
-
568
- fs[key][:fs_type] = $2
569
- end
570
- end
571
- end
572
-
573
- # Grab mount information from /bin/mount
574
- run_with_check("mount") do
575
- so = shell_out("mount")
576
- so.stdout.lines do |line|
577
- next unless line =~ /^(.+?) on (.+?) (.+?) on (.+?)$/
578
-
579
- key = "#{$2},#{$1}"
580
- fs[key] ||= Mash.new
581
- fs[key][:mount] = $1
582
- fs[key][:mount_time] = $4 # $4 must come before "split", else it becomes nil
583
- fs[key][:mount_options] = $3.split("/")
584
- end
585
- end
586
-
587
- # Grab any zfs data from "zfs get"
588
- zfs = Mash.new
589
- zfs_get = "zfs get -p -H all"
590
- run_with_check("zfs") do
591
- so = shell_out(zfs_get)
592
- so.stdout.lines do |line|
593
- next unless line =~ /^([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)$/
594
-
595
- filesystem = $1
596
- property = $2
597
- value = $3
598
- source = $4.chomp
599
- zfs[filesystem] ||= Mash.new
600
- # if this fs doesn't exist, put in the bare minimum
601
- zfs[filesystem][property] = {
602
- value: value,
603
- source: source,
604
- }
605
- end
606
- end
607
-
608
- zfs.each do |fs_name, attributes|
609
- mountpoint = attributes[:mountpoint][:value] if attributes[:mountpoint]
610
- key = "#{fs_name},#{mountpoint}"
611
- fs[key] ||= Mash.new
612
- fs[key][:fs_type] = "zfs"
613
- fs[key][:mount] = mountpoint if mountpoint
614
- fs[key][:device] = fs_name
615
- fs[key][:zfs_properties] = attributes
616
- # find all zfs parents
617
- parents = fs_name.split("/")
618
- zfs_parents = []
619
- (0..parents.length - 1).to_a.each do |parent_index|
620
- next_parent = parents[0..parent_index].join("/")
621
- zfs_parents.push(next_parent)
622
- end
623
- zfs_parents.pop
624
- fs[key][:zfs_parents] = zfs_parents
625
- fs[key][:zfs_zpool] = (zfs_parents.length == 0)
626
- end
627
-
628
- # create views
629
- by_pair = fs
630
- by_device = generate_device_view(fs)
631
- by_mountpoint = generate_mountpoint_view(fs)
632
-
633
- fs_data = Mash.new
634
- fs_data["by_device"] = by_device
635
- fs_data["by_mountpoint"] = by_mountpoint
636
- fs_data["by_pair"] = by_pair
637
-
638
- filesystem fs_data
639
- end
640
-
641
- collect_data(:aix) do
642
- def parse_df_or_mount(shell_out)
643
- oldie = Mash.new
644
-
645
- shell_out.lines.each do |line|
646
- fields = line.split
647
- case line
648
- # headers and horizontal rules to skip
649
- when /^\s*(node|---|^Filesystem\s+1024-blocks)/
650
- next
651
- # strictly a df entry
652
- when /^(.+?)\s+([0-9-]+)\s+([0-9-]+)\s+([0-9-]+)\s+([0-9-]+\%*)\s+(.+)$/
653
- if $1 == "Global"
654
- dev = "#{$1}:#{$6}"
655
- else
656
- dev = $1
657
- end
658
- mountpoint = $6
659
- key = "#{dev},#{mountpoint}"
660
- oldie[key] ||= Mash.new
661
- oldie[key][:kb_size] = $2
662
- oldie[key][:kb_used] = $3
663
- oldie[key][:kb_available] = $4
664
- oldie[key][:percent_used] = $5
665
- oldie[key][:mount] = mountpoint
666
- oldie[key][:device] = dev
667
- # an entry starting with 'G' or / (E.G. /tmp or /var)
668
- when %r{^\s*(G.*?|/\w)}
669
- if fields[0] == "Global"
670
- dev = fields[0] + ":" + fields[1]
671
- else
672
- dev = fields[0]
673
- end
674
- mountpoint = fields[1]
675
- key = "#{dev},#{mountpoint}"
676
- oldie[key] ||= Mash.new
677
- oldie[key][:mount] = mountpoint
678
- oldie[key][:fs_type] = fields[2]
679
- oldie[key][:mount_options] = fields[6].split(",")
680
- oldie[key][:device] = dev
681
- # entries occupying the 'Node' column parsed here
682
- else
683
- dev = fields[0] + ":" + fields[1]
684
- mountpoint = fields[2]
685
- key = "#{dev},#{mountpoint}"
686
- oldie[key] ||= Mash.new
687
- oldie[key][:mount] = mountpoint
688
- oldie[key][:device] = dev
689
- oldie[key][:fs_type] = fields[3]
690
- oldie[key][:mount_options] = fields[7].split(",")
691
- end
692
- end
693
- oldie
694
- end
695
-
696
- def collect_old_version(shell_outs)
697
- mount_hash = parse_df_or_mount shell_outs[:mount]
698
- df_hash = parse_df_or_mount shell_outs[:df_Pk]
699
-
700
- mount_hash.each do |key, hash|
701
- df_hash[key].merge!(hash) if df_hash.key?(key)
702
- end
703
-
704
- mount_hash.merge(df_hash)
705
- end
706
-
707
- # Cache the command output
708
- shell_outs = Mash.new
709
-
710
- run_with_check("mount") do
711
- shell_outs[:mount] = shell_out("mount").stdout
712
- end
713
-
714
- run_with_check("df") do
715
- shell_outs[:df_Pk] = shell_out("df -Pk").stdout
716
- end
717
-
718
- fs = collect_old_version(shell_outs)
719
- by_pair = fs
720
- by_device = generate_device_view(fs)
721
- by_mountpoint = generate_mountpoint_view(fs)
722
-
723
- fs_data = Mash.new
724
- fs_data["by_device"] = by_device
725
- fs_data["by_mountpoint"] = by_mountpoint
726
- fs_data["by_pair"] = by_pair
727
-
728
- filesystem fs_data
729
- end
730
-
731
- collect_data(:windows) do
732
- require "set" unless defined?(Set)
733
- require "wmi-lite/wmi" unless defined?(WmiLite::Wmi)
734
- require_relative "../mash"
735
-
736
- fs = merge_info(logical_info, encryptable_info)
737
-
738
- by_pair = fs
739
- by_device = generate_device_view(fs)
740
- by_mountpoint = generate_mountpoint_view(fs)
741
-
742
- fs_data = Mash.new
743
- fs_data["by_device"] = by_device
744
- fs_data["by_mountpoint"] = by_mountpoint
745
- fs_data["by_pair"] = by_pair
746
-
747
- # Chef 16 added 'filesystem2'
748
- # In Chef 17 we made 'filesystem' and 'filesystem2' match (both new-style)
749
- # In Chef 18 we will drop 'filesystem2'
750
- filesystem fs_data
751
- filesystem2 fs_data
752
- end
753
- end
1
+ #
2
+ # Author:: Phil Dibowitz <phil@ipom.com>
3
+ # Author:: Adam Jacob <adam@chef.io>
4
+ # Author:: Kurt Yoder (ktyopscode@yoderhome.com)
5
+ # Author:: Deepali Jagtap (<deepali.jagtap@clogeny.com>)
6
+ # Author:: Prabhu Das (<prabhu.das@clogeny.com>)
7
+ # Author:: Isa Farnik (<isa@chef.io>)
8
+ # Author:: James Gartrell (<jgartrel@gmail.com>)
9
+ # Copyright:: Copyright (c) Chef Software Inc.
10
+ # Copyright:: Copyright (c) 2015 Facebook, Inc.
11
+ # License:: Apache License, Version 2.0
12
+ #
13
+ # Licensed under the Apache License, Version 2.0 (the "License");
14
+ # you may not use this file except in compliance with the License.
15
+ # You may obtain a copy of the License at
16
+ #
17
+ # http://www.apache.org/licenses/LICENSE-2.0
18
+ #
19
+ # Unless required by applicable law or agreed to in writing, software
20
+ # distributed under the License is distributed on an "AS IS" BASIS,
21
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
+ # See the License for the specific language governing permissions and
23
+ # limitations under the License.
24
+ #
25
+
26
+ Ohai.plugin(:Filesystem) do
27
+ provides "filesystem".freeze
28
+
29
+ def find_device(name)
30
+ %w{/dev /dev/mapper}.each do |dir|
31
+ path = File.join(dir, name)
32
+ return path if file_exist?(path)
33
+ end
34
+ name
35
+ end
36
+
37
+ def parse_line(line, cmdtype)
38
+ case cmdtype
39
+ when "lsblk"
40
+ regex = /NAME="(\S+).*?" UUID="(\S*)" LABEL="(\S*)" FSTYPE="(\S*)"/
41
+ if line =~ regex
42
+ dev = $1
43
+ dev = find_device(dev) unless dev.start_with?("/")
44
+ uuid = $2
45
+ label = $3
46
+ fs_type = $4
47
+ return { dev: dev, uuid: uuid, label: label, fs_type: fs_type }
48
+ end
49
+ when "blkid"
50
+ bits = line.split
51
+ dev = bits.shift.split(":")[0]
52
+ f = { dev: dev }
53
+ bits.each do |keyval|
54
+ if keyval =~ /(\S+)="(\S+)"/
55
+ key = $1.downcase.to_sym
56
+ key = :fs_type if key == :type
57
+ f[key] = $2
58
+ end
59
+ end
60
+ return f
61
+ end
62
+ nil
63
+ end
64
+
65
+ def generate_device_view(fs)
66
+ view = {}
67
+ fs.each_value do |entry|
68
+ view[entry[:device]] ||= Mash.new
69
+ entry.each do |key, val|
70
+ next if %w{device mount}.include?(key)
71
+
72
+ view[entry[:device]][key] = val
73
+ end
74
+ view[entry[:device]][:mounts] ||= []
75
+ if entry[:mount]
76
+ view[entry[:device]][:mounts] << entry[:mount]
77
+ end
78
+ end
79
+ view
80
+ end
81
+
82
+ def generate_mountpoint_view(fs)
83
+ view = {}
84
+ fs.each_value do |entry|
85
+ next unless entry[:mount]
86
+
87
+ view[entry[:mount]] ||= Mash.new
88
+ entry.each do |key, val|
89
+ next if %w{mount device}.include?(key)
90
+
91
+ view[entry[:mount]][key] = val
92
+ end
93
+ view[entry[:mount]][:devices] ||= []
94
+ if entry[:device]
95
+ view[entry[:mount]][:devices] << entry[:device]
96
+ end
97
+ end
98
+ view
99
+ end
100
+
101
+ def parse_common_df(out)
102
+ fs = {}
103
+ out.each_line do |line|
104
+ case line
105
+ when /^Filesystem\s+1024-blocks/
106
+ next
107
+ when /^(.+?)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\%)\s+(.+)$/
108
+ key = "#{$1},#{$6}"
109
+ fs[key] = Mash.new
110
+ fs[key][:device] = $1
111
+ fs[key][:kb_size] = $2
112
+ fs[key][:kb_used] = $3
113
+ fs[key][:kb_available] = $4
114
+ fs[key][:percent_used] = $5
115
+ fs[key][:mount] = $6
116
+ end
117
+ end
118
+ fs
119
+ end
120
+
121
+ def run_with_check(bin, &block)
122
+ yield
123
+ rescue Ohai::Exceptions::Exec => e
124
+ unless Ohai.config[:plugin][:filesystem][:allow_partial_data]
125
+ raise e
126
+ end
127
+
128
+ logger.warn("Plugin Filesystem: #{bin} binary is not available. Some data will not be available.")
129
+ end
130
+
131
+ ### Windows specific methods BEGINS
132
+ # Drive types
133
+ DRIVE_TYPE ||= %w{unknown no_root_dir removable local network cd ram}.freeze
134
+
135
+ # Volume encryption or decryption status
136
+ #
137
+ # @see https://docs.microsoft.com/en-us/windows/desktop/SecProv/getconversionstatus-win32-encryptablevolume#parameters
138
+ #
139
+ CONVERSION_STATUS ||= %w{
140
+ FullyDecrypted FullyEncrypted EncryptionInProgress
141
+ DecryptionInProgress EncryptionPaused DecryptionPaused
142
+ }.freeze
143
+
144
+ # Returns a Mash loaded with logical details
145
+ #
146
+ # Uses Win32_LogicalDisk and logical_properties to return general details of volumes.
147
+ #
148
+ # Returns an empty Mash in case of any WMI exception.
149
+ #
150
+ # @see https://docs.microsoft.com/en-us/windows/desktop/CIMWin32Prov/win32-logicaldisk
151
+ #
152
+ # @return [Mash]
153
+ #
154
+ def logical_info
155
+ wmi = WmiLite::Wmi.new("Root\\CIMV2")
156
+
157
+ # TODO: We should really be parsing Win32_Volume and Win32_MountPoint.
158
+ disks = wmi.instances_of("Win32_LogicalDisk")
159
+ logical_properties(disks)
160
+ rescue WmiLite::WmiException
161
+ Ohai::Log.debug("Unable to access Win32_LogicalDisk. Skipping logical details")
162
+ Mash.new
163
+ end
164
+
165
+ # Returns a Mash loaded with encryption details
166
+ #
167
+ # Uses Win32_EncryptableVolume and encryption_properties to return encryption details of volumes.
168
+ #
169
+ # Returns an empty Mash in case of any WMI exception.
170
+ #
171
+ # @note We are fetching Encryption Status only as of now
172
+ #
173
+ # @see https://msdn.microsoft.com/en-us/library/windows/desktop/aa376483(v=vs.85).aspx
174
+ #
175
+ # @return [Mash]
176
+ #
177
+ def encryptable_info
178
+ wmi = WmiLite::Wmi.new("Root\\CIMV2\\Security\\MicrosoftVolumeEncryption")
179
+ disks = wmi.instances_of("Win32_EncryptableVolume")
180
+ encryption_properties(disks)
181
+ rescue WmiLite::WmiException
182
+ Ohai::Log.debug("Unable to access Win32_EncryptableVolume. Skipping encryptable details")
183
+ Mash.new
184
+ end
185
+
186
+ # Refines and calculates logical properties out of given instances.
187
+ #
188
+ # Note that :device here is the same as Volume name and there for compatibility with other OSes.
189
+ #
190
+ # @param [WmiLite::Wmi::Instance] disks
191
+ #
192
+ # @return [Mash] Each drive containing following properties:
193
+ #
194
+ # * :kb_size (Integer)
195
+ # * :kb_available (Integer)
196
+ # * :kb_used (Integer)
197
+ # * :percent_used (Integer)
198
+ # * :mount (String)
199
+ # * :fs_type (String)
200
+ # * :volume_name (String)
201
+ # * :device (String)
202
+ #
203
+ def logical_properties(disks)
204
+ properties = Mash.new
205
+ disks.each do |disk|
206
+ property = Mash.new
207
+ # In windows the closest thing we have to a device is the volume name
208
+ # and the "mountpoint" is the drive letter...
209
+ device = disk["volumename"].to_s.downcase
210
+ mount = disk["deviceid"]
211
+ property[:kb_size] = disk["size"] ? disk["size"].to_i / 1000 : 0
212
+ property[:kb_available] = disk["freespace"].to_i / 1000
213
+ property[:kb_used] = property[:kb_size] - property[:kb_available]
214
+ property[:percent_used] = (property[:kb_size] == 0 ? 0 : (property[:kb_used] * 100 / property[:kb_size]))
215
+ property[:mount] = mount
216
+ property[:fs_type] = disk["filesystem"].to_s.downcase
217
+ property[:drive_type] = disk["drivetype"].to_i
218
+ property[:drive_type_string] = DRIVE_TYPE[disk["drivetype"].to_i]
219
+ property[:drive_type_human] = disk["description"].to_s
220
+ property[:volume_name] = disk["volumename"].to_s
221
+ property[:device] = device
222
+
223
+ key = "#{device},#{mount}"
224
+ properties[key] = property
225
+ end
226
+ properties
227
+ end
228
+
229
+ # Refines and calculates encryption properties out of given instances
230
+ #
231
+ # @param [WmiLite::Wmi::Instance] disks
232
+ #
233
+ # @return [Mash] Each drive containing following properties:
234
+ #
235
+ # * :encryption_status (String)
236
+ #
237
+ def encryption_properties(disks)
238
+ properties = Mash.new
239
+ disks.each do |disk|
240
+ property = Mash.new
241
+ property[:encryption_status] = disk["conversionstatus"] ? CONVERSION_STATUS[disk["conversionstatus"]] : ""
242
+ key = disk["driveletter"]
243
+ properties[key] = property
244
+ end
245
+ properties
246
+ end
247
+
248
+ # Merges all the various properties of filesystems
249
+ #
250
+ # @param [Array<Mash>] disks_info
251
+ # Array of the Mashes containing disk properties
252
+ #
253
+ # @return [Mash]
254
+ #
255
+ def merge_info(logical_info, encryption_info)
256
+ fs = Mash.new
257
+
258
+ encryption_keys_used = Set.new
259
+ logical_info.each do |key, info|
260
+ if encryption_info[info["mount"]]
261
+ encryption_keys_used.add(info["mount"])
262
+ fs[key] = info.merge(encryption_info[info["mount"]])
263
+ else
264
+ fs[key] = info.dup
265
+ end
266
+ end
267
+ left_enc = encryption_info.reject { |x| encryption_keys_used.include?(x) }
268
+ left_enc.each do |key, info|
269
+ fs[",#{key}"] = info
270
+ end
271
+ fs
272
+ end
273
+
274
+ def collect_btrfs_data(entry)
275
+ btrfs = Mash.new
276
+ if entry[:fs_type] == "btrfs" && entry["uuid"]
277
+ uuid = entry["uuid"]
278
+ alloc = "/sys/fs/btrfs/#{uuid}/allocation"
279
+ if Dir.exist?(alloc)
280
+ %w{data metadata system}.each do |bg_type|
281
+ dir = "#{alloc}/#{bg_type}"
282
+ %w{single dup}.each do |raid|
283
+ if file_exist?("#{dir}/#{raid}")
284
+ btrfs["raid"] = raid
285
+ end
286
+ end
287
+ logger.trace("Plugin Filesystem: reading btrfs allocation files at #{dir}")
288
+ btrfs["allocation"] ||= Mash.new
289
+ btrfs["allocation"][bg_type] ||= Mash.new
290
+ %w{total_bytes bytes_used}.each do |field|
291
+ bytes = file_read("#{dir}/#{field}").chomp.to_i
292
+ btrfs["allocation"][bg_type][field] = "#{bytes}"
293
+ end
294
+ end
295
+ end
296
+ end
297
+ btrfs
298
+ end
299
+
300
+ collect_data(:linux) do
301
+ fs = Mash.new
302
+
303
+ # Grab filesystem data from df
304
+ run_with_check("df") do
305
+ fs.merge!(parse_common_df(shell_out("df -P").stdout))
306
+
307
+ # Grab filesystem inode data from df
308
+ shell_out("df -iP").stdout.each_line do |line|
309
+ case line
310
+ when /^Filesystem\s+Inodes/
311
+ next
312
+ when /^(.+?)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\%)\s+(.+)$/
313
+ key = "#{$1},#{$6}"
314
+ fs[key] ||= Mash.new
315
+ fs[key][:device] = $1
316
+ fs[key][:total_inodes] = $2
317
+ fs[key][:inodes_used] = $3
318
+ fs[key][:inodes_available] = $4
319
+ fs[key][:inodes_percent_used] = $5
320
+ fs[key][:mount] = $6
321
+ end
322
+ end
323
+ end
324
+
325
+ # Grab mount information from /bin/mount
326
+ run_with_check("mount") do
327
+ shell_out("mount").stdout.each_line do |line|
328
+ if line =~ /^(.+?) on (.+?) type (.+?) \((.+?)\)$/
329
+ key = "#{$1},#{$2}"
330
+ fs[key] ||= Mash.new
331
+ fs[key][:device] = $1
332
+ fs[key][:mount] = $2
333
+ fs[key][:fs_type] = $3
334
+ fs[key][:mount_options] = $4.split(",")
335
+ end
336
+ end
337
+ end
338
+
339
+ # We used to try to decide if we wanted to run lsblk or blkid
340
+ # but they each have a variety of cases were they fail to report
341
+ # data. For example, there are a variety of cases where lsblk won't
342
+ # report unmounted filesystems, but blkid will. And vise-versa. Sweet.
343
+ # So for reliability, we'll run both, if we have them.
344
+
345
+ lsblk = which("lsblk")
346
+ blkid = which("blkid")
347
+ cmds = []
348
+ # These should be in order of preference... first writer wins.
349
+ if lsblk
350
+ cmds << "#{lsblk} -n -P -o NAME,UUID,LABEL,FSTYPE"
351
+ end
352
+ if blkid
353
+ cmds << blkid
354
+ end
355
+
356
+ cmds.each do |cmd|
357
+ cmdtype = File.basename(cmd.split.first)
358
+ # setting the timeout here for `lsblk` and `blkid` commands to 60
359
+ # this is to allow machines with large amounts of attached LUNs
360
+ # to respond back to the command successfully
361
+ run_with_check(cmdtype) do
362
+ shell_out(cmd, timeout: 60).stdout.each_line do |line|
363
+ parsed = parse_line(line, cmdtype)
364
+ next if parsed.nil?
365
+
366
+ # lsblk lists each device once, so we need to update all entries
367
+ # in the hash that are related to this device
368
+ keys_to_update = []
369
+ fs.each_key do |key|
370
+ keys_to_update << key if key.start_with?("#{parsed[:dev]},")
371
+ end
372
+
373
+ if keys_to_update.empty?
374
+ key = "#{parsed[:dev]},"
375
+ fs[key] = Mash.new
376
+ fs[key][:device] = parsed[:dev]
377
+ keys_to_update << key
378
+ end
379
+
380
+ keys_to_update.each do |k|
381
+ %i{fs_type uuid label}.each do |subkey|
382
+ if parsed[subkey] && !parsed[subkey].empty?
383
+ fs[k][subkey] = parsed[subkey]
384
+ end
385
+ end
386
+ end
387
+ end
388
+ end
389
+ end
390
+
391
+ # Grab any missing mount information from /proc/mounts
392
+ if file_exist?("/proc/mounts")
393
+ mounts = ""
394
+ # Due to https://tickets.opscode.com/browse/OHAI-196
395
+ # we have to non-block read dev files. Ew.
396
+ f = file_open("/proc/mounts")
397
+ loop do
398
+ data = f.read_nonblock(4096)
399
+ mounts << data
400
+ # We should just catch EOFError, but the kernel had a period of
401
+ # bugginess with reading virtual files, so we're being extra
402
+ # cautious here, catching all exceptions, and then we'll read
403
+ # whatever data we might have
404
+ rescue Exception
405
+ break
406
+ end
407
+ f.close
408
+
409
+ mounts.each_line do |line|
410
+ if line =~ /^(\S+) (\S+) (\S+) (\S+) \S+ \S+$/
411
+ key = "#{$1},#{$2}"
412
+ next if fs.key?(key)
413
+
414
+ fs[key] = Mash.new
415
+ fs[key][:device] = $1
416
+ fs[key][:mount] = $2
417
+ fs[key][:fs_type] = $3
418
+ fs[key][:mount_options] = $4.split(",")
419
+ end
420
+ end
421
+ end
422
+
423
+ fs.each do |key, entry|
424
+ if entry[:fs_type] == "btrfs"
425
+ fs[key][:btrfs] = collect_btrfs_data(entry)
426
+ end
427
+ end
428
+
429
+ by_pair = fs
430
+ by_device = generate_device_view(fs)
431
+ by_mountpoint = generate_mountpoint_view(fs)
432
+
433
+ fs_data = Mash.new
434
+ fs_data["by_device"] = by_device
435
+ fs_data["by_mountpoint"] = by_mountpoint
436
+ fs_data["by_pair"] = by_pair
437
+
438
+ # Set the filesystem data
439
+ filesystem fs_data
440
+ end
441
+
442
+ collect_data(:freebsd, :openbsd, :netbsd, :dragonflybsd) do
443
+ fs = Mash.new
444
+
445
+ # Grab filesystem data from df
446
+ run_with_check("df") do
447
+ so = shell_out("df")
448
+ fs.merge!(parse_common_df(so.stdout))
449
+
450
+ so = shell_out("df -iP")
451
+ so.stdout.lines do |line|
452
+ case line
453
+ when /^Filesystem/ # skip the header
454
+ next
455
+ when /^(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\%\s+(\d+)\s+(\d+)\s+(\d+)%\s+(\S+)$/
456
+ key = "#{$1},#{$9}"
457
+ fs[key] ||= Mash.new
458
+ fs[key][:device] = $1
459
+ fs[key][:inodes_used] = $6
460
+ fs[key][:inodes_available] = $7
461
+ fs[key][:total_inodes] = ($6.to_i + $7.to_i).to_s
462
+ fs[key][:inodes_percent_used] = $8
463
+ fs[key][:mount] = $9
464
+ end
465
+ end
466
+ end
467
+
468
+ # Grab mount information from mount
469
+ run_with_check("mount") do
470
+ so = shell_out("mount -l")
471
+ so.stdout.lines do |line|
472
+ if line =~ /^(.+?) on (.+?) \((.+?), (.+?)\)$/
473
+ key = "#{$1},#{$2}"
474
+ fs[key] ||= Mash.new
475
+ fs[key][:device] = $1
476
+ fs[key][:mount] = $2
477
+ fs[key][:fs_type] = $3
478
+ fs[key][:mount_options] = $4.split(/,\s*/)
479
+ end
480
+ end
481
+ end
482
+
483
+ # create views
484
+ by_pair = fs
485
+ by_device = generate_device_view(fs)
486
+ by_mountpoint = generate_mountpoint_view(fs)
487
+
488
+ fs_data = Mash.new
489
+ fs_data["by_device"] = by_device
490
+ fs_data["by_mountpoint"] = by_mountpoint
491
+ fs_data["by_pair"] = by_pair
492
+
493
+ filesystem fs_data
494
+ end
495
+
496
+ collect_data(:darwin) do
497
+ fs = Mash.new
498
+ block_size = 0
499
+ # on new versions of OSX, -i is default, on old versions it's not, so
500
+ # specifying it gets consistent output
501
+ run_with_check("df") do
502
+ so = shell_out("df -i")
503
+ so.stdout.each_line do |line|
504
+ case line
505
+ when /^Filesystem\s+(\d+)-/
506
+ block_size = $1.to_i
507
+ next
508
+ when /^(.+?)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\%)\s+(\d+)\s+(\d+)\s+(\d+%)\s+(.+)$/
509
+ key = "#{$1},#{$9}"
510
+ fs[key] = Mash.new
511
+ fs[key][:block_size] = block_size
512
+ fs[key][:device] = $1
513
+ fs[key][:kb_size] = ($2.to_i / (1024 / block_size)).to_s
514
+ fs[key][:kb_used] = ($3.to_i / (1024 / block_size)).to_s
515
+ fs[key][:kb_available] = ($4.to_i / (1024 / block_size)).to_s
516
+ fs[key][:percent_used] = $5
517
+ fs[key][:inodes_used] = $6
518
+ fs[key][:inodes_available] = $7
519
+ fs[key][:total_inodes] = ($6.to_i + $7.to_i).to_s
520
+ fs[key][:inodes_percent_used] = $8
521
+ fs[key][:mount] = $9
522
+ end
523
+ end
524
+ end
525
+
526
+ run_with_check("mount") do
527
+ so = shell_out("mount")
528
+ so.stdout.lines do |line|
529
+ if line =~ /^(.+?) on (.+?) \((.+?), (.+?)\)$/
530
+ key = "#{$1},#{$2}"
531
+ fs[key] ||= Mash.new
532
+ fs[key][:mount] = $2
533
+ fs[key][:fs_type] = $3
534
+ fs[key][:mount_options] = $4.split(/,\s*/)
535
+ end
536
+ end
537
+ end
538
+
539
+ by_pair = fs
540
+ by_device = generate_device_view(fs)
541
+ by_mountpoint = generate_mountpoint_view(fs)
542
+
543
+ fs_data = Mash.new
544
+ fs_data["by_device"] = by_device
545
+ fs_data["by_mountpoint"] = by_mountpoint
546
+ fs_data["by_pair"] = by_pair
547
+
548
+ filesystem fs_data
549
+ end
550
+
551
+ collect_data(:solaris2) do
552
+ fs = Mash.new
553
+
554
+ # Grab filesystem data from df
555
+ run_with_check("df") do
556
+ so = shell_out("df -Pka")
557
+ fs.merge!(parse_common_df(so.stdout))
558
+
559
+ # Grab file system type from df (must be done separately)
560
+ so = shell_out("df -na")
561
+ so.stdout.lines do |line|
562
+ next unless line =~ /^(.+?)\s*: (\S+)\s*$/
563
+
564
+ mount = $1
565
+ fs.each do |key, fs_attributes|
566
+ next unless fs_attributes[:mount] == mount
567
+
568
+ fs[key][:fs_type] = $2
569
+ end
570
+ end
571
+ end
572
+
573
+ # Grab mount information from /bin/mount
574
+ run_with_check("mount") do
575
+ so = shell_out("mount")
576
+ so.stdout.lines do |line|
577
+ next unless line =~ /^(.+?) on (.+?) (.+?) on (.+?)$/
578
+
579
+ key = "#{$2},#{$1}"
580
+ fs[key] ||= Mash.new
581
+ fs[key][:mount] = $1
582
+ fs[key][:mount_time] = $4 # $4 must come before "split", else it becomes nil
583
+ fs[key][:mount_options] = $3.split("/")
584
+ end
585
+ end
586
+
587
+ # Grab any zfs data from "zfs get"
588
+ zfs = Mash.new
589
+ zfs_get = "zfs get -p -H all"
590
+ run_with_check("zfs") do
591
+ so = shell_out(zfs_get)
592
+ so.stdout.lines do |line|
593
+ next unless line =~ /^([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)$/
594
+
595
+ filesystem = $1
596
+ property = $2
597
+ value = $3
598
+ source = $4.chomp
599
+ zfs[filesystem] ||= Mash.new
600
+ # if this fs doesn't exist, put in the bare minimum
601
+ zfs[filesystem][property] = {
602
+ value: value,
603
+ source: source,
604
+ }
605
+ end
606
+ end
607
+
608
+ zfs.each do |fs_name, attributes|
609
+ mountpoint = attributes[:mountpoint][:value] if attributes[:mountpoint]
610
+ key = "#{fs_name},#{mountpoint}"
611
+ fs[key] ||= Mash.new
612
+ fs[key][:fs_type] = "zfs"
613
+ fs[key][:mount] = mountpoint if mountpoint
614
+ fs[key][:device] = fs_name
615
+ fs[key][:zfs_properties] = attributes
616
+ # find all zfs parents
617
+ parents = fs_name.split("/")
618
+ zfs_parents = []
619
+ (0..parents.length - 1).to_a.each do |parent_index|
620
+ next_parent = parents[0..parent_index].join("/")
621
+ zfs_parents.push(next_parent)
622
+ end
623
+ zfs_parents.pop
624
+ fs[key][:zfs_parents] = zfs_parents
625
+ fs[key][:zfs_zpool] = (zfs_parents.length == 0)
626
+ end
627
+
628
+ # create views
629
+ by_pair = fs
630
+ by_device = generate_device_view(fs)
631
+ by_mountpoint = generate_mountpoint_view(fs)
632
+
633
+ fs_data = Mash.new
634
+ fs_data["by_device"] = by_device
635
+ fs_data["by_mountpoint"] = by_mountpoint
636
+ fs_data["by_pair"] = by_pair
637
+
638
+ filesystem fs_data
639
+ end
640
+
641
+ collect_data(:aix) do
642
+ def parse_df_or_mount(shell_out)
643
+ oldie = Mash.new
644
+
645
+ shell_out.lines.each do |line|
646
+ fields = line.split
647
+ case line
648
+ # headers and horizontal rules to skip
649
+ when /^\s*(node|---|^Filesystem\s+1024-blocks)/
650
+ next
651
+ # strictly a df entry
652
+ when /^(.+?)\s+([0-9-]+)\s+([0-9-]+)\s+([0-9-]+)\s+([0-9-]+\%*)\s+(.+)$/
653
+ if $1 == "Global"
654
+ dev = "#{$1}:#{$6}"
655
+ else
656
+ dev = $1
657
+ end
658
+ mountpoint = $6
659
+ key = "#{dev},#{mountpoint}"
660
+ oldie[key] ||= Mash.new
661
+ oldie[key][:kb_size] = $2
662
+ oldie[key][:kb_used] = $3
663
+ oldie[key][:kb_available] = $4
664
+ oldie[key][:percent_used] = $5
665
+ oldie[key][:mount] = mountpoint
666
+ oldie[key][:device] = dev
667
+ # an entry starting with 'G' or / (E.G. /tmp or /var)
668
+ when %r{^\s*(G.*?|/\w)}
669
+ if fields[0] == "Global"
670
+ dev = fields[0] + ":" + fields[1]
671
+ else
672
+ dev = fields[0]
673
+ end
674
+ mountpoint = fields[1]
675
+ key = "#{dev},#{mountpoint}"
676
+ oldie[key] ||= Mash.new
677
+ oldie[key][:mount] = mountpoint
678
+ oldie[key][:fs_type] = fields[2]
679
+ oldie[key][:mount_options] = fields[6].split(",")
680
+ oldie[key][:device] = dev
681
+ # entries occupying the 'Node' column parsed here
682
+ else
683
+ dev = fields[0] + ":" + fields[1]
684
+ mountpoint = fields[2]
685
+ key = "#{dev},#{mountpoint}"
686
+ oldie[key] ||= Mash.new
687
+ oldie[key][:mount] = mountpoint
688
+ oldie[key][:device] = dev
689
+ oldie[key][:fs_type] = fields[3]
690
+ oldie[key][:mount_options] = fields[7].split(",")
691
+ end
692
+ end
693
+ oldie
694
+ end
695
+
696
+ def collect_old_version(shell_outs)
697
+ mount_hash = parse_df_or_mount shell_outs[:mount]
698
+ df_hash = parse_df_or_mount shell_outs[:df_Pk]
699
+
700
+ mount_hash.each do |key, hash|
701
+ df_hash[key].merge!(hash) if df_hash.key?(key)
702
+ end
703
+
704
+ mount_hash.merge(df_hash)
705
+ end
706
+
707
+ # Cache the command output
708
+ shell_outs = Mash.new
709
+
710
+ run_with_check("mount") do
711
+ shell_outs[:mount] = shell_out("mount").stdout
712
+ end
713
+
714
+ run_with_check("df") do
715
+ shell_outs[:df_Pk] = shell_out("df -Pk").stdout
716
+ end
717
+
718
+ fs = collect_old_version(shell_outs)
719
+ by_pair = fs
720
+ by_device = generate_device_view(fs)
721
+ by_mountpoint = generate_mountpoint_view(fs)
722
+
723
+ fs_data = Mash.new
724
+ fs_data["by_device"] = by_device
725
+ fs_data["by_mountpoint"] = by_mountpoint
726
+ fs_data["by_pair"] = by_pair
727
+
728
+ filesystem fs_data
729
+ end
730
+
731
+ collect_data(:windows) do
732
+ require "set" unless defined?(Set)
733
+ require "wmi-lite/wmi" unless defined?(WmiLite::Wmi)
734
+ require_relative "../mash"
735
+
736
+ fs = merge_info(logical_info, encryptable_info)
737
+
738
+ by_pair = fs
739
+ by_device = generate_device_view(fs)
740
+ by_mountpoint = generate_mountpoint_view(fs)
741
+
742
+ fs_data = Mash.new
743
+ fs_data["by_device"] = by_device
744
+ fs_data["by_mountpoint"] = by_mountpoint
745
+ fs_data["by_pair"] = by_pair
746
+
747
+ # Chef 16 added 'filesystem2'
748
+ # In Chef 17 we made 'filesystem' and 'filesystem2' match (both new-style)
749
+ # In Chef 18 we will drop 'filesystem2'
750
+ filesystem fs_data
751
+ filesystem2 fs_data
752
+ end
753
+ end