#!/usr/bin/env ruby

# is_store:
# 	if set this param equal "yes" when submit job, this board info will upload to es'index(hosts) automatically.
# 	like:
# 	- is_store: "yes"

require 'json'
require 'yaml'

def get_description(line, board_info)
  line = line.gsub(/^[0-9a-z]{2}:[0-9a-z]{2}.[0-9]/, '').gsub(/\[[0-9]{4}\]/, '').gsub(/\[.{9}\]/, ':')
  descriptions = line.gsub('Ltd.', 'Ltd.:').split(':')

  board_info['description'] = descriptions[0].gsub(/^\s/, '').gsub(/\s$/, '')
  board_info['vendor'] = descriptions[1].gsub(/^\s/, '').gsub(/\s$/, '')
  board_info['product'] = descriptions[2].gsub(/^\s/, '').gsub(/\s$/, '')
end

def get_board(dev_info)
  found = false
  board_info = { 'vendorID' => '', 'deviceID' => '', 'svID' => '', 'ssID' => '' }
  dev_info.each do |line|
    if line.include?('Endpoint') && !line.include?('Root Complex Integrated')
      found = true
    elsif line.include?('Subsystem')
      index = (line =~ /[0-9a-z]{4}:[0-9a-z]{4}/)
      board_info['svID'] = line[index..index + 3]
      board_info['ssID'] = line[index + 5..index + 8]
    elsif line =~ /^[0-9a-z]{2}:[0-9a-z]{2}.[0-9]/
      index = (line =~ /[0-9a-z]{4}:[0-9a-z]{4}/)
      board_info['vendorID'] = line[index..index + 3]
      board_info['deviceID'] = line[index + 5..index + 8]
      get_description(line, board_info)
    end
  end
  return board_info if found

  return nil
end

def get_cpu_info
  lscpu = JSON.parse(%x(lscpu -J))['lscpu']
  cpu_info = {}
  lscpu.each do |i|
    k = i['field']
    v = i['data']
    next if k =~ /^On-line/
    if k =~ /^Vulnerability/
      next if v == 'Not affected'
      k.sub!(' ', '.')
    end
    k.sub!('CPU op-mode(s)', 'op-modes')
    k.sub!(/NUMA node(\d+) CPU\(s\)/, 'NUMA node\1 CPUs')
    k.sub!(/(.*)\(s\)/, 'nr_\1')
    k.gsub!(' ', '_')
    k.chomp!(':')
    k.downcase!
    cpu_info[k] = v
  end
  info_to_num(cpu_info)
  return cpu_info
end

def get_memory_info
  lshw_string = %x(lshw -numeric -json).gsub(/\s{2,}/, '')
  memory_list = []
  lshw_info = JSON.parse(lshw_string)['children'][0]['children']
  lshw_info.each do |element|
    memory_list = element['children'] if element['id'] == 'memory'
  end

  memory_info = []
  memory_list.each do |mem|
    next if mem['description'].include?('empty')

    mem.delete('class')
    mem.delete('claimed')
    mem.delete('handle')
    info_to_num(mem)
    memory_info << mem
  end
  return memory_info
end

def new_card?(cards, card)
  return false unless card

  cards.each do |exist_card|
    return false if card == exist_card
  end
  return true
end

def get_cards_info
  pci_info = %x(lspci -nnv).lines
  start = 0
  cards = []
  pci_info.each_index do |index|
    next unless pci_info[index] == "\n"

    dev_info = pci_info[start..index]
    card = get_board(dev_info)
    cards << card if new_card?(cards, card)
    start = index + 1
  end
  return cards
end

def get_system_disk_info
  facter_string = %x(facter -j)
  facter_info = JSON.parse(facter_string)
  server_info = {}
  server_info['id'] = ENV['HOSTNAME']
  server_info['manufacturer'] = facter_info['dmi']['manufacturer']
  server_info['product'] = facter_info['dmi']['product']

  server_info['bios'] = facter_info['dmi']['bios']
  info_to_num(server_info['bios'])
  server_info['disks'] = facter_info['disks']
  server_info['disks'].each do |k, v|
    info_to_num(v)
  end

  return server_info
end

def info_to_num(obj)
  return unless obj.is_a?(Hash)

  obj.each do |k, v|
    next unless v.is_a?(String)

    if v =~ /^([0-9]{1,})$/
      obj[k] = v.to_i
    elsif v =~ /^([0-9]{1,}\.[0-9]{1,})$/
      obj[k] = v.to_f
    end
  end
end

server_info = get_system_disk_info
server_info['cpu'] = get_cpu_info
server_info['memory'] = get_memory_info
server_info['cards'] = get_cards_info

puts JSON.pretty_generate(server_info)
