ManageIQ/manageiq-providers-nuage

View on GitHub
app/models/manageiq/providers/nuage/inventory/collector/target_collection.rb

Summary

Maintainability
A
1 hr
Test Coverage
A
100%
class ManageIQ::Providers::Nuage::Inventory::Collector::TargetCollection < ManageIQ::Providers::Nuage::Inventory::Collector
  def initialize(_manager, _target)
    super
    initialize_cache
    parse_targets!
    infer_related_ems_refs!

    # Reset the target cache, so we can access new targets inside
    target.manager_refs_by_association_reset
  end

  def cloud_networks_floating
    return [] if references(:cloud_networks).blank?
    references(:cloud_networks).collect { |ems_ref| shared_resource(ems_ref) }
    @shared_resources_map.values.compact.select { |res| res['type'] == 'FLOATING' }
  end

  def cloud_subnets
    return [] if (refs = references_with_kind(:cloud_subnets, 'L3')).blank?
    refs.map { |subnet| cloud_subnet(subnet[:ems_ref]) }.compact
  end

  def l2_cloud_subnets
    return [] if (refs = references_with_kind(:cloud_subnets, 'L2')).blank?
    refs.map { |subnet| l2_cloud_subnet(subnet[:ems_ref]) }.compact
  end

  def security_groups
    return [] if (refs = references(:security_groups)).blank?
    refs.map { |ems_ref| security_group(ems_ref) }.compact
  end

  def cloud_tenants
    return [] if (refs = references(:cloud_tenants)).blank?
    refs.map { |ems_ref| cloud_tenant(ems_ref) }.compact
  end

  def network_routers
    return [] if (refs = references(:network_routers)).blank?
    refs.map { |ems_ref| network_router(ems_ref) }.compact
  end

  def floating_ips
    return [] if (refs = references(:floating_ips)).blank?
    refs.map { |ems_ref| floating_ip(ems_ref) }.compact
  end

  def network_ports
    return [] if (refs = references(:network_ports)).blank?
    refs.map { |ems_ref| network_port(ems_ref) }.compact
  end

  def security_groups_for_network_port(port_ems_ref)
    safe_call { vsd_client.get_policy_groups_for_vport(port_ems_ref) }
  end

  def vm_interfaces_for_network_port(port_ems_ref)
    safe_call { vsd_client.get_vm_interfaces_for_vport(port_ems_ref) }
  end

  def container_interfaces_for_network_port(port_ems_ref)
    safe_call { vsd_client.get_container_interfaces_for_vport(port_ems_ref) }
  end

  def host_interfaces_for_network_port(port_ems_ref)
    safe_call { vsd_client.get_host_interfaces_for_vport(port_ems_ref) }
  end

  def vms_for_network_port(port_ems_ref)
    safe_call { vsd_client.get_vms_for_vport(port_ems_ref) }
  end

  def cloud_subnet(ems_ref)
    safe_call { vsd_client.get_subnet(ems_ref) }
  end

  def l2_cloud_subnet(ems_ref)
    safe_call { vsd_client.get_l2_domain(ems_ref) }
  end

  def shared_resource(ems_ref)
    return @shared_resources_map[ems_ref] if @shared_resources_map.key?(ems_ref)
    @shared_resources_map[ems_ref] = safe_call { vsd_client.get_sharednetworkresource(ems_ref) }
  end

  def security_group(ems_ref)
    safe_call { vsd_client.get_policy_group(ems_ref) }
  end

  def cloud_tenant(ems_ref)
    safe_call { vsd_client.get_enterprise(ems_ref) }
  end

  def zone(ems_ref)
    safe_call { vsd_client.get_zone(ems_ref) }
  end

  def network_router(ems_ref)
    safe_call { vsd_client.get_domain(ems_ref) }
  end

  def floating_ip(ems_ref)
    safe_call { vsd_client.get_floating_ip(ems_ref) }
  end

  def network_port(ems_ref)
    safe_call { vsd_client.get_vport(ems_ref) }
  end

  private

  def initialize_cache
    @shared_resources_map = {}
  end

  def cloud_subnets_for_router(router_ref)
    safe_list { vsd_client.get_subnets_for_domain(router_ref) }
  end

  def parse_targets!
    # parse native targets (e.g. CloudSubnet::L3) here in case we support manual triggering targeted refresh for them.
  end

  def infer_related_ems_refs!
    infer_related_ems_refs_db!
    infer_related_ems_refs_api!
  end

  def infer_related_ems_refs_db!
    references_with_options(:cloud_subnet_templates).each do |template|
      next unless template[:operation] == 'DELETE'
      manager.cloud_subnets_by_extra_attr('template_id', template[:ems_ref]).each do |subnet|
        add_target!(:cloud_subnets, subnet.ems_ref, :kind => 'L3', :deleted => true)
      end
    end

    references_with_options(:zone_templates).each do |template|
      next unless template[:operation] == 'DELETE'
      manager.cloud_subnets_by_extra_attr('zone_template_id', template[:ems_ref]).each do |subnet|
        add_target!(:cloud_subnets, subnet.ems_ref, :kind => 'L3', :deleted => true)
      end
    end

    references_with_options(:zones).each do |zone|
      next unless zone[:operation] == 'DELETE'
      manager.cloud_subnets_by_extra_attr('zone_id', zone[:ems_ref]).each do |subnet|
        add_target!(:cloud_subnets, subnet.ems_ref, :kind => 'L3', :deleted => true)
      end
    end
  end

  def infer_related_ems_refs_api!
    # Overcome Nuage glitch upon router template instantiation.
    # GLITCH: even if template contains subnets, no "subnet create" event is emitted hence targeted
    # refresh isn't triggered for those.
    # SOLUTION: manually infer all subnets for such router.
    references_with_options(:network_routers).each do |router|
      next unless router[:operation] == 'CREATE'
      cloud_subnets_for_router(router[:ems_ref]).each do |subnet|
        add_target!(:cloud_subnets, subnet['ID'], :kind => 'L3')
      end
    end
  end

  def safe_call
    response = yield

    # TODO: This error handling is funny because the vsd_client returns 'true' when any other status than 200 is
    # returned :) We need to refactor the vsd_client and then rescue from actual errors here...
    case response
    when Array, Hash
      response
    end
  end

  def safe_list(&block)
    safe_call(&block) || []
  end

  def references_with_kind(association, kind)
    references_with_options(association).select { |t| t[:kind] == kind }
  end

  def references_with_options(association)
    target.targets
          .select { |t| t.association == association }
          .map    { |t| t.options.merge(:ems_ref => t.manager_ref[:ems_ref]) }
          .reject { |t| t[:deleted] } # Sometimes we know it is deleted, so we spare an API call for performance reasons.
  end
end