Browse Source

Let's get rid of ohai.

Ohai required support additional packages,
and unfortunatelly, not all of them are
opensource friendly (ruby-sigar, for example).

This changes will let us to rid ruby-mixlib*,
ruby-sigar and ruby-yajl packages.

Also, it may sound strange, but ohai[:virtualization]
makes decision based on /proc/cpuinfo information
only (this applies only to kvm/qemu, other virt-systems
determines correctly, AFAICS).

So, if someone will choose a non-default (qemu)
processor configuration, ohai will return incomplete
information about virtualization on a kvm-based virtual host.
Facter doing it more intelligently.

Blueprint: get-rid-of-ohai

Change-Id: Ia8021a3ab83bbf973eff548880ae10a540476b1c
Ivan Suzdal 2 years ago
parent
commit
93e84d1649
3 changed files with 262 additions and 58 deletions
  1. 252
    51
      agent
  2. 6
    0
      debian/changelog
  3. 4
    7
      debian/control

+ 252
- 51
agent View File

@@ -18,7 +18,7 @@ begin
18 18
   require 'rubygems'
19 19
 rescue LoadError
20 20
 end
21
-require 'ohai/system'
21
+require 'facter'
22 22
 require 'json'
23 23
 require 'httpclient'
24 24
 require 'logger'
@@ -160,7 +160,8 @@ class NodeAgent
160 160
       @logger.info("API URL is #{@api_url}")
161 161
     end
162 162
 
163
-    @os = ohai_system_info
163
+    @facter = facter_system_info
164
+    @network = _network
164 165
     @numa_topology = get_numa_topology
165 166
     @mpath_devices, @skip_devices = multipath_devices
166 167
   end
@@ -195,19 +196,9 @@ class NodeAgent
195 196
     agent_settings.merge(cmdline_settings)
196 197
   end
197 198
 
198
-  def ohai_system_info
199
-    Timeout::timeout(30) do
200
-      os = Ohai::System.new()
201
-      os.all_plugins
202
-      os
203
-    end
204
-  rescue Timeout::Error
205
-    # When one of disks is broken, do not collect data about block devices
206
-    # More details: https://bugs.launchpad.net/fuel/+bug/1396086
207
-    Ohai::Config[:disabled_plugins]=['linux::block_device', 'linux::filesystem']
208
-    os = Ohai::System.new()
209
-    os.all_plugins
210
-    os
199
+  def facter_system_info
200
+    Facter.loadfacts
201
+    Facter.to_hash
211 202
   end
212 203
 
213 204
   def put
@@ -239,15 +230,219 @@ class NodeAgent
239 230
     client
240 231
   end
241 232
 
242
-  def _interfaces
243
-    interfaces = @os[:network][:interfaces].inject([]) do |result, elm|
244
-      result << { :name => elm[0], :addresses => elm[1]["addresses"] }
233
+  def _get_iface_info(ifname)
234
+    info = {}
235
+    info[:name] = ifname
236
+    info[:addresses] = {}
237
+    if ifname =~ /^(\D+)(\d+.*)/ # enp0s11, enp0, eth0
238
+      info[:type] = $1 # enp, enp, eth
239
+      info[:number] = $2 # 0s11, 0, 0
240
+    end
241
+    data = `ip a s dev #{ifname}`
242
+    #2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br-fw-admin state UP group default qlen 1000
243
+    #    link/ether 64:de:13:ab:f4:1d brd ff:ff:ff:ff:ff:ff
244
+    #    inet6 fe80::66de:13ff:feab:f41d/64 scope link
245
+    #       valid_lft forever preferred_lft forever
246
+    data.each_line do |line|
247
+      case line.strip
248
+        when /(\d+): #{ifname}: <([^>]*)> mtu (\d+) (.+) state (\w+)/
249
+          info[:flags] = $2.split(',')
250
+          info[:mtu] = $3
251
+          info[:state] = $5.downcase
252
+        when /link\/(\w+) ([\da-f\:]+) brd ([\da-f\:]+)/
253
+          info[:addresses][$2.upcase] = { :family => "lladdr" } if $2 != "00:00:00:00:00:00"
254
+          info[:encapsulation] = case $1
255
+            when /loopback/i then 'Loopback'
256
+            when /IPIP Tunnel/ then 'IPIP'
257
+            when /Point-to-Point Protocol/ then 'PPP'
258
+            when /IPv6-in-IPv4/ then '6to4'
259
+            when /ether/ then'Ethernet'
260
+            else nil
261
+          end
262
+        when /inet (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(\/(\d{1,2}))( brd (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))? scope (\w+)?/
263
+          info[:addresses][$1] = { :family => "inet", "prefixlen" => $3 ||32 }
264
+          info[:addresses][$1][:scope] = ($6.eql?("host") ? "Node" : $6.capitalize)
265
+          info[:addresses][$1][:netmask] = IPAddr.new("255.255.255.255").mask(($3 ||32).to_i).to_s
266
+          info[:addresses][$1][:broadcast] = $5
267
+        when /inet6 ([a-f0-9\:]+)\/(\d+) scope (\w+)/
268
+          info[:addresses][$1] = { :family => "inet6", "prefixlen" => $2, "scope" => ($3.eql?("host") ? "Node" : $3.capitalize) }
269
+      end
270
+    end
271
+    data = `ip -d link show dev #{ifname}`
272
+    #2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br-fw-admin state UP mode DEFAULT group default qlen 1000
273
+    #    link/ether 64:de:13:ab:f4:1d brd ff:ff:ff:ff:ff:ff promiscuity 1
274
+    #    bridge_slave state forwarding priority 32 cost 4 hairpin off guard off root_block off fastleave off learning on flood on addrgenmode eui64
275
+    data.each_line do |line|
276
+      next if line =~ /^\d+/
277
+      if line =~ /state (\w+)/
278
+        info[:state] = $1.downcase
279
+      end
280
+      if line =~ /vlan id (\d+)/
281
+        vid = $1
282
+        info[:state][:vlan] = {}
283
+        info[:state][:vlan][:id] = vid
284
+      end
285
+    end
286
+    info
287
+  end
288
+
289
+  def _get_all_interfaces_info
290
+    res = {}
291
+    res[:interfaces] = {}
292
+    Facter::Util::IP.get_interfaces().each do |ifname|
293
+      res[:interfaces][ifname] = _get_iface_info(ifname)
294
+    end
295
+    %w[inet inet6].each do |family|
296
+      #default via 10.109.3.1 dev br-ex 
297
+      #10.109.0.0/24 dev br-fw-admin  proto kernel  scope link  src 10.109.0.4 
298
+      #10.109.1.0/24 dev br-mgmt  proto kernel  scope link  src 10.109.1.3 
299
+      #10.109.2.0/24 dev br-storage  proto kernel  scope link  src 10.109.2.3 
300
+      #10.109.3.0/24 dev br-ex  proto kernel  scope link  src 10.109.3.3 
301
+      #240.0.0.0/30 dev hapr-host  proto kernel  scope link  src 240.0.0.1 
302
+      #240.0.0.4/30 dev vr-host-base  proto kernel  scope link  src 240.0.0.5 
303
+      `ip -f #{family} route show`.each_line do |line|
304
+        if line =~ /^([^\s]+)\s(.*)$/
305
+          rdest = $1
306
+          rend = $2
307
+          next if not rend =~ /\bdev\s+([^\s]+)\b/
308
+          rint = $1
309
+          next if not res[:interfaces].has_key?(rint)
310
+          rent = {:destination => rdest, :family => family}
311
+          %w[via scope metric proto src].each do |k|
312
+            rent[k] = $1 if rend =~ /\b#{k}\s+([^\s]+)\b/
313
+          end
314
+          next if rent[:src] and not res[:interfaces][rint].has_key?(rent[:src])
315
+          res[:interfaces][rint][:routes] = [] if not res[:interfaces][rint][:routes]
316
+          res[:interfaces][rint][:routes] << rent
317
+        end
318
+      end
319
+    end
320
+    res
321
+  end
322
+
323
+  def _network
324
+    iface = nil
325
+    gw = nil
326
+    route = `ip r list 0/0`.strip # 'default via 10.21.5.1 dev eth0'
327
+    if route =~ /^default via ?([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) dev ([a-zA-Z0-9_-]+)/
328
+      gw = $1
329
+      iface = $2
330
+    end
331
+    result = _get_all_interfaces_info
332
+    result[:default_gateway] = gw
333
+    result[:default_interface] = iface
334
+    result[:mac] = @facter["macaddress_#{iface.gsub('-', '_')}"].upcase
335
+    result
336
+  end
337
+
338
+  def _get_detailed_cpuinfo
339
+    real = {}
340
+    info = {}
341
+    info[:total] = 0
342
+    curr_proc = nil
343
+    File.open('/proc/cpuinfo').each do |l|
344
+      case l.strip
345
+        when /processor\s+:\s(.+)/
346
+          info[:total] += 1
347
+          curr_proc = $1
348
+          info[curr_proc] = {}
349
+        when /^cpu MHz\s+:\s(.+)/
350
+          info[curr_proc][:mhz] = $1
351
+        when /^physical id\s+:\s(.+)/
352
+          info[curr_proc][:physical_id] = $1
353
+          real[$1] = true
354
+        when /^flags\s+:\s(.+)/
355
+          info[curr_proc][:flags] = $1.split
356
+        when /^address sizes\s+:\s(\d+) bits (\w+), (\d+) bits (\w+)/
357
+          info[curr_proc][:address_sizes] = {}
358
+          info[curr_proc][:address_sizes][$2.to_sym] = $1
359
+          info[curr_proc][:address_sizes][$4.to_sym] = $3
360
+        when /(.+)\s+:\s(.+)/
361
+          value = $2
362
+          key = $1.strip.downcase.gsub(/ /, '_')
363
+          info[curr_proc][key.to_sym] = value
364
+      end
365
+    end
366
+    info[:real] = real.keys.size
367
+    info
368
+  end
369
+
370
+  def _get_blkdev_info
371
+    info = {}
372
+    if File.directory?('/sys/block/')
373
+      begin
374
+      Timeout::timeout(10) do
375
+        Dir['/sys/block/*'].each do |blkdir|
376
+          blkdev = File.basename(blkdir)
377
+          info[blkdev] = Hash.new
378
+          Dir.glob("/sys/block/#{blkdev}/{size,removable}").each do |g|
379
+            File.open(g) { |f| info[blkdev][File.basename(g).to_sym] = f.read_nonblock(1024).strip }
380
+          end
381
+          Dir.glob("/sys/block/#{blkdev}/device/{model,rev,state,timeout,vendor}").each do |g|
382
+            File.open(g) { |f| info[blkdev][File.basename(g).to_sym] = f.read_nonblock(1024).strip }
383
+          end
384
+        end # of blkdir
385
+      end # of timeout
386
+      rescue => e
387
+        @logger.error("Error '#{e.message}' in gathering disks metadata: #{e.backtrace}")
388
+      end
389
+    end # File.directory
390
+    info
391
+  end
392
+
393
+  def _get_dmi_info
394
+    info = {}
395
+    Dir['/sys/class/dmi/id/*'].each do |key|
396
+      if File.file?(key)
397
+        case File.basename(key)
398
+          when /product_uuid/
399
+            File.open(key) {|f| info[:uuid] = f.read_nonblock(1024).strip}
400
+          when /sys_vendor/
401
+            File.open(key) {|f| info[:sys_vendor] = info[:manufacturer] = f.read_nonblock(1024).strip}
402
+          else
403
+            File.open(key) {|f| info[File.basename(key).to_sym] = f.read_nonblock(1024).strip}
404
+        end
405
+      end
406
+    end
407
+    info
408
+  end
409
+
410
+  def _get_dmidecode_system_info
411
+    info = {}
412
+    info[:system] = {}
413
+    # dmidecode -t system
414
+    ## dmidecode 3.0
415
+    #Getting SMBIOS data from sysfs.
416
+    #SMBIOS 2.8 present.
417
+    #
418
+    #Handle 0x0100, DMI type 1, 27 bytes
419
+    #System Information
420
+    #  Manufacturer: QEMU
421
+    #  Product Name: Standard PC (i440FX + PIIX, 1996)
422
+    #  Version: pc-i440fx-2.4
423
+    #  Serial Number: Not Specified
424
+    #  UUID: 0C2041CE-94E4-453D-95DD-1682D5D8E487
425
+    #  Wake-up Type: Power Switch
426
+    #  SKU Number: Not Specified
427
+    #  Family: Not Specified
428
+    #
429
+    #Handle 0x2000, DMI type 32, 11 bytes
430
+    #System Boot Information
431
+    #  Status: No errors detected
432
+    `dmidecode -t system`.each_line do |l|
433
+      case l.strip
434
+        when /^SMBIOS (\S+)/
435
+          info[:dmidecode_version] = $1.strip
436
+        when /(.+):(.+)/
437
+          k = $1
438
+          v = $2
439
+          info[:system][k.downcase.gsub(/([ -])/, '_').to_sym] = v.strip
440
+      end
245 441
     end
246
-    interfaces << { "default_interface" => @os["network"]["default_interface"] }
247
-    interfaces << { "default_gateway"   => @os["network"]["default_gateway"] }
248
-    interfaces
442
+    info
249 443
   end
250 444
 
445
+
251 446
   # transform input array into array of the objects
252 447
   # Example:
253 448
   # [{
@@ -387,19 +582,21 @@ class NodeAgent
387 582
       :system => _system_info,
388 583
       :interfaces => [],
389 584
       :cpu => {
390
-        :total => (@os[:cpu][:total].to_i rescue nil),
391
-        :real => (@os[:cpu][:real].to_i rescue nil),
392
-        :spec => [],
585
+        :total => (@facter['processorcount'].to_i rescue nil),
586
+        :real  => (@facter['physicalprocessorcount'].to_i rescue nil),
587
+        :spec  => [],
393 588
       },
394 589
       :disks => [],
395
-      :memory => (_dmi_memory or _ohai_memory),
590
+      :memory => (_dmi_memory or _facter_memory),
396 591
       :pci_devices => _get_pci_dev_list,
397 592
       :numa_topology => @numa_topology,
398 593
     }
399 594
 
400
-    admin_mac = (_master_ip_and_mac[:mac] or @os[:macaddress]) rescue nil
595
+    admin_mac = (_master_ip_and_mac[:mac] or @network[:mac]) rescue nil
401 596
     begin
402
-      (@os[:network][:interfaces] or {} rescue {}).each do |int, intinfo|
597
+      (@network[:interfaces] or {} rescue {}).each do |int, intinfo|
598
+        #next if not intinfo.has_key?(:name)
599
+        #int = intinfo[:name]
403 600
         # Send info about physical interfaces only
404 601
 
405 602
         next if intinfo[:encapsulation] !~ /^Ethernet.*/
@@ -489,7 +686,7 @@ class NodeAgent
489 686
     end
490 687
 
491 688
     begin
492
-      (@os[:cpu] or {} rescue {}).each do |cpu, cpuinfo|
689
+      (_get_detailed_cpuinfo or {} rescue {}).each do |cpu, cpuinfo|
493 690
         if cpu =~ /^[\d]+/ and cpuinfo
494 691
           frequency = cpuinfo[:mhz].to_i rescue nil
495 692
           begin
@@ -520,7 +717,7 @@ class NodeAgent
520 717
         # http://lxr.free-electrons.com/source/drivers/scsi/sd.c?v=4.4#L2340
521 718
         block_size = 512
522 719
 
523
-        (@os[:block_device] or {} rescue {}).each do |bname, binfo|
720
+        (_get_blkdev_info or {} rescue {}).each do |bname, binfo|
524 721
           @logger.debug("Found block device: #{bname}")
525 722
           @logger.debug("Block device info: #{binfo.inspect}")
526 723
           dname = bname.gsub(/!/, '/')
@@ -829,31 +1026,35 @@ class NodeAgent
829 1026
   end
830 1027
 
831 1028
   def _is_virtualbox
832
-    @os[:dmi][:system][:product_name] == "VirtualBox" rescue false
1029
+    @facter['productname'] == "VirtualBox"
833 1030
   end
834 1031
 
835 1032
   def _is_virtual
836
-    _is_virtualbox or @os[:virtualization][:role] == "guest" rescue false
1033
+    @facter[:is_virtual]
837 1034
   end
838 1035
 
1036
+  # JFYI: if /QEMU/ doesn't matched in /proc/cpuinfo
1037
+  # ohai[:virtualization] will return empty hash on kvm systems
1038
+  # So, this code have exactly same behavior.
1039
+  # But in my opinion here should be returned real value.
839 1040
   def _manufacturer
840 1041
     if _is_virtualbox
841
-      @os[:dmi][:system][:product_name] rescue nil
842
-    elsif _is_virtual
843
-      @os[:virtualization][:system].upcase.strip rescue nil
1042
+      @facter['productname']
1043
+    elsif (@facter['manufacturer'].upcase != 'QEMU' && @facter['is_virtual'])
1044
+      @facter['virtual']
844 1045
     else
845
-      @os[:dmi][:system][:manufacturer].strip rescue nil
1046
+      @facter['manufacturer']
846 1047
     end
847 1048
   end
848 1049
 
849 1050
   def _product_name
850 1051
     unless _is_virtual
851
-      @os[:dmi][:system][:product_name].strip rescue nil
1052
+      @facter['productname']
852 1053
     end
853 1054
   end
854 1055
 
855 1056
   def _serial
856
-    @os[:dmi][:system][:serial_number].strip rescue nil
1057
+    @facter['serialnumber']
857 1058
   end
858 1059
 
859 1060
   # Returns unique identifier of machine
@@ -861,7 +1062,7 @@ class NodeAgent
861 1062
   # * for physical HW that would be unique chassis id (from BIOS settings)
862 1063
   # * for other hypervizors - not tested
863 1064
   def uuid
864
-    node_uuid = @os.data.fetch(:dmi, {}).fetch(:system, {}).fetch(:uuid, nil)
1065
+    node_uuid = @facter['uuid']
865 1066
     node_uuid && node_uuid.strip
866 1067
   end
867 1068
 
@@ -872,9 +1073,9 @@ class NodeAgent
872 1073
       :uuid => uuid,
873 1074
       :runtime_uuid => @settings['runtime_uuid'],
874 1075
       :product => _product_name,
875
-      :family => (@os[:dmi][:system][:family].strip rescue nil),
876
-      :version => (@os[:dmi][:system][:version].strip rescue nil),
877
-      :fqdn => (@os[:fqdn].strip rescue @os[:hostname].strip rescue nil),
1076
+      :family => (_get_dmidecode_system_info[:system][:family].strip rescue nil),
1077
+      :version => _get_dmi_info[:chassis_version],
1078
+      :fqdn => (@facter['fqdn'].strip rescue @facter['hostname'].strip rescue nil),
878 1079
     }.delete_if { |key, value| value.nil? or value.empty? or value == "Not Specified" }
879 1080
   end
880 1081
 
@@ -929,15 +1130,15 @@ class NodeAgent
929 1130
     end
930 1131
   end
931 1132
 
932
-  def _ohai_memory
1133
+  def _facter_memory
933 1134
     info = {}
934
-    size = @os['memory']['total'].gsub(/(kb|mb|gb)$/i, "").to_i rescue (return nil)
1135
+    size = @facter['memorysize'].gsub(/(kb|mb|gb)$/i, "").to_i rescue (return nil)
935 1136
     info[:total] = _size(size, $1)
936 1137
     info
937 1138
   end
938 1139
 
939 1140
   def _get_ip_mac_pair_for(local_addr)
940
-    @os[:network][:interfaces].each do |_, intinfo|
1141
+    @network[:interfaces].each do |int, intinfo|
941 1142
       next unless intinfo.has_key?(:addresses)
942 1143
       intinfo[:addresses].each do |k, v|
943 1144
         # Here we need to check family because IPAddr.new with bad
@@ -977,16 +1178,16 @@ class NodeAgent
977 1178
 
978 1179
   def _data
979 1180
     res = {
980
-      :mac => (@os[:macaddress] rescue nil),
981
-      :ip  => (@os[:ipaddress] rescue nil),
982
-      :os_platform => (@os[:platform] rescue nil),
1181
+      :mac => (@network[:mac] rescue nil),
1182
+      :ip  => (@facter['ipaddress'] rescue nil),
1183
+      :os_platform => (@facter['operatingsystem'].downcase rescue nil)
983 1184
     }
984 1185
     begin
985 1186
       detailed_data = _detailed
986 1187
       master_data=_master_ip_and_mac
987 1188
       res.merge!({
988
-        :ip  => (( master_data[:ip] or @os[:ipaddress]) rescue nil),
989
-        :mac  => (( master_data[:mac] or @os[:macaddress]) rescue nil),
1189
+        :ip => (( master_data[:ip] or @facter['ipaddress']) rescue nil),
1190
+        :mac => (( master_data[:mac] or @network[:mac]) rescue nil),
990 1191
         :manufacturer => _manufacturer,
991 1192
         :platform_name => _product_name,
992 1193
         :meta => detailed_data
@@ -1074,8 +1275,8 @@ class NodeAgent
1074 1275
   end
1075 1276
 
1076 1277
   def supported_hugepages
1077
-    return [2048, 1048576] if @os[:cpu]['0']['flags'].include?('pdpe1gb')
1078
-    return [2048] if @os[:cpu]['0']['flags'].include?('pse')
1278
+    return [2048, 1048576] if _get_detailed_cpuinfo['0'][:flags].include?('pdpe1gb')
1279
+    return [2048] if _get_detailed_cpuinfo['0'][:flags].include?('pse')
1079 1280
     []
1080 1281
   end
1081 1282
 

+ 6
- 0
debian/changelog View File

@@ -1,3 +1,9 @@
1
+nailgun-agent (10.0.1) trusty; urgency=low
2
+
3
+  * Remove ohai-related code
4
+
5
+ -- Ivan Suzdal <isuzdal@mirantis.com>  Tue, 10 May 2016 15:35:26 +0000
6
+
1 7
 nailgun-agent (10.0.0-1) trusty; urgency=low
2 8
 
3 9
   *  Bump version to 10.0.0

+ 4
- 7
debian/control View File

@@ -4,24 +4,21 @@ Priority: extra
4 4
 Maintainer: PKG OpenStack <openstack-devel@lists.alioth.debian.org>
5 5
 Build-Depends: debhelper (>= 9), openstack-pkg-tools
6 6
 Standards-Version: 3.9.6
7
-Homepage: https://github.com/stackforge/fuel-nailgun-agent
8
-Vcs-Git: https://github.com/stackforge/fuel-nailgun-agent.git
9
-Vcs-Browser: https://github.com/stackforge/fuel-nailgun-agent
7
+Homepage: https://github.com/openstack/fuel-nailgun-agent
8
+Vcs-Git: https://github.com/openstack/fuel-nailgun-agent.git
9
+Vcs-Browser: https://github.com/openstack/fuel-nailgun-agent
10 10
 
11 11
 Package: nailgun-agent
12 12
 Architecture: all
13 13
 XB-Ruby-Versions: ${ruby:Versions}
14 14
 Recommends: lshw,
15
-            pciutils
16
-Depends: ohai (<< 7),
17
-         dmidecode,
15
+Depends: facter,
18 16
          ethtool,
19 17
          ruby-cstruct,
20 18
          ruby-httpclient,
21 19
          ruby-ipaddress,
22 20
          ruby-json,
23 21
          ruby-rethtool,
24
-         ruby | ruby-interpreter,
25 22
          ${misc:Depends}
26 23
 Description: collects the node hardware data and submits it to the Fuel master
27 24
  The nailgun agent collects the server’s hardware information and submits it

Loading…
Cancel
Save