From 79deb6ac0ae8d7670494912dd6a2cffa1e0fc2ff Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 7 Jun 2018 18:01:10 +0200 Subject: [PATCH] WIP: first auto-bond implementation refs #23851 --- app/models/setting/discovered.rb | 1 + .../foreman_discovery/host_converter.rb | 36 ++++++ .../foreman_discovery/lldp_neighbors.rb | 67 +++++++++++ test/test_helper_discovery.rb | 1 + test/unit/facts_with_lldp.json | 107 ++++++++++++++++++ test/unit/facts_with_lldp_bond_candidate.json | 107 ++++++++++++++++++ test/unit/host_discovered_test.rb | 64 ++++++++++- test/unit/lldp_neighbors_test.rb | 56 +++++++++ 8 files changed, 438 insertions(+), 1 deletion(-) create mode 100644 app/services/foreman_discovery/lldp_neighbors.rb create mode 100644 test/unit/facts_with_lldp.json create mode 100644 test/unit/facts_with_lldp_bond_candidate.json create mode 100644 test/unit/lldp_neighbors_test.rb diff --git a/app/models/setting/discovered.rb b/app/models/setting/discovered.rb index 42b0fffe..4d845fa9 100644 --- a/app/models/setting/discovered.rb +++ b/app/models/setting/discovered.rb @@ -17,6 +17,7 @@ def self.load_defaults Setting.transaction do [ self.set('discovery_fact', N_("Fact name to use for primary interface detection"), "discovery_bootif", N_("Interface fact")), + self.set('discovery_auto_bond', N_("Automatic bond interface (if another interface is detected on the same VLAN via LLDP)"), false, N_("Create bond interfaces")), self.set('discovery_clean_facts', N_("Clean all reported facts during provisioning (except discovery facts)"), false, N_("Clean all facts")), self.set('discovery_hostname', N_("List of facts to use for the hostname (separated by comma, first wins)"), "discovery_bootif", N_("Hostname facts")), self.set('discovery_auto', N_("Automatically provision newly discovered hosts, according to the provisioning rules"), false, N_("Auto provisioning")), diff --git a/app/services/foreman_discovery/host_converter.rb b/app/services/foreman_discovery/host_converter.rb index a469c86f..b21d8b53 100644 --- a/app/services/foreman_discovery/host_converter.rb +++ b/app/services/foreman_discovery/host_converter.rb @@ -4,6 +4,7 @@ class ForemanDiscovery::HostConverter # Record must be saved explicitly (using save! or update_attributes! or similar). # Creates shallow copy. def self.to_managed(original_host, set_managed = true, set_build = true, added_attributes = {}) + eventually_make_bond(original_host) if Setting[:discovery_auto_bond] host = original_host.becomes(::Host::Managed) host.type = 'Host::Managed' host.attributes = host.apply_inherited_attributes(added_attributes) @@ -19,6 +20,41 @@ def self.to_managed(original_host, set_managed = true, set_build = true, added_a host end + def self.eventually_make_bond(host) + primary = host.primary_interface + return if primary.nil? + return if primary.type == 'Nic::Bond' + + neighbors = ::ForemanDiscovery::LldpNeighbors + .from_facts(host.facts_hash) + .get_neighbors_by_interface(primary.identifier) + + return if neighbors.nil? + + ip = primary.ip + name = primary.name + primary.update( + :primary => false, + :provision => false, + :managed => false, + :name => nil, + :ip => nil + ) + + bond = Nic::Bond.create( + :identifier => "bond0", + :attached_devices => neighbors, + :primary => true, + :provision => true, + :name => name, + :ip => ip, + :host => host + ) + + bond.save! + host.interfaces.push bond + end + def self.set_build_clean_facts(host) # fact cleaning if Setting['discovery_clean_facts'] diff --git a/app/services/foreman_discovery/lldp_neighbors.rb b/app/services/foreman_discovery/lldp_neighbors.rb new file mode 100644 index 00000000..4614e8b5 --- /dev/null +++ b/app/services/foreman_discovery/lldp_neighbors.rb @@ -0,0 +1,67 @@ +module ForemanDiscovery + class LldpNeighbors + def set(iface, neighbor) + interfaces[iface] = neighbor + end + + def get(iface) + interfaces[iface] + end + + def list_by_pvid + list = {} + interfaces.each do |name, neighbor| + next unless neighbor.has_key? 'PVID' + vlan = neighbor['PVID'] + list[vlan] = [] unless list.has_key? vlan + list[vlan].push name + end + + list.each { |_, interfaces| interfaces.sort!} + list + end + + def interfaces + @interfaces ||= {} + end + + def get_neighbors_by_interface(ifname) + return nil unless interfaces.has_key? ifname + + interface = get(ifname) + return nil unless interface.has_key? 'PVID' + + list = [] + interfaces.each do |name, neighbor| + next unless neighbor.has_key? 'PVID' + list.push name if neighbor['PVID'] == interface['PVID'] + end + + return if list.size < 2 + list.sort + end + + def self.from_facts(facts) + neighbors = self.new + interfaces = {} + facts.keys.each do |key| + key_s = key.to_s + next unless key_s.start_with? 'lldp_neighbor_' + + property, iface = key_s[14..-1].split('_', 2) + if property == 'mngAddr' + protocol, iface = iface.split('_', 2) + property += '_' + protocol + end + interfaces[iface] = {} unless interfaces.has_key? iface + interfaces[iface][property] = facts[key] + end + + interfaces.each do |name, properties| + neighbors.set name, properties + end + + neighbors + end + end +end diff --git a/test/test_helper_discovery.rb b/test/test_helper_discovery.rb index 3c93a088..9278ded4 100644 --- a/test/test_helper_discovery.rb +++ b/test/test_helper_discovery.rb @@ -67,6 +67,7 @@ def set_default_settings FactoryBot.create(:setting, :name => 'discovery_pxegrub2_lock_template', :value => 'pxegrub2_discovery', :category => 'Setting::Discovered') FactoryBot.create(:setting, :name => 'discovery_always_rebuild_dns', :value => true, :category => 'Setting::Discovered') FactoryBot.create(:setting, :name => 'discovery_error_on_existing', :value => false, :category => 'Setting::Discovered') + FactoryBot.create(:setting, :name => 'discovery_auto_bond', :value => false, :category => 'Setting::Discovered') end def setup_hostgroup(host) diff --git a/test/unit/facts_with_lldp.json b/test/unit/facts_with_lldp.json new file mode 100644 index 00000000..20e875e1 --- /dev/null +++ b/test/unit/facts_with_lldp.json @@ -0,0 +1,107 @@ +{ + "name": "sinn1636.lan", + "facts": { + "processor3": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "productname": "IBM System x -[7870K4G]-", + "kernelmajversion": "2.6", + "kernelversion": "2.6.32", + "macaddress_vnet0": "FE:54:00:1E:45:13", + "macaddress_br181": "E4:1F:13:CC:36:58", + "macaddress_usb0": "E6:1F:13:D0:43:A3", + "macaddress_vnet1": "FE:54:00:45:E5:3E", + "rubysitedir": "/usr/lib/ruby/site_ruby/1.8", + "macaddress_br182": "E4:1F:13:CC:36:58", + "processor4": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "macaddress_vnet2": "FE:54:00:93:F5:56", + "macaddress_br183": "E4:1F:13:CC:36:58", + "ps": "ps -ef", + "processor5": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "macaddress_vnet10": "FE:54:00:7D:F8:24", + "uniqueid": "230a021b", + "macaddress_vnet3": "FE:54:00:05:49:DE", + "lsbdistcodename": "Santiago", + "hardwareisa": "x86_64", + "lsbdistrelease": "6.2", + "processor6": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "serialnumber": "abcdefg", + "processor7": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "macaddress_vnet4": "FE:54:00:62:53:67", + "hostname": "h02", + "osfamily": "RedHat", + "gateway_if": "br180", + "lsbrelease": ":core-4.0-amd64:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-noarch:printing-4.0-amd64:printing-4.0-noarch", + "kernelrelease": "2.6.32-131.12.1.el6.x86_64", + "kernel": "Linux", + "facterversion": "1.6.2", + "uptime_seconds": "13651752", + "macaddress_vnet5": "FE:54:00:01:B9:9C", + "interfaces": "br180,br181,br182,br183,eth0,eth0_181,eth0_182,eth0_183,eth1,lo,usb0,vnet0,vnet1,vnet2,vnet3,vnet4,vnet5,vnet7,vnet10", + "macaddress_vnet7": "FE:54:00:D8:F9:38", + "netmask_br180": "255.255.255.192", + "macaddress_eth0": "E4:1F:13:CC:36:58", + "is_virtual": "false", + "macaddress": "E4:1F:13:CC:36:58", + "sshdsakey": "AAAAB3NzaC1kc3MAAACBAIbK60jlhYKSd4Nx6LCQPMUZcq/P1lTY3sQVrwpmfbF7d4JOGed2RWSoTBAzP3/Tjwlf+8DZ3Jd8a6Yhd6WuBzinE+owym8LXo2gDTvelhnzOVrTytM8hAi8uFEiBTGbnN1zka2y7qCPVxEu+kV20qC3mPODGxpV6y/4KOZ5Y4SHAAAAFQCyjeF+Jy9CDhiD2tfnmtMGAn1xzQAAAIBwtnUhhkB0r3ivPdQZgVy6qha8AJQG94I9FU9FnGJJEjkkfqlgNrj9SxV6Cg9EcrntnAKxZxk7PQhgvDesx69Lap9PAx3Ffb5Pl47cd7ilOXv2RJGKuxZQHNdKUF2445POwNjxSuhEkIB3/zpFXBB/zPMRfqUtlXJEwP9m6z9UFgAAAIAQDBpmcGChFP7i8T2RdMZo3hSMnU4y5lpEGhmwfbODoLSAX37wWQwUmwRP8byTEqFL0+5bp3hRbt/67EoD1O6n39/We19g/UqHkbUdZQqLqFjJfyzqfepq4HXtIIT8GbqW+FGJkH9x2fY5gtQrn9nVdmPDTwERFS98qU9glufbIw==", + "uptime": "158 days", + "manufacturer": "IBM", + "macaddress_eth1": "E4:1F:13:CC:36:5A", + "timezone": "GMT", + "fqdn": "a.server.b.domain", + "ipaddress_br180": "10.35.27.2", + "ipaddress_eth0": "10.35.27.3", + "ipaddress_eth1": "10.35.27.4", + "puppetversion": "2.6.12", + "path": "/root/.gem/bin:/root/.gem/ruby/1.8/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/sbin:/usr/sbin:/root/scripts", + "hardwaremodel": "x86_64", + "macaddress_eth0_181": "E4:1F:13:CC:36:58", + "netmask_lo": "255.0.0.0", + "uptime_days": "158", + "virtual": "physical", + "operatingsystemrelease": "6.2", + "processorcount": "8", + "sshrsakey": "AAAAB3NzaC1xcEabbAABIwAAAQEAwXfnuA0+6C6G152kzCbSZvTyUBrMwc0PGnmZgFpC3x4SkcMgRmtayPNqmY1vyYwyka1lNUoyU9u3TXOUkd7WrodoNYBbDQXWCAVpUci5099zUuZ8RVLcokKwNmQO0d1URgSLt2TtuLiUDB7JU2mHkoOuW9n7ODq+UzYsd2wf+/TQrvgsuyi25MROH2Z3WfMO+fmsj77h34gkuaavwB4e6lNRynjZLuEc4POFvDEW6kGCYm1g/8kRwUlB/Z1Cu0egxtxnTjILFFwfYFQu2Ksig020q1gLX3o4YL9X/YZz+JQuAOLGpK3F0owQUL7Ie3vokDsVmz2AweuLDFeh54i2Bw==", + "macaddress_eth0_182": "E4:1F:13:CC:36:58", + "memorysize": "15.57 GB", + "ipaddress": "10.35.27.2", + "rubyversion": "1.8.7", + "macaddress_eth0_183": "E4:1F:13:CC:36:58", + "physicalprocessorcount": "1", + "network_lo": "127.0.0.0", + "gateway": "10.35.27.62", + "lsbdistid": "RedHatEnterpriseServer", + "network_br180": "10.35.27.0", + "lsbdistdescription": "Red Hat Enterprise Linux Server release 6.2 (Santiago)", + "swapsize": "17.70 GB", + "processor0": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "processor1": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "macaddress_br180": "E4:1F:13:CC:36:58", + "selinux": "false", + "domain": "server.b.domain", + "architecture": "x86_64", + "uptime_hours": "3792", + "memoryfree": "6.87 GB", + "augeasversion": "0.9.0", + "operatingsystem": "RedHat", + "ipaddress_lo": "127.0.0.1", + "processor2": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "type": "Main Server Chassis", + "id": "root", + "vlans": "181,182,183", + "lsbmajdistrelease": "6", + "swapfree": "16.72 GB", + "netmask": "255.255.255.192", + "discovery_bootif": "E4:1F:13:CC:36:58", + "memorysize_mb": "742.13", + "blockdevice_sda_size": "214749184", + "lldp_neighbor_chassisID_eth1": "0c:27:24:2c:c5:04", + "lldp_neighbor_portID_eth1": "Eth170/1/3", + "lldp_neighbor_sysName_eth1": "DC2-A03-DR-01", + "lldp_neighbor_mngAddr_ipv4_eth1": "192.0.2.13", + "lldp_neighbor_PVID_eth1": "184", + "lldp_neighbor_chassisID_eth2": "e0:d1:73:38:5a:84", + "lldp_neighbor_portID_eth2": "Eth171/1/3", + "lldp_neighbor_sysName_eth2": "DC2-A04-DR-03", + "lldp_neighbor_mngAddr_ipv4_eth2": "192.0.2.13", + "lldp_neighbor_PVID_eth2": "182" + } +} diff --git a/test/unit/facts_with_lldp_bond_candidate.json b/test/unit/facts_with_lldp_bond_candidate.json new file mode 100644 index 00000000..de540d99 --- /dev/null +++ b/test/unit/facts_with_lldp_bond_candidate.json @@ -0,0 +1,107 @@ +{ + "name": "sinn1636.lan", + "facts": { + "processor3": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "productname": "IBM System x -[7870K4G]-", + "kernelmajversion": "2.6", + "kernelversion": "2.6.32", + "macaddress_vnet0": "FE:54:00:1E:45:13", + "macaddress_br181": "E4:1F:13:CC:36:58", + "macaddress_usb0": "E6:1F:13:D0:43:A3", + "macaddress_vnet1": "FE:54:00:45:E5:3E", + "rubysitedir": "/usr/lib/ruby/site_ruby/1.8", + "macaddress_br182": "E4:1F:13:CC:36:58", + "processor4": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "macaddress_vnet2": "FE:54:00:93:F5:56", + "macaddress_br183": "E4:1F:13:CC:36:58", + "ps": "ps -ef", + "processor5": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "macaddress_vnet10": "FE:54:00:7D:F8:24", + "uniqueid": "230a021b", + "macaddress_vnet3": "FE:54:00:05:49:DE", + "lsbdistcodename": "Santiago", + "hardwareisa": "x86_64", + "lsbdistrelease": "6.2", + "processor6": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "serialnumber": "abcdefg", + "processor7": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "macaddress_vnet4": "FE:54:00:62:53:67", + "hostname": "h02", + "osfamily": "RedHat", + "gateway_if": "br180", + "lsbrelease": ":core-4.0-amd64:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-noarch:printing-4.0-amd64:printing-4.0-noarch", + "kernelrelease": "2.6.32-131.12.1.el6.x86_64", + "kernel": "Linux", + "facterversion": "1.6.2", + "uptime_seconds": "13651752", + "macaddress_vnet5": "FE:54:00:01:B9:9C", + "interfaces": "br180,br181,br182,br183,eth0,eth0_181,eth0_182,eth0_183,eth1,lo,usb0,vnet0,vnet1,vnet2,vnet3,vnet4,vnet5,vnet7,vnet10", + "macaddress_vnet7": "FE:54:00:D8:F9:38", + "netmask_br180": "255.255.255.192", + "macaddress_eth0": "E4:1F:13:CC:36:58", + "is_virtual": "false", + "macaddress": "E4:1F:13:CC:36:58", + "sshdsakey": "AAAAB3NzaC1kc3MAAACBAIbK60jlhYKSd4Nx6LCQPMUZcq/P1lTY3sQVrwpmfbF7d4JOGed2RWSoTBAzP3/Tjwlf+8DZ3Jd8a6Yhd6WuBzinE+owym8LXo2gDTvelhnzOVrTytM8hAi8uFEiBTGbnN1zka2y7qCPVxEu+kV20qC3mPODGxpV6y/4KOZ5Y4SHAAAAFQCyjeF+Jy9CDhiD2tfnmtMGAn1xzQAAAIBwtnUhhkB0r3ivPdQZgVy6qha8AJQG94I9FU9FnGJJEjkkfqlgNrj9SxV6Cg9EcrntnAKxZxk7PQhgvDesx69Lap9PAx3Ffb5Pl47cd7ilOXv2RJGKuxZQHNdKUF2445POwNjxSuhEkIB3/zpFXBB/zPMRfqUtlXJEwP9m6z9UFgAAAIAQDBpmcGChFP7i8T2RdMZo3hSMnU4y5lpEGhmwfbODoLSAX37wWQwUmwRP8byTEqFL0+5bp3hRbt/67EoD1O6n39/We19g/UqHkbUdZQqLqFjJfyzqfepq4HXtIIT8GbqW+FGJkH9x2fY5gtQrn9nVdmPDTwERFS98qU9glufbIw==", + "uptime": "158 days", + "manufacturer": "IBM", + "macaddress_eth1": "E4:1F:13:CC:36:5A", + "timezone": "GMT", + "fqdn": "a.server.b.domain", + "ipaddress_br180": "10.35.27.2", + "ipaddress_eth0": "10.35.27.3", + "ipaddress_eth1": "10.35.27.4", + "puppetversion": "2.6.12", + "path": "/root/.gem/bin:/root/.gem/ruby/1.8/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/sbin:/usr/sbin:/root/scripts", + "hardwaremodel": "x86_64", + "macaddress_eth0_181": "E4:1F:13:CC:36:58", + "netmask_lo": "255.0.0.0", + "uptime_days": "158", + "virtual": "physical", + "operatingsystemrelease": "6.2", + "processorcount": "8", + "sshrsakey": "AAAAB3NzaC1xcEabbAABIwAAAQEAwXfnuA0+6C6G152kzCbSZvTyUBrMwc0PGnmZgFpC3x4SkcMgRmtayPNqmY1vyYwyka1lNUoyU9u3TXOUkd7WrodoNYBbDQXWCAVpUci5099zUuZ8RVLcokKwNmQO0d1URgSLt2TtuLiUDB7JU2mHkoOuW9n7ODq+UzYsd2wf+/TQrvgsuyi25MROH2Z3WfMO+fmsj77h34gkuaavwB4e6lNRynjZLuEc4POFvDEW6kGCYm1g/8kRwUlB/Z1Cu0egxtxnTjILFFwfYFQu2Ksig020q1gLX3o4YL9X/YZz+JQuAOLGpK3F0owQUL7Ie3vokDsVmz2AweuLDFeh54i2Bw==", + "macaddress_eth0_182": "E4:1F:13:CC:36:58", + "memorysize": "15.57 GB", + "ipaddress": "10.35.27.2", + "rubyversion": "1.8.7", + "macaddress_eth0_183": "E4:1F:13:CC:36:58", + "physicalprocessorcount": "1", + "network_lo": "127.0.0.0", + "gateway": "10.35.27.62", + "lsbdistid": "RedHatEnterpriseServer", + "network_br180": "10.35.27.0", + "lsbdistdescription": "Red Hat Enterprise Linux Server release 6.2 (Santiago)", + "swapsize": "17.70 GB", + "processor0": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "processor1": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "macaddress_br180": "E4:1F:13:CC:36:58", + "selinux": "false", + "domain": "server.b.domain", + "architecture": "x86_64", + "uptime_hours": "3792", + "memoryfree": "6.87 GB", + "augeasversion": "0.9.0", + "operatingsystem": "RedHat", + "ipaddress_lo": "127.0.0.1", + "processor2": "Intel(R) Xeon(R) CPU E5620 @ 2.40GHz", + "type": "Main Server Chassis", + "id": "root", + "vlans": "181,182,183", + "lsbmajdistrelease": "6", + "swapfree": "16.72 GB", + "netmask": "255.255.255.192", + "discovery_bootif": "E4:1F:13:CC:36:58", + "memorysize_mb": "742.13", + "blockdevice_sda_size": "214749184", + "lldp_neighbor_chassisID_eth1": "0c:27:24:2c:c5:04", + "lldp_neighbor_portID_eth1": "Eth170/1/3", + "lldp_neighbor_sysName_eth1": "DC2-A03-DR-01", + "lldp_neighbor_mngAddr_ipv4_eth1": "192.0.2.13", + "lldp_neighbor_PVID_eth1": "182", + "lldp_neighbor_chassisID_eth0": "e0:d1:73:38:5a:84", + "lldp_neighbor_portID_eth0": "Eth171/1/3", + "lldp_neighbor_sysName_eth0": "DC2-A04-DR-03", + "lldp_neighbor_mngAddr_ipv4_eth0": "192.0.2.13", + "lldp_neighbor_PVID_eth0": "182" + } +} diff --git a/test/unit/host_discovered_test.rb b/test/unit/host_discovered_test.rb index 1aeba269..1ceba362 100644 --- a/test/unit/host_discovered_test.rb +++ b/test/unit/host_discovered_test.rb @@ -357,7 +357,69 @@ class HostDiscoveredTest < ActiveSupport::TestCase end end + test "provision_interface isn't touched with no LLDP facts" do + Setting[:discovery_auto_bond] = true + raw = parse_json_fixture('/facts.json')['facts'] + host = discover_host_from_facts(raw) + host.save + managed = ::ForemanDiscovery::HostConverter.to_managed(host) + refute_nil managed.provision_interface + assert_equal "eth0", managed.provision_interface.identifier + end + + test "provision_interface isn't touched with no peer on the same VLAN" do + Setting[:discovery_auto_bond] = true + raw = parse_json_fixture('/facts_with_lldp.json')['facts'] + host = discover_host_from_facts(raw) + host.save + managed = ::ForemanDiscovery::HostConverter.to_managed(host) + refute_nil managed.provision_interface + assert_equal "eth0", managed.provision_interface.identifier + end + + test "provision_interface is switched to bond0 with more than one interface on the same VLAN" do + Setting[:discovery_auto_bond] = true + raw = parse_json_fixture('/facts_with_lldp_bond_candidate.json')['facts'] + host = discover_host_from_facts(raw) + host.save + managed = ::ForemanDiscovery::HostConverter.to_managed(host) + refute_nil managed.provision_interface + assert_equal "bond0", managed.provision_interface.identifier + end + + test "provision_interface is not switched to bond0 if disabled" do + Setting[:discovery_auto_bond] = false + raw = parse_json_fixture('/facts_with_lldp_bond_candidate.json')['facts'] + host = discover_host_from_facts(raw) + host.save + managed = ::ForemanDiscovery::HostConverter.to_managed(host) + refute_nil managed.provision_interface + assert_equal "eth0", managed.provision_interface.identifier + end + + test "former provision_interface is not managed after switching to bond0" do + Setting[:discovery_auto_bond] = true + raw = parse_json_fixture('/facts_with_lldp_bond_candidate.json')['facts'] + host = discover_host_from_facts(raw) + host.save + managed = ::ForemanDiscovery::HostConverter.to_managed(host) + refute_nil managed.provision_interface + + former_interface = nil + managed.interfaces.each do |interface| + former_interface = interface if interface.identifier == 'eth0' + end + + refute_nil former_interface + assert_instance_of ::Nic::Managed, former_interface + assert_nil former_interface.ip + assert_nil former_interface.name + assert_equal false, former_interface.primary + assert_equal false, former_interface.managed + assert_equal false, former_interface.provision + end + def parse_json_fixture(relative_path) - return JSON.parse(File.read(File.expand_path(File.dirname(__FILE__) + relative_path))) + JSON.parse(File.read(File.expand_path(File.dirname(__FILE__) + relative_path))) end end diff --git a/test/unit/lldp_neighbors_test.rb b/test/unit/lldp_neighbors_test.rb new file mode 100644 index 00000000..add360fa --- /dev/null +++ b/test/unit/lldp_neighbors_test.rb @@ -0,0 +1,56 @@ +require 'test_plugin_helper' + +class LldpNeighborsTest < ActiveSupport::TestCase + test "#get_neighbors_by_interface gives nothing with no LLDP facts" do + assert_nil simple_facts.get_neighbors_by_interface('eth0') + end + + test "#get_neighbors_by_interface gives nothing with unmatching PVID in LLDP facts" do + assert_nil lldp_facts.get_neighbors_by_interface('eth0') + end + + test "#get_neighbors_by_interface gives a valid pair for LLDP facts with matching PVID" do + assert_equal %w(eth0 eth1), bond_facts.get_neighbors_by_interface('eth0') + assert_equal %w(eth0 eth1), bond_facts.get_neighbors_by_interface('eth1') + assert_nil bond_facts.get_neighbors_by_interface('br182') + end + + test "#list_by_pvid gives an empty Hash without LLDP facts" do + assert_equal({}, simple_facts.list_by_pvid) + assert_equal({}, ForemanDiscovery::LldpNeighbors.from_facts({}).list_by_pvid) + end + + test "#list_by_pvid gives an empty Hash without LLDP facts" do + assert_equal({'182' => %w(eth2), '184' => %w(eth1)}, lldp_facts.list_by_pvid) + end + + test "#list_by_pvid pairs interfaces by PVID" do + assert_equal({'182' => %w(eth0 eth1)}, bond_facts.list_by_pvid) + end + + test "#list_by_pvid supports multiple PVIDs" do + assert_equal({'182' => %w(eth2), '184' => %w(eth1)}, lldp_facts.list_by_pvid) + end + + private + + def simple_facts + neighbors 'facts' + end + + def lldp_facts + neighbors 'facts_with_lldp' + end + + def bond_facts + neighbors 'facts_with_lldp_bond_candidate' + end + + def neighbors(name) + ForemanDiscovery::LldpNeighbors.from_facts(get_facts name) + end + + def get_facts(name) + JSON.parse(File.read(File.expand_path(File.dirname(__FILE__) + "/#{name}.json")))['facts'] + end +end