ohai 0.6.12 → 0.6.14.rc.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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")