ohai 0.6.12 → 0.6.14.rc.1

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.
@@ -21,10 +21,23 @@ require 'mixlib/config'
21
21
  module Ohai
22
22
  class Config
23
23
  extend Mixlib::Config
24
+
25
+ # from chef/config.rb, should maybe be moved to mixlib-config?
26
+ def self.platform_specific_path(path)
27
+ if RUBY_PLATFORM =~ /mswin|mingw|windows/
28
+ # turns /etc/chef/client.rb into C:/chef/client.rb
29
+ path = File.join(ENV['SYSTEMDRIVE'], path.split('/')[2..-1])
30
+ # ensure all forward slashes are backslashes
31
+ path.gsub!(File::SEPARATOR, (File::ALT_SEPARATOR || '\\'))
32
+ end
33
+ path
34
+ end
35
+
24
36
 
25
37
  log_level :info
26
38
  log_location STDOUT
27
39
  plugin_path [ File.expand_path(File.join(File.dirname(__FILE__), 'plugins'))]
28
40
  disabled_plugins []
41
+ hints_path [ platform_specific_path('/etc/chef/ohai/hints') ]
29
42
  end
30
43
  end
@@ -128,7 +128,12 @@ module Ohai
128
128
  #
129
129
  # Thanks Ara!
130
130
  def popen4(cmd, args={}, &b)
131
-
131
+
132
+ ## Disable garbage collection to work around possible bug in MRI
133
+ # Ruby 1.8 suffers from intermittent segfaults believed to be due to GC while IO.select
134
+ # See OHAI-330 / CHEF-2916 / CHEF-1305
135
+ GC.disable
136
+
132
137
  # Waitlast - this is magic.
133
138
  #
134
139
  # Do we wait for the child process to die before we yield
@@ -334,6 +339,9 @@ module Ohai
334
339
  end
335
340
  rescue Errno::ENOENT
336
341
  raise Ohai::Exceptions::Exec, "command #{cmd} doesn't exist or is not in the PATH"
342
+ ensure
343
+ # we disabled GC entering
344
+ GC.enable
337
345
  end
338
346
 
339
347
  module_function :popen4
@@ -71,6 +71,7 @@ def encaps_lookup(ifname)
71
71
  end
72
72
 
73
73
  def scope_lookup(scope)
74
+ return "Node" if scope.eql?("::1")
74
75
  return "Link" if scope.match(/^fe80\:/)
75
76
  return "Site" if scope.match(/^fec0\:/)
76
77
  "Global"
@@ -114,7 +115,7 @@ popen4("ifconfig -a") do |pid, stdin, stdout, stderr|
114
115
  iface[cint][:encapsulation] = encaps_lookup($1)
115
116
  end
116
117
  end
117
- if line =~ /^\s+ether ([0-9a-f\:]+)\s/
118
+ if line =~ /^\s+ether ([0-9a-f\:]+)/
118
119
  iface[cint][:addresses] = Mash.new unless iface[cint][:addresses]
119
120
  iface[cint][:addresses][$1] = { "family" => "lladdr" }
120
121
  iface[cint][:encapsulation] = "Ethernet"
@@ -131,9 +132,9 @@ popen4("ifconfig -a") do |pid, stdin, stdout, stderr|
131
132
  iface[cint][:addresses] = Mash.new unless iface[cint][:addresses]
132
133
  iface[cint][:addresses][$1] = { "family" => "inet", "netmask" => $2.scanf('%2x'*4)*".", "broadcast" => $4 }
133
134
  end
134
- if line =~ /\s+inet6 ([a-f0-9\:]+)(\s*|(\%[a-z0-9]+)\s*) prefixlen (\d+)\s*$/
135
+ if line =~ /\s+inet6 ([a-f0-9\:]+)(\s*|(\%[a-z0-9]+)\s*) prefixlen (\d+)\s*/
135
136
  iface[cint][:addresses] = Mash.new unless iface[cint][:addresses]
136
- iface[cint][:addresses][$1] = { "family" => "inet6", "prefixlen" => $4 , "scope" => "Node" }
137
+ iface[cint][:addresses][$1] = { "family" => "inet6", "prefixlen" => $4 , "scope" => scope_lookup($1) }
137
138
  end
138
139
  if line =~ /\s+inet6 ([a-f0-9\:]+)(\s*|(\%[a-z0-9]+)\s*) prefixlen (\d+) scopeid 0x([a-f0-9]+)/
139
140
  iface[cint][:addresses] = Mash.new unless iface[cint][:addresses]
@@ -154,7 +155,7 @@ end
154
155
  popen4("arp -an") do |pid, stdin, stdout, stderr|
155
156
  stdin.close
156
157
  stdout.each do |line|
157
- if line =~ /^\S+ \((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\) at ([a-fA-F0-9\:]+) on ([a-zA-Z0-9\.\:\-]+) \[(\w+)\]/
158
+ if line =~ /^\S+ \((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\) at ([a-fA-F0-9\:]+) on ([a-zA-Z0-9\.\:\-]+).*\[(\w+)\]/
158
159
  # MAC addr really should be normalized to include all the zeroes.
159
160
  next if iface[$3].nil? # this should never happen
160
161
  iface[$3][:arp] = Mash.new unless iface[$3][:arp]
@@ -16,7 +16,7 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- provides "platform", "platform_version", "platform_build"
19
+ provides "platform", "platform_version", "platform_build", "platform_family"
20
20
 
21
21
  popen4("/usr/bin/sw_vers") do |pid, stdin, stdout, stderr|
22
22
  stdin.close
@@ -33,4 +33,6 @@ popen4("/usr/bin/sw_vers") do |pid, stdin, stdout, stderr|
33
33
  platform_build $1
34
34
  end
35
35
  end
36
- end
36
+ end
37
+
38
+ platform_family "mac_os_x"
@@ -43,7 +43,7 @@ end
43
43
  def looks_like_ec2?
44
44
  # Try non-blocking connect so we don't "block" if
45
45
  # the Xen environment is *not* EC2
46
- has_ec2_mac? && can_metadata_connect?(EC2_METADATA_ADDR,80)
46
+ hint?('ec2') || has_ec2_mac? && can_metadata_connect?(EC2_METADATA_ADDR,80)
47
47
  end
48
48
 
49
49
  if looks_like_ec2?
@@ -50,7 +50,7 @@ end
50
50
  def looks_like_euca?
51
51
  # Try non-blocking connect so we don't "block" if
52
52
  # the Xen environment is *not* EC2
53
- has_euca_mac? && can_metadata_connect?(EC2_METADATA_ADDR,80)
53
+ hint?('eucalyptus') || has_euca_mac? && can_metadata_connect?(EC2_METADATA_ADDR,80)
54
54
  end
55
55
 
56
56
  if looks_like_euca?
@@ -21,7 +21,14 @@ require_plugin "languages"
21
21
 
22
22
  java = Mash.new
23
23
 
24
- status, stdout, stderr = run_command(:no_status_check => true, :command => "java -version")
24
+ status, stdout, stderr = nil
25
+ if RUBY_PLATFORM.downcase.include?("darwin")
26
+ if system("/usr/libexec/java_home 2&>1 >/dev/null")
27
+ status, stdout, stderr = run_command(:no_status_check => true, :command => "java -version")
28
+ end
29
+ else
30
+ status, stdout, stderr = run_command(:no_status_check => true, :command => "java -version")
31
+ end
25
32
 
26
33
  if status == 0
27
34
  stderr.split("\n").each do |line|
@@ -19,18 +19,6 @@
19
19
  require 'ipaddr'
20
20
  provides "network", "counters/network"
21
21
 
22
- begin
23
- route_result = from("route -n \| grep -m 1 ^0.0.0.0").split(/[ \t]+/)
24
- if route_result.last =~ /(venet\d+)/
25
- network[:default_interface] = from("ip addr show dev #{$1} | grep -v 127.0.0. | grep -m 1 inet").split(/[ \t]+/).last
26
- network[:default_gateway] = route_result[1]
27
- else
28
- network[:default_gateway], network[:default_interface] = route_result.values_at(1,7)
29
- end
30
- rescue Ohai::Exceptions::Exec
31
- Ohai::Log.debug("Unable to determine default interface")
32
- end
33
-
34
22
  def encaps_lookup(encap)
35
23
  return "Loopback" if encap.eql?("Local Loopback") || encap.eql?("loopback")
36
24
  return "PPP" if encap.eql?("Point-to-Point Protocol")
@@ -52,18 +40,21 @@ IPROUTE_INT_REGEX = /^(\d+): ([0-9a-zA-Z@:\.\-_]*?)(@[0-9a-zA-Z]+|):\s/
52
40
 
53
41
  if File.exist?("/sbin/ip")
54
42
 
55
- begin
56
- route_result = from("ip route show exact 0.0.0.0/0").chomp.split(/[ \t]+/)
57
-
58
- if route_result[4] =~ /(venet\d+)/
59
- network[:default_interface] = from("ip addr show dev #{$1} | grep -v 127.0.0.1 | grep -m 1 inet").split(/[ \t]+/).last
60
- network[:default_gateway] = route_result[1]
61
- else
62
- network[:default_gateway], network[:default_interface] = route_result.values_at(2,4)
63
- end
64
- rescue Ohai::Exceptions::Exec
65
- Ohai::Log.debug("Unable to determine default interface")
66
- end
43
+ # families to get default routes from
44
+ families = [
45
+ {
46
+ :name => "inet",
47
+ :default_route => "0.0.0.0/0",
48
+ :default_prefix => :default,
49
+ :neighbour_attribute => :arp
50
+ },
51
+ {
52
+ :name => "inet6",
53
+ :default_route => "::/0",
54
+ :default_prefix => :default_inet6,
55
+ :neighbour_attribute => :neighbour_inet6
56
+ }
57
+ ]
67
58
 
68
59
  popen4("ip addr") do |pid, stdin, stdout, stderr|
69
60
  stdin.close
@@ -185,33 +176,138 @@ if File.exist?("/sbin/ip")
185
176
  end
186
177
  end
187
178
 
188
- popen4("ip neighbor show") do |pid, stdin, stdout, stderr|
189
- stdin.close
190
- stdout.each do |line|
191
- if line =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) dev ([0-9a-zA-Z\.\:\-]+) lladdr ([a-fA-F0-9\:]+)/
192
- next unless iface[$2]
193
- iface[$2][:arp] = Mash.new unless iface[$2][:arp]
194
- iface[$2][:arp][$1] = $3.downcase
179
+ families.each do |family|
180
+ neigh_attr = family[:neighbour_attribute]
181
+ default_prefix = family[:default_prefix]
182
+
183
+ popen4("ip -f #{family[:name]} neigh show") do |pid, stdin, stdout, stderr|
184
+ stdin.close
185
+ stdout.each do |line|
186
+ if line =~ /^([a-f0-9\:\.]+)\s+dev\s+([^\s]+)\s+lladdr\s+([a-fA-F0-9\:]+)/
187
+ unless iface[$2]
188
+ Ohai::Log.warn("neighbour list has entries for unknown interface #{iface[$2]}")
189
+ next
190
+ end
191
+ iface[$2][neigh_attr] = Mash.new unless iface[$2][neigh_attr]
192
+ iface[$2][neigh_attr][$1] = $3.downcase
193
+ end
195
194
  end
196
195
  end
197
- end
198
196
 
199
- popen4("ip route show scope link") do |pid, stdin, stdout, stderr|
200
- stdin.close
201
- stdout.each do |line|
202
- if line =~ /^([^\s]+)\s+dev\s+([^\s]+).*\s+src\s+([^\s]+)\b/
203
- tmp_route_cidr = $1
204
- tmp_int = $2
205
- tmp_source_addr = $3
206
- unless iface[tmp_int]
207
- Ohai::Log.debug("Skipping previously unseen interface from 'ip route show scope link': #{tmp_int}")
208
- next
197
+ # checking the routing tables
198
+ # why ?
199
+ # 1) to set the default gateway and default interfaces attributes
200
+ # 2) on some occasions, the best way to select node[:ipaddress] is to look at
201
+ # the routing table source field.
202
+ # 3) and since we're at it, let's populate some :routes attributes
203
+ # (going to do that for both inet and inet6 addresses)
204
+ popen4("ip -f #{family[:name]} route show") do |pid, stdin, stdout, stderr|
205
+ stdin.close
206
+ stdout.each do |line|
207
+ if line =~ /^([^\s]+)\s(.*)$/
208
+ route_dest = $1
209
+ route_ending = $2
210
+ #
211
+ if route_ending =~ /\bdev\s+([^\s]+)\b/
212
+ route_int = $1
213
+ else
214
+ Ohai::Log.debug("Skipping route entry without a device: '#{line}'")
215
+ next
216
+ end
217
+
218
+ unless iface[route_int]
219
+ Ohai::Log.debug("Skipping previously unseen interface from 'ip route show': #{route_int}")
220
+ next
221
+ end
222
+
223
+ route_entry = Mash.new( :destination => route_dest,
224
+ :family => family[:name] )
225
+ %w[via scope metric proto src].each do |k|
226
+ route_entry[k] = $1 if route_ending =~ /\b#{k}\s+([^\s]+)\b/
227
+ end
228
+
229
+ # a sanity check, especially for Linux-VServer, OpenVZ and LXC:
230
+ # don't report the route entry if the src address isn't set on the node
231
+ next if route_entry[:src] and not iface[route_int][:addresses].has_key? route_entry[:src]
232
+
233
+ iface[route_int][:routes] = Array.new unless iface[route_int][:routes]
234
+ iface[route_int][:routes] << route_entry
209
235
  end
210
- iface[tmp_int][:routes] = Mash.new unless iface[tmp_int][:routes]
211
- iface[tmp_int][:routes][tmp_route_cidr] = Mash.new( :scope => "Link", :src => tmp_source_addr )
212
- if (network[:default_interface] == tmp_int ) && (IPAddr.new(tmp_route_cidr).include? network[:default_gateway])
213
- ipaddress tmp_source_addr
214
- macaddress iface[tmp_int][:addresses].select{|k,v| v["family"]=="lladdr"}.first.first
236
+ end
237
+ end
238
+ # now looking at the routes to set the default attributes
239
+ # for information, default routes can be of this form :
240
+ # - default via 10.0.2.4 dev br0
241
+ # - default dev br0 scope link
242
+ # - default via 10.0.3.1 dev eth1 src 10.0.3.2 metric 10
243
+ # - default via 10.0.4.1 dev eth2 src 10.0.4.2 metric 20
244
+
245
+ # using a temporary var to hold routes and their interface name
246
+ routes = iface.collect do |i,iv|
247
+ iv[:routes].collect do |r|
248
+ r.merge(:dev=>i) if r[:family] == family[:name]
249
+ end.compact if iv[:routes]
250
+ end.compact.flatten
251
+
252
+ # using a temporary var to hold the default route
253
+ # in case there are more than 1 default route, sort it by its metric
254
+ # and return the first one
255
+ # (metric value when unspecified is 0)
256
+ default_route = routes.select do |r|
257
+ r[:destination] == "default"
258
+ end.sort do |x,y|
259
+ (x[:metric].nil? ? 0 : x[:metric].to_i) <=> (y[:metric].nil? ? 0 : y[:metric].to_i)
260
+ end.first
261
+
262
+ if default_route.nil? or default_route.empty?
263
+ Ohai::Log.debug("Unable to determine default #{family[:name]} interface")
264
+ else
265
+ network["#{default_prefix}_interface"] = default_route[:dev]
266
+ Ohai::Log.debug("#{default_prefix}_interface set to #{default_route[:dev]}")
267
+
268
+ # setting gateway to 0.0.0.0 or :: if the default route is a link level one
269
+ network["#{default_prefix}_gateway"] = default_route[:via] ? default_route[:via] : family[:default_route].chomp("/0")
270
+ Ohai::Log.debug("#{default_prefix}_gateway set to #{network["#{default_prefix}_gateway"]}")
271
+
272
+ # since we're at it, let's populate {ip,mac,ip6}address with the best values
273
+ # using the source field when it's specified :
274
+ # 1) in the default route
275
+ # 2) in the route entry used to reach the default gateway
276
+ route = routes.select do |r|
277
+ # selecting routes
278
+ r[:src] and # it has a src field
279
+ iface[r[:dev]] and # the iface exists
280
+ iface[r[:dev]][:addresses].has_key? r[:src] and # the src ip is set on the node
281
+ iface[r[:dev]][:addresses][r[:src]][:scope].downcase != "link" and # this isn't a link level addresse
282
+ ( r[:destination] == "default" or
283
+ ( default_route[:via] and # the default route has a gateway
284
+ IPAddress(r[:destination]).include? IPAddress(default_route[:via]) # the route matches the gateway
285
+ )
286
+ )
287
+ end.sort_by do |r|
288
+ # sorting the selected routes:
289
+ # - getting default routes first
290
+ # - then sort by metric
291
+ # - then by prefixlen
292
+ [
293
+ r[:destination] == "default" ? 0 : 1,
294
+ r[:metric].nil? ? 0 : r[:metric].to_i,
295
+ # for some reason IPAddress doesn't accept "::/0", it doesn't like prefix==0
296
+ # just a quick workaround: use 0 if IPAddress fails
297
+ begin
298
+ IPAddress( r[:destination] == "default" ? family[:default_route] : r[:destination] ).prefix
299
+ rescue
300
+ 0
301
+ end
302
+ ]
303
+ end.first
304
+
305
+ unless route.nil? or route.empty?
306
+ if family[:name] == "inet"
307
+ ipaddress route[:src]
308
+ macaddress iface[route[:dev]][:addresses].select{|k,v| v["family"]=="lladdr"}.first.first unless iface[route[:dev]][:flags].include? "NOARP"
309
+ else
310
+ ip6address route[:src]
215
311
  end
216
312
  end
217
313
  end
@@ -37,18 +37,6 @@ elsif File.exists?("/etc/enterprise-release")
37
37
  contents = File.read("/etc/enterprise-release").chomp
38
38
  platform "oracle"
39
39
  platform_version get_redhatish_version(contents)
40
- elsif lsb[:id] =~ /RedHat/i
41
- platform "redhat"
42
- platform_version lsb[:release]
43
- elsif lsb[:id] =~ /Amazon/i
44
- platform "amazon"
45
- platform_version lsb[:release]
46
- elsif lsb[:id] =~ /ScientificSL/i
47
- platform "scientific"
48
- platform_version lsb[:release]
49
- elsif lsb[:id]
50
- platform lsb[:id].downcase
51
- platform_version lsb[:release]
52
40
  elsif File.exists?("/etc/debian_version")
53
41
  platform "debian"
54
42
  platform_version File.read("/etc/debian_version").chomp
@@ -74,11 +62,23 @@ elsif File.exists?('/etc/arch-release')
74
62
  platform "arch"
75
63
  # no way to determine platform_version in a rolling release distribution
76
64
  # kernel release will be used - ex. 2.6.32-ARCH
65
+ elsif lsb[:id] =~ /RedHat/i
66
+ platform "redhat"
67
+ platform_version lsb[:release]
68
+ elsif lsb[:id] =~ /Amazon/i
69
+ platform "amazon"
70
+ platform_version lsb[:release]
71
+ elsif lsb[:id] =~ /ScientificSL/i
72
+ platform "scientific"
73
+ platform_version lsb[:release]
74
+ elsif lsb[:id] # LSB can provide odd data that changes between releases, so we currently fall back on it rather than dealing with its subtleties
75
+ platform lsb[:id].downcase
76
+ platform_version lsb[:release]
77
77
  end
78
78
 
79
79
 
80
80
  case platform
81
- when /debian/, /ubuntu/, /mint/
81
+ when /debian/, /ubuntu/, /linuxmint/
82
82
  platform_family "debian"
83
83
  when /fedora/
84
84
  platform_family "fedora"
@@ -72,15 +72,14 @@ if File.exists?("/proc/cpuinfo")
72
72
  end
73
73
  end
74
74
 
75
- # http://wiki.openvz.org/Proc/user_beancounters
76
- if File.exists?("/proc/user_beancounters")
77
- if File.read("/proc/user_beancounters") =~ /\n\s+0:\s+/
78
- virtualization[:emulator] = "openvz"
79
- virtualization[:role] = "host"
80
- else
81
- virtualization[:emulator] = "openvz"
82
- virtualization[:role] = "guest"
83
- end
75
+ # Detect OpenVZ / Virtuozzo.
76
+ # http://wiki.openvz.org/BC_proc_entries
77
+ if File.exists?("/proc/bc/0")
78
+ virtualization[:system] = "openvz"
79
+ virtualization[:role] = "host"
80
+ elsif File.exists?("/proc/vz")
81
+ virtualization[:system] = "openvz"
82
+ virtualization[:role] = "guest"
84
83
  end
85
84
 
86
85
  # http://www.dmo.ca/blog/detecting-virtualization-on-linux
@@ -124,6 +123,3 @@ if File.exists?("/proc/self/status")
124
123
  end
125
124
  end
126
125
  end
127
-
128
- # Detect OpenVZ
129
- # something in /proc/vz/veinfo
@@ -26,28 +26,30 @@ counters Mash.new unless counters
26
26
  counters[:network] = Mash.new unless counters[:network]
27
27
 
28
28
  ipaddress nil
29
+ ip6address
29
30
  macaddress nil
30
31
 
31
32
  require_plugin "hostname"
32
33
  require_plugin "#{os}::network"
33
34
 
34
35
  # ipaddress and macaddress can be set from the #{os}::network plugin
35
- # both ipaddress and macaddress have to be set in that case
36
- return unless ipaddress.nil? or macaddress.nil?
36
+ return unless ipaddress.nil?
37
37
 
38
38
  def find_ip_and_mac(addresses, match = nil)
39
- ip = nil; mac = nil
39
+ ip = nil; mac = nil; ip6 = nil
40
40
  addresses.keys.each do |addr|
41
41
  if match.nil?
42
42
  ip = addr if addresses[addr]["family"].eql?("inet")
43
43
  else
44
44
  ip = addr if addresses[addr]["family"].eql?("inet") && network_contains_address(match, addr, addresses[addr])
45
45
  end
46
+ ip6 = addr if addresses[addr]["family"].eql?("inet6") && addresses[addr]["scope"].eql?("Global")
46
47
  mac = addr if addresses[addr]["family"].eql?("lladdr")
47
48
  break if (ip and mac)
48
49
  end
49
50
  Ohai::Log.debug("Found IPv4 address #{ip} with MAC #{mac} #{match.nil? ? '' : 'matching address ' + match}")
50
- [ip, mac]
51
+ Ohai::Log.debug("Found IPv6 address #{ip6}") if ip6
52
+ [ip, mac, ip6]
51
53
  end
52
54
 
53
55
  def network_contains_address(address_to_match, network_ip, network_opts)
@@ -61,13 +63,17 @@ def network_contains_address(address_to_match, network_ip, network_opts)
61
63
  end
62
64
 
63
65
  # If we have a default interface that has addresses, populate the short-cut attributes
66
+ # 0.0.0.0 is not a valid gateway address in this case
64
67
  if network[:default_interface] and
68
+ network[:default_gateway] and
69
+ network[:default_gateway] != "0.0.0.0" and
65
70
  network["interfaces"][network[:default_interface]] and
66
71
  network["interfaces"][network[:default_interface]]["addresses"]
67
72
  Ohai::Log.debug("Using default interface for default ip and mac address")
68
73
  im = find_ip_and_mac(network["interfaces"][network[:default_interface]]["addresses"], network[:default_gateway])
69
74
  ipaddress im.shift
70
75
  macaddress im.shift
76
+ ip6address im.shift
71
77
  else
72
78
  network["interfaces"].keys.sort.each do |iface|
73
79
  if network["interfaces"][iface]["encapsulation"].eql?("Ethernet")