ManageIQ/manageiq-providers-vmware

View on GitHub
app/models/manageiq/providers/vmware/inventory/parser.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
90%
class ManageIQ::Providers::Vmware::Inventory::Parser < ManageIQ::Providers::Inventory::Parser
  def parse
    vdcs
    vapps
    vms
    vapp_templates
    images
    networks
    network_subnets
    network_routers
    network_ports
    floating_ips
  end

  private

  def vdcs
    collector.vdcs.each do |vdc|
      persister.availability_zones.find_or_build(vdc.id).assign_attributes(
        :name => vdc.name
      )
    end
  end

  def vapps
    collector.vapps.each do |vapp|
      persister.orchestration_stacks.find_or_build(vapp.id).assign_attributes(
        :name        => vapp.name,
        :description => vapp.name,
        :status      => vapp.human_status,
      )
    end
  end

  def vms
    collector.vms.each do |vm|
      parsed_vm = persister.vms.find_or_build(vm[:vm].id).assign_attributes(
        :uid_ems                => vm[:vm].id,
        :name                   => vm[:vm].name,
        :hostname               => vm[:hostname],
        :location               => vm[:vm].id,
        :vendor                 => 'vmware',
        :connection_state       => "connected",
        :raw_power_state        => vm[:vm].status,
        :orchestration_stack    => persister.orchestration_stacks.lazy_find(vm[:vm].vapp_id),
        :snapshots              => [],
        :cpu_hot_add_enabled    => vm[:vm].cpu_hot_add,
        :memory_hot_add_enabled => vm[:vm].memory_hot_add,
      )

      if (resp = vm[:snapshot]) && (snapshot = resp.fetch_path(:body, :Snapshot))
        uid = "#{vm[:vm].id}_#{snapshot[:created]}"
        persister.snapshots.find_or_build_by(:vm_or_template => parsed_vm, :uid => uid).assign_attributes(
          :name        => "#{vm[:vm].name} (snapshot)",
          :uid         => uid,
          :ems_ref     => uid,
          :parent_uid  => vm[:vm].id,
          :create_time => Time.zone.parse(snapshot[:created]),
          :total_size  => snapshot[:size]
        )
      end

      hardware = persister.hardwares.find_or_build(parsed_vm).assign_attributes(
        :guest_os             => vm[:vm].operating_system,
        :guest_os_full_name   => vm[:vm].operating_system,
        :bitness              => vm[:vm].operating_system =~ /64-bit/ ? 64 : 32,
        :cpu_sockets          => vm[:vm].cpu / vm[:vm].cores_per_socket,
        :cpu_cores_per_socket => vm[:vm].cores_per_socket,
        :cpu_total_cores      => vm[:vm].cpu,
        :memory_mb            => vm[:vm].memory,
        :disk_capacity        => vm[:vm].hard_disks.inject(0) { |sum, x| sum + x.values[0] } * 1.megabyte,
      )

      vm[:vm].disks.all.select { |d| hdd? d.bus_type }.each_with_index do |disk, i|
        device_name = "Disk #{i}"
        persister.disks.find_or_build_by(:hardware => hardware, :device_name => device_name).assign_attributes(
          :device_name     => device_name,
          :device_type     => "disk",
          :disk_type       => controller_description(disk.bus_sub_type).sub(' controller', ''),
          :controller_type => controller_description(disk.bus_sub_type),
          :size            => disk.capacity * 1.megabyte,
          :location        => "#{vm[:vm].id}/#{disk.address}/#{disk.address_on_parent}/#{disk.id}",
          :filename        => "Disk #{i}"
        )
      end

      persister.operating_systems.find_or_build(parsed_vm).assign_attributes(
        :product_name => vm[:vm].operating_system,
      )
    end
  end

  def vapp_templates
    collector.vapp_templates.each do |vapp_template|
      persister.orchestration_templates.find_or_build(vapp_template[:vapp_template].id).assign_attributes(
        :name        => vapp_template[:vapp_template].name,
        :description => vapp_template[:vapp_template].description,
        :orderable   => true,
        :content     => "<!-- #{vapp_template[:vapp_template].id} -->\n#{vapp_template[:content]}",
      )
    end
  end

  def images
    collector.images.each do |image|
      persister.miq_templates.find_or_build(image[:image].id).assign_attributes(
        :uid_ems            => image[:image].id,
        :name               => image[:image].name,
        :location           => image[:image].id,
        :vendor             => 'vmware',
        :connection_state   => "connected",
        :raw_power_state    => 'never',
        :publicly_available => image[:is_published]
      )
    end
  end

  def networks
    collector.vdc_networks.each { |n| parse_network(n) }
    collector.vapp_networks.each { |n| parse_network(n) }
  end

  def parse_network(network)
    uid = network.id
    network_type = if network.type.include?("vcloud.orgNetwork")
      "ManageIQ::Providers::Vmware::NetworkManager::CloudNetwork::OrgVdcNet"
    else
      "ManageIQ::Providers::Vmware::NetworkManager::CloudNetwork::VappNet"
    end

    new_result = {
      :name                   => network.name,
      :ems_ref                => uid,
      :shared                 => network.is_shared,
      :type                   => network_type,
      :orchestration_stack_id => network.try(:orchestration_stack).try(:id)
    }
    new_result[:cidr] = to_cidr(network.gateway, network.netmask)
    new_result[:enabled] = network.enabled if network.respond_to?(:enabled)

    persister.cloud_networks.build(new_result)
  end

  def network_subnets
    collector.vdc_networks.each { |n| parse_network_subnet(n) }
    collector.vapp_networks.each { |n| parse_network_subnet(n) }
  end

  def parse_network_subnet(network)
    uid = subnet_id(network)
    router     = collector.routers.detect { |r| r[:network_id] == network.id }
    network_router = persister.network_routers.lazy_find("#{router[:network_id]}---#{router[:parent_net].id}") if router

    new_result = {
      :name            => network.name,
      :ems_ref         => uid,
      :gateway         => network.gateway,
      :dns_nameservers => [network.dns1, network.dns2].compact,
      :type            => "ManageIQ::Providers::Vmware::NetworkManager::CloudSubnet",
      :cloud_network   => persister.cloud_networks.lazy_find(network.id),
      :network_router  => network_router
    }
    new_result[:cidr] = to_cidr(network.gateway, network.netmask)
    new_result[:dhcp_enabled] = network.dhcp_enabled if network.respond_to?(:dhcp_enabled)

    persister.cloud_subnets.build(new_result)
  end

  def network_routers
    collector.routers.each { |r| parse_network_router(r) }
  end

  def parse_network_router(router)
    parent_id  = router[:parent_net].id
    uid        = "#{router[:network_id]}---#{parent_id}"
    new_result = {
      :type          => "ManageIQ::Providers::Vmware::NetworkManager::NetworkRouter",
      :name          => "Router #{router[:parent_net].name} -> #{router.dig(:net_conf, :networkName)}",
      :ems_ref       => uid,
      :cloud_network => persister.cloud_networks.lazy_find(parent_id)
    }
    router = persister.network_routers.build(new_result)

    network_subnet = persister.cloud_subnets.find("subnet-#{router[:network_id]}")
    network_subnet.network_router = router unless network_subnet.nil?
  end

  def network_ports
    collector.network_ports.each { |n| parse_network_port(n) }
  end

  def parse_network_port(nic_data)
    uid = port_id(nic_data)
    vm_uid = nic_data[:vm].id

    new_result = {
      :type        => "ManageIQ::Providers::Vmware::NetworkManager::NetworkPort",
      :name        => "NIC##{nic_data[:NetworkConnectionIndex]}",
      :ems_ref     => uid,
      :device_ref  => vm_uid,
      :device_type => "VmOrTemplate",
      :device_id   => vm_uid,
      :mac_address => nic_data.dig(:MACAddress),
      :source      => "refresh"
    }

    network_port = persister.network_ports.build(new_result)

    network_id = collector.read_network_name_mapping(nic_data[:vm].vapp_id, nic_data.dig(:network))
    unless network_id.nil?
      persister.cloud_subnet_network_ports.build(
        :address      => nic_data[:IpAddress],
        :cloud_subnet => persister.cloud_subnets.lazy_find("subnet-#{network_id}"),
        :network_port => network_port
      )
    end
  end

  def floating_ips
    collector.network_ports.each { |n| parse_floating_ip(n) }
  end

  def parse_floating_ip(nic_data)
    floating_ip = nic_data[:ExternalIpAddress]
    return unless floating_ip

    uid = floating_ip_id(nic_data)
    network_id = collector.read_network_name_mapping(nic_data[:vm].vapp_id, nic_data[:network])

    new_result = {
      :type             => "ManageIQ::Providers::Vmware::NetworkManager::FloatingIp",
      :ems_ref          => uid,
      :address          => floating_ip,
      :fixed_ip_address => floating_ip,
      :cloud_network    => persister.cloud_networks.lazy_find(network_id),
      :network_port     => persister.network_ports.lazy_find(port_id(nic_data)),
      :vm               => persister.vms.lazy_find(nic_data[:vm].id)
    }

    persister.floating_ips.build(new_result)
  end

  def subnet_id(network)
    "subnet-#{network.id}"
  end

  def port_id(nic_data)
    "#{nic_data[:vm].id}#NIC##{nic_data[:NetworkConnectionIndex]}"
  end

  def floating_ip_id(nic_data)
    "floating_ip-#{port_id(nic_data)}"
  end

  def to_cidr(address, netmask)
    return unless address.to_s =~ Resolv::IPv4::Regex && netmask.to_s =~ Resolv::IPv4::Regex
    address + '/' + netmask.to_s.split(".").map { |e| e.to_i.to_s(2).rjust(8, "0") }.join.count("1").to_s
  end

  # See https://pubs.vmware.com/vcd-80/index.jsp#com.vmware.vcloud.api.sp.doc_90/GUID-E1BA999D-87FA-4E2C-B638-24A211AB8160.html
  def controller_description(bus_subtype)
    case bus_subtype
    when 'buslogic'
      'BusLogic Parallel SCSI controller'
    when 'lsilogic'
      'LSI Logic Parallel SCSI controller'
    when 'lsilogicsas'
      'LSI Logic SAS SCSI controller'
    when 'VirtualSCSI'
      'Paravirtual SCSI controller'
    when 'vmware.sata.ahci'
      'SATA controller'
    else
      'IDE controller'
    end
  end

  # See https://pubs.vmware.com/vcd-80/index.jsp#com.vmware.vcloud.api.sp.doc_90/GUID-E1BA999D-87FA-4E2C-B638-24A211AB8160.html
  def hdd?(bus_type)
    [5, 6, 20].include?(bus_type)
  end
end