diff --git a/app/models/manageiq/providers/vmware/cloud_manager.rb b/app/models/manageiq/providers/vmware/cloud_manager.rb index c29031572..cfc8fd3df 100644 --- a/app/models/manageiq/providers/vmware/cloud_manager.rb +++ b/app/models/manageiq/providers/vmware/cloud_manager.rb @@ -5,7 +5,6 @@ class ManageIQ::Providers::Vmware::CloudManager < ManageIQ::Providers::CloudMana require_nested :OrchestrationTemplate require_nested :EventCatcher require_nested :EventParser - require_nested :RefreshParser require_nested :RefreshWorker require_nested :Refresher require_nested :Template @@ -148,6 +147,10 @@ def supported_catalog_types %w(vmware) end + def inventory_object_refresh? + true + end + def self.display_name(number = 1) n_('Cloud Provider (VMware vCloud)', 'Cloud Providers (VMware vCloud)', number) end diff --git a/app/models/manageiq/providers/vmware/cloud_manager/refresh_parser.rb b/app/models/manageiq/providers/vmware/cloud_manager/refresh_parser.rb deleted file mode 100644 index 934424215..000000000 --- a/app/models/manageiq/providers/vmware/cloud_manager/refresh_parser.rb +++ /dev/null @@ -1,271 +0,0 @@ -class ManageIQ::Providers::Vmware::CloudManager::RefreshParser < ManageIQ::Providers::CloudManager::RefreshParser - include ManageIQ::Providers::Vmware::RefreshHelperMethods - - # While parsing the VMWare catalog only those vapp templates whose status - # is reported to be "8" are ready to be used. The documentation says this - # status is POWERED_OFF, however the cloud director shows it as "Ready" - VAPP_TEMPLATE_STATUS_READY = "8".freeze - - def initialize(ems, options = nil) - @ems = ems - @connection = ems.connect - @options = options || {} - @data = {} - @data_index = {} - @inv = Hash.new { |h, k| h[k] = [] } - end - - def ems_inv_to_hashes - log_header = "MIQ(#{self.class.name}.#{__method__}) Collecting data for EMS name: [#{@ems.name}] id: [#{@ems.id}]" - - $vcloud_log.info("#{log_header}...") - - get_ems - get_orgs - get_vdcs - get_vapps - get_vms - get_vapp_templates - get_images - - $vcloud_log.info("#{log_header}...Complete") - - @data - end - - private - - def get_ems - @ems.api_version = @connection.api_version - end - - def get_orgs - @inv[:orgs] = @connection.organizations.all.to_a - end - - def get_vdcs - @inv[:orgs].each do |org| - @inv[:vdcs] += org.vdcs.all - end - - process_collection(@inv[:vdcs], :availability_zones) { |vdc| parse_vdc(vdc) } - end - - def get_vapps - @inv[:vdcs].each do |vdc| - @inv[:vapps] += vdc.vapps.all - end - - process_collection(@inv[:vapps], :orchestration_stacks) { |vapp| parse_stack(vapp) } - end - - def get_vms - @inv[:vapps].each do |vapp| - @inv[:vms] += vapp.vms.all - end - - process_collection(@inv[:vms], :vms) { |vm| parse_vm(vm) } - end - - def get_vapp_templates - @inv[:orgs].each do |org| - org.catalogs.each do |catalog| - next if catalog.is_published && !@options.get_public_images - - catalog.catalog_items.each do |item| - # Skip all Catalog Items which are not vApp Templates (e.g. Media & Other) - next unless item.vapp_template_id.starts_with?('vappTemplate-') - - @inv[:vapp_templates] << { - :vapp_template => item.vapp_template, - :is_published => catalog.is_published - } if item.vapp_template.status == VAPP_TEMPLATE_STATUS_READY - end - end - end - - process_collection(@inv[:vapp_templates], :orchestration_templates) { |vapp_template_obj| parse_vapp_template(vapp_template_obj[:vapp_template]) } - end - - def get_images - @inv[:vapp_templates].each do |template_obj| - @inv[:images] += template_obj[:vapp_template].vms.map { |image| { :image => image, :is_published => template_obj[:is_published] } } - end - - process_collection(@inv[:images], :vms) { |image_obj| parse_image(image_obj[:image], image_obj[:is_published]) } - end - - def parse_vdc(vdc) - id = vdc.id - - new_result = { - :type => "ManageIQ::Providers::Vmware::CloudManager::AvailabilityZone", - :ems_ref => id, - :name => vdc.name - } - - return id, new_result - end - - def parse_vm(vm) - status = vm.status - uid = vm.id - name = vm.name - hostname = vm.customization.try(:computer_name) - guest_os = vm.operating_system - bitness = vm.operating_system =~ /64-bit/ ? 64 : 32 - cpus = vm.cpu - cores_per_socket = vm.cores_per_socket - memory_mb = vm.memory - vapp_uid = vm.vapp_id - stack = @data_index.fetch_path(:orchestration_stacks, vapp_uid) - disk_capacity = vm.hard_disks.inject(0) { |sum, x| sum + x.values[0] } * 1.megabyte - cpu_hot_add = vm.cpu_hot_add - mem_hot_add = vm.memory_hot_add - - disks = vm.disks.all.select { |d| hdd? d.bus_type }.each_with_index.map do |disk, i| - { - :device_name => "Disk #{i}", - :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.id}/#{disk.address}/#{disk.address_on_parent}/#{disk.id}", - :filename => "Disk #{i}" - } - end - - new_result = { - :type => ManageIQ::Providers::Vmware::CloudManager::Vm.name, - :uid_ems => uid, - :ems_ref => uid, - :name => name, - :hostname => hostname, - :location => uid, - :vendor => "vmware", - :connection_state => "connected", - :raw_power_state => status, - :snapshots => [parse_snapshot(vm)].compact, - :cpu_hot_add_enabled => cpu_hot_add, - :memory_hot_add_enabled => mem_hot_add, - - :hardware => { - :guest_os => guest_os, - :guest_os_full_name => guest_os, - :bitness => bitness, - :cpu_sockets => cpus / cores_per_socket, - :cpu_cores_per_socket => cores_per_socket, - :cpu_total_cores => cpus, - :memory_mb => memory_mb, - :disk_capacity => disk_capacity, - :disks => disks, - }, - - :operating_system => { - :product_name => guest_os, - }, - - :orchestration_stack => stack, - } - - return uid, new_result - end - - def parse_stack(vapp) - status = vapp.human_status - uid = vapp.id - name = vapp.name - - new_result = { - :type => ManageIQ::Providers::Vmware::CloudManager::OrchestrationStack.name, - :ems_ref => uid, - :name => name, - :description => name, - :status => status, - } - return uid, new_result - end - - def parse_image(image, is_public) - uid = image.id - name = image.name - - new_result = { - :type => ManageIQ::Providers::Vmware::CloudManager::Template.name, - :uid_ems => uid, - :ems_ref => uid, - :name => name, - :location => uid, - :vendor => "vmware", - :connection_state => "connected", - :raw_power_state => "never", - :publicly_available => is_public - } - - return uid, new_result - end - - def parse_vapp_template(vapp_template) - uid = vapp_template.id - - # The content of the template is the OVF specification of the vApp template - content = @connection.get_vapp_template_ovf_descriptor(uid).body - # Prepend comment containing template uid which is then used istead of MD5 checksum. - content = "\n#{content}" - - new_result = { - :type => ManageIQ::Providers::Vmware::CloudManager::OrchestrationTemplate.name, - :ems_ref => uid, - :name => vapp_template.name, - :description => vapp_template.description, - :orderable => true, - :content => content, - # By default #save_orchestration_templates_inventory does not set the EMS - # ID because templates are not EMS specific. We are setting the EMS - # explicitly here, because vapps are specific to concrete EMS. - :ems_id => @ems.id - } - - return uid, new_result - end - - def parse_snapshot(vm) - resp = @connection.get_snapshot_section(vm.id).data - if (snapshot_resp = resp.fetch_path(:body, :Snapshot)) - { - :name => "#{vm.name} (snapshot)", - :uid => "#{vm.id}_#{snapshot_resp[:created]}", - :ems_ref => "#{vm.id}_#{snapshot_resp[:created]}", - :parent_id => vm.id, - :parent_uid => vm.id, - :create_time => snapshot_resp[:created], - :total_size => snapshot_resp[:size] - } - else - return nil - 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 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 diff --git a/app/models/manageiq/providers/vmware/cloud_manager/refresh_worker.rb b/app/models/manageiq/providers/vmware/cloud_manager/refresh_worker.rb index 5b16b9da8..82226182b 100644 --- a/app/models/manageiq/providers/vmware/cloud_manager/refresh_worker.rb +++ b/app/models/manageiq/providers/vmware/cloud_manager/refresh_worker.rb @@ -1,7 +1,28 @@ class ManageIQ::Providers::Vmware::CloudManager::RefreshWorker < ManageIQ::Providers::BaseManager::RefreshWorker require_nested :Runner - def self.settings_name - :ems_refresh_worker_vmware_cloud + # overriding queue_name_for_ems so PerEmsWorkerMixin picks up *all* of the + # manager types from here. + # This way, the refresher for Vmware's CloudManager will refresh *all* + # of the vCloud inventory across all managers. + class << self + def settings_name + :ems_refresh_worker_vmware_cloud + end + + def queue_name_for_ems(ems) + return ems unless ems.kind_of?(ExtManagementSystem) + combined_managers(ems).collect(&:queue_name).sort + end + + private + + def combined_managers(ems) + [ems].concat(ems.child_managers) + end + end + + # MiQ complains if this isn't defined + def queue_name_for_ems(ems) end end diff --git a/app/models/manageiq/providers/vmware/cloud_manager/refresher.rb b/app/models/manageiq/providers/vmware/cloud_manager/refresher.rb index d35024743..e741a7431 100644 --- a/app/models/manageiq/providers/vmware/cloud_manager/refresher.rb +++ b/app/models/manageiq/providers/vmware/cloud_manager/refresher.rb @@ -1,8 +1,4 @@ class ManageIQ::Providers::Vmware::CloudManager::Refresher < ManageIQ::Providers::BaseManager::Refresher - def parse_legacy_inventory(ems) - ManageIQ::Providers::Vmware::CloudManager::RefreshParser.ems_inv_to_hashes(ems, refresher_options) - end - def save_inventory(ems, target, hashes) super EmsRefresh.queue_refresh(ems.network_manager) diff --git a/app/models/manageiq/providers/vmware/inventory/collector.rb b/app/models/manageiq/providers/vmware/inventory/collector.rb index f79c57cf1..69c9f8cc1 100644 --- a/app/models/manageiq/providers/vmware/inventory/collector.rb +++ b/app/models/manageiq/providers/vmware/inventory/collector.rb @@ -1,5 +1,6 @@ class ManageIQ::Providers::Vmware::Inventory::Collector < ManageIQ::Providers::Inventory::Collector require_nested :CloudManager + require_nested :NetworkManager def initialize(_manager, _target) super diff --git a/app/models/manageiq/providers/vmware/inventory/collector/network_manager.rb b/app/models/manageiq/providers/vmware/inventory/collector/network_manager.rb new file mode 100644 index 000000000..6f7d97bec --- /dev/null +++ b/app/models/manageiq/providers/vmware/inventory/collector/network_manager.rb @@ -0,0 +1,175 @@ +class ManageIQ::Providers::Vmware::Inventory::Collector::NetworkManager < ManageIQ::Providers::Vmware::Inventory::Collector + VappNetwork = Struct.new(:id, :name, :type, :is_shared, :gateway, :dns1, :dns2, :netmask, :enabled, :dhcp_enabled, :orchestration_stack) + + def initialize(_manager, _target) + super + + initialize_network_inventory_sources + end + + def initialize_network_inventory_sources + @vdc_networks = [] + @vdc_networks_idx = {} + @vapp_networks = [] + @network_ports = [] + @routers = [] + @network_name_mapping = {} + end + + def orgs + return @orgs if @orgs.any? + @orgs = connection.organizations || [] + end + + def org + orgs.first + end + + def vdc_networks + return @vdc_networks if @vdc_networks.any? + @vdc_networks = org.networks || [] + @vdc_networks_idx = @vdc_networks.index_by(&:id) + + @vdc_networks + end + + def routers + return @routers if @routers.any? + + # Routers can't be independently collected, we have to get vapp_networks first + vapp_networks + + @routers + end + + def vapp_networks + return @vapp_networks if @vapp_networks.any? + @vapp_networks = manager.orchestration_stacks.each_with_object([]) do |stack, res| + fetch_network_configurations_for_vapp(stack.ems_ref).map do |net_conf| + # 'none' is special network placeholder that we must ignore + next if net_conf[:networkName] == 'none' + + network_id = network_id_from_links(net_conf) + if (vdc_net = corresponding_vdc_network(net_conf, @vdc_networks_idx)) + memorize_network_name_mapping(stack.ems_ref, vdc_net.name, vdc_net.id) + else + memorize_network_name_mapping(stack.ems_ref, net_conf[:networkName], network_id) + res << build_vapp_network(stack, network_id, net_conf) + + # routers connecting vApp networks to VDC networks + if (parent_net = parent_vdc_network(net_conf, @vdc_networks_idx)) + @routers << { + :net_conf => net_conf, + :network_id => network_id, + :parent_net => parent_net + } + end + end + end + end + end + + def network_ports + return @network_ports if @network_ports.any? + @network_ports = manager.vms.each_with_object([]) do |vm, res| + fetch_nic_configurations_for_vm(vm.ems_ref).each do |nic| + next unless nic[:IsConnected] + nic[:vm] = vm + res << nic + end + end + end + + def read_network_name_mapping(vapp_id, network_name) + @network_name_mapping.dig(vapp_id, network_name) + end + + private + + # Utility + + def build_vapp_network(vapp, network_id, net_conf) + n = VappNetwork.new(network_id) + n.name = vapp_network_name(net_conf[:networkName], vapp) + n.orchestration_stack = vapp + n.is_shared = false + n.type = 'application/vnd.vmware.vcloud.vAppNetwork+xml' + Array.wrap(net_conf.dig(:Configuration, :IpScopes)).each do |ip_scope| + n.gateway = ip_scope.dig(:IpScope, :Gateway) + n.netmask = ip_scope.dig(:IpScope, :Netmask) + n.enabled = ip_scope.dig(:IpScope, :IsEnabled) + end + Array.wrap(net_conf.dig(:Configuration, :Features)).each do |feature| + if feature[:DhcpService] + n.dhcp_enabled = feature.dig(:DhcpService, :IsEnabled) + end + end + n + end + + def vapp_network_name(name, vapp) + "#{name} (#{vapp.name})" + end + + # vCD API does not provide us with vApp network IDs for some reason. Luckily it provides + # "Links" section whith API link to edit network page and network ID is part of this link. + def network_id_from_links(data) + return unless data[:Link] + links = Array.wrap(data[:Link]) + links.each do |link| + m = /.*\/network\/(?[^\/]+)\/.*/.match(link[:href]) + return m[:id] unless m.nil? || m[:id].nil? + end + nil + end + + + # Detect when network configuration as reported by vapp is actually a VDC network. + # In such cases vCD reports duplicate of VDC networks (all the same, only ID is different) + # instead the original one, which would result in duplicate entries in the VMDB. When the + # function above returns not nil, such network was detected. The returned value is then the + # actual VDC network specification. + def corresponding_vdc_network(net_conf, vdc_networks) + if net_conf.dig(:networkName) == net_conf.dig(:Configuration, :ParentNetwork, :name) + parent_vdc_network(net_conf, vdc_networks) + end + end + + def parent_vdc_network(net_conf, vdc_networks) + vdc_networks[net_conf.dig(:Configuration, :ParentNetwork, :id)] + end + + # Remember network id for given network name. Generally network names are not unique, + # but inside vapp network specification they are. Therefore we must remember what network + # id was listed for given network name in corresponding vapp in order to be able to later + # hook VM to the appropriate network (VM only reports network name, without network ID...). + def memorize_network_name_mapping(vapp_id, network_name, network_id) + @network_name_mapping[vapp_id] ||= {} + @network_name_mapping[vapp_id][network_name] = network_id + end + + # Fetch vapp network configuration via vCD API. This call is implemented in Fog, but it's not + # managed, therefore we must handle errors by ourselves. + def fetch_network_configurations_for_vapp(vapp_id) + begin + # fog-vcloud-director now uses a more user-friendly parser that yields vApp instance. However, vapp networking + # is not parsed there yet so we need to fallback to basic ToHashDocument parser that only converts XML to hash. + # TODO(miha-plesko): update default parser to do the XML parsing for us. + data = @connection.get_vapp(vapp_id, :parser => Fog::ToHashDocument).body + rescue Fog::VcloudDirector::Errors::ServiceError => e + $vcloud_log.error("#{log_header} could not fetch network configuration for vapp #{vapp_id}: #{e}") + return [] + end + Array.wrap(data.dig(:NetworkConfigSection, :NetworkConfig)) + end + + def fetch_nic_configurations_for_vm(vm_id) + begin + data = @connection.get_network_connection_system_section_vapp(vm_id).body + rescue Fog::VcloudDirector::Errors::ServiceError => e + $vcloud_log.error("#{log_header} could not fetch NIC configuration for vm #{vm_id}: #{e}") + return [] + end + Array.wrap(data[:NetworkConnection]) + end +end diff --git a/app/models/manageiq/providers/vmware/inventory/parser.rb b/app/models/manageiq/providers/vmware/inventory/parser.rb index 6f4cf2ec1..280c84607 100644 --- a/app/models/manageiq/providers/vmware/inventory/parser.rb +++ b/app/models/manageiq/providers/vmware/inventory/parser.rb @@ -1,5 +1,6 @@ class ManageIQ::Providers::Vmware::Inventory::Parser < ManageIQ::Providers::Inventory::Parser require_nested :CloudManager + require_nested :NetworkManager # 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) diff --git a/app/models/manageiq/providers/vmware/inventory/parser/network_manager.rb b/app/models/manageiq/providers/vmware/inventory/parser/network_manager.rb new file mode 100644 index 000000000..54471cc02 --- /dev/null +++ b/app/models/manageiq/providers/vmware/inventory/parser/network_manager.rb @@ -0,0 +1,154 @@ +class ManageIQ::Providers::Vmware::Inventory::Parser::NetworkManager < ManageIQ::Providers::Vmware::Inventory::Parser + def parse + networks + network_subnets + network_routers + network_ports + floating_ips + end + + private + + 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].orchestration_stack.ems_ref, 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].orchestration_stack.ems_ref, nic_data[:network]) + #network = @data_index.fetch_path(:cloud_networks, network_id) + + 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].ems_ref) + } + + persister.floating_ips.build(new_result) + end + + def subnet_id(network) + "subnet-#{network.id}" + end + + def port_id(nic_data) + "#{nic_data[:vm].ems_ref}#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 +end diff --git a/app/models/manageiq/providers/vmware/inventory/persister.rb b/app/models/manageiq/providers/vmware/inventory/persister.rb index ce0b31d3e..62e27c81c 100644 --- a/app/models/manageiq/providers/vmware/inventory/persister.rb +++ b/app/models/manageiq/providers/vmware/inventory/persister.rb @@ -1,3 +1,4 @@ class ManageIQ::Providers::Vmware::Inventory::Persister < ManageIQ::Providers::Inventory::Persister require_nested :CloudManager + require_nested :NetworkManager end diff --git a/app/models/manageiq/providers/vmware/inventory/persister/definitions/network_collections.rb b/app/models/manageiq/providers/vmware/inventory/persister/definitions/network_collections.rb new file mode 100644 index 000000000..4845af54b --- /dev/null +++ b/app/models/manageiq/providers/vmware/inventory/persister/definitions/network_collections.rb @@ -0,0 +1,26 @@ +module ManageIQ::Providers::Vmware::Inventory::Persister::Definitions::NetworkCollections + extend ActiveSupport::Concern + + def initialize_network_inventory_collections + %i(cloud_networks + cloud_subnets + cloud_subnet_network_ports + floating_ips + network_routers + network_ports + security_groups + load_balancers + load_balancer_pools + load_balancer_pool_members + load_balancer_pool_member_pools + load_balancer_listeners + load_balancer_listener_pools + load_balancer_health_checks + load_balancer_health_check_members).each do |name| + + add_collection(network, name) do |builder| + builder.add_properties(:parent => manager.network_manager) if targeted? + end + end + end +end diff --git a/app/models/manageiq/providers/vmware/inventory/persister/network_manager.rb b/app/models/manageiq/providers/vmware/inventory/persister/network_manager.rb new file mode 100644 index 000000000..a7c76ee24 --- /dev/null +++ b/app/models/manageiq/providers/vmware/inventory/persister/network_manager.rb @@ -0,0 +1,23 @@ +class ManageIQ::Providers::Vmware::Inventory::Persister::NetworkManager < ManageIQ::Providers::Vmware::Inventory::Persister + include ::ManageIQ::Providers::Vmware::Inventory::Persister::Definitions::CloudCollections + include ::ManageIQ::Providers::Vmware::Inventory::Persister::Definitions::NetworkCollections + + def initialize_inventory_collections + initialize_cloud_inventory_collections + initialize_network_inventory_collections + end + + def initialize_cloud_inventory_collections + %i(availability_zones + orchestration_stacks + vms).each do |name| + + add_collection(cloud, name) do |builder| + builder.add_properties( + :parent => manager.parent_manager, + :strategy => :local_db_cache_all + ) + end + end + end +end diff --git a/app/models/manageiq/providers/vmware/network_manager.rb b/app/models/manageiq/providers/vmware/network_manager.rb index f072434e2..3b7944523 100644 --- a/app/models/manageiq/providers/vmware/network_manager.rb +++ b/app/models/manageiq/providers/vmware/network_manager.rb @@ -2,8 +2,6 @@ class ManageIQ::Providers::Vmware::NetworkManager < ManageIQ::Providers::Network require_nested :CloudNetwork require_nested :CloudSubnet require_nested :NetworkPort - require_nested :RefreshParser - require_nested :RefreshWorker require_nested :Refresher include ManageIQ::Providers::Vmware::ManagerAuthMixin @@ -24,6 +22,7 @@ class ManageIQ::Providers::Vmware::NetworkManager < ManageIQ::Providers::Network :default_endpoint, :endpoints, :provider_region, + :snapshots, :to => :parent_manager, :allow_nil => true @@ -42,4 +41,8 @@ def self.hostname_required? def description @description ||= "VMware Cloud Network".freeze end + + def inventory_object_refresh? + true + end end diff --git a/app/models/manageiq/providers/vmware/network_manager/refresh_parser.rb b/app/models/manageiq/providers/vmware/network_manager/refresh_parser.rb deleted file mode 100644 index c643acbea..000000000 --- a/app/models/manageiq/providers/vmware/network_manager/refresh_parser.rb +++ /dev/null @@ -1,355 +0,0 @@ -module ManageIQ::Providers - class Vmware::NetworkManager::RefreshParser - include ManageIQ::Providers::Vmware::RefreshHelperMethods - VappNetwork = Struct.new(:id, :name, :type, :is_shared, :gateway, :dns1, :dns2, :netmask, :enabled, :dhcp_enabled, :orchestration_stack) - - def initialize(ems, options = nil) - @ems = ems - @options = options || {} - @data = {} - @data_index = {} - @inv = Hash.new { |h, k| h[k] = [] } - @network_name_mapping = {} - end - - def ems_inv_to_hashes - $vcloud_log.info("#{log_header} Collecting data for EMS name: [#{@ems.name}] id: [#{@ems.id}]...") - - connect - - get_org - get_vdc_networks - get_vapp_networks - get_network_ports - - $vcloud_log.info("#{log_header}...Complete") - - @data - end - - private - - def connect - @connection ||= @ems.connect - end - - def get_org - @org = @connection.organizations.first - end - - def get_vdc_networks - @inv[:vdc_networks] = @org.networks || [] - @inv[:vdc_networks_idx] = @inv[:vdc_networks].index_by(&:id) - - process_collection(@inv[:vdc_networks], :cloud_networks) { |n| parse_network(n) } - process_collection(@inv[:vdc_networks], :cloud_subnets, false) { |n| parse_network_subnet(n) } - - $vcloud_log.info("#{log_header} Fetched #{@inv[:vdc_networks].count} VDC networks") - end - - def get_vapp_networks - @inv[:vapp_networks] = [] - @inv[:routers] = [] - - @ems.orchestration_stacks.each do |stack| - fetch_network_configurations_for_vapp(stack.ems_ref).map do |net_conf| - # 'none' is special network placeholder that we must ignore - next if net_conf[:networkName] == 'none' - - $vcloud_log.debug("#{log_header} processing net_conf for vapp #{stack.ems_ref}: #{net_conf}") - network_id = network_id_from_links(net_conf) - $vcloud_log.debug("#{log_header} calculated vApp network id: #{network_id}") - if (vdc_net = corresponding_vdc_network(net_conf, @inv[:vdc_networks_idx])) - $vcloud_log.debug("#{log_header} skipping VDC network duplicate") - memorize_network_name_mapping(stack.ems_ref, vdc_net.name, vdc_net.id) - else - memorize_network_name_mapping(stack.ems_ref, net_conf[:networkName], network_id) - @inv[:vapp_networks] << build_vapp_network(stack, network_id, net_conf) - - # routers connecting vApp networks to VDC networks - if (parent_net = parent_vdc_network(net_conf, @inv[:vdc_networks_idx])) - $vcloud_log.debug("#{log_header} connecting router to parent: #{parent_net}") - @inv[:routers] << { - :net_conf => net_conf, - :network_id => network_id, - :parent_net => parent_net - } - end - end - end - end - - process_collection(@inv[:vapp_networks], :cloud_networks) { |n| parse_network(n) } - process_collection(@inv[:vapp_networks], :cloud_subnets, false) { |n| parse_network_subnet(n) } - process_collection(@inv[:routers], :network_routers) { |r| parse_network_router(r) } - - $vcloud_log.info("#{log_header} Fetched #{@inv[:vapp_networks].count} vApp networks") - $vcloud_log.info("#{log_header} Fetched #{@inv[:routers].count} network routers") - end - - def get_network_ports - @inv[:nics] = [] - @ems.vms.each do |vm| - fetch_nic_configurations_for_vm(vm.ems_ref).each do |nic| - next unless nic[:IsConnected] - $vcloud_log.debug("#{log_header} processing NIC configuration for vm #{vm.ems_ref}: #{nic}") - nic[:vm] = vm - @inv[:nics] << nic - end - end - - process_collection(@inv[:nics], :network_ports) { |n| parse_network_port(n) } - process_collection(@inv[:nics], :floating_ips) { |n| parse_floating_ip(n) } - - $vcloud_log.info("#{log_header} Fetched #{@inv[:nics].count} network ports") - $vcloud_log.info("#{log_header} Fetched #{@data[:floating_ips].count} floating ips") - end - - # Parsing - - def parse_network(network) - uid = network.id - network_type = network.type.include?("vcloud.orgNetwork") ? - self.class.cloud_network_vdc_type : self.class.cloud_network_vapp_type - - new_result = { - :name => network.name, - :ems_ref => uid, - :shared => network.is_shared, - :type => network_type, - :cloud_subnets => [], - :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) - - return uid, new_result - end - - def parse_network_subnet(network) - uid = subnet_id(network) - new_result = { - :name => network.name, - :ems_ref => uid, - :gateway => network.gateway, - :dns_nameservers => [network.dns1, network.dns2].compact, - :type => self.class.cloud_subnet_type, - :network_ports => [] - } - new_result[:cidr] = to_cidr(network.gateway, network.netmask) - new_result[:dhcp_enabled] = network.dhcp_enabled if network.respond_to?(:dhcp_enabled) - - # assign myself to the network - @data_index.fetch_path(:cloud_networks, network.id)[:cloud_subnets] << new_result - - return uid, new_result - end - - def parse_network_router(router) - parent_id = router[:parent_net].id - uid = "#{router[:network_id]}---#{parent_id}" - new_result = { - :type => self.class.network_router_type, - :name => "Router #{router[:parent_net].name} -> #{router.dig(:net_conf, :networkName)}", - :ems_ref => uid, - :cloud_network => @data_index.fetch_path(:cloud_networks, parent_id), - :cloud_subnets => [] - } - - # assign myself to the vapp network - @data_index.store_path(:cloud_subnets, "subnet-#{router[:network_id]}", :network_router, new_result) - - return uid, new_result - end - - def parse_network_port(nic_data) - uid = port_id(nic_data) - vm_uid = nic_data[:vm].id - - new_result = { - :type => self.class.network_port_type, - :name => "NIC##{nic_data[:NetworkConnectionIndex]}", - :ems_ref => uid, - :device_ref => vm_uid, - :device => nic_data[:vm], - :mac_address => nic_data.dig(:MACAddress) - } - - network_id = read_network_name_mapping(nic_data[:vm].orchestration_stack.ems_ref, nic_data.dig(:network)) - network = @data_index.fetch_path(:cloud_networks, network_id) - - unless network.nil? - subnet = network[:cloud_subnets].first - cloud_subnet_network_port = { - :address => nic_data[:IpAddress], - :cloud_subnet => subnet - } - new_result[:cloud_subnet_network_ports] = [cloud_subnet_network_port] - end - - return uid, new_result - end - - def parse_floating_ip(nic_data) - floating_ip = nic_data[:ExternalIpAddress] - return unless floating_ip - - uid = floating_ip_id(nic_data) - network_id = read_network_name_mapping(nic_data[:vm].orchestration_stack.ems_ref, nic_data[:network]) - network = @data_index.fetch_path(:cloud_networks, network_id) - - new_result = { - :type => self.class.floating_ip_type, - :ems_ref => uid, - :address => floating_ip, - :fixed_ip_address => floating_ip, - :cloud_network => network, - :network_port => @data_index.fetch_path(:network_ports, port_id(nic_data)), - :vm => nic_data[:vm] - } - - return uid, new_result - end - - # Utility - - def build_vapp_network(vapp, network_id, net_conf) - n = VappNetwork.new(network_id) - n.name = vapp_network_name(net_conf[:networkName], vapp) - n.orchestration_stack = vapp - n.is_shared = false - n.type = 'application/vnd.vmware.vcloud.vAppNetwork+xml' - Array.wrap(net_conf.dig(:Configuration, :IpScopes)).each do |ip_scope| - n.gateway = ip_scope.dig(:IpScope, :Gateway) - n.netmask = ip_scope.dig(:IpScope, :Netmask) - n.enabled = ip_scope.dig(:IpScope, :IsEnabled) - end - Array.wrap(net_conf.dig(:Configuration, :Features)).each do |feature| - if feature[:DhcpService] - n.dhcp_enabled = feature.dig(:DhcpService, :IsEnabled) - end - end - n - end - - def subnet_id(network) - "subnet-#{network.id}" - end - - def vapp_network_name(name, vapp) - "#{name} (#{vapp.name})" - end - - def port_id(nic_data) - "#{nic_data[:vm].ems_ref}#NIC##{nic_data[:NetworkConnectionIndex]}" - end - - def floating_ip_id(nic_data) - "floating_ip-#{port_id(nic_data)}" - end - - # vCD API does not provide us with vApp network IDs for some reason. Luckily it provides - # "Links" section whith API link to edit network page and network ID is part of this link. - def network_id_from_links(data) - return unless data[:Link] - links = Array.wrap(data[:Link]) - links.each do |link| - m = /.*\/network\/(?[^\/]+)\/.*/.match(link[:href]) - return m[:id] unless m.nil? || m[:id].nil? - end - nil - end - - # Detect when network configuration as reported by vapp is actually a VDC network. - # In such cases vCD reports duplicate of VDC networks (all the same, only ID is different) - # instead the original one, which would result in duplicate entries in the VMDB. When the - # function above returns not nil, such network was detected. The returned value is then the - # actual VDC network specification. - def corresponding_vdc_network(net_conf, vdc_networks) - if net_conf.dig(:networkName) == net_conf.dig(:Configuration, :ParentNetwork, :name) - parent_vdc_network(net_conf, vdc_networks) - end - end - - def parent_vdc_network(net_conf, vdc_networks) - vdc_networks[net_conf.dig(:Configuration, :ParentNetwork, :id)] - end - - # Remember network id for given network name. Generally network names are not unique, - # but inside vapp network specification they are. Therefore we must remember what network - # id was listed for given network name in corresponding vapp in order to be able to later - # hook VM to the appropriate network (VM only reports network name, without network ID...). - def memorize_network_name_mapping(vapp_id, network_name, network_id) - @network_name_mapping[vapp_id] ||= {} - @network_name_mapping[vapp_id][network_name] = network_id - end - - def read_network_name_mapping(vapp_id, network_name) - @network_name_mapping.dig(vapp_id, network_name) - 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 - - def log_header - location = caller_locations(1, 1) - location = location.first if location.kind_of?(Array) - "MIQ(#{self.class.name}.#{location.base_label})" - end - - # Additional API calls - - # Fetch vapp network configuration via vCD API. This call is implemented in Fog, but it's not - # managed, therefore we must handle errors by ourselves. - def fetch_network_configurations_for_vapp(vapp_id) - begin - # fog-vcloud-director now uses a more user-friendly parser that yields vApp instance. However, vapp networking - # is not parsed there yet so we need to fallback to basic ToHashDocument parser that only converts XML to hash. - # TODO(miha-plesko): update default parser to do the XML parsing for us. - data = @connection.get_vapp(vapp_id, :parser => Fog::ToHashDocument).body - rescue Fog::VcloudDirector::Errors::ServiceError => e - $vcloud_log.error("#{log_header} could not fetch network configuration for vapp #{vapp_id}: #{e}") - return [] - end - Array.wrap(data.dig(:NetworkConfigSection, :NetworkConfig)) - end - - def fetch_nic_configurations_for_vm(vm_id) - begin - data = @connection.get_network_connection_system_section_vapp(vm_id).body - rescue Fog::VcloudDirector::Errors::ServiceError => e - $vcloud_log.error("#{log_header} could not fetch NIC configuration for vm #{vm_id}: #{e}") - return [] - end - Array.wrap(data[:NetworkConnection]) - end - - class << self - def cloud_network_vdc_type - "ManageIQ::Providers::Vmware::NetworkManager::CloudNetwork::OrgVdcNet" - end - - def cloud_network_vapp_type - "ManageIQ::Providers::Vmware::NetworkManager::CloudNetwork::VappNet" - end - - def cloud_subnet_type - "ManageIQ::Providers::Vmware::NetworkManager::CloudSubnet" - end - - def network_router_type - "ManageIQ::Providers::Vmware::NetworkManager::NetworkRouter" - end - - def network_port_type - "ManageIQ::Providers::Vmware::NetworkManager::NetworkPort" - end - - def floating_ip_type - "ManageIQ::Providers::Vmware::NetworkManager::FloatingIp" - end - end - end -end diff --git a/app/models/manageiq/providers/vmware/network_manager/refresh_worker.rb b/app/models/manageiq/providers/vmware/network_manager/refresh_worker.rb deleted file mode 100644 index 3f87af4fe..000000000 --- a/app/models/manageiq/providers/vmware/network_manager/refresh_worker.rb +++ /dev/null @@ -1,7 +0,0 @@ -class ManageIQ::Providers::Vmware::NetworkManager::RefreshWorker < ::MiqEmsRefreshWorker - require_nested :Runner - - def self.settings_name - :ems_refresh_worker_vmware_cloud - end -end diff --git a/app/models/manageiq/providers/vmware/network_manager/refresh_worker/runner.rb b/app/models/manageiq/providers/vmware/network_manager/refresh_worker/runner.rb deleted file mode 100644 index 73f82356c..000000000 --- a/app/models/manageiq/providers/vmware/network_manager/refresh_worker/runner.rb +++ /dev/null @@ -1,2 +0,0 @@ -class ManageIQ::Providers::Vmware::NetworkManager::RefreshWorker::Runner < ManageIQ::Providers::BaseManager::RefreshWorker::Runner -end diff --git a/app/models/manageiq/providers/vmware/network_manager/refresher.rb b/app/models/manageiq/providers/vmware/network_manager/refresher.rb index 8f7f5faec..f1fb8deb4 100644 --- a/app/models/manageiq/providers/vmware/network_manager/refresher.rb +++ b/app/models/manageiq/providers/vmware/network_manager/refresher.rb @@ -1,8 +1,4 @@ class ManageIQ::Providers::Vmware::NetworkManager::Refresher < ManageIQ::Providers::BaseManager::Refresher - def parse_legacy_inventory(ems) - ManageIQ::Providers::Vmware::NetworkManager::RefreshParser.ems_inv_to_hashes(ems, refresher_options) - end - def post_process_refresh_classes [] end diff --git a/spec/models/manageiq/providers/vmware/cloud_manager/refresher_spec.rb b/spec/models/manageiq/providers/vmware/cloud_manager/refresher_spec.rb index 6d720b5ab..503a87b94 100644 --- a/spec/models/manageiq/providers/vmware/cloud_manager/refresher_spec.rb +++ b/spec/models/manageiq/providers/vmware/cloud_manager/refresher_spec.rb @@ -1,28 +1,21 @@ describe ManageIQ::Providers::Vmware::CloudManager::Refresher do ALL_REFRESH_SETTINGS = [ { - :inventory_object_refresh => false - }, - { - :inventory_object_refresh => true, :inventory_collections => { :saver_strategy => :default, }, }, { - :inventory_object_refresh => true, :inventory_collections => { :saver_strategy => :batch, :use_ar_object => true, }, }, { - :inventory_object_refresh => true, :inventory_collections => { :saver_strategy => :batch, :use_ar_object => false, }, }, { :inventory_object_saving_strategy => :recursive, - :inventory_object_refresh => true } ].freeze diff --git a/spec/models/manageiq/providers/vmware/network_manager/refresh_parser_spec.rb b/spec/models/manageiq/providers/vmware/network_manager/refresh_parser_spec.rb deleted file mode 100644 index 72809e7bd..000000000 --- a/spec/models/manageiq/providers/vmware/network_manager/refresh_parser_spec.rb +++ /dev/null @@ -1,323 +0,0 @@ -describe ManageIQ::Providers::Vmware::NetworkManager::RefreshParser do - describe "Utility" do - before do - allow($vcloud_log).to receive(:debug) - allow($vcloud_log).to receive(:error) - allow($vcloud_log).to receive(:info) - end - let(:ems) do - FactoryBot.create(:ems_vmware_cloud).tap do |ems| - ems.authentications << FactoryBot.create(:authentication, :status => "Valid") - end - end - let(:refresher) { described_class.new(ems) } - let(:network) { double(:id => 'id1', :name => 'name1') } - - describe 'build_vapp_network' do - [ - { - :name => 'only network name', - :net_conf => { :networkName => 'network-name' }, - :expected => { :name => 'network-name (vapp-name)' } - }, - { - :name => 'empty IpScopes section', - :net_conf => { - :networkName => 'network-name', - :Configuration => { :IpScopes => {}} - }, - :expected => { :name => 'network-name (vapp-name)' } - }, - { - :name => 'valid IpScopes section - hash', - :net_conf => { - :networkName => 'network-name', - :Configuration => { - :IpScopes => { :IpScope => { :Gateway => '1.1.1.1', :Netmask => '2.2.2.2', :IsEnabled => true }} - } - }, - :expected => { - :name => 'network-name (vapp-name)', - :gateway => '1.1.1.1', - :netmask => '2.2.2.2', - :enabled => true - } - }, - { - :name => 'valid IpScopes section - list', - :net_conf => { - :networkName => 'network-name', - :Configuration => { - :IpScopes => [ - { :IpScope => { :Gateway => '1.1.1.1', :Netmask => '2.2.2.2', :IsEnabled => true }} - ] - } - }, - :expected => { - :name => 'network-name (vapp-name)', - :gateway => '1.1.1.1', - :netmask => '2.2.2.2', - :enabled => true - } - }, - { - :name => 'with Features section', - :net_conf => { - :networkName => 'network-name', - :Configuration => { - :Features => { :DhcpService => { :IsEnabled => true }} - } - }, - :expected => { - :name => 'network-name (vapp-name)', - :dhcp_enabled => true - } - } - ].each do |test_case| - it "build_vapp_network - #{test_case[:name]}" do - test_case[:expected][:type] ||= 'application/vnd.vmware.vcloud.vAppNetwork+xml' - test_case[:expected][:is_shared] ||= false - test_case[:expected][:gateway] ||= nil - test_case[:expected][:netmask] ||= nil - test_case[:expected][:enabled] ||= nil - test_case[:expected][:dhcp_enabled] ||= nil - vapp = double(:name => 'vapp-name') - n = refresher.send(:build_vapp_network, vapp, 'network-id', test_case[:net_conf]) - expect(n).to have_attributes(test_case[:expected]) - end - end - end - - describe 'network_id_from_links' do - [ - { - :name => 'regular case', - :data => { - :Link => [ - { - :rel => 'repair', - :href => 'https://vmwarecloudhost/api/admin/network/3d3da9a8-1db1-40cd-9fff-c770d6411486/action/reset' - }, - { - :rel => 'syncSyslogSettings', - :href => 'https://vmwarecloudhost/api/admin/network/3d3da9a8-1db1-40cd-9fff-c770d6411486/action/syncSyslogServerSettings', - :type => 'application/vnd.vmware.vcloud.task+xml' - } - ] - }, - :expected => '3d3da9a8-1db1-40cd-9fff-c770d6411486' - }, - { - :name => 'missing Link section', - :data => {}, - :expected => nil - }, - { - :name => 'Link section is not array', - :data => { - :Link => { - :rel => 'repair', - :href => 'https://vmwarecloudhost/api/admin/network/3d3da9a8-1db1-40cd-9fff-c770d6411486/action/reset' - } - }, - :expected => '3d3da9a8-1db1-40cd-9fff-c770d6411486' - }, - { - :name => 'first link does not match', - :data => { - :Link => [ - { - :rel => 'repair', - :href => 'https://vmwarecloudhost/api/admin/not-network/123/stop' - }, - { - :rel => 'syncSyslogSettings', - :href => 'https://vmwarecloudhost/api/admin/network/3d3da9a8-1db1-40cd-9fff-c770d6411486/action/syncSyslogServerSettings', - :type => 'application/vnd.vmware.vcloud.task+xml' - } - ] - }, - :expected => '3d3da9a8-1db1-40cd-9fff-c770d6411486' - }, - { - :name => 'no link matches', - :data => { - :Link => [ - { - :rel => 'repair', - :href => 'https://vmwarecloudhost/api/admin/not-network/123/action1' - }, - { - :rel => 'syncSyslogSettings', - :href => 'https://vmwarecloudhost/api/admin/not-network/123/action2', - } - ] - }, - :expected => nil - }, - ].each do |test_case| - it "network_id_from_links - #{test_case[:name]}" do - expect(refresher.send(:network_id_from_links, test_case[:data])).to eq(test_case[:expected]) - end - end - end - - describe 'parent_vdc_network' do - [ - { - :name => 'regular case', - :net_conf => { :Configuration => { :ParentNetwork => { :id => 'parent1' }}}, - :vdc_networks => { 'parent1' => 'OK' }, - :expected => 'OK' - }, - { - :name => 'no parent specified', - :net_conf => { :Configuration => {}}, - :vdc_networks => {}, - :expected => nil - }, - ].each do |test_case| - it "parent_vdc_network - #{test_case[:name]}" do - expect(refresher.send(:parent_vdc_network, test_case[:net_conf], test_case[:vdc_networks])) - .to eq(test_case[:expected]) - end - end - end - - describe 'corresponding_vdc_network' do - [ - { - :name => 'regular case - is corresponding', - :net_conf => { - :networkName => 'same name', - :Configuration => { :ParentNetwork => { :id => 'parent1', :name => 'same name' }} - }, - :vdc_networks => { 'parent1' => 'OK' }, - :expected => 'OK' - }, - { - :name => 'regular case - not corresponding', - :net_conf => { - :networkName => 'different name 1', - :Configuration => { :ParentNetwork => { :id => 'parent1', :name => 'different name 2' }} - }, - :vdc_networks => { 'parent1' => 'OK' }, - :expected => nil - }, - { - :name => 'is corresponding, but network not found', - :net_conf => { - :networkName => 'same name', - :Configuration => { :ParentNetwork => { :id => 'parent1', :name => 'same name' }} - }, - :vdc_networks => {}, - :expected => nil - } - ].each do |test_case| - it "parent_vdc_network - #{test_case[:name]}" do - expect(refresher.send(:corresponding_vdc_network, test_case[:net_conf], test_case[:vdc_networks])) - .to eq(test_case[:expected]) - end - end - end - - describe 'to_cidr' do - [ - { - :name => 'regular case', - :address => '0.0.0.0', - :netmask => '128.0.0.0', - :expected => '0.0.0.0/1' - }, - { - :name => 'missing address', - :address => nil, - :netmask => '128.0.0.0', - :expected => nil - }, - { - :name => 'empty address', - :address => '', - :netmask => '128.0.0.0', - :expected => nil - }, - { - :name => 'missing netmask', - :address => '0.0.0.0', - :netmask => nil, - :expected => nil - }, - { - :name => 'empty netmask', - :address => '0.0.0.0', - :netmask => '', - :expected => nil - } - ].each do |test_case| - it "to_cidr - #{test_case[:name]}" do - expect(refresher.send(:to_cidr, test_case[:address], test_case[:netmask])).to eq(test_case[:expected]) - end - end - end - - describe 'fetch_network_configurations_for_vapp' do - [ - { - :name => 'regular case', - :data => { :NetworkConfigSection => { :NetworkConfig => 'DATA'}}, - :expected => ['DATA'] - }, - { - :name => 'regular case - list', - :data => { :NetworkConfigSection => { :NetworkConfig => ['DATA']}}, - :expected => ['DATA'] - }, - { - :name => 'error response', - :data => -> { raise Fog::VcloudDirector::Compute::Forbidden, 'simulated error' }, - :expected => [] - } - ].each do |test_case| - it "to_list - #{test_case[:name]}" do - mock_api_response(:get_vapp, test_case[:data]) - expect(refresher.send(:fetch_network_configurations_for_vapp, 'vapp-id')).to eq(test_case[:expected]) - end - end - end - - describe 'fetch_nic_configurations_for_vm' do - [ - { - :name => 'regular case', - :data => { :NetworkConnection => 'DATA' }, - :expected => ['DATA'] - }, - { - :name => 'regular case - list', - :data => { :NetworkConnection => ['DATA'] }, - :expected => ['DATA'] - }, - { - :name => 'error response', - :data => -> { raise Fog::VcloudDirector::Compute::Forbidden, 'simulated error' }, - :expected => [] - } - ].each do |test_case| - it "to_list - #{test_case[:name]}" do - mock_api_response(:get_network_connection_system_section_vapp, test_case[:data]) - expect(refresher.send(:fetch_nic_configurations_for_vm, 'vm-id')).to eq(test_case[:expected]) - end - end - end - - def mock_api_response(fun_name, response) - d = double('api-mock') - if response.respond_to?(:call) - allow(d).to receive(fun_name) { response.call } - else - allow(d).to receive(fun_name).with(any_args).and_return(double(:body => response)) - end - refresher.instance_variable_set(:@connection, d) - end - end -end diff --git a/spec/models/manageiq/providers/vmware/network_manager/refresher_spec.rb b/spec/models/manageiq/providers/vmware/network_manager/refresher_spec.rb index 773f37c6d..1a0744603 100644 --- a/spec/models/manageiq/providers/vmware/network_manager/refresher_spec.rb +++ b/spec/models/manageiq/providers/vmware/network_manager/refresher_spec.rb @@ -237,7 +237,7 @@ def refresh_network_manager(cassete) 2.times do # Run twice to verify that a second run with existing data does not change anything @ems.reload @ems_network.reload - VCR.use_cassette(cassete) do + VCR.use_cassette(cassete, :allow_unused_http_interactions => true) do EmsRefresh.refresh(@ems) EmsRefresh.refresh(@ems_network) end