ohai 18.0.26 → 18.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (168) 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 +86 -86
  17. data/lib/ohai/mixin/azure_metadata.rb +111 -111
  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 +256 -256
  24. data/lib/ohai/mixin/gce_metadata.rb +83 -83
  25. data/lib/ohai/mixin/http_helper.rb +64 -64
  26. data/lib/ohai/mixin/network_helper.rb +65 -65
  27. data/lib/ohai/mixin/oci_metadata.rb +69 -0
  28. data/lib/ohai/mixin/os.rb +128 -128
  29. data/lib/ohai/mixin/scaleway_metadata.rb +51 -51
  30. data/lib/ohai/mixin/seconds_to_human.rb +52 -52
  31. data/lib/ohai/mixin/shell_out.rb +51 -51
  32. data/lib/ohai/mixin/softlayer_metadata.rb +74 -74
  33. data/lib/ohai/mixin/string.rb +31 -31
  34. data/lib/ohai/mixin/train_helpers.rb +36 -36
  35. data/lib/ohai/mixin/which.rb +39 -39
  36. data/lib/ohai/plugin_config.rb +47 -47
  37. data/lib/ohai/plugins/aix/kernel.rb +50 -50
  38. data/lib/ohai/plugins/aix/memory.rb +37 -37
  39. data/lib/ohai/plugins/aix/network.rb +142 -142
  40. data/lib/ohai/plugins/aix/platform.rb +30 -30
  41. data/lib/ohai/plugins/aix/uptime.rb +54 -54
  42. data/lib/ohai/plugins/aix/virtualization.rb +154 -154
  43. data/lib/ohai/plugins/alibaba.rb +72 -72
  44. data/lib/ohai/plugins/azure.rb +154 -154
  45. data/lib/ohai/plugins/bsd/virtualization.rb +121 -121
  46. data/lib/ohai/plugins/c.rb +178 -178
  47. data/lib/ohai/plugins/chef.rb +50 -50
  48. data/lib/ohai/plugins/cloud.rb +379 -357
  49. data/lib/ohai/plugins/command.rb +26 -26
  50. data/lib/ohai/plugins/cpu.rb +635 -635
  51. data/lib/ohai/plugins/darwin/hardware.rb +99 -99
  52. data/lib/ohai/plugins/darwin/memory.rb +62 -62
  53. data/lib/ohai/plugins/darwin/network.rb +207 -207
  54. data/lib/ohai/plugins/darwin/platform.rb +38 -38
  55. data/lib/ohai/plugins/darwin/virtualization.rb +90 -93
  56. data/lib/ohai/plugins/digital_ocean.rb +67 -67
  57. data/lib/ohai/plugins/dmi.rb +134 -134
  58. data/lib/ohai/plugins/docker.rb +58 -58
  59. data/lib/ohai/plugins/dragonflybsd/memory.rb +60 -60
  60. data/lib/ohai/plugins/dragonflybsd/network.rb +128 -128
  61. data/lib/ohai/plugins/dragonflybsd/platform.rb +28 -28
  62. data/lib/ohai/plugins/ec2.rb +148 -148
  63. data/lib/ohai/plugins/elixir.rb +36 -36
  64. data/lib/ohai/plugins/erlang.rb +60 -60
  65. data/lib/ohai/plugins/eucalyptus.rb +86 -86
  66. data/lib/ohai/plugins/filesystem.rb +751 -751
  67. data/lib/ohai/plugins/fips.rb +36 -36
  68. data/lib/ohai/plugins/freebsd/memory.rb +60 -60
  69. data/lib/ohai/plugins/freebsd/network.rb +128 -128
  70. data/lib/ohai/plugins/freebsd/platform.rb +28 -28
  71. data/lib/ohai/plugins/gce.rb +89 -89
  72. data/lib/ohai/plugins/go.rb +34 -34
  73. data/lib/ohai/plugins/groovy.rb +38 -38
  74. data/lib/ohai/plugins/grub2.rb +40 -40
  75. data/lib/ohai/plugins/habitat.rb +73 -73
  76. data/lib/ohai/plugins/haskell.rb +96 -96
  77. data/lib/ohai/plugins/hostname.rb +133 -133
  78. data/lib/ohai/plugins/init_package.rb +26 -26
  79. data/lib/ohai/plugins/java.rb +78 -78
  80. data/lib/ohai/plugins/kernel.rb +292 -292
  81. data/lib/ohai/plugins/keys.rb +27 -27
  82. data/lib/ohai/plugins/languages.rb +26 -26
  83. data/lib/ohai/plugins/libvirt.rb +114 -114
  84. data/lib/ohai/plugins/linode.rb +73 -73
  85. data/lib/ohai/plugins/linux/block_device.rb +48 -48
  86. data/lib/ohai/plugins/linux/hostnamectl.rb +34 -34
  87. data/lib/ohai/plugins/linux/interrupts.rb +84 -84
  88. data/lib/ohai/plugins/linux/ipc.rb +52 -52
  89. data/lib/ohai/plugins/linux/livepatch.rb +38 -38
  90. data/lib/ohai/plugins/linux/lsb.rb +46 -46
  91. data/lib/ohai/plugins/linux/lspci.rb +76 -76
  92. data/lib/ohai/plugins/linux/machineid.rb +36 -36
  93. data/lib/ohai/plugins/linux/mdadm.rb +120 -120
  94. data/lib/ohai/plugins/linux/memory.rb +106 -106
  95. data/lib/ohai/plugins/linux/network.rb +879 -879
  96. data/lib/ohai/plugins/linux/os_release.rb +38 -38
  97. data/lib/ohai/plugins/linux/platform.rb +314 -314
  98. data/lib/ohai/plugins/linux/selinux.rb +69 -69
  99. data/lib/ohai/plugins/linux/sessions.rb +54 -54
  100. data/lib/ohai/plugins/linux/sysctl.rb +39 -39
  101. data/lib/ohai/plugins/linux/systemd_paths.rb +36 -36
  102. data/lib/ohai/plugins/linux/tc.rb +61 -61
  103. data/lib/ohai/plugins/linux/virtualization.rb +300 -300
  104. data/lib/ohai/plugins/lua.rb +39 -39
  105. data/lib/ohai/plugins/mono.rb +50 -50
  106. data/lib/ohai/plugins/netbsd/memory.rb +99 -99
  107. data/lib/ohai/plugins/netbsd/network.rb +122 -122
  108. data/lib/ohai/plugins/netbsd/platform.rb +28 -28
  109. data/lib/ohai/plugins/network.rb +186 -186
  110. data/lib/ohai/plugins/nodejs.rb +40 -40
  111. data/lib/ohai/plugins/oci.rb +94 -0
  112. data/lib/ohai/plugins/ohai.rb +29 -29
  113. data/lib/ohai/plugins/ohai_time.rb +26 -26
  114. data/lib/ohai/plugins/openbsd/memory.rb +99 -99
  115. data/lib/ohai/plugins/openbsd/network.rb +122 -122
  116. data/lib/ohai/plugins/openbsd/platform.rb +28 -28
  117. data/lib/ohai/plugins/openstack.rb +84 -84
  118. data/lib/ohai/plugins/os.rb +55 -55
  119. data/lib/ohai/plugins/packages.rb +234 -234
  120. data/lib/ohai/plugins/passwd.rb +104 -104
  121. data/lib/ohai/plugins/perl.rb +45 -45
  122. data/lib/ohai/plugins/php.rb +52 -52
  123. data/lib/ohai/plugins/platform.rb +29 -29
  124. data/lib/ohai/plugins/powershell.rb +82 -82
  125. data/lib/ohai/plugins/ps.rb +35 -35
  126. data/lib/ohai/plugins/python.rb +43 -43
  127. data/lib/ohai/plugins/rackspace.rb +177 -177
  128. data/lib/ohai/plugins/root_group.rb +41 -41
  129. data/lib/ohai/plugins/rpm.rb +121 -121
  130. data/lib/ohai/plugins/ruby.rb +66 -66
  131. data/lib/ohai/plugins/rust.rb +34 -34
  132. data/lib/ohai/plugins/scala.rb +38 -38
  133. data/lib/ohai/plugins/scaleway.rb +58 -58
  134. data/lib/ohai/plugins/scsi.rb +52 -52
  135. data/lib/ohai/plugins/shard.rb +142 -142
  136. data/lib/ohai/plugins/shells.rb +32 -32
  137. data/lib/ohai/plugins/softlayer.rb +48 -48
  138. data/lib/ohai/plugins/solaris2/dmi.rb +191 -191
  139. data/lib/ohai/plugins/solaris2/memory.rb +32 -32
  140. data/lib/ohai/plugins/solaris2/network.rb +192 -192
  141. data/lib/ohai/plugins/solaris2/platform.rb +58 -58
  142. data/lib/ohai/plugins/solaris2/virtualization.rb +90 -90
  143. data/lib/ohai/plugins/ssh_host_key.rb +84 -84
  144. data/lib/ohai/plugins/sysconf.rb +46 -46
  145. data/lib/ohai/plugins/timezone.rb +45 -25
  146. data/lib/ohai/plugins/train.rb +35 -35
  147. data/lib/ohai/plugins/uptime.rb +95 -95
  148. data/lib/ohai/plugins/virtualbox.rb +197 -197
  149. data/lib/ohai/plugins/vmware.rb +109 -94
  150. data/lib/ohai/plugins/windows/dmi.rb +95 -95
  151. data/lib/ohai/plugins/windows/drivers.rb +52 -52
  152. data/lib/ohai/plugins/windows/memory.rb +39 -39
  153. data/lib/ohai/plugins/windows/network.rb +222 -222
  154. data/lib/ohai/plugins/windows/platform.rb +34 -34
  155. data/lib/ohai/plugins/windows/system_enclosure.rb +29 -29
  156. data/lib/ohai/plugins/windows/virtualization.rb +45 -45
  157. data/lib/ohai/plugins/zpools.rb +94 -94
  158. data/lib/ohai/provides_map.rb +208 -208
  159. data/lib/ohai/runner.rb +128 -128
  160. data/lib/ohai/system.rb +258 -258
  161. data/lib/ohai/train_transport.rb +29 -29
  162. data/lib/ohai/util/file_helper.rb +6 -6
  163. data/lib/ohai/util/ip_helper.rb +56 -56
  164. data/lib/ohai/util/win32.rb +47 -47
  165. data/lib/ohai/version.rb +23 -23
  166. data/lib/ohai.rb +23 -23
  167. data/ohai.gemspec +35 -35
  168. metadata +5 -3
@@ -1,751 +1,751 @@
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
- %w{data metadata system}.each do |bg_type|
280
- dir = "#{alloc}/#{bg_type}"
281
- %w{single dup}.each do |raid|
282
- if file_exist?("#{dir}/#{raid}")
283
- btrfs["raid"] = raid
284
- end
285
- end
286
- logger.trace("Plugin Filesystem: reading btrfs allocation files at #{dir}")
287
- btrfs["allocation"] ||= Mash.new
288
- btrfs["allocation"][bg_type] ||= Mash.new
289
- %w{total_bytes bytes_used}.each do |field|
290
- bytes = file_read("#{dir}/#{field}").chomp.to_i
291
- btrfs["allocation"][bg_type][field] = "#{bytes}"
292
- end
293
- end
294
- end
295
- btrfs
296
- end
297
-
298
- collect_data(:linux) do
299
- fs = Mash.new
300
-
301
- # Grab filesystem data from df
302
- run_with_check("df") do
303
- fs.merge!(parse_common_df(shell_out("df -P").stdout))
304
-
305
- # Grab filesystem inode data from df
306
- shell_out("df -iP").stdout.each_line do |line|
307
- case line
308
- when /^Filesystem\s+Inodes/
309
- next
310
- when /^(.+?)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\%)\s+(.+)$/
311
- key = "#{$1},#{$6}"
312
- fs[key] ||= Mash.new
313
- fs[key][:device] = $1
314
- fs[key][:total_inodes] = $2
315
- fs[key][:inodes_used] = $3
316
- fs[key][:inodes_available] = $4
317
- fs[key][:inodes_percent_used] = $5
318
- fs[key][:mount] = $6
319
- end
320
- end
321
- end
322
-
323
- # Grab mount information from /bin/mount
324
- run_with_check("mount") do
325
- shell_out("mount").stdout.each_line do |line|
326
- if line =~ /^(.+?) on (.+?) type (.+?) \((.+?)\)$/
327
- key = "#{$1},#{$2}"
328
- fs[key] ||= Mash.new
329
- fs[key][:device] = $1
330
- fs[key][:mount] = $2
331
- fs[key][:fs_type] = $3
332
- fs[key][:mount_options] = $4.split(",")
333
- end
334
- end
335
- end
336
-
337
- # We used to try to decide if we wanted to run lsblk or blkid
338
- # but they each have a variety of cases were they fail to report
339
- # data. For example, there are a variety of cases where lsblk won't
340
- # report unmounted filesystems, but blkid will. And vise-versa. Sweet.
341
- # So for reliability, we'll run both, if we have them.
342
-
343
- lsblk = which("lsblk")
344
- blkid = which("blkid")
345
- cmds = []
346
- # These should be in order of preference... first writer wins.
347
- if lsblk
348
- cmds << "#{lsblk} -n -P -o NAME,UUID,LABEL,FSTYPE"
349
- end
350
- if blkid
351
- cmds << blkid
352
- end
353
-
354
- cmds.each do |cmd|
355
- cmdtype = File.basename(cmd.split.first)
356
- # setting the timeout here for `lsblk` and `blkid` commands to 60
357
- # this is to allow machines with large amounts of attached LUNs
358
- # to respond back to the command successfully
359
- run_with_check(cmdtype) do
360
- shell_out(cmd, timeout: 60).stdout.each_line do |line|
361
- parsed = parse_line(line, cmdtype)
362
- next if parsed.nil?
363
-
364
- # lsblk lists each device once, so we need to update all entries
365
- # in the hash that are related to this device
366
- keys_to_update = []
367
- fs.each_key do |key|
368
- keys_to_update << key if key.start_with?("#{parsed[:dev]},")
369
- end
370
-
371
- if keys_to_update.empty?
372
- key = "#{parsed[:dev]},"
373
- fs[key] = Mash.new
374
- fs[key][:device] = parsed[:dev]
375
- keys_to_update << key
376
- end
377
-
378
- keys_to_update.each do |k|
379
- %i{fs_type uuid label}.each do |subkey|
380
- if parsed[subkey] && !parsed[subkey].empty?
381
- fs[k][subkey] = parsed[subkey]
382
- end
383
- end
384
- end
385
- end
386
- end
387
- end
388
-
389
- # Grab any missing mount information from /proc/mounts
390
- if file_exist?("/proc/mounts")
391
- mounts = ""
392
- # Due to https://tickets.opscode.com/browse/OHAI-196
393
- # we have to non-block read dev files. Ew.
394
- f = file_open("/proc/mounts")
395
- loop do
396
- data = f.read_nonblock(4096)
397
- mounts << data
398
- # We should just catch EOFError, but the kernel had a period of
399
- # bugginess with reading virtual files, so we're being extra
400
- # cautious here, catching all exceptions, and then we'll read
401
- # whatever data we might have
402
- rescue Exception
403
- break
404
- end
405
- f.close
406
-
407
- mounts.each_line do |line|
408
- if line =~ /^(\S+) (\S+) (\S+) (\S+) \S+ \S+$/
409
- key = "#{$1},#{$2}"
410
- next if fs.key?(key)
411
-
412
- fs[key] = Mash.new
413
- fs[key][:device] = $1
414
- fs[key][:mount] = $2
415
- fs[key][:fs_type] = $3
416
- fs[key][:mount_options] = $4.split(",")
417
- end
418
- end
419
- end
420
-
421
- fs.each do |key, entry|
422
- if entry[:fs_type] == "btrfs"
423
- fs[key][:btrfs] = collect_btrfs_data(entry)
424
- end
425
- end
426
-
427
- by_pair = fs
428
- by_device = generate_device_view(fs)
429
- by_mountpoint = generate_mountpoint_view(fs)
430
-
431
- fs_data = Mash.new
432
- fs_data["by_device"] = by_device
433
- fs_data["by_mountpoint"] = by_mountpoint
434
- fs_data["by_pair"] = by_pair
435
-
436
- # Set the filesystem data
437
- filesystem fs_data
438
- end
439
-
440
- collect_data(:freebsd, :openbsd, :netbsd, :dragonflybsd) do
441
- fs = Mash.new
442
-
443
- # Grab filesystem data from df
444
- run_with_check("df") do
445
- so = shell_out("df")
446
- fs.merge!(parse_common_df(so.stdout))
447
-
448
- so = shell_out("df -iP")
449
- so.stdout.lines do |line|
450
- case line
451
- when /^Filesystem/ # skip the header
452
- next
453
- when /^(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\%\s+(\d+)\s+(\d+)\s+(\d+)%\s+(\S+)$/
454
- key = "#{$1},#{$9}"
455
- fs[key] ||= Mash.new
456
- fs[key][:device] = $1
457
- fs[key][:inodes_used] = $6
458
- fs[key][:inodes_available] = $7
459
- fs[key][:total_inodes] = ($6.to_i + $7.to_i).to_s
460
- fs[key][:inodes_percent_used] = $8
461
- fs[key][:mount] = $9
462
- end
463
- end
464
- end
465
-
466
- # Grab mount information from mount
467
- run_with_check("mount") do
468
- so = shell_out("mount -l")
469
- so.stdout.lines do |line|
470
- if line =~ /^(.+?) on (.+?) \((.+?), (.+?)\)$/
471
- key = "#{$1},#{$2}"
472
- fs[key] ||= Mash.new
473
- fs[key][:device] = $1
474
- fs[key][:mount] = $2
475
- fs[key][:fs_type] = $3
476
- fs[key][:mount_options] = $4.split(/,\s*/)
477
- end
478
- end
479
- end
480
-
481
- # create views
482
- by_pair = fs
483
- by_device = generate_device_view(fs)
484
- by_mountpoint = generate_mountpoint_view(fs)
485
-
486
- fs_data = Mash.new
487
- fs_data["by_device"] = by_device
488
- fs_data["by_mountpoint"] = by_mountpoint
489
- fs_data["by_pair"] = by_pair
490
-
491
- filesystem fs_data
492
- end
493
-
494
- collect_data(:darwin) do
495
- fs = Mash.new
496
- block_size = 0
497
- # on new versions of OSX, -i is default, on old versions it's not, so
498
- # specifying it gets consistent output
499
- run_with_check("df") do
500
- so = shell_out("df -i")
501
- so.stdout.each_line do |line|
502
- case line
503
- when /^Filesystem\s+(\d+)-/
504
- block_size = $1.to_i
505
- next
506
- when /^(.+?)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\%)\s+(\d+)\s+(\d+)\s+(\d+%)\s+(.+)$/
507
- key = "#{$1},#{$9}"
508
- fs[key] = Mash.new
509
- fs[key][:block_size] = block_size
510
- fs[key][:device] = $1
511
- fs[key][:kb_size] = ($2.to_i / (1024 / block_size)).to_s
512
- fs[key][:kb_used] = ($3.to_i / (1024 / block_size)).to_s
513
- fs[key][:kb_available] = ($4.to_i / (1024 / block_size)).to_s
514
- fs[key][:percent_used] = $5
515
- fs[key][:inodes_used] = $6
516
- fs[key][:inodes_available] = $7
517
- fs[key][:total_inodes] = ($6.to_i + $7.to_i).to_s
518
- fs[key][:inodes_percent_used] = $8
519
- fs[key][:mount] = $9
520
- end
521
- end
522
- end
523
-
524
- run_with_check("mount") do
525
- so = shell_out("mount")
526
- so.stdout.lines do |line|
527
- if line =~ /^(.+?) on (.+?) \((.+?), (.+?)\)$/
528
- key = "#{$1},#{$2}"
529
- fs[key] ||= Mash.new
530
- fs[key][:mount] = $2
531
- fs[key][:fs_type] = $3
532
- fs[key][:mount_options] = $4.split(/,\s*/)
533
- end
534
- end
535
- end
536
-
537
- by_pair = fs
538
- by_device = generate_device_view(fs)
539
- by_mountpoint = generate_mountpoint_view(fs)
540
-
541
- fs_data = Mash.new
542
- fs_data["by_device"] = by_device
543
- fs_data["by_mountpoint"] = by_mountpoint
544
- fs_data["by_pair"] = by_pair
545
-
546
- filesystem fs_data
547
- end
548
-
549
- collect_data(:solaris2) do
550
- fs = Mash.new
551
-
552
- # Grab filesystem data from df
553
- run_with_check("df") do
554
- so = shell_out("df -Pka")
555
- fs.merge!(parse_common_df(so.stdout))
556
-
557
- # Grab file system type from df (must be done separately)
558
- so = shell_out("df -na")
559
- so.stdout.lines do |line|
560
- next unless line =~ /^(.+?)\s*: (\S+)\s*$/
561
-
562
- mount = $1
563
- fs.each do |key, fs_attributes|
564
- next unless fs_attributes[:mount] == mount
565
-
566
- fs[key][:fs_type] = $2
567
- end
568
- end
569
- end
570
-
571
- # Grab mount information from /bin/mount
572
- run_with_check("mount") do
573
- so = shell_out("mount")
574
- so.stdout.lines do |line|
575
- next unless line =~ /^(.+?) on (.+?) (.+?) on (.+?)$/
576
-
577
- key = "#{$2},#{$1}"
578
- fs[key] ||= Mash.new
579
- fs[key][:mount] = $1
580
- fs[key][:mount_time] = $4 # $4 must come before "split", else it becomes nil
581
- fs[key][:mount_options] = $3.split("/")
582
- end
583
- end
584
-
585
- # Grab any zfs data from "zfs get"
586
- zfs = Mash.new
587
- zfs_get = "zfs get -p -H all"
588
- run_with_check("zfs") do
589
- so = shell_out(zfs_get)
590
- so.stdout.lines do |line|
591
- next unless line =~ /^([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)$/
592
-
593
- filesystem = $1
594
- property = $2
595
- value = $3
596
- source = $4.chomp
597
- zfs[filesystem] ||= Mash.new
598
- # if this fs doesn't exist, put in the bare minimum
599
- zfs[filesystem][property] = {
600
- value: value,
601
- source: source,
602
- }
603
- end
604
- end
605
-
606
- zfs.each do |fs_name, attributes|
607
- mountpoint = attributes[:mountpoint][:value] if attributes[:mountpoint]
608
- key = "#{fs_name},#{mountpoint}"
609
- fs[key] ||= Mash.new
610
- fs[key][:fs_type] = "zfs"
611
- fs[key][:mount] = mountpoint if mountpoint
612
- fs[key][:device] = fs_name
613
- fs[key][:zfs_properties] = attributes
614
- # find all zfs parents
615
- parents = fs_name.split("/")
616
- zfs_parents = []
617
- (0..parents.length - 1).to_a.each do |parent_index|
618
- next_parent = parents[0..parent_index].join("/")
619
- zfs_parents.push(next_parent)
620
- end
621
- zfs_parents.pop
622
- fs[key][:zfs_parents] = zfs_parents
623
- fs[key][:zfs_zpool] = (zfs_parents.length == 0)
624
- end
625
-
626
- # create views
627
- by_pair = fs
628
- by_device = generate_device_view(fs)
629
- by_mountpoint = generate_mountpoint_view(fs)
630
-
631
- fs_data = Mash.new
632
- fs_data["by_device"] = by_device
633
- fs_data["by_mountpoint"] = by_mountpoint
634
- fs_data["by_pair"] = by_pair
635
-
636
- filesystem fs_data
637
- end
638
-
639
- collect_data(:aix) do
640
- def parse_df_or_mount(shell_out)
641
- oldie = Mash.new
642
-
643
- shell_out.lines.each do |line|
644
- fields = line.split
645
- case line
646
- # headers and horizontal rules to skip
647
- when /^\s*(node|---|^Filesystem\s+1024-blocks)/
648
- next
649
- # strictly a df entry
650
- when /^(.+?)\s+([0-9-]+)\s+([0-9-]+)\s+([0-9-]+)\s+([0-9-]+\%*)\s+(.+)$/
651
- if $1 == "Global"
652
- dev = "#{$1}:#{$6}"
653
- else
654
- dev = $1
655
- end
656
- mountpoint = $6
657
- key = "#{dev},#{mountpoint}"
658
- oldie[key] ||= Mash.new
659
- oldie[key][:kb_size] = $2
660
- oldie[key][:kb_used] = $3
661
- oldie[key][:kb_available] = $4
662
- oldie[key][:percent_used] = $5
663
- oldie[key][:mount] = mountpoint
664
- oldie[key][:device] = dev
665
- # an entry starting with 'G' or / (E.G. /tmp or /var)
666
- when %r{^\s*(G.*?|/\w)}
667
- if fields[0] == "Global"
668
- dev = fields[0] + ":" + fields[1]
669
- else
670
- dev = fields[0]
671
- end
672
- mountpoint = fields[1]
673
- key = "#{dev},#{mountpoint}"
674
- oldie[key] ||= Mash.new
675
- oldie[key][:mount] = mountpoint
676
- oldie[key][:fs_type] = fields[2]
677
- oldie[key][:mount_options] = fields[6].split(",")
678
- oldie[key][:device] = dev
679
- # entries occupying the 'Node' column parsed here
680
- else
681
- dev = fields[0] + ":" + fields[1]
682
- mountpoint = fields[2]
683
- key = "#{dev},#{mountpoint}"
684
- oldie[key] ||= Mash.new
685
- oldie[key][:mount] = mountpoint
686
- oldie[key][:device] = dev
687
- oldie[key][:fs_type] = fields[3]
688
- oldie[key][:mount_options] = fields[7].split(",")
689
- end
690
- end
691
- oldie
692
- end
693
-
694
- def collect_old_version(shell_outs)
695
- mount_hash = parse_df_or_mount shell_outs[:mount]
696
- df_hash = parse_df_or_mount shell_outs[:df_Pk]
697
-
698
- mount_hash.each do |key, hash|
699
- df_hash[key].merge!(hash) if df_hash.key?(key)
700
- end
701
-
702
- mount_hash.merge(df_hash)
703
- end
704
-
705
- # Cache the command output
706
- shell_outs = Mash.new
707
-
708
- run_with_check("mount") do
709
- shell_outs[:mount] = shell_out("mount").stdout
710
- end
711
-
712
- run_with_check("df") do
713
- shell_outs[:df_Pk] = shell_out("df -Pk").stdout
714
- end
715
-
716
- fs = collect_old_version(shell_outs)
717
- by_pair = fs
718
- by_device = generate_device_view(fs)
719
- by_mountpoint = generate_mountpoint_view(fs)
720
-
721
- fs_data = Mash.new
722
- fs_data["by_device"] = by_device
723
- fs_data["by_mountpoint"] = by_mountpoint
724
- fs_data["by_pair"] = by_pair
725
-
726
- filesystem fs_data
727
- end
728
-
729
- collect_data(:windows) do
730
- require "set" unless defined?(Set)
731
- require "wmi-lite/wmi" unless defined?(WmiLite::Wmi)
732
- require_relative "../mash"
733
-
734
- fs = merge_info(logical_info, encryptable_info)
735
-
736
- by_pair = fs
737
- by_device = generate_device_view(fs)
738
- by_mountpoint = generate_mountpoint_view(fs)
739
-
740
- fs_data = Mash.new
741
- fs_data["by_device"] = by_device
742
- fs_data["by_mountpoint"] = by_mountpoint
743
- fs_data["by_pair"] = by_pair
744
-
745
- # Chef 16 added 'filesystem2'
746
- # In Chef 17 we made 'filesystem' and 'filesystem2' match (both new-style)
747
- # In Chef 18 we will drop 'filesystem2'
748
- filesystem fs_data
749
- filesystem2 fs_data
750
- end
751
- 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
+ %w{data metadata system}.each do |bg_type|
280
+ dir = "#{alloc}/#{bg_type}"
281
+ %w{single dup}.each do |raid|
282
+ if file_exist?("#{dir}/#{raid}")
283
+ btrfs["raid"] = raid
284
+ end
285
+ end
286
+ logger.trace("Plugin Filesystem: reading btrfs allocation files at #{dir}")
287
+ btrfs["allocation"] ||= Mash.new
288
+ btrfs["allocation"][bg_type] ||= Mash.new
289
+ %w{total_bytes bytes_used}.each do |field|
290
+ bytes = file_read("#{dir}/#{field}").chomp.to_i
291
+ btrfs["allocation"][bg_type][field] = "#{bytes}"
292
+ end
293
+ end
294
+ end
295
+ btrfs
296
+ end
297
+
298
+ collect_data(:linux) do
299
+ fs = Mash.new
300
+
301
+ # Grab filesystem data from df
302
+ run_with_check("df") do
303
+ fs.merge!(parse_common_df(shell_out("df -P").stdout))
304
+
305
+ # Grab filesystem inode data from df
306
+ shell_out("df -iP").stdout.each_line do |line|
307
+ case line
308
+ when /^Filesystem\s+Inodes/
309
+ next
310
+ when /^(.+?)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\%)\s+(.+)$/
311
+ key = "#{$1},#{$6}"
312
+ fs[key] ||= Mash.new
313
+ fs[key][:device] = $1
314
+ fs[key][:total_inodes] = $2
315
+ fs[key][:inodes_used] = $3
316
+ fs[key][:inodes_available] = $4
317
+ fs[key][:inodes_percent_used] = $5
318
+ fs[key][:mount] = $6
319
+ end
320
+ end
321
+ end
322
+
323
+ # Grab mount information from /bin/mount
324
+ run_with_check("mount") do
325
+ shell_out("mount").stdout.each_line do |line|
326
+ if line =~ /^(.+?) on (.+?) type (.+?) \((.+?)\)$/
327
+ key = "#{$1},#{$2}"
328
+ fs[key] ||= Mash.new
329
+ fs[key][:device] = $1
330
+ fs[key][:mount] = $2
331
+ fs[key][:fs_type] = $3
332
+ fs[key][:mount_options] = $4.split(",")
333
+ end
334
+ end
335
+ end
336
+
337
+ # We used to try to decide if we wanted to run lsblk or blkid
338
+ # but they each have a variety of cases were they fail to report
339
+ # data. For example, there are a variety of cases where lsblk won't
340
+ # report unmounted filesystems, but blkid will. And vise-versa. Sweet.
341
+ # So for reliability, we'll run both, if we have them.
342
+
343
+ lsblk = which("lsblk")
344
+ blkid = which("blkid")
345
+ cmds = []
346
+ # These should be in order of preference... first writer wins.
347
+ if lsblk
348
+ cmds << "#{lsblk} -n -P -o NAME,UUID,LABEL,FSTYPE"
349
+ end
350
+ if blkid
351
+ cmds << blkid
352
+ end
353
+
354
+ cmds.each do |cmd|
355
+ cmdtype = File.basename(cmd.split.first)
356
+ # setting the timeout here for `lsblk` and `blkid` commands to 60
357
+ # this is to allow machines with large amounts of attached LUNs
358
+ # to respond back to the command successfully
359
+ run_with_check(cmdtype) do
360
+ shell_out(cmd, timeout: 60).stdout.each_line do |line|
361
+ parsed = parse_line(line, cmdtype)
362
+ next if parsed.nil?
363
+
364
+ # lsblk lists each device once, so we need to update all entries
365
+ # in the hash that are related to this device
366
+ keys_to_update = []
367
+ fs.each_key do |key|
368
+ keys_to_update << key if key.start_with?("#{parsed[:dev]},")
369
+ end
370
+
371
+ if keys_to_update.empty?
372
+ key = "#{parsed[:dev]},"
373
+ fs[key] = Mash.new
374
+ fs[key][:device] = parsed[:dev]
375
+ keys_to_update << key
376
+ end
377
+
378
+ keys_to_update.each do |k|
379
+ %i{fs_type uuid label}.each do |subkey|
380
+ if parsed[subkey] && !parsed[subkey].empty?
381
+ fs[k][subkey] = parsed[subkey]
382
+ end
383
+ end
384
+ end
385
+ end
386
+ end
387
+ end
388
+
389
+ # Grab any missing mount information from /proc/mounts
390
+ if file_exist?("/proc/mounts")
391
+ mounts = ""
392
+ # Due to https://tickets.opscode.com/browse/OHAI-196
393
+ # we have to non-block read dev files. Ew.
394
+ f = file_open("/proc/mounts")
395
+ loop do
396
+ data = f.read_nonblock(4096)
397
+ mounts << data
398
+ # We should just catch EOFError, but the kernel had a period of
399
+ # bugginess with reading virtual files, so we're being extra
400
+ # cautious here, catching all exceptions, and then we'll read
401
+ # whatever data we might have
402
+ rescue Exception
403
+ break
404
+ end
405
+ f.close
406
+
407
+ mounts.each_line do |line|
408
+ if line =~ /^(\S+) (\S+) (\S+) (\S+) \S+ \S+$/
409
+ key = "#{$1},#{$2}"
410
+ next if fs.key?(key)
411
+
412
+ fs[key] = Mash.new
413
+ fs[key][:device] = $1
414
+ fs[key][:mount] = $2
415
+ fs[key][:fs_type] = $3
416
+ fs[key][:mount_options] = $4.split(",")
417
+ end
418
+ end
419
+ end
420
+
421
+ fs.each do |key, entry|
422
+ if entry[:fs_type] == "btrfs"
423
+ fs[key][:btrfs] = collect_btrfs_data(entry)
424
+ end
425
+ end
426
+
427
+ by_pair = fs
428
+ by_device = generate_device_view(fs)
429
+ by_mountpoint = generate_mountpoint_view(fs)
430
+
431
+ fs_data = Mash.new
432
+ fs_data["by_device"] = by_device
433
+ fs_data["by_mountpoint"] = by_mountpoint
434
+ fs_data["by_pair"] = by_pair
435
+
436
+ # Set the filesystem data
437
+ filesystem fs_data
438
+ end
439
+
440
+ collect_data(:freebsd, :openbsd, :netbsd, :dragonflybsd) do
441
+ fs = Mash.new
442
+
443
+ # Grab filesystem data from df
444
+ run_with_check("df") do
445
+ so = shell_out("df")
446
+ fs.merge!(parse_common_df(so.stdout))
447
+
448
+ so = shell_out("df -iP")
449
+ so.stdout.lines do |line|
450
+ case line
451
+ when /^Filesystem/ # skip the header
452
+ next
453
+ when /^(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\%\s+(\d+)\s+(\d+)\s+(\d+)%\s+(\S+)$/
454
+ key = "#{$1},#{$9}"
455
+ fs[key] ||= Mash.new
456
+ fs[key][:device] = $1
457
+ fs[key][:inodes_used] = $6
458
+ fs[key][:inodes_available] = $7
459
+ fs[key][:total_inodes] = ($6.to_i + $7.to_i).to_s
460
+ fs[key][:inodes_percent_used] = $8
461
+ fs[key][:mount] = $9
462
+ end
463
+ end
464
+ end
465
+
466
+ # Grab mount information from mount
467
+ run_with_check("mount") do
468
+ so = shell_out("mount -l")
469
+ so.stdout.lines do |line|
470
+ if line =~ /^(.+?) on (.+?) \((.+?), (.+?)\)$/
471
+ key = "#{$1},#{$2}"
472
+ fs[key] ||= Mash.new
473
+ fs[key][:device] = $1
474
+ fs[key][:mount] = $2
475
+ fs[key][:fs_type] = $3
476
+ fs[key][:mount_options] = $4.split(/,\s*/)
477
+ end
478
+ end
479
+ end
480
+
481
+ # create views
482
+ by_pair = fs
483
+ by_device = generate_device_view(fs)
484
+ by_mountpoint = generate_mountpoint_view(fs)
485
+
486
+ fs_data = Mash.new
487
+ fs_data["by_device"] = by_device
488
+ fs_data["by_mountpoint"] = by_mountpoint
489
+ fs_data["by_pair"] = by_pair
490
+
491
+ filesystem fs_data
492
+ end
493
+
494
+ collect_data(:darwin) do
495
+ fs = Mash.new
496
+ block_size = 0
497
+ # on new versions of OSX, -i is default, on old versions it's not, so
498
+ # specifying it gets consistent output
499
+ run_with_check("df") do
500
+ so = shell_out("df -i")
501
+ so.stdout.each_line do |line|
502
+ case line
503
+ when /^Filesystem\s+(\d+)-/
504
+ block_size = $1.to_i
505
+ next
506
+ when /^(.+?)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\%)\s+(\d+)\s+(\d+)\s+(\d+%)\s+(.+)$/
507
+ key = "#{$1},#{$9}"
508
+ fs[key] = Mash.new
509
+ fs[key][:block_size] = block_size
510
+ fs[key][:device] = $1
511
+ fs[key][:kb_size] = ($2.to_i / (1024 / block_size)).to_s
512
+ fs[key][:kb_used] = ($3.to_i / (1024 / block_size)).to_s
513
+ fs[key][:kb_available] = ($4.to_i / (1024 / block_size)).to_s
514
+ fs[key][:percent_used] = $5
515
+ fs[key][:inodes_used] = $6
516
+ fs[key][:inodes_available] = $7
517
+ fs[key][:total_inodes] = ($6.to_i + $7.to_i).to_s
518
+ fs[key][:inodes_percent_used] = $8
519
+ fs[key][:mount] = $9
520
+ end
521
+ end
522
+ end
523
+
524
+ run_with_check("mount") do
525
+ so = shell_out("mount")
526
+ so.stdout.lines do |line|
527
+ if line =~ /^(.+?) on (.+?) \((.+?), (.+?)\)$/
528
+ key = "#{$1},#{$2}"
529
+ fs[key] ||= Mash.new
530
+ fs[key][:mount] = $2
531
+ fs[key][:fs_type] = $3
532
+ fs[key][:mount_options] = $4.split(/,\s*/)
533
+ end
534
+ end
535
+ end
536
+
537
+ by_pair = fs
538
+ by_device = generate_device_view(fs)
539
+ by_mountpoint = generate_mountpoint_view(fs)
540
+
541
+ fs_data = Mash.new
542
+ fs_data["by_device"] = by_device
543
+ fs_data["by_mountpoint"] = by_mountpoint
544
+ fs_data["by_pair"] = by_pair
545
+
546
+ filesystem fs_data
547
+ end
548
+
549
+ collect_data(:solaris2) do
550
+ fs = Mash.new
551
+
552
+ # Grab filesystem data from df
553
+ run_with_check("df") do
554
+ so = shell_out("df -Pka")
555
+ fs.merge!(parse_common_df(so.stdout))
556
+
557
+ # Grab file system type from df (must be done separately)
558
+ so = shell_out("df -na")
559
+ so.stdout.lines do |line|
560
+ next unless line =~ /^(.+?)\s*: (\S+)\s*$/
561
+
562
+ mount = $1
563
+ fs.each do |key, fs_attributes|
564
+ next unless fs_attributes[:mount] == mount
565
+
566
+ fs[key][:fs_type] = $2
567
+ end
568
+ end
569
+ end
570
+
571
+ # Grab mount information from /bin/mount
572
+ run_with_check("mount") do
573
+ so = shell_out("mount")
574
+ so.stdout.lines do |line|
575
+ next unless line =~ /^(.+?) on (.+?) (.+?) on (.+?)$/
576
+
577
+ key = "#{$2},#{$1}"
578
+ fs[key] ||= Mash.new
579
+ fs[key][:mount] = $1
580
+ fs[key][:mount_time] = $4 # $4 must come before "split", else it becomes nil
581
+ fs[key][:mount_options] = $3.split("/")
582
+ end
583
+ end
584
+
585
+ # Grab any zfs data from "zfs get"
586
+ zfs = Mash.new
587
+ zfs_get = "zfs get -p -H all"
588
+ run_with_check("zfs") do
589
+ so = shell_out(zfs_get)
590
+ so.stdout.lines do |line|
591
+ next unless line =~ /^([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)$/
592
+
593
+ filesystem = $1
594
+ property = $2
595
+ value = $3
596
+ source = $4.chomp
597
+ zfs[filesystem] ||= Mash.new
598
+ # if this fs doesn't exist, put in the bare minimum
599
+ zfs[filesystem][property] = {
600
+ value: value,
601
+ source: source,
602
+ }
603
+ end
604
+ end
605
+
606
+ zfs.each do |fs_name, attributes|
607
+ mountpoint = attributes[:mountpoint][:value] if attributes[:mountpoint]
608
+ key = "#{fs_name},#{mountpoint}"
609
+ fs[key] ||= Mash.new
610
+ fs[key][:fs_type] = "zfs"
611
+ fs[key][:mount] = mountpoint if mountpoint
612
+ fs[key][:device] = fs_name
613
+ fs[key][:zfs_properties] = attributes
614
+ # find all zfs parents
615
+ parents = fs_name.split("/")
616
+ zfs_parents = []
617
+ (0..parents.length - 1).to_a.each do |parent_index|
618
+ next_parent = parents[0..parent_index].join("/")
619
+ zfs_parents.push(next_parent)
620
+ end
621
+ zfs_parents.pop
622
+ fs[key][:zfs_parents] = zfs_parents
623
+ fs[key][:zfs_zpool] = (zfs_parents.length == 0)
624
+ end
625
+
626
+ # create views
627
+ by_pair = fs
628
+ by_device = generate_device_view(fs)
629
+ by_mountpoint = generate_mountpoint_view(fs)
630
+
631
+ fs_data = Mash.new
632
+ fs_data["by_device"] = by_device
633
+ fs_data["by_mountpoint"] = by_mountpoint
634
+ fs_data["by_pair"] = by_pair
635
+
636
+ filesystem fs_data
637
+ end
638
+
639
+ collect_data(:aix) do
640
+ def parse_df_or_mount(shell_out)
641
+ oldie = Mash.new
642
+
643
+ shell_out.lines.each do |line|
644
+ fields = line.split
645
+ case line
646
+ # headers and horizontal rules to skip
647
+ when /^\s*(node|---|^Filesystem\s+1024-blocks)/
648
+ next
649
+ # strictly a df entry
650
+ when /^(.+?)\s+([0-9-]+)\s+([0-9-]+)\s+([0-9-]+)\s+([0-9-]+\%*)\s+(.+)$/
651
+ if $1 == "Global"
652
+ dev = "#{$1}:#{$6}"
653
+ else
654
+ dev = $1
655
+ end
656
+ mountpoint = $6
657
+ key = "#{dev},#{mountpoint}"
658
+ oldie[key] ||= Mash.new
659
+ oldie[key][:kb_size] = $2
660
+ oldie[key][:kb_used] = $3
661
+ oldie[key][:kb_available] = $4
662
+ oldie[key][:percent_used] = $5
663
+ oldie[key][:mount] = mountpoint
664
+ oldie[key][:device] = dev
665
+ # an entry starting with 'G' or / (E.G. /tmp or /var)
666
+ when %r{^\s*(G.*?|/\w)}
667
+ if fields[0] == "Global"
668
+ dev = fields[0] + ":" + fields[1]
669
+ else
670
+ dev = fields[0]
671
+ end
672
+ mountpoint = fields[1]
673
+ key = "#{dev},#{mountpoint}"
674
+ oldie[key] ||= Mash.new
675
+ oldie[key][:mount] = mountpoint
676
+ oldie[key][:fs_type] = fields[2]
677
+ oldie[key][:mount_options] = fields[6].split(",")
678
+ oldie[key][:device] = dev
679
+ # entries occupying the 'Node' column parsed here
680
+ else
681
+ dev = fields[0] + ":" + fields[1]
682
+ mountpoint = fields[2]
683
+ key = "#{dev},#{mountpoint}"
684
+ oldie[key] ||= Mash.new
685
+ oldie[key][:mount] = mountpoint
686
+ oldie[key][:device] = dev
687
+ oldie[key][:fs_type] = fields[3]
688
+ oldie[key][:mount_options] = fields[7].split(",")
689
+ end
690
+ end
691
+ oldie
692
+ end
693
+
694
+ def collect_old_version(shell_outs)
695
+ mount_hash = parse_df_or_mount shell_outs[:mount]
696
+ df_hash = parse_df_or_mount shell_outs[:df_Pk]
697
+
698
+ mount_hash.each do |key, hash|
699
+ df_hash[key].merge!(hash) if df_hash.key?(key)
700
+ end
701
+
702
+ mount_hash.merge(df_hash)
703
+ end
704
+
705
+ # Cache the command output
706
+ shell_outs = Mash.new
707
+
708
+ run_with_check("mount") do
709
+ shell_outs[:mount] = shell_out("mount").stdout
710
+ end
711
+
712
+ run_with_check("df") do
713
+ shell_outs[:df_Pk] = shell_out("df -Pk").stdout
714
+ end
715
+
716
+ fs = collect_old_version(shell_outs)
717
+ by_pair = fs
718
+ by_device = generate_device_view(fs)
719
+ by_mountpoint = generate_mountpoint_view(fs)
720
+
721
+ fs_data = Mash.new
722
+ fs_data["by_device"] = by_device
723
+ fs_data["by_mountpoint"] = by_mountpoint
724
+ fs_data["by_pair"] = by_pair
725
+
726
+ filesystem fs_data
727
+ end
728
+
729
+ collect_data(:windows) do
730
+ require "set" unless defined?(Set)
731
+ require "wmi-lite/wmi" unless defined?(WmiLite::Wmi)
732
+ require_relative "../mash"
733
+
734
+ fs = merge_info(logical_info, encryptable_info)
735
+
736
+ by_pair = fs
737
+ by_device = generate_device_view(fs)
738
+ by_mountpoint = generate_mountpoint_view(fs)
739
+
740
+ fs_data = Mash.new
741
+ fs_data["by_device"] = by_device
742
+ fs_data["by_mountpoint"] = by_mountpoint
743
+ fs_data["by_pair"] = by_pair
744
+
745
+ # Chef 16 added 'filesystem2'
746
+ # In Chef 17 we made 'filesystem' and 'filesystem2' match (both new-style)
747
+ # In Chef 18 we will drop 'filesystem2'
748
+ filesystem fs_data
749
+ filesystem2 fs_data
750
+ end
751
+ end