From 78bcf995fbb54d8b6da454b838d055d0f1454498 Mon Sep 17 00:00:00 2001 From: Teresa Ho Date: Mon, 27 May 2019 11:33:39 -0400 Subject: [PATCH] Clean up interface network assignment The networktype field is no longer used in the interface table since an interface can have multiple networks associated with it. This commit is to remove the networktype field and use the network association instead. The host interface commands used to allow associating an initial network to the interface. This has been changed such that network association can only be done with the interface-network commands. The address assignment of an interface is performed when a network is assigned to an interface, not when the interface is provisioned. Data interface no longer requires at least one datanetwork associated with it. The REST API doc is also updated to reflect the changes. Story: 2004273 Task: 30947 Task: 33504 Change-Id: I952008408826f4c630246477d71687628357622c Signed-off-by: Teresa Ho --- api-ref/source/api-ref-sysinv-v1-config.rst | 12 - .../bringup-essential-services/tasks/main.yml | 5 +- .../cgtsclient/v1/iinterface_shell.py | 28 +- .../cgtsclient/v1/interface_network.py | 8 + .../cgtsclient/v1/iprofile_shell.py | 13 +- sysinv/sysinv/sysinv/sysinv/agent/manager.py | 12 +- .../sysinv/api/controllers/v1/address.py | 36 +- .../sysinv/sysinv/api/controllers/v1/host.py | 32 +- .../sysinv/api/controllers/v1/interface.py | 712 ++++-------------- .../controllers/v1/interface_datanetwork.py | 6 +- .../api/controllers/v1/interface_network.py | 237 +++++- .../sysinv/api/controllers/v1/profile.py | 25 + .../sysinv/sysinv/api/controllers/v1/route.py | 9 +- .../sysinv/api/controllers/v1/system.py | 12 +- .../sysinv/sysinv/api/controllers/v1/utils.py | 22 + .../sysinv/sysinv/sysinv/common/constants.py | 7 + .../sysinv/sysinv/sysinv/common/exception.py | 12 +- sysinv/sysinv/sysinv/sysinv/common/utils.py | 47 +- .../sysinv/sysinv/sysinv/conductor/manager.py | 53 +- sysinv/sysinv/sysinv/sysinv/db/api.py | 2 - .../sysinv/sysinv/sysinv/db/sqlalchemy/api.py | 46 +- .../versions/088_networktype_remove.py | 26 + .../sysinv/sysinv/db/sqlalchemy/models.py | 1 - sysinv/sysinv/sysinv/sysinv/helm/neutron.py | 6 +- .../sysinv/sysinv/sysinv/objects/address.py | 4 +- .../sysinv/sysinv/sysinv/objects/interface.py | 9 +- sysinv/sysinv/sysinv/sysinv/objects/route.py | 4 +- sysinv/sysinv/sysinv/sysinv/objects/utils.py | 3 +- .../sysinv/sysinv/sysinv/puppet/interface.py | 12 +- .../sysinv/sysinv/sysinv/puppet/platform.py | 10 +- .../sysinv/sysinv/tests/api/test_interface.py | 205 +---- .../tests/api/test_interface_network.py | 88 ++- sysinv/sysinv/sysinv/sysinv/tests/db/utils.py | 18 +- .../sysinv/tests/puppet/test_interface.py | 11 +- 34 files changed, 715 insertions(+), 1018 deletions(-) create mode 100644 sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/088_networktype_remove.py diff --git a/api-ref/source/api-ref-sysinv-v1-config.rst b/api-ref/source/api-ref-sysinv-v1-config.rst index 5b2e3011c1..a88e6260d1 100644 --- a/api-ref/source/api-ref-sysinv-v1-config.rst +++ b/api-ref/source/api-ref-sysinv-v1-config.rst @@ -2406,7 +2406,6 @@ itemNotFound (404) "aemode (Optional)", "plain", "xsd:string", "Only applicable if ``iftype : ae``, this attribute indicates the basic mode of operation for the AE/LAG interface. Supported modes are: balanced round robin, active-backup, balanced xor, broadcast, 802.3ad, balance-tlb, balance-alb. NOTE only balanced xor and active-standby modes are supported by interfaces of ifclass=data." "txhashpolicy (Optional)", "plain", "xsd:string", "Only applicable if ``iftype : ae`` and ``aemode : balanced``, this attribute indicates what packet headers the AE/LAG is using to distribute packets across the different links/ports of the AE/LAG group; ``layer2``, ``layer2+3`` or ``layer3+4``." "vlan_id (Optional)", "plain", "xsd:integer", "Only applicable if ``iftype : vlan``, this attribute indicates that the vlan interface id. A vlan id between 1 and 4094 (inclusive) must be selected. NOTE The vlan id must be unique for the host interface." - "networks (Optional)", "plain", "xsd:list", "Only applicable if ``ifclass : platform``, this attribute provides a list of platform networks that this interface is attached to." "datanetworks (Optional)", "plain", "xsd:list", "Only applicable if ``ifclass : data``, this attribute provides a list of data networks that this ``data`` interface is attached to." "imac (Optional)", "plain", "xsd:string", "The MAC Address being used by the interface. In the case of AE/LAG, the MAC address of one of the physical ports of the AE/LAG group is used." "imtu (Optional)", "plain", "xsd:integer", "The Maximum Transmission Unit (MTU) of the interface, in bytes." @@ -2447,7 +2446,6 @@ itemNotFound (404) "vlan_id": null, "imtu": 1500, "aemode": null, - "networks": ["1"], "datanetworks": [], "ifclass": "platform" "ifname": "eth1" @@ -2480,7 +2478,6 @@ itemNotFound (404) ], "aemode": "balanced", - "networks": [], "datanetworks": ["physnet-0,physnet-1"], "ifclass": "data" "ifname": "data1" @@ -2526,7 +2523,6 @@ itemNotFound (404) "aemode (Optional)", "plain", "xsd:string", "Only applicable if ``iftype : ae``, this attribute indicates the basic mode of operation for the AE/LAG interface. Supported modes are: balanced round robin, active-backup, balanced xor, broadcast, 802.3ad, balance-tlb, balance-alb. NOTE only balanced xor and active-standby modes are supported by interfaces of ifclass=data." "txhashpolicy (Optional)", "plain", "xsd:string", "Only applicable if ``iftype : ae`` and ``aemode : balanced``, this attribute indicates what packet headers the AE/LAG is using to distribute packets across the different links/ports of the AE/LAG group; ``layer2``, ``layer2+3`` or ``layer3+4``." "vlan_id (Optional)", "plain", "xsd:integer", "Only applicable if ``iftype : vlan``, this attribute indicates that the vlan interface id. A vlan id between 1 and 4094 (inclusive) must be selected. NOTE The vlan id must be unique for the host interface." - "networks (Optional)", "plain", "xsd:list", "Only applicable if ``ifclass : platform``, this attribute provides a list of platform networks that this interface is attached to." "datanetworks (Optional)", "plain", "xsd:list", "Only applicable if ``ifclass : data``, this attribute provides a list of data networks that this ``data`` interface is attached to." "imac (Optional)", "plain", "xsd:string", "The MAC Address being used by the interface. In the case of AE/LAG, the MAC address of one of the physical ports of the AE/LAG group is used." "imtu (Optional)", "plain", "xsd:integer", "The Maximum Transmission Unit (MTU) of the interface, in bytes." @@ -2557,7 +2553,6 @@ itemNotFound (404) } ], "datanetworks" : ["physnet-0,physnet-1"], - "networks": [], "txhashpolicy" : "layer2", "schedpolicy" : null, "uuid" : "740a5bec-b7a8-4645-93ed-aea0d4cfbf86", @@ -2622,7 +2617,6 @@ badMediaType (415) "aemode (Optional)", "plain", "xsd:string", "Only applicable if ``iftype : ae``, this attribute specifies whether the AE/LAG should operate as ``balanced`` or ``active_standby`` or ``802.3ad`` across its links. The ``balanced`` and ``active_standby`` are the only modes supported by ``data`` type interface. For ``mgmt`` type interface the ``802.3ad`` option must be selected." "txhashpolicy (Optional)", "plain", "xsd:string", "Only applicable if ``iftype : ae`` and ``aemode : balanced``, this attribute specifies what packet headers the AE/LAG should use to distribute packets across the different links/ports of the AE/LAG group; ``layer2``, ``layer2+3`` or ``layer3+4``." "vlan_id (Optional)", "plain", "xsd:integer", "Only applicable if ``iftype : vlan``, this attribute specifies a virtual lan id for a vlan interface type." - "networks (Optional)", "plain", "xsd:list", "Only applicable if ``ifclass : platform``, this attribute provides a list of platform networks that this interface is attached to." "datanetworks (Optional)", "plain", "xsd:list", "Only applicable if ``ifclass : data``, this attribute specifies a list of data networks that this ``data`` interface is attached to." "ports (Optional)", "plain", "xsd:list", "This attribute specifies a comma-separated list of ports that this interface contains. If ``iftype : ethernet`` then only one port is allowed." "uses (Optional)", "plain", "xsd:list", "Only applicable if ``iftype : ae`` or ``iftype: vlan``, this attribute specifies a comma-separated list of interfaces that this interface uses." @@ -2643,7 +2637,6 @@ badMediaType (415) "aemode (Optional)", "plain", "xsd:string", "Only applicable if ``iftype : ae``, this attribute indicates the basic mode of operation for the AE/LAG interface. Supported modes are: balanced round robin, active-backup, balanced xor, broadcast, 802.3ad, balance-tlb, balance-alb. NOTE only balanced xor and active-standby modes are supported by interfaces of ifclass=data." "txhashpolicy (Optional)", "plain", "xsd:string", "Only applicable if ``iftype : ae`` and ``aemode : balanced``, this attribute indicates what packet headers the AE/LAG is using to distribute packets across the different links/ports of the AE/LAG group; ``layer2``, ``layer2+3`` or ``layer3+4``." "vlan_id (Optional)", "plain", "xsd:integer", "Only applicable if ``iftype : vlan``, this attribute indicates that the vlan interface id. A vlan id between 1 and 4094 (inclusive) must be selected. NOTE The vlan id must be unique for the host interface." - "networks (Optional)", "plain", "xsd:list", "Only applicable if ``ifclass : platform``, this attribute provides a list of platform networks that this interface is attached to." "datanetworks (Optional)", "plain", "xsd:list", "Only applicable if ``ifclass : data``, this attribute provides a list of data networks that this ``data`` interface is attached to." "imac (Optional)", "plain", "xsd:string", "The MAC Address being used by the interface. In the case of AE/LAG, the MAC address of one of the physical ports of the AE/LAG group is used." "imtu (Optional)", "plain", "xsd:integer", "The Maximum Transmission Unit (MTU) of the interface, in bytes." @@ -2667,7 +2660,6 @@ badMediaType (415) "txhashpolicy": "layer2", "ihost_uuid": "ff453a51-1d3b-437f-a65e-b2d163f79f85", "imtu": "1500", - "networks": [], "datanetworks": "physnet-0,physnet1", "ifclass": "data", "ifname": "data1", @@ -2722,7 +2714,6 @@ badMediaType (415) ], "aemode": "balanced", "sriov_numvfs": 0, - 'networks": [], "datanetworks": ["physnet-0,physnet-1"], "ifclass": "data", "ifname": "data1", @@ -2757,7 +2748,6 @@ badMediaType (415) "aemode (Optional)", "plain", "xsd:string", "Only applicable if ``iftype : ae``, this attribute specifies whether the AE/LAG should operate as ``balanced`` or ``active_standby`` across its links. These are the only modes supported by ``data`` type interface." "txhashpolicy (Optional)", "plain", "xsd:string", "Only applicable if ``iftype : ae`` and ``aemode : balanced``, this attribute specifies what packet headers the AE/LAG should use to distribute packets across the different links/ports of the AE/LAG group; ``layer2``, ``layer2+3`` or ``layer3+4``." "vlan_id (Optional)", "plain", "xsd:integer", "Only applicable if ``iftype : vlan``, this attribute specifies a virtual lan id for a vlan interface type." - "networks (Optional)", "plain", "xsd:list", "Only applicable if ``ifclass : platform``, this attribute provides a list of platform networks that this interface is attached to." "datanetworks (Optional)", "plain", "xsd:list", "Only applicable if ``ifclass : data``, this attribute specifies a list of data networks that this ``data`` interface is attached to." "ports (Optional)", "plain", "xsd:list", "This attribute specifies a comma-separated list of ports that this interface contains. If ``iftype : ethernet`` then only one port is allowed." "uses (Optional)", "plain", "xsd:list", "Only applicable if ``iftype : ae`` or ``iftype: vlan``, this attribute specifies a comma-separated list of interfaces that this interface uses." @@ -2777,7 +2767,6 @@ badMediaType (415) "aemode (Optional)", "plain", "xsd:string", "Only applicable if ``iftype : ae``, this attribute indicates the basic mode of operation for the AE/LAG interface. Supported modes are: balanced round robin, active-backup, balanced xor, broadcast, 802.3ad, balance-tlb, balance-alb. NOTE only balanced xor and active-standby modes are supported by interfaces of ifclass=data." "txhashpolicy (Optional)", "plain", "xsd:string", "Only applicable if ``iftype : ae`` and ``aemode : balanced``, this attribute indicates what packet headers the AE/LAG is using to distribute packets across the different links/ports of the AE/LAG group; ``layer2``, ``layer2+3`` or ``layer3+4``." "vlan_id (Optional)", "plain", "xsd:integer", "Only applicable if ``iftype : vlan``, this attribute indicates that the vlan interface id. A vlan id between 1 and 4094 (inclusive) must be selected. NOTE The vlan id must be unique for the host interface." - "networks (Optional)", "plain", "xsd:list", "Only applicable if ``ifclass : platform``, this attribute provides a list of platform networks that this interface is attached to." "datanetworks (Optional)", "plain", "xsd:list", "Only applicable if ``ifclass : data``, this attribute provides a list of data networks that this ``data`` interface is attached to." "imac (Optional)", "plain", "xsd:string", "The MAC Address being used by the interface. In the case of AE/LAG, the MAC address of one of the physical ports of the AE/LAG group is used." "imtu (Optional)", "plain", "xsd:integer", "The Maximum Transmission Unit (MTU) of the interface, in bytes." @@ -2870,7 +2859,6 @@ badMediaType (415) ], "aemode": "active_standby", - "networks": [], "datanetworks": ["physnet-0,physnet-1"], "ifclass": "data", "ifname": "data1", diff --git a/playbookconfig/playbookconfig/playbooks/bootstrap/roles/bringup-essential-services/tasks/main.yml b/playbookconfig/playbookconfig/playbooks/bootstrap/roles/bringup-essential-services/tasks/main.yml index de31518ad7..2809b8a905 100644 --- a/playbookconfig/playbookconfig/playbooks/bootstrap/roles/bringup-essential-services/tasks/main.yml +++ b/playbookconfig/playbookconfig/playbooks/bootstrap/roles/bringup-essential-services/tasks/main.yml @@ -14,8 +14,9 @@ # Use shell instead of command module as source is an internal shell command shell: "{{ item }}" with_items: - - source /etc/platform/openrc; system host-if-add controller-0 lo virtual none lo -c platform --networks mgmt -m 1500 - - source /etc/platform/openrc; system host-if-modify controller-0 -c platform --networks cluster-host lo + - source /etc/platform/openrc; system host-if-add controller-0 lo virtual none lo -c platform -m 1500 + - source /etc/platform/openrc; system interface-network-assign controller-0 lo mgmt + - source /etc/platform/openrc; system interface-network-assign controller-0 lo cluster-host - ip addr add {{ cluster_virtual }} brd {{ cluster_broadcast }} dev lo scope host label lo:5 - ip addr add {{ mgmt_virtual }} brd {{ management_broadcast }} dev lo scope host label lo:1 - ip addr add {{ pxe_virtual }} dev lo scope host diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/iinterface_shell.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/iinterface_shell.py index 5ddde98e26..44b6b594c5 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/iinterface_shell.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/iinterface_shell.py @@ -13,25 +13,17 @@ from cgtsclient.common import utils from cgtsclient import exc from cgtsclient.v1 import ihost as ihost_utils from cgtsclient.v1 import iinterface as iinterface_utils -from cgtsclient.v1 import network as network_utils def _print_iinterface_show(cc, iinterface): fields = ['ifname', 'iftype', 'ports', 'datanetworks', - 'imac', 'imtu', 'ifclass', 'networks', + 'imac', 'imtu', 'ifclass', 'aemode', 'schedpolicy', 'txhashpolicy', 'uuid', 'ihost_uuid', 'vlan_id', 'uses', 'used_by', 'created_at', 'updated_at', 'sriov_numvfs', 'sriov_vf_driver'] optional_fields = ['ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool'] rename_fields = [{'field': 'dpdksupport', 'label': 'accelerated'}] - network_names = "" - networks = getattr(iinterface, 'networks', []) - for n in networks: - network = network_utils._find_network(cc, n) - network_names += "{},".format(network.name) - network_names = network_names.strip(',') - setattr(iinterface, 'networks', network_names) data = [(f, getattr(iinterface, f, '')) for f in fields] data += [(f, getattr(iinterface, f, '')) for f in optional_fields if hasattr(iinterface, f)] @@ -153,9 +145,6 @@ def do_host_if_delete(cc, args): metavar='', choices=['platform', 'data', 'pci-passthrough', 'pci-sriov', 'none'], help='The class of the interface') -@utils.arg('--networks', - metavar='', - help="Name or ID of network") @utils.arg('portsorifaces', metavar='', nargs='+', @@ -177,7 +166,7 @@ def do_host_if_delete(cc, args): def do_host_if_add(cc, args): """Add an interface.""" - field_list = ['ifname', 'iftype', 'imtu', 'ifclass', 'networks', 'aemode', + field_list = ['ifname', 'iftype', 'imtu', 'ifclass', 'aemode', 'txhashpolicy', 'datanetworks', 'vlan_id', 'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool'] @@ -204,10 +193,6 @@ def do_host_if_add(cc, args): user_specified_fields['datanetworks'] = \ user_specified_fields['datanetworks'].split(',') - if 'networks' in user_specified_fields.keys(): - network = network_utils._find_network(cc, args.networks) - user_specified_fields['networks'] = [str(network.id)] - user_specified_fields['ihost_uuid'] = ihost.uuid user_specified_fields['ports'] = portnamesoruuids user_specified_fields['uses'] = uses @@ -251,9 +236,6 @@ def do_host_if_add(cc, args): @utils.arg('-c', '--ifclass', metavar='', help='The class of the interface') -@utils.arg('--networks', - metavar='', - help="Name or ID of network") @utils.arg('--ipv4-mode', metavar='', choices=['disabled', 'static', 'pool'], @@ -281,7 +263,7 @@ def do_host_if_modify(cc, args): """Modify interface attributes.""" rwfields = ['iftype', 'ifname', 'imtu', 'aemode', 'txhashpolicy', - 'datanetworks', 'providernetworks', 'ports', 'ifclass', 'networks', + 'datanetworks', 'providernetworks', 'ports', 'ifclass', 'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool', 'sriov_numvfs', 'sriov_vf_driver'] @@ -303,10 +285,6 @@ def do_host_if_modify(cc, args): fields = interface.__dict__ fields.update(user_specified_fields) - if 'networks' in user_specified_fields.keys(): - network = network_utils._find_network(cc, args.networks) - user_specified_fields['networks'] = str(network.id) - # Allow setting an interface back to a None type if 'ifclass' in user_specified_fields.keys(): if args.ifclass == 'none': diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/interface_network.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/interface_network.py index 93b7e88ff1..24323832be 100755 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/interface_network.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/interface_network.py @@ -56,3 +56,11 @@ class InterfaceNetworkManager(base.Manager): def remove(self, interface_network_uuid): path = '/v1/interface_networks/%s' % interface_network_uuid return self._delete(path) + + +def get_network_names(cc, interface): + network_names = [] + ifnets = cc.interface_network.list_by_interface(interface.uuid) + for ifnet in ifnets: + network_names.append(getattr(ifnet, 'network_name')) + return network_names diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/iprofile_shell.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/iprofile_shell.py index 5544bb5bee..41530eb3d9 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/iprofile_shell.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/iprofile_shell.py @@ -16,6 +16,7 @@ from cgtsclient import exc from cgtsclient.v1 import ethernetport as ethernetport_utils from cgtsclient.v1 import icpu as icpu_utils from cgtsclient.v1 import ihost as ihost_utils +from cgtsclient.v1 import interface_network as ifnet_utils from cgtsclient.v1 import iprofile as iprofile_utils import math @@ -58,11 +59,13 @@ def get_portconfig(iprofile): return pstr -def get_interfaceconfig(iprofile): +def get_interfaceconfig(cc, iprofile): istr = '' for interface in iprofile.interfaces: - istr = istr + "%s: %s" % (interface.ifname, interface.networktype) - if interface.networktype == 'data': + if interface.ifclass == 'platform': + network_names = ifnet_utils.get_network_names(cc, interface) + istr = istr + "%s: %s" % (interface.ifname, network_names) + elif interface.ifclass == 'data': istr = istr + "( %s )" % interface.datanetworks _get_interface_ports_interfaces(iprofile, interface) if interface.ports: @@ -83,7 +86,7 @@ def get_ifprofile_data(cc, iprofile): if iprofile.ports: # an 'interface' profile iprofile.portconfig = get_portconfig(iprofile) iprofile.interfaces = cc.iprofile.list_iinterface(iprofile.uuid) - iprofile.interfaceconfig = get_interfaceconfig(iprofile) + iprofile.interfaceconfig = get_interfaceconfig(cc, iprofile) def do_ifprofile_list(cc, args): @@ -91,7 +94,7 @@ def do_ifprofile_list(cc, args): profiles = cc.iprofile.list_interface_profiles() for profile in profiles: profile.portconfig = get_portconfig(profile) - profile.interfaceconfig = get_interfaceconfig(profile) + profile.interfaceconfig = get_interfaceconfig(cc, profile) field_labels = ['uuid', 'name', 'port config', 'interface config'] fields = ['uuid', 'profilename', 'portconfig', 'interfaceconfig'] diff --git a/sysinv/sysinv/sysinv/sysinv/agent/manager.py b/sysinv/sysinv/sysinv/sysinv/agent/manager.py index 66f8862be8..7fe3655a15 100644 --- a/sysinv/sysinv/sysinv/sysinv/agent/manager.py +++ b/sysinv/sysinv/sysinv/sysinv/agent/manager.py @@ -213,22 +213,22 @@ class AgentManager(service.PeriodicService): # do not bother with alarms since that adds too much noise. LOG.info("Cross-numa performance degradation over port %s " "on processor %d on host %s. Better performance " - "if you configure %s interface on port " + "if you configure platform interface on port " "residing on processor 0, or configure a platform " "core on processor %d." % (info['name'], info['numa_node'], self.host, - info['networktype'], info['numa_node'])) + info['numa_node'])) - LOG.info("Affine %s interface %s with cpulist %s" % - (info['networktype'], info['name'], cpulist)) + LOG.info("Affine platform interface %s with cpulist %s" % + (info['name'], cpulist)) cmd = '/usr/bin/affine-interrupts.sh %s %s' % \ (info['name'], cpulist) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) proc.communicate() LOG.info("%s return %d" % (cmd, proc.returncode)) if proc.returncode == 1: - LOG.error("Failed to affine %s %s interrupts with %s" % - (info['networktype'], info['name'], cpulist)) + LOG.error("Failed to affine platform interface %s interrupts with %s" % + (info['name'], cpulist)) def _update_ttys_dcd_status(self, context, host_id): # Retrieve the serial line carrier detect flag diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/address.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/address.py index 68bc3d0540..9dcddb3e98 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/address.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/address.py @@ -246,11 +246,13 @@ class AddressController(rest.RestController): def _check_interface_type(self, interface_id): interface = pecan.request.dbapi.iinterface_get(interface_id) - if not interface.networktype: - raise exception.InterfaceNetworkTypeNotSet() - if interface.networktype not in ALLOWED_NETWORK_TYPES: - raise exception.UnsupportedInterfaceNetworkType( - networktype=interface.networktype) + if (interface['ifclass'] == constants.INTERFACE_TYPE_PLATFORM and + interface['networktypelist'] is None): + raise exception.InterfaceNetworkNotSet() + for nt in interface['networktypelist']: + if nt not in ALLOWED_NETWORK_TYPES: + raise exception.UnsupportedInterfaceNetworkType( + networktype=nt) return def _check_address_mode(self, interface_id, family): @@ -292,10 +294,9 @@ class AddressController(rest.RestController): def _check_address_count(self, interface_id, host_id): interface = pecan.request.dbapi.iinterface_get(interface_id) - networktype = interface['networktype'] sdn_enabled = utils.get_sdn_enabled() - if networktype == constants.NETWORK_TYPE_DATA and not sdn_enabled: + if interface['ifclass'] == constants.INTERFACE_CLASS_DATA and not sdn_enabled: # Is permitted to add multiple addresses only # if SDN L3 mode is not enabled. return @@ -309,12 +310,12 @@ class AddressController(rest.RestController): # skip the one we came in with if uuid == interface_id: continue - if iface['ifclass'] == constants.NETWORK_TYPE_DATA: + if iface['ifclass'] == constants.INTERFACE_CLASS_DATA: addresses = ( pecan.request.dbapi.addresses_get_by_interface(uuid)) if len(addresses) != 0: - raise exception.\ - AddressLimitedToOneWithSDN(iftype=networktype) + raise exception.AddressLimitedToOneWithSDN( + iftype=constants.INTERFACE_CLASS_DATA) def _check_address_conflicts(self, host_id, interface_id, address): self._check_address_count(interface_id, host_id) @@ -357,13 +358,14 @@ class AddressController(rest.RestController): def _check_managed_addr(self, host_id, interface_id): # Check that static address alloc is enabled interface = pecan.request.dbapi.iinterface_get(interface_id) - networktype = interface['networktype'] - if networktype not in [constants.NETWORK_TYPE_MGMT, - constants.NETWORK_TYPE_OAM]: - return - network = pecan.request.dbapi.network_get_by_type(networktype) - if network.dynamic: - raise exception.StaticAddressNotConfigured() + if interface['networktypelist']: + for networktype in interface['networktypelist']: + if networktype not in [constants.NETWORK_TYPE_MGMT, + constants.NETWORK_TYPE_OAM]: + continue + network = pecan.request.dbapi.network_get_by_type(networktype) + if network.dynamic: + raise exception.StaticAddressNotConfigured() host = pecan.request.dbapi.ihost_get(host_id) if host['personality'] in [constants.STORAGE]: raise exception.ManagedIPAddress() diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py index eab59a8857..5efb557e46 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py @@ -3106,8 +3106,8 @@ class HostController(rest.RestController): """ count = 0 - if interface.networktype not in address_api.ALLOWED_NETWORK_TYPES: - return + if not any(nt in address_api.ALLOWED_NETWORK_TYPES for nt in interface.networktypelist): + return # Check IPv4 address presence addresses = pecan.request.dbapi.addresses_get_by_interface( interface['id'], family=constants.IPV4_FAMILY) @@ -3134,9 +3134,9 @@ class HostController(rest.RestController): raise wsme.exc.ClientSideError(msg) if min_count and (count < min_count): msg = (_("Expecting at least %(min)s IP address(es) on " - "%(networktype)s interface %(ifname)s; found %(count)s") % + "%(ifclass)s interface %(ifname)s; found %(count)s") % {'min': min_count, - 'networktype': interface.networktype, + 'ifclass': interface.ifclass, 'ifname': interface.ifname, 'count': count}) raise wsme.exc.ClientSideError(msg) @@ -3220,7 +3220,7 @@ class HostController(rest.RestController): interfaces = ( pecan.request.dbapi.iinterface_get_by_ihost(ihost['uuid'])) for interface in interfaces: - if interface.networktype == constants.NETWORK_TYPE_OAM: + if constants.NETWORK_TYPE_OAM in interface.networktypelist: break else: msg = _("Can not unlock a controller host without an oam " @@ -3333,7 +3333,7 @@ class HostController(rest.RestController): address_count = 0 interfaces = pecan.request.dbapi.iinterface_get_by_ihost(ihost['uuid']) for iface in interfaces: - if iface.networktype != constants.NETWORK_TYPE_DATA: + if iface.ifclass != constants.INTERFACE_TYPE_DATA: continue addresses = ( pecan.request.dbapi.addresses_get_by_interface(iface['uuid'])) @@ -5900,11 +5900,10 @@ class HostController(rest.RestController): # controller/worker/storage ihost_iinterfaces = pecan.request.dbapi.iinterface_get_by_ihost( ihost['uuid']) - network = pecan.request.dbapi.network_get_by_type( - constants.NETWORK_TYPE_MGMT) mgmt_interface_configured = False for iif in ihost_iinterfaces: - if iif.networks and str(network.id) in iif.networks: + if (iif.networktypelist and + constants.NETWORK_TYPE_MGMT in iif.networktypelist): mgmt_interface_configured = True if not mgmt_interface_configured: @@ -5917,10 +5916,9 @@ class HostController(rest.RestController): # controller/worker/storage host_interfaces = pecan.request.dbapi.iinterface_get_by_ihost( ihost['uuid']) - network = pecan.request.dbapi.network_get_by_type( - constants.NETWORK_TYPE_CLUSTER_HOST) for iif in host_interfaces: - if iif.networks and str(network.id) in iif.networks: + if (iif.networktypelist and + constants.NETWORK_TYPE_CLUSTER_HOST in iif.networktypelist): break else: msg = _("Cannot unlock host %s " @@ -5969,15 +5967,9 @@ class HostController(rest.RestController): # updated management interfaces idata = {} for iif in ihost_iinterfaces: - iif_networktype = [] - if iif.networktype: - iif_networktype = [network.strip() for network in iif.networktype.split(",")] - if any(network in [constants.NETWORK_TYPE_MGMT] for network in iif_networktype): + if constants.NETWORK_TYPE_MGMT in iif.networktypelist: for ila in interface_list_active: - ila_networktype = [] - if ila.networktype: - ila_networktype = [network.strip() for network in ila.networktype.split(",")] - if any(network in ila_networktype for network in iif_networktype): + if constants.NETWORK_TYPE_MGMT in ila.networktypelist: idata['imtu'] = ila.imtu pecan.request.dbapi.iinterface_update(iif.uuid, idata) break diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py index 3ca11aca8c..1b9c9c9585 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py @@ -23,7 +23,6 @@ import jsonpatch -import os import six import uuid @@ -89,8 +88,7 @@ NEUTRON_INTERFACE_CLASS = [constants.INTERFACE_CLASS_DATA, # Interface network types that are PCI based PCI_NETWORK_TYPES = [constants.NETWORK_TYPE_PCI_PASSTHROUGH, constants.NETWORK_TYPE_PCI_SRIOV] - -VALID_AEMODE_LIST = ['active_standby', 'balanced', '802.3ad'] +PCI_INTERFACE_CLASS = [constants.INTERFACE_CLASS_PCI_PASSTHROUGH, constants.INTERFACE_CLASS_PCI_SRIOV] DATA_NETWORK_TYPES = [constants.NETWORK_TYPE_DATA] @@ -138,9 +136,6 @@ class Interface(base.APIBase): ifclass = wtypes.text "Represent the class of the interface" - networktype = wtypes.text - "Represent the network type of the interface" - aemode = wtypes.text "Represent the aemode of the interface" @@ -199,9 +194,6 @@ class Interface(base.APIBase): sriov_vf_driver = wtypes.text "The driver of configured SR-IOV VFs" - networks = [wtypes.text] - "Represent the networks of the interface" - def __init__(self, **kwargs): self.fields = list(objects.interface.fields.keys()) for k in self.fields: @@ -222,7 +214,7 @@ class Interface(base.APIBase): interface = Interface(**kwargs) if not expand: interface.unset_fields_except(['uuid', 'ifname', 'iftype', - 'imac', 'imtu', 'ifclass', 'networktype', 'networks', + 'imac', 'imtu', 'ifclass', 'ihost_uuid', 'forihostid', 'aemode', 'schedpolicy', 'txhashpolicy', 'vlan_id', 'uses', 'usesmodify', 'used_by', @@ -232,7 +224,6 @@ class Interface(base.APIBase): # never expose the ihost_id attribute interface.ihost_id = wtypes.Unset - # interface.networktype = wtypes.Unset interface.links = [link.Link.make_link('self', pecan.request.host_url, 'iinterfaces', interface.uuid), @@ -255,20 +246,7 @@ class Interface(base.APIBase): bookmark=True) ] - ifclass = rpc_interface.as_dict()['ifclass'] - networks = rpc_interface.as_dict()['networks'] - networktypelist = [] - if ifclass == constants.INTERFACE_CLASS_PLATFORM: - for network_id in networks: - network = pecan.request.dbapi.network_get_by_id(network_id) - networktypelist.append(network.type) - elif ifclass: - networktypelist.append(ifclass) - else: - networktypelist.append(constants.INTERFACE_CLASS_NONE) - if not any(networktype in address.ALLOWED_NETWORK_TYPES - for networktype in networktypelist): - + if _is_interface_address_allowed(rpc_interface.as_dict()): # Hide this functionality when the network type does not support # setting or updating the network type interface.ipv4_mode = wtypes.Unset @@ -452,10 +430,6 @@ class InterfaceController(rest.RestController): uses = None ports = None - ethernet_port_mac = None - networks = [] - networks_to_add = [] - interface_networks_to_remove = [] datanetworks = [] datanetworks_to_add = [] interface_datanetworks_to_remove = [] @@ -470,15 +444,6 @@ class InterfaceController(rest.RestController): elif '/ports' == p['path']: ports = p['value'] patches_to_remove.append(p) - elif '/networks' == p['path']: - networks = p['value'].split(',') - patches_to_remove.append(p) - elif '/networks_to_add' == p['path']: - networks_to_add = p['value'].split(',') - patches_to_remove.append(p) - elif '/interface_networks_to_remove' == p['path']: - interface_networks_to_remove = p['value'].split(',') - patches_to_remove.append(p) elif '/datanetworks' == p['path']: datanetworks = p['value'].split(',') patches_to_remove.append(p) @@ -495,7 +460,6 @@ class InterfaceController(rest.RestController): patch = [p for p in patch if p not in patches_to_remove] LOG.debug("patch_ports: %s" % ports) - LOG.debug("patch_networks: %s" % networks) rpc_interface = objects.interface.get_by_uuid(pecan.request.context, interface_uuid) @@ -550,7 +514,6 @@ class InterfaceController(rest.RestController): for p in interface_ports: if p is not None: ports = p.name - ethernet_port_mac = p.mac break # Process updates @@ -585,19 +548,13 @@ class InterfaceController(rest.RestController): raise exception.PatchError(patch=patch, reason=e) # if the aemode is changed adjust the txhashpolicy if necessary - if interface['aemode'] == 'active_standby': + if interface['aemode'] == constants.AE_MODE_ACTIVE_STANDBY: interface['txhashpolicy'] = None - # The variable 'networks' contains a list of networks that the - # interface should have by the end of this update. These should be - # compared to the previous networks assigned to the interface - interface['networks'] = networks - if (not interface['ifclass'] or interface['ifclass'] == constants.INTERFACE_CLASS_NONE): # If the interface class is reset, make sure any network # specific fields are reset as well - interface['networktype'] = None interface['sriov_numvfs'] = 0 interface['sriov_vf_driver'] = None interface['ipv4_mode'] = None @@ -635,15 +592,14 @@ class InterfaceController(rest.RestController): orig_ifclass = rpc_interface['ifclass'] if (not ifclass and orig_ifclass == constants.INTERFACE_CLASS_PLATFORM): - for network_id in rpc_interface['networks']: - network = pecan.request.dbapi.network_get_by_id(network_id) - if network.type == constants.NETWORK_TYPE_MGMT: - # Remove mgmt address associated with this interface - pecan.request.rpcapi.mgmt_ip_set_by_ihost( - pecan.request.context, - ihost['uuid'], - interface['id'], - None) + if _is_interface_network_assigned(interface, + constants.NETWORK_TYPE_MGMT): + # Remove mgmt address associated with this interface + pecan.request.rpcapi.mgmt_ip_set_by_ihost( + pecan.request.context, + ihost['uuid'], + interface['id'], + None) if delete_addressing: for family in constants.IP_FAMILIES: @@ -699,44 +655,6 @@ class InterfaceController(rest.RestController): saved_interface = copy.deepcopy(rpc_interface) - # Update interface-network - try: - if networks_to_add: - for network_id in networks_to_add: - values = {'interface_id': interface['id'], - 'network_id': network_id} - pecan.request.dbapi.interface_network_create(values) - elif networks: - for network_id in networks: - values = {'interface_id': interface['id'], - 'network_id': network_id} - pecan.request.dbapi.interface_network_create(values) - except exception.InterfaceNetworkAlreadyExists: - pass - except Exception as e: - LOG.exception(e) - msg = _("Failed to create interface network association for " - "interface %s" % (interface['ifname'])) - raise wsme.exc.ClientSideError(msg) - - try: - # Remove old networks from the interface - if interface_networks_to_remove: - for ifnet_id in interface_networks_to_remove: - pecan.request.dbapi.interface_network_destroy(ifnet_id) - elif (orig_ifclass == constants.INTERFACE_CLASS_PLATFORM and - (not ifclass or - ifclass != constants.INTERFACE_CLASS_PLATFORM)): - ifnets = pecan.request.dbapi.interface_network_get_by_interface( - rpc_interface['uuid']) - for ifnet in ifnets: - pecan.request.dbapi.interface_network_destroy(ifnet.uuid) - except Exception as e: - LOG.exception(e) - msg = _("Failed to remove interface network association for " - "interface %s" % (interface['ifname'])) - raise wsme.exc.ClientSideError(msg) - try: # Update only the fields that have changed for field in objects.interface.fields: @@ -749,31 +667,6 @@ class InterfaceController(rest.RestController): new_interface = objects.interface.get_by_uuid( pecan.request.context, rpc_interface.uuid) - networktypelist = [] - if new_interface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM: - for network_id in new_interface['networks']: - network = pecan.request.dbapi.network_get_by_id(network_id) - networktypelist.append(network.type) - elif new_interface['ifclass']: - networktypelist = [new_interface['ifclass']] - else: - networktypelist = [constants.NETWORK_TYPE_NONE] - - # Update mgmt_ip and mgmt_mac (if required) - if constants.NETWORK_TYPE_MGMT in networktypelist: - _update_host_mgmt_address(ihost, interface) - _update_host_mgmt_mac(ihost, ethernet_port_mac) - - if constants.NETWORK_TYPE_CLUSTER_HOST in networktypelist: - _update_host_cluster_address(ihost, interface) - if constants.NETWORK_TYPE_IRONIC in networktypelist: - _update_host_ironic_address(ihost, interface) - if ihost['personality'] == constants.CONTROLLER: - if constants.NETWORK_TYPE_OAM in networktypelist: - _update_host_oam_address(ihost, interface) - elif constants.NETWORK_TYPE_PXEBOOT in networktypelist: - _update_host_pxeboot_address(ihost, interface) - # Update the MTU of underlying interfaces of an AE if new_interface['iftype'] == constants.INTERFACE_TYPE_AE: for ifname in new_interface['uses']: @@ -904,44 +797,18 @@ def _set_address_family_defaults_by_pool(defaults, pool_type): def _set_defaults(interface): defaults = {'imtu': DEFAULT_MTU, - 'networktype': constants.NETWORK_TYPE_DATA, - 'aemode': 'active_standby', + 'aemode': constants.AE_MODE_ACTIVE_STANDBY, 'txhashpolicy': None, 'vlan_id': None, 'sriov_numvfs': 0, 'sriov_vf_driver': None} - networktypelist = [] - if interface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM: - if interface['networks']: - for network_id in interface['networks']: - network = pecan.request.dbapi.network_get_by_id(network_id) - networktypelist.append(network.type) - interface['networktype'] = ",".join(networktypelist) - elif interface['networktype']: - networks = [] - networktypelist = interface['networktype'].split(',') - for network_type in networktypelist: - if network_type in constants.PLATFORM_NETWORK_TYPES: - network = pecan.request.dbapi.network_get_by_type( - network_type - ) - networks.append(str(network.id)) - interface['networks'] = networks - elif interface['ifclass'] in NEUTRON_NETWORK_TYPES: - interface['networktype'] = interface['ifclass'] - - family_defaults = [constants.NETWORK_TYPE_MGMT, - constants.NETWORK_TYPE_OAM, - constants.NETWORK_TYPE_CLUSTER_HOST] if interface['ifclass'] == constants.INTERFACE_CLASS_DATA: defaults['ipv4_mode'] = constants.IPV4_DISABLED defaults['ipv6_mode'] = constants.IPV6_DISABLED - else: - for network_type in networktypelist: - if network_type in family_defaults: - _set_address_family_defaults_by_pool(defaults, - network_type) + elif interface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM: + defaults['ipv4_mode'] = None + defaults['ipv6_mode'] = None interface_merged = interface.copy() for key in interface_merged: @@ -1012,7 +879,7 @@ def _check_interface_name(op, interface, ihost, from_profile=False): if i.id == this_interface_id: continue if i.ifname == ifname: - raise wsme.exc.ClientSideError(_("Name must be unique.")) + raise wsme.exc.ClientSideError(_("Interface Name {} must be unique.".format(ifname))) return interface @@ -1110,61 +977,22 @@ def _check_host(ihost): raise wsme.exc.ClientSideError(_("Host must be locked.")) -def _check_network_type_validity(networktypelist): - if any(nt not in VALID_NETWORK_TYPES for nt in networktypelist): - msg = (_("Network type list may only contain one or more of these " - "values: {}").format(', '.join(VALID_NETWORK_TYPES))) +def _check_interface_class_and_host_type(ihost, interface): + if (interface['ifclass'] == constants.INTERFACE_CLASS_DATA and + constants.WORKER not in ihost['subfunctions']): + msg = _("The data interface class is only supported on nodes " + "supporting worker functions") raise wsme.exc.ClientSideError(msg) -def _check_network_type_and_host_type(ihost, networktypelist): - for nt in DATA_NETWORK_TYPES: - if (nt in networktypelist and - constants.WORKER not in ihost['subfunctions']): - msg = _("The '%s' network type is only supported on nodes " - "supporting worker functions" % nt) - raise wsme.exc.ClientSideError(msg) - - if (constants.NETWORK_TYPE_OAM in networktypelist and - ihost['personality'] != constants.CONTROLLER): - msg = _("The '%s' network type is only supported on controller nodes." % - constants.NETWORK_TYPE_OAM) - raise wsme.exc.ClientSideError(msg) - - -def _check_network_type_and_interface_type(interface, networktypelist): - if interface['iftype'] == 'vlan': - if constants.NETWORK_TYPE_NONE in networktypelist: - msg = _("VLAN interfaces cannot have an interface class of %s." % - constants.NETWORK_TYPE_NONE) - raise wsme.exc.ClientSideError(msg) - - if (any(nt in networktypelist for nt in PCI_NETWORK_TYPES) and +def _check_interface_class_and_type(interface): + if (interface['ifclass'] in PCI_INTERFACE_CLASS and interface['iftype'] != "ethernet"): - msg = (_("The {} network types are only valid on Ethernet interfaces"). - format(', '.join(PCI_NETWORK_TYPES))) + msg = (_("The {} interface class is only valid on Ethernet interfaces"). + format(', '.join(PCI_INTERFACE_CLASS))) raise wsme.exc.ClientSideError(msg) -def _check_network_type_duplicates(ihost, interface, networktypelist): - # Check that we are not creating duplicate interface types - interfaces = pecan.request.dbapi.iinterface_get_by_ihost(ihost['uuid']) - for host_interface in interfaces: - if not host_interface['networks']: - continue - host_networktypelist = [] - for network_id in host_interface['networks']: - network = pecan.request.dbapi.network_get_by_id(network_id) - host_networktypelist.append(network.type) - - for nt in interface_network.NONDUPLICATE_NETWORK_TYPES: - if nt in host_networktypelist and nt in networktypelist: - if host_interface['uuid'] != interface['uuid']: - msg = _("An interface with '%s' network type is " - "already provisioned on this node" % nt) - raise wsme.exc.ClientSideError(msg) - - def _check_interface_class_transition(interface, existing_interface): if not existing_interface: return @@ -1175,8 +1003,7 @@ def _check_interface_class_transition(interface, existing_interface): if (ifclass and existing_interface[ 'ifclass'] == constants.INTERFACE_CLASS_PLATFORM and - existing_interface['used_by'] and - existing_interface['networks']): + existing_interface['used_by']): msg = _("The class of an interface with platform networks cannot " "be changed to %s since it is being used by %s" % (ifclass, existing_interface['used_by'])) @@ -1190,67 +1017,16 @@ def _check_interface_class_transition(interface, existing_interface): raise wsme.exc.ClientSideError(msg) -def _check_network_type_and_interface_name(interface, networktypelist): +def _check_interface_class_and_interface_name(interface): if (utils.get_system_mode() == constants.SYSTEM_MODE_SIMPLEX and - constants.NETWORK_TYPE_NONE in networktypelist and + interface['ifclass'] == constants.INTERFACE_CLASS_NONE and interface['ifname'] == constants.LOOPBACK_IFNAME): msg = _("The loopback interface cannot be changed for an all-in-one " "simplex system") raise wsme.exc.ClientSideError(msg) -def _check_network_type(op, interface, ihost, existing_interface): - networktypelist = [] - if interface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM: - for network_id in interface['networks']: - network = pecan.request.dbapi.network_get_by_id(network_id) - networktypelist.append(network.type) - elif interface['ifclass']: - networktypelist.append(interface['ifclass']) - else: - networktypelist.append(constants.INTERFACE_CLASS_NONE) - - _check_network_type_validity(networktypelist) - _check_interface_class_transition(interface, existing_interface) - _check_network_type_and_host_type(ihost, networktypelist) - _check_network_type_and_interface_type(interface, networktypelist) - _check_network_type_duplicates(ihost, interface, networktypelist) - _check_network_type_and_interface_name(interface, networktypelist) - - -def _check_network_type_and_port(interface, ihost, - interface_port, - host_port, - networktypelist): - if interface_port.pciaddr == host_port.pciaddr and \ - interface_port.dev_id != host_port.dev_id: - pif = pecan.request.dbapi.iinterface_get(host_port.interface_id) - if interface['id'] == pif['id']: - return - # shared devices cannot be assigned to a data and non-data - # interface at the same time - pif_networktypelist = [] - if pif.networktype is None and pif.used_by: - for name in pif.used_by: - used_by_if = pecan.request.dbapi.iinterface_get(name, - ihost['uuid']) - if used_by_if and used_by_if.networktype: - pif_networktypelist = cutils.get_network_type_list(used_by_if) - elif pif.networktype: - pif_networktypelist = cutils.get_network_type_list(pif) - if (pif_networktypelist and - ((constants.NETWORK_TYPE_DATA in pif_networktypelist and - constants.NETWORK_TYPE_DATA not in networktypelist) or - (constants.NETWORK_TYPE_DATA not in pif_networktypelist and - constants.NETWORK_TYPE_DATA in networktypelist))): - msg = (_("Shared device %(device)s cannot be shared " - "with different network types when device " - "is associated with a data network type") % - {'device': interface_port.pciaddr}) - raise wsme.exc.ClientSideError(msg) - - -def _check_interface_class(interface, existing_interface): +def _check_interface_class(ihost, interface, existing_interface): if not interface['ifclass'] or interface['ifclass'] == constants.INTERFACE_CLASS_NONE: return @@ -1258,19 +1034,10 @@ def _check_interface_class(interface, existing_interface): msg = (_("Invalid interface class %s" % interface['ifclass'])) raise wsme.exc.ClientSideError(msg) - if interface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM: - for network_id in interface['networks']: - network = pecan.request.dbapi.network_get_by_id(network_id) - if network.type not in constants.PLATFORM_NETWORK_TYPES: - msg = (_("Invalid network type %s for interface class %s" % - (network.type, interface['ifclass']))) - raise wsme.exc.ClientSideError(msg) - - if (interface['ifclass'] in NEUTRON_INTERFACE_CLASS and - interface['networks']): - msg = _("Associating platform network to interface with %s class " - "is not allowed" % interface['ifclass']) - raise wsme.exc.ClientSideError(msg) + _check_interface_class_transition(interface, existing_interface) + _check_interface_class_and_host_type(ihost, interface) + _check_interface_class_and_type(interface) + _check_interface_class_and_interface_name(interface) def _check_address_mode(op, interface, ihost, existing_interface): @@ -1282,18 +1049,7 @@ def _check_address_mode(op, interface, ihost, existing_interface): object_utils.ipv6_mode_or_none(ipv6_mode) # Check for supported interface network types - networktypelist = [] - if interface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM: - for network_id in interface['networks']: - network = pecan.request.dbapi.network_get_by_id(network_id) - networktypelist.append(network.type) - elif interface['ifclass']: - networktypelist.append(interface['ifclass']) - else: - networktypelist.append(constants.INTERFACE_CLASS_NONE) - - if not any(network_type in address.ALLOWED_NETWORK_TYPES - for network_type in networktypelist): + if _is_interface_address_allowed(interface): if (ipv4_mode and ipv4_mode != constants.IPV4_DISABLED): raise exception.AddressModeOnlyOnSupportedTypes( types=", ".join(address.ALLOWED_NETWORK_TYPES)) @@ -1355,30 +1111,31 @@ def _check_address_mode(op, interface, ihost, existing_interface): family=constants.IP_FAMILIES[constants.IPV6_FAMILY]) -def _check_networks(interface): - NONASSIGNABLE_WITH_OAM = [constants.NETWORK_TYPE_MGMT, - constants.NETWORK_TYPE_PXEBOOT, - constants.NETWORK_TYPE_CLUSTER_HOST] - ifclass = interface['ifclass'] - networks = interface['networks'] - if ifclass == constants.INTERFACE_CLASS_PLATFORM and len(networks) > 1: - networktypelist = [] - for network_id in networks: - network = pecan.request.dbapi.network_get_by_id(network_id) - networktypelist.append(network.type) - if constants.NETWORK_TYPE_PXEBOOT in networktypelist: - msg = _("An interface assigned with a network of " - "type '%s' cannot contain additional networks." - % constants.NETWORK_TYPE_PXEBOOT) - raise wsme.exc.ClientSideError(msg) - elif any(network_type in NONASSIGNABLE_WITH_OAM - for network_type in networktypelist) and \ - any(network_type == constants.NETWORK_TYPE_OAM - for network_type in networktypelist): - msg = _("An interface assigned with a network of " - "type '%s' cannot assign any networks " - "of type '%s'." - % (constants.NETWORK_TYPE_OAM, NONASSIGNABLE_WITH_OAM)) +def _check_network_type_and_port(interface, ihost, + interface_port, + host_port): + if interface_port.pciaddr == host_port.pciaddr and \ + interface_port.dev_id != host_port.dev_id: + pif = pecan.request.dbapi.iinterface_get(host_port.interface_id) + if interface['id'] == pif['id']: + return + # shared devices cannot be assigned to different interface classes + # at the same time + error_found = False + if not pif['ifclass'] and pif.used_by: + for name in pif.used_by: + used_by_if = pecan.request.dbapi.iinterface_get(name, + ihost['uuid']) + if used_by_if['ifclass'] != interface['ifclass']: + error_found = True + break + elif pif['ifclass'] and pif['ifclass'] != interface['ifclass']: + error_found = True + + if error_found: + msg = (_("Shared device %(device)s cannot be shared " + "with different interface classes ") % + {'device': interface_port.pciaddr}) raise wsme.exc.ClientSideError(msg) @@ -1386,7 +1143,6 @@ def _check_datanetworks(ihost, interface, interface_list, existing_interface, - networktypelist, datanetworks=None): if 'id' in interface: @@ -1427,9 +1183,6 @@ def _check_datanetworks(ihost, datanetworks_list.append(dn.name) if interface['ifclass'] in NEUTRON_INTERFACE_CLASS: - if not datanetworks: - msg = _("At least one data network must be selected.") - raise wsme.exc.ClientSideError(msg) if len(datanetworks) > MAX_DATANETWORK_LEN: msg = _("Data network list must not exceed %d characters." % MAX_DATANETWORK_LEN) @@ -1466,7 +1219,7 @@ def _check_datanetworks(ihost, # This ensures that a specific data network type can # only be assigned to 1 data interface. Such as the case of # when only 1 vxlan data is required when SDN is enabled - if constants.NETWORK_TYPE_DATA in networktypelist and interface_list: + if interface['ifclass'] == constants.INTERFACE_CLASS_DATA and interface_list: for pn in [n.strip() for n in datanetworks_list]: for i in interface_list: if i.id == this_interface_id: @@ -1488,7 +1241,7 @@ def _check_datanetworks(ihost, raise wsme.exc.ClientSideError(msg) elif (not _neutron_providernet_extension_supported() and - any(nt in PCI_NETWORK_TYPES for nt in networktypelist)): + interface['ifclass'] in PCI_INTERFACE_CLASS): # When the neutron implementation is not our own and it does not # support our data network extension we still want to do minimal # validation of the data network list but we cannot do more @@ -1516,16 +1269,6 @@ def _check_interface_data(op, interface, ihost, existing_interface, # Get data ihost_id = interface['forihostid'] ihost_uuid = interface['ihost_uuid'] - ifclass = interface['ifclass'] - networktypelist = [] - if ifclass == constants.INTERFACE_CLASS_PLATFORM: - for network_id in interface['networks']: - platform_network = pecan.request.dbapi.network_get_by_id(network_id) - networktypelist.append(platform_network.type) - elif ifclass: - networktypelist.append(ifclass) - else: - networktypelist.append(constants.INTERFACE_CLASS_NONE) # Check interface name for validity _check_interface_name(op, interface, ihost, existing_interface) @@ -1610,14 +1353,7 @@ def _check_interface_data(op, interface, ihost, existing_interface, raise wsme.exc.ClientSideError(msg) # check interface class validity - _check_interface_class(interface, existing_interface) - - # check networktype combinations and transitions for validity - _check_network_type(op, interface, ihost, existing_interface) - - # check to ensure that the interface assigned with an OAM or - # PXEBOOT network has no other networks - _check_networks(interface) + _check_interface_class(ihost, interface, existing_interface) # check mode/pool combinations and transitions for validity _check_address_mode(op, interface, ihost, existing_interface) @@ -1626,12 +1362,15 @@ def _check_interface_data(op, interface, ihost, existing_interface, aemode = interface['aemode'] txhashpolicy = interface['txhashpolicy'] - if aemode in ['balanced', '802.3ad'] and not txhashpolicy: + if iftype == constants.INTERFACE_TYPE_AE and aemode not in constants.VALID_AEMODE_LIST: + msg = _("Invalid aggregated ethernet mode '%s' " % aemode) + raise wsme.exc.ClientSideError(msg) + if aemode in [constants.AE_MODE_BALANCED, constants.AE_MODE_LACP] and not txhashpolicy: msg = _("Device interface with interface type 'aggregated ethernet' " "in 'balanced' or '802.3ad' mode require a valid Tx Hash " "Policy.") raise wsme.exc.ClientSideError(msg) - elif aemode in ['active_standby'] and txhashpolicy is not None: + elif aemode in [constants.AE_MODE_ACTIVE_STANDBY] and txhashpolicy is not None: msg = _("Device interface with interface type 'aggregated ethernet' " "in '%s' mode should not specify a Tx Hash Policy." % aemode) raise wsme.exc.ClientSideError(msg) @@ -1649,37 +1388,40 @@ def _check_interface_data(op, interface, ihost, existing_interface, # Make sure network type 'data' with if type 'ae' can only be in ae mode # 'active_standby', 'balanced', or '802.3ad', and can only support a # txhashpolicy of 'layer2'. - for nt in DATA_NETWORK_TYPES: - if iftype == 'ae' and nt in networktypelist: - if aemode not in ['balanced', 'active_standby', '802.3ad']: - msg = _("Device interface with network type '%s', and interface " - "type 'aggregated ethernet' must be in mode " - "'active_standby', 'balanced', or '802.3ad'." % nt) - raise wsme.exc.ClientSideError(msg) - if aemode in ['balanced', '802.3ad'] and txhashpolicy != 'layer2': - msg = _("Device interface with network type '%s', and interface " - "type 'aggregated ethernet' must have a Tx Hash Policy of " - "'layer2'." % nt) - raise wsme.exc.ClientSideError(msg) + if (iftype == constants.INTERFACE_TYPE_AE and + interface['ifclass'] == constants.INTERFACE_CLASS_DATA): + if aemode not in constants.VALID_AEMODE_LIST: + msg = _("Device interface with interface class '%s', and interface " + "type 'aggregated ethernet' must be in mode " + "'active_standby', 'balanced', or '802.3ad'." % constants.INTERFACE_CLASS_DATA) + raise wsme.exc.ClientSideError(msg) + if aemode in [constants.AE_MODE_BALANCED, constants.AE_MODE_LACP] and txhashpolicy != 'layer2': + msg = _("Device interface with interface class '%s', and interface " + "type 'aggregated ethernet' must have a Tx Hash Policy of " + "'layer2'." % constants.INTERFACE_CLASS_DATA) + raise wsme.exc.ClientSideError(msg) # Make sure network type 'mgmt', with if type 'ae', # can only be in ae mode 'active_standby' or '802.3ad' - valid_mgmt_aemode = ['802.3ad', 'active_standby'] - if (constants.NETWORK_TYPE_MGMT in networktypelist and iftype == 'ae' and - aemode not in valid_mgmt_aemode): - msg = _("Device interface with network type {}, and interface " - "type 'aggregated ethernet' must be in mode {}").format( - (str(networktypelist)), ', '.join(valid_mgmt_aemode)) - raise wsme.exc.ClientSideError(msg) + valid_mgmt_aemode = [constants.AE_MODE_LACP, constants.AE_MODE_ACTIVE_STANDBY] + if interface['networktypelist'] is not None: + if (constants.NETWORK_TYPE_MGMT in interface['networktypelist'] and + iftype == constants.INTERFACE_TYPE_AE and + aemode not in valid_mgmt_aemode): + msg = _("Device interface with network type {}, and interface " + "type 'aggregated ethernet' must be in mode {}").format( + (str(interface['networktypelist'])), ', '.join(valid_mgmt_aemode)) + raise wsme.exc.ClientSideError(msg) # Make sure network type 'oam' or 'cluster-host', with if type 'ae', # can only be in ae mode 'active_standby' or 'balanced' or '802.3ad' - if (any(network in [constants.NETWORK_TYPE_OAM, constants.NETWORK_TYPE_CLUSTER_HOST] for network in networktypelist) and - iftype == 'ae' and (aemode not in VALID_AEMODE_LIST)): - msg = _("Device interface with network type '%s', and interface " - "type 'aggregated ethernet' must be in mode 'active_standby' " - "or 'balanced' or '802.3ad'." % (str(networktypelist))) - raise wsme.exc.ClientSideError(msg) + if interface['networktypelist'] is not None: + if (any(network in [constants.NETWORK_TYPE_OAM, constants.NETWORK_TYPE_CLUSTER_HOST] for network in interface['networktypelist']) and + iftype == constants.INTERFACE_TYPE_AE and (aemode not in constants.VALID_AEMODE_LIST)): + msg = _("Device interface with network type '%s', and interface " + "type 'aggregated ethernet' must be in mode 'active_standby' " + "or 'balanced' or '802.3ad'." % (str(interface['networktypelist']))) + raise wsme.exc.ClientSideError(msg) # Ensure that data and non-data interfaces are not using the same # shared device @@ -1695,15 +1437,13 @@ def _check_interface_data(op, interface, ihost, existing_interface, for host_port in port_list_host: _check_network_type_and_port(interface, ihost, interface_port, - host_port, - networktypelist) + host_port) # Check datanetworks (formerly known as providernetworks) _check_datanetworks(ihost, interface, interface_list, existing_interface, - networktypelist, datanetworks) # check MTU @@ -1724,34 +1464,6 @@ def _check_interface_data(op, interface, ihost, existing_interface, (interface['imtu'], mtu)) raise wsme.exc.ClientSideError(msg) - # Check if cluster-host exists on controller, if it doesn't then fail - if (ihost['personality'] != constants.CONTROLLER and - constants.NETWORK_TYPE_CLUSTER_HOST in networktypelist): - host_list = pecan.request.dbapi.ihost_get_by_personality( - personality=constants.CONTROLLER) - cluster_host_on_controller = False - for h in host_list: - # find any interface in controller host that is of type cluster-host - interfaces = pecan.request.dbapi.iinterface_get_by_ihost(ihost=h['uuid']) - for host_interface in interfaces: - if host_interface['ifclass']: - hi_networktypelist = [] - if host_interface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM: - for network_id in host_interface['networks']: - network = pecan.request.dbapi.network_get_by_id(network_id) - hi_networktypelist.append(network.type) - else: - hi_networktypelist.append(host_interface['ifclass']) - if constants.NETWORK_TYPE_CLUSTER_HOST in hi_networktypelist: - cluster_host_on_controller = True - break - if cluster_host_on_controller is True: - break - if not cluster_host_on_controller: - msg = _("Interface %s does not have associated" - " cluster-host interface on controller." % interface['ifname']) - raise wsme.exc.ClientSideError(msg) - return interface @@ -1788,7 +1500,7 @@ def _check_ports(op, interface, ihost, ports): # The the bottom line will prevent it if p.name == "".join(interface['ifname'].split()): - if interface['iftype'] == 'ae': + if interface['iftype'] == constants.INTERFACE_TYPE_AE: msg = _("Aggregated Ethernet interface name cannot be '%s'. " "An Aggregated Ethernet name must not be the same as" " an existing port name. " % p.name) @@ -1826,56 +1538,21 @@ def _check_ports(op, interface, ihost, ports): # raise wsme.exc.ClientSideError(msg) # Perform network type checks for shared PCI devices. - networktypelist = [] - if interface['networktype']: - networktypelist = cutils.get_network_type_list(interface) - if constants.NETWORK_TYPE_NONE not in networktypelist: + if interface['networktypelist']: for p in port_list: interface_port = \ pecan.request.dbapi.ethernet_port_get(p, ihost['id']) for host_port in host_ports: _check_network_type_and_port(interface, ihost, interface_port, - host_port, - networktypelist) - - -def _update_address_mode(interface, family, mode, pool): - interface_id = interface['id'] - pool_id = pecan.request.dbapi.address_pool_get(pool)['id'] if pool else None - try: - # retrieve the existing value and compare - existing = pecan.request.dbapi.address_mode_query( - interface_id, family) - if existing.mode == mode: - if (mode != 'pool' or existing.pool_uuid == pool): - return - if existing.mode == 'pool' or (not mode or mode == 'disabled'): - pecan.request.dbapi.routes_destroy_by_interface( - interface_id, family) - pecan.request.dbapi.addresses_destroy_by_interface( - interface_id, family) - except exception.AddressModeNotFoundByFamily: - # continue and update DB with new record - pass - updates = {'family': family, 'mode': mode, 'address_pool_id': pool_id} - pecan.request.dbapi.address_mode_update(interface_id, updates) + host_port) def _delete_addressing(interface, family, existing_interface): interface_id = interface['id'] pecan.request.dbapi.routes_destroy_by_interface( interface_id, family) - for network_id in existing_interface['networks']: - network = pecan.request.dbapi.network_get_by_id(network_id) - orig_networktype = network.type - if ((orig_networktype == constants.NETWORK_TYPE_OAM) or - (orig_networktype == constants.NETWORK_TYPE_PXEBOOT)): - pecan.request.dbapi.addresses_remove_interface_by_interface( - interface['id'] - ) - elif ((orig_networktype != constants.NETWORK_TYPE_MGMT) and - (orig_networktype != constants.NETWORK_TYPE_CLUSTER_HOST)): + if existing_interface['ifclass'] != constants.INTERFACE_CLASS_PLATFORM: pecan.request.dbapi.addresses_destroy_by_interface( interface_id, family) pecan.request.dbapi.address_modes_destroy_by_interface( @@ -1891,7 +1568,7 @@ def _update_ipv6_address_mode(interface, mode=None, pool=None, from_profile=False): mode = interface['ipv6_mode'] if not mode else mode pool = interface['ipv6_pool'] if not pool else pool - _update_address_mode(interface, constants.IPV6_FAMILY, mode, pool) + utils.update_address_mode(interface, constants.IPV6_FAMILY, mode, pool) if mode == constants.IPV6_POOL and not from_profile: _allocate_pool_address(interface['id'], pool) @@ -1900,7 +1577,7 @@ def _update_ipv4_address_mode(interface, mode=None, pool=None, interface_profile=False): mode = interface['ipv4_mode'] if not mode else mode pool = interface['ipv4_pool'] if not pool else pool - _update_address_mode(interface, constants.IPV4_FAMILY, mode, pool) + utils.update_address_mode(interface, constants.IPV4_FAMILY, mode, pool) if mode == constants.IPV4_POOL and not interface_profile: _allocate_pool_address(interface['id'], pool) @@ -1929,11 +1606,12 @@ def _add_extended_attributes(ihost, interface, attributes): that got added before sending the object to the database. """ interface_data = interface.as_dict() - networktype = interface_data['networktype'] - if networktype not in address.ALLOWED_NETWORK_TYPES: + if interface_data['ifclass'] not in [constants.INTERFACE_CLASS_DATA, + constants.INTERFACE_CLASS_PLATFORM]: # No need to create new address mode records if the interface type # does not support it return + if attributes.get('ipv4_mode'): _update_ipv4_address_mode(interface_data, attributes.get('ipv4_mode'), @@ -1977,91 +1655,6 @@ def _update_ports(op, interface, ihost, ports): raise wsme.exc.ClientSideError(msg) -def _update_host_mgmt_address(host, interface): - """Check if the host has a static management IP address assigned - and ensure the address is populated against the interface. Otherwise, - if using dynamic address allocation, then allocate an address - """ - - mgmt_ip = utils.lookup_static_ip_address( - host.hostname, constants.NETWORK_TYPE_MGMT) - - if mgmt_ip: - pecan.request.rpcapi.mgmt_ip_set_by_ihost( - pecan.request.context, host.uuid, interface['id'], mgmt_ip) - elif _dynamic_address_allocation(): - mgmt_pool_uuid = pecan.request.dbapi.network_get_by_type( - constants.NETWORK_TYPE_MGMT - ).pool_uuid - address_name = cutils.format_address_name(host.hostname, - constants.NETWORK_TYPE_MGMT) - _allocate_pool_address(interface['id'], mgmt_pool_uuid, address_name) - - -def _update_host_mgmt_mac(host, mgmt_mac): - """Update host mgmt mac to reflect interface change. - """ - - if (os.path.isfile(constants.ANSIBLE_BOOTSTRAP_FLAG) and - mgmt_mac is not None): - # This must be called during management interface provisioning - # following controller-0 bootstrap. - if host['mgmt_mac'] != mgmt_mac: - pecan.request.rpcapi.mgmt_mac_set_by_ihost( - pecan.request.context, host, mgmt_mac) - - -def _update_host_oam_address(host, interface): - if utils.get_system_mode() == constants.SYSTEM_MODE_SIMPLEX: - address_name = cutils.format_address_name(constants.CONTROLLER_HOSTNAME, - constants.NETWORK_TYPE_OAM) - else: - address_name = cutils.format_address_name(host.hostname, - constants.NETWORK_TYPE_OAM) - address = pecan.request.dbapi.address_get_by_name(address_name) - if not interface['networktype']: - updates = {'interface_id': None} - else: - updates = {'interface_id': interface['id']} - pecan.request.dbapi.address_update(address.uuid, updates) - - -def _update_host_pxeboot_address(host, interface): - address_name = cutils.format_address_name(host.hostname, - constants.NETWORK_TYPE_PXEBOOT) - address = pecan.request.dbapi.address_get_by_name(address_name) - updates = {'interface_id': interface['id']} - pecan.request.dbapi.address_update(address.uuid, updates) - - -def _update_host_cluster_address(host, interface): - """ - Check if the host has a cluster-host IP address assigned - and the address is populated against the interface. - Otherwise, allocate an address from the pool. - """ - address_name = cutils.format_address_name( - host.hostname, constants.NETWORK_TYPE_CLUSTER_HOST) - try: - address = pecan.request.dbapi.address_get_by_name(address_name) - updates = {'interface_id': interface['id']} - pecan.request.dbapi.address_update(address.uuid, updates) - except exception.AddressNotFoundByName: - cluster_host_pool_uuid = pecan.request.dbapi.network_get_by_type( - constants.NETWORK_TYPE_CLUSTER_HOST - ).pool_uuid - _allocate_pool_address(interface['id'], cluster_host_pool_uuid, - address_name) - - -def _update_host_ironic_address(host, interface): - address_name = cutils.format_address_name(host.hostname, - constants.NETWORK_TYPE_IRONIC) - address = pecan.request.dbapi.address_get_by_name(address_name) - updates = {'interface_id': interface['id']} - pecan.request.dbapi.address_update(address.uuid, updates) - - def _get_interface_vlans(ihost_uuid, interface): """ Retrieve the VLAN id values (if any) that are dependent on this @@ -2396,7 +1989,6 @@ def _create(interface, from_profile=False): # Get ports ports = None - ethernet_port_mac = None uses_if = None if 'uses' in interface: @@ -2414,11 +2006,6 @@ def _create(interface, from_profile=False): elif 'used_by' not in interface: interface.update({'used_by': []}) - if 'networks' in interface and interface['networks'] is None: - interface.update({'networks': []}) - elif 'networks' not in interface: - interface.update({'networks': []}) - # Check mtu before setting defaults interface = _check_interface_mtu(interface, ihost, from_profile=from_profile) @@ -2435,7 +2022,6 @@ def _create(interface, from_profile=False): if not from_profile: # Select appropriate MAC address from lower interface(s) interface = set_interface_mac(ihost, interface) - ethernet_port_mac = interface['imac'] new_interface = pecan.request.dbapi.iinterface_create( forihostid, @@ -2444,22 +2030,6 @@ def _create(interface, from_profile=False): # Create interface-datanetworks _update_interface_datanetworks(ihost['uuid'], new_interface, datanetworks) - # Create network-interface - try: - if (new_interface['ifclass'] and - new_interface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM): - if 'networks' in interface.keys() and interface['networks']: - for network_id in interface['networks']: - values = {'interface_id': new_interface['id'], - 'network_id': network_id} - pecan.request.dbapi.interface_network_create(values) - except Exception as e: - LOG.exception("Failed to create network interface association: " - "new_interface={} interface={}".format( - new_interface.as_dict(), interface)) - pecan.request.dbapi.iinterface_destroy(new_interface.as_dict()['uuid']) - raise e - try: # Add extended attributes stored in other tables _add_extended_attributes(ihost['uuid'], new_interface, interface) @@ -2519,41 +2089,6 @@ def _create(interface, from_profile=False): pecan.request.dbapi.iinterface_destroy(new_interface['uuid']) raise e - if ihost['recordtype'] != "profile": - try: - ifclass = new_interface['ifclass'] - if ifclass == constants.INTERFACE_CLASS_PLATFORM and interface['networks']: - for network_id in interface['networks']: - network = pecan.request.dbapi.network_get_by_id(network_id) - if network.type == constants.NETWORK_TYPE_MGMT: - _update_host_mgmt_address(ihost, new_interface.as_dict()) - _update_host_mgmt_mac(ihost, ethernet_port_mac) - elif network.type == constants.NETWORK_TYPE_CLUSTER_HOST: - _update_host_cluster_address(ihost, - new_interface.as_dict()) - if ihost['personality'] == constants.CONTROLLER: - if network.type == constants.NETWORK_TYPE_OAM: - _update_host_oam_address(ihost, new_interface.as_dict()) - elif network.type == constants.NETWORK_TYPE_PXEBOOT: - _update_host_pxeboot_address(ihost, new_interface.as_dict()) - - except Exception as e: - LOG.exception( - "Failed to add static interface address: " - "interface={}".format(new_interface.as_dict())) - pecan.request.dbapi.iinterface_destroy( - new_interface.as_dict()['uuid']) - raise e - - # Covers off LAG case here. - ifclass = new_interface['ifclass'] - if ifclass == constants.INTERFACE_CLASS_PLATFORM and interface['networks']: - for network_id in interface['networks']: - network = pecan.request.dbapi.network_get_by_id(network_id) - if network.type == constants.NETWORK_TYPE_MGMT: - cutils.perform_distributed_cloud_config(pecan.request.dbapi, - new_interface['id']) - return new_interface @@ -2661,9 +2196,10 @@ def _delete(interface, from_profile=False): # Allow the removal of the virtual management interface during bootstrap. # Once the initial configuration is complete, this type of request will be # rejected. - if (interface['networks'] and cutils.is_initial_config_complete()): - for network_id in interface['networks']: - network = pecan.request.dbapi.network_get_by_id(network_id) + if cutils.is_initial_config_complete(): + ifnets = pecan.request.dbapi.interface_network_get_by_interface(interface['uuid']) + for ifnet in ifnets: + network = pecan.request.dbapi.network_get_by_id(ifnet.network_id) if interface['iftype'] == constants.INTERFACE_TYPE_VIRTUAL and \ network.type == constants.NETWORK_TYPE_MGMT: msg = _("Cannot delete a virtual management interface.") @@ -2706,8 +2242,9 @@ def _delete(interface, from_profile=False): try: primary_ifclass = interface['ifclass'] if primary_ifclass == constants.INTERFACE_CLASS_PLATFORM: - for network_id in interface['networks']: - network = pecan.request.dbapi.network_get_by_id(network_id) + ifnets = pecan.request.dbapi.interface_network_get_by_interface(interface['uuid']) + for ifnet in ifnets: + network = pecan.request.dbapi.network_get_by_id(ifnet.network_id) if ((network.type == constants.NETWORK_TYPE_MGMT) or (network.type == constants.NETWORK_TYPE_CLUSTER_HOST) or (network.type == constants.NETWORK_TYPE_PXEBOOT) or @@ -2729,3 +2266,26 @@ def _delete(interface, from_profile=False): msg = _("Delete interface failed: host %s if %s" % (ihost['hostname'], interface['ifname'])) raise wsme.exc.ClientSideError(msg) + + +def _is_interface_network_assigned(interface, network_type): + network = pecan.request.dbapi.network_get_by_type(network_type) + interface_network_dict = {} + interface_network_dict['interface_id'] = interface['id'] + interface_network_dict['network_id'] = network['id'] + try: + pecan.request.dbapi.interface_network_query(interface_network_dict) + except exception.InterfaceNetworkNotFoundByHostInterfaceNetwork: + return False + return True + + +def _is_interface_address_allowed(interface): + if interface['ifclass'] in PCI_INTERFACE_CLASS: + return False + elif interface['ifclass'] == constants.INTERFACE_CLASS_DATA: + return True + elif (interface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM and + interface['networktypelist'] is not None): + if not any(nt in address.ALLOWED_NETWORK_TYPES for nt in interface['networktypelist']): + return False diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface_datanetwork.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface_datanetwork.py index 1c37683682..ff4910fa3b 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface_datanetwork.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface_datanetwork.py @@ -200,8 +200,10 @@ class InterfaceDataNetworkController(rest.RestController): pecan.request.dbapi.iinterface_update(interface_obj.uuid, values) return else: - # Allow ifclass data to assign another; disallow other ifclass - if interface_obj.ifclass != constants.INTERFACE_CLASS_DATA: + # Allow ifclass data, pcipt and sriov to assign data networks + if interface_obj.ifclass not in [constants.INTERFACE_CLASS_DATA, + constants.INTERFACE_CLASS_PCI_PASSTHROUGH, + constants.INTERFACE_CLASS_PCI_SRIOV]: msg = _("An interface with interface class '%s' " "cannot assign datanetworks." % interface_obj.ifclass) diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface_network.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface_network.py index 61e0bc95ed..4a144261ce 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface_network.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface_network.py @@ -19,6 +19,7 @@ # Copyright (c) 2013-2018 Wind River Systems, Inc. # +import os import uuid import wsme import pecan @@ -26,6 +27,7 @@ from pecan import rest from wsme import types as wtypes import wsmeext.pecan as wsme_pecan +from sysinv.api.controllers.v1 import address_pool from sysinv.api.controllers.v1 import base from sysinv.api.controllers.v1 import collection from sysinv.api.controllers.v1 import types @@ -134,8 +136,10 @@ class InterfaceNetworkController(rest.RestController): interface_uuid = interface_network_dict.pop('interface_uuid') network_uuid = interface_network_dict.pop('network_uuid') - interface_id = self._get_interface_id(interface_uuid) + interface_obj = pecan.request.dbapi.iinterface_get(interface_uuid) + interface_id = interface_obj.id network_id, network_type = self._get_network_id_and_type(network_uuid) + host = pecan.request.dbapi.ihost_get(interface_obj.ihost_uuid) interface_network_dict['interface_id'] = interface_id interface_network_dict['network_id'] = network_id @@ -143,16 +147,51 @@ class InterfaceNetworkController(rest.RestController): self._check_interface_class(interface_uuid) self._check_assigned_network_type(network_type) self._check_duplicate_interface_network(interface_network_dict) - self._check_duplicate_type(interface_id, network_type) + self._check_duplicate_type(host, interface_uuid, network_type) self._check_pxeboot_network(interface_id, network_type) self._check_oam_network(interface_id, network_type) + self._check_network_type_and_host_type(host, network_type) + self._check_network_type_and_interface_type(interface_obj, network_type) + self._check_cluster_host_on_controller(host, interface_obj, network_type) result = pecan.request.dbapi.interface_network_create(interface_network_dict) - interface = pecan.request.dbapi.iinterface_get(interface_uuid) - if not interface.networktype: - values = {'networktype': network_type} - pecan.request.dbapi.iinterface_update(interface_uuid, values) + # Update address mode based on network type + if network_type in [constants.NETWORK_TYPE_MGMT, + constants.NETWORK_TYPE_OAM, + constants.NETWORK_TYPE_CLUSTER_HOST]: + pool_uuid = pecan.request.dbapi.network_get_by_type(network_type).pool_uuid + pool = pecan.request.dbapi.address_pool_get(pool_uuid) + if pool.family == constants.IPV4_FAMILY: + utils.update_address_mode(interface_obj, constants.IPV4_FAMILY, + constants.IPV4_STATIC, None) + utils.update_address_mode(interface_obj, constants.IPV6_FAMILY, + constants.IPV6_DISABLED, None) + else: + utils.update_address_mode(interface_obj, constants.IPV6_FAMILY, + constants.IPV6_STATIC, None) + utils.update_address_mode(interface_obj, constants.IPV4_FAMILY, + constants.IPV4_DISABLED, None) + + # Assign an address to the interface + if host.recordtype != "profile": + _update_host_address(host, interface_obj, network_type) + if network_type == constants.NETWORK_TYPE_MGMT: + ethernet_port_mac = None + if not interface_obj.uses: + # Get the ethernet port associated with the interface + interface_ports = pecan.request.dbapi.ethernet_port_get_by_interface( + interface_obj.uuid) + for p in interface_ports: + if p is not None: + ethernet_port_mac = p.mac + break + else: + tmp_interface = interface_obj.as_dict() + ethernet_port_mac = tmp_interface['imac'] + _update_host_mgmt_mac(host, ethernet_port_mac) + cutils.perform_distributed_cloud_config(pecan.request.dbapi, + interface_id) return InterfaceNetwork.convert_with_links(result) @@ -218,16 +257,15 @@ class InterfaceNetworkController(rest.RestController): % (interface_network['interface_id'], interface_network['network_id'])) raise wsme.exc.ClientSideError(msg) - def _check_duplicate_type(self, interface_id, network_type): + def _check_duplicate_type(self, host, interface_uuid, network_type): if network_type in NONDUPLICATE_NETWORK_TYPES: - interface_networks = pecan.request.dbapi.interface_network_get_all() - for i in interface_networks: - if i.interface_id == interface_id and i.network_type == network_type: - msg = _("An interface with network type '%s' is " - "already provisioned on this node." % network_type) + interfaces = pecan.request.dbapi.iinterface_get_by_ihost(host['uuid']) + for host_interface in interfaces: + if (network_type in host_interface['networktypelist'] and + host_interface['uuid'] != interface_uuid): + msg = _("An interface with '%s' network type is " + "already provisioned on this node" % network_type) raise wsme.exc.ClientSideError(msg) - else: - return def _check_assigned_network_type(self, network_type): if network_type not in NONASSIGNABLE_NETWORK_TYPES: @@ -275,6 +313,56 @@ class InterfaceNetworkController(rest.RestController): % (i.network_type, network_type)) raise wsme.exc.ClientSideError(msg) + def _check_network_type_and_host_type(self, ihost, network_type): + if (network_type == constants.NETWORK_TYPE_OAM and + ihost['personality'] != constants.CONTROLLER): + msg = _("The '%s' network type is only supported on controller nodes." % + constants.NETWORK_TYPE_OAM) + raise wsme.exc.ClientSideError(msg) + + def _check_network_type_and_interface_type(self, interface, network_type): + # Make sure network type 'mgmt', with if type 'ae', + # can only be in ae mode 'active_standby' or '802.3ad' + if (network_type == constants.NETWORK_TYPE_MGMT): + valid_mgmt_aemode = [constants.AE_MODE_LACP, + constants.AE_MODE_ACTIVE_STANDBY] + if (interface.iftype == constants.INTERFACE_TYPE_AE and + interface.aemode not in valid_mgmt_aemode): + msg = _("Device interface with network type {}, and interface " + "type 'aggregated ethernet' must be in mode {}").format( + network_type, ', '.join(valid_mgmt_aemode)) + raise wsme.exc.ClientSideError(msg) + # Make sure network type 'oam' or 'cluster-host', with if type 'ae', + # can only be in ae mode 'active_standby' or 'balanced' or '802.3ad' + elif (network_type in [constants.NETWORK_TYPE_OAM, + constants.NETWORK_TYPE_CLUSTER_HOST] and + interface.iftype == constants.INTERFACE_TYPE_AE and + (interface.aemode not in constants.VALID_AEMODE_LIST)): + msg = _("Device interface with network type '%s', and interface " + "type 'aggregated ethernet' must be in mode 'active_standby' " + "or 'balanced' or '802.3ad'." % network_type) + raise wsme.exc.ClientSideError(msg) + + def _check_cluster_host_on_controller(self, host, interface, network_type): + # Check if cluster-host exists on controller, if it doesn't then fail + if (host['personality'] != constants.CONTROLLER and + network_type == constants.NETWORK_TYPE_CLUSTER_HOST): + host_list = pecan.request.dbapi.ihost_get_by_personality( + personality=constants.CONTROLLER) + cluster_host_on_controller = False + for h in host_list: + interfaces = pecan.request.dbapi.iinterface_get_by_ihost(ihost=h['uuid']) + for host_interface in interfaces: + if (host_interface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM and + constants.NETWORK_TYPE_CLUSTER_HOST in host_interface['networktypelist']): + cluster_host_on_controller = True + break + if not cluster_host_on_controller: + msg = _("Interface %s does not have associated" + " cluster-host interface on controller." % + interface['ifname']) + raise wsme.exc.ClientSideError(msg) + def _get_interface_id(self, interface_uuid): interface = pecan.request.dbapi.iinterface_get(interface_uuid) return interface['id'] @@ -301,4 +389,125 @@ class InterfaceNetworkController(rest.RestController): @cutils.synchronized(LOCK_NAME) @wsme_pecan.wsexpose(None, types.uuid, status_code=204) def delete(self, interface_network_uuid): + # Delete address allocated to the interface + if_network_obj = pecan.request.dbapi.interface_network_get( + interface_network_uuid) + network = pecan.request.dbapi.network_get(if_network_obj.network_uuid) + pool_uuid = pecan.request.dbapi.network_get_by_type(network.type).pool_uuid + address = None + try: + address = pecan.request.dbapi.addresses_get_by_interface_pool( + if_network_obj.interface_uuid, pool_uuid) + except exception.AddressNotFoundByInterfacePool: + pass + if address: + pecan.request.dbapi.address_remove_interface(address.uuid) + pecan.request.dbapi.interface_network_destroy(interface_network_uuid) + + +def _update_host_address(host, interface, network_type): + if network_type == constants.NETWORK_TYPE_MGMT: + _update_host_mgmt_address(host, interface) + elif network_type == constants.NETWORK_TYPE_CLUSTER_HOST: + _update_host_cluster_address(host, interface) + elif network_type == constants.NETWORK_TYPE_IRONIC: + _update_host_ironic_address(host, interface) + if host.personality == constants.CONTROLLER: + if network_type == constants.NETWORK_TYPE_OAM: + _update_host_oam_address(host, interface) + elif network_type == constants.NETWORK_TYPE_PXEBOOT: + _update_host_pxeboot_address(host, interface) + + +def _dynamic_address_allocation(): + mgmt_network = pecan.request.dbapi.network_get_by_type( + constants.NETWORK_TYPE_MGMT) + return mgmt_network.dynamic + + +def _allocate_pool_address(interface_id, pool_uuid, address_name=None): + address_pool.AddressPoolController.assign_address( + interface_id, pool_uuid, address_name) + + +def _update_host_mgmt_address(host, interface): + """Check if the host has a static management IP address assigned + and ensure the address is populated against the interface. Otherwise, + if using dynamic address allocation, then allocate an address + """ + + mgmt_ip = utils.lookup_static_ip_address( + host.hostname, constants.NETWORK_TYPE_MGMT) + + if mgmt_ip: + pecan.request.rpcapi.mgmt_ip_set_by_ihost( + pecan.request.context, host.uuid, interface['id'], mgmt_ip) + elif _dynamic_address_allocation(): + mgmt_pool_uuid = pecan.request.dbapi.network_get_by_type( + constants.NETWORK_TYPE_MGMT + ).pool_uuid + address_name = cutils.format_address_name(host.hostname, + constants.NETWORK_TYPE_MGMT) + _allocate_pool_address(interface['id'], mgmt_pool_uuid, address_name) + + +def _update_host_oam_address(host, interface): + if utils.get_system_mode() == constants.SYSTEM_MODE_SIMPLEX: + address_name = cutils.format_address_name(constants.CONTROLLER_HOSTNAME, + constants.NETWORK_TYPE_OAM) + else: + address_name = cutils.format_address_name(host.hostname, + constants.NETWORK_TYPE_OAM) + address = pecan.request.dbapi.address_get_by_name(address_name) + updates = {'interface_id': interface['id']} + pecan.request.dbapi.address_update(address.uuid, updates) + + +def _update_host_pxeboot_address(host, interface): + address_name = cutils.format_address_name(host.hostname, + constants.NETWORK_TYPE_PXEBOOT) + address = pecan.request.dbapi.address_get_by_name(address_name) + updates = {'interface_id': interface['id']} + pecan.request.dbapi.address_update(address.uuid, updates) + + +def _update_host_cluster_address(host, interface): + """ + Check if the host has a cluster-host IP address assigned + and the address is populated against the interface. + Otherwise, allocate an address from the pool. + """ + address_name = cutils.format_address_name( + host.hostname, constants.NETWORK_TYPE_CLUSTER_HOST) + try: + address = pecan.request.dbapi.address_get_by_name(address_name) + updates = {'interface_id': interface['id']} + pecan.request.dbapi.address_update(address.uuid, updates) + except exception.AddressNotFoundByName: + cluster_host_pool_uuid = pecan.request.dbapi.network_get_by_type( + constants.NETWORK_TYPE_CLUSTER_HOST + ).pool_uuid + _allocate_pool_address(interface['id'], cluster_host_pool_uuid, + address_name) + + +def _update_host_ironic_address(host, interface): + address_name = cutils.format_address_name(host.hostname, + constants.NETWORK_TYPE_IRONIC) + address = pecan.request.dbapi.address_get_by_name(address_name) + updates = {'interface_id': interface['id']} + pecan.request.dbapi.address_update(address.uuid, updates) + + +def _update_host_mgmt_mac(host, mgmt_mac): + """Update host mgmt mac to reflect interface change. + """ + + if (os.path.isfile(constants.ANSIBLE_BOOTSTRAP_FLAG) and + mgmt_mac is not None): + # This must be called during management interface provisioning + # following controller-0 bootstrap. + if host['mgmt_mac'] != mgmt_mac: + pecan.request.rpcapi.mgmt_mac_set_by_ihost( + pecan.request.context, host, mgmt_mac) diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/profile.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/profile.py index 549750c501..cce627fb7f 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/profile.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/profile.py @@ -35,6 +35,7 @@ from sysinv.api.controllers.v1 import cpu as cpu_api from sysinv.api.controllers.v1 import disk as disk_api from sysinv.api.controllers.v1 import partition as partition_api from sysinv.api.controllers.v1 import interface as interface_api +from sysinv.api.controllers.v1 import interface_network as ifnet_api from sysinv.api.controllers.v1 import memory as memory_api from sysinv.api.controllers.v1 import node as node_api from sysinv.api.controllers.v1 import storage as storage_api @@ -2022,6 +2023,13 @@ def ifprofile_copy_data(host, profile): pdict = {k: v for (k, v) in p.as_dict().items() if k in ethernet_port_fields} pecan.request.dbapi.ethernet_port_create(iprofile_id, pdict) + interface_networks = pecan.request.dbapi.interface_network_get_by_interface(i.id) + for ifnet in interface_networks: + ifnetdict = {} + ifnetdict['interface_id'] = newIf.id + ifnetdict['network_id'] = ifnet.network_id + pecan.request.dbapi.interface_network_create(ifnetdict) + # Generate the uses/used_by relationships for i in newIfList: uses_list = [] @@ -2614,6 +2622,14 @@ def ifprofile_apply_to_host(host, profile): if interface_found is False: hinterface = interface_api._create(data, from_profile=True) + interface_networks = pecan.request.dbapi.interface_network_get_by_interface(interface.id) + for ifnet in interface_networks: + ifnetdict = {} + ifnetdict['interface_id'] = hinterface.id + ifnetdict['network_id'] = ifnet.network_id + pecan.request.dbapi.interface_network_create(ifnetdict) + network = pecan.request.dbapi.network_get_by_id(ifnet.network_id) + ifnet_api._update_host_address(host, hinterface, network.type) except Exception as e: # Delete all Host's interfaces @@ -2678,6 +2694,15 @@ def ifprofile_apply_to_host(host, profile): data['forihostid'] = host.id hinterface = interface_api._create(data, from_profile=True) + interface_networks = pecan.request.dbapi.interface_network_get_by_interface(i.id) + for ifnet in interface_networks: + ifnetdict = {} + ifnetdict['interface_id'] = hinterface.id + ifnetdict['network_id'] = ifnet.network_id + pecan.request.dbapi.interface_network_create(ifnetdict) + network = pecan.request.dbapi.network_get_by_id(ifnet.network_id) + ifnet_api._update_host_address(host, hinterface, network.type) + for r in profile.routes.get(i.uuid, []): pecan.request.dbapi.route_create(hinterface.id, r) diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/route.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/route.py index 4424df2b42..01930d2a26 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/route.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/route.py @@ -263,9 +263,12 @@ class RouteController(rest.RestController): def _check_interface_type(self, interface_id): interface = pecan.request.dbapi.iinterface_get(interface_id) - networktype = interface['networktype'] - if networktype not in ALLOWED_NETWORK_TYPES: - raise exception.RoutesNotSupportedOnInterfaces(iftype=networktype) + if (interface['ifclass'] == constants.INTERFACE_TYPE_PLATFORM and + interface['networktypelist'] is None): + raise exception.InterfaceNetworkNotSet() + for nt in interface['networktypelist']: + if nt not in ALLOWED_NETWORK_TYPES: + raise exception.RoutesNotSupportedOnInterfaces(type=nt) return def _check_duplicate_route(self, host_id, route): diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/system.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/system.py index c196a18d5f..079a08f003 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/system.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/system.py @@ -598,11 +598,9 @@ class SystemController(rest.RestController): host_id = controller['id'] interface_list = pecan.request.dbapi.iinterface_get_by_ihost(host_id) for interface in interface_list: - for network_id in interface['networks']: - network = pecan.request.dbapi.network_get_by_id(network_id) - if network.type == constants.NETWORK_TYPE_MGMT: - if 'vlan_id' not in interface: - return 0 - else: - return interface['vlan_id'] + if constants.NETWORK_TYPE_MGMT in interface['networktypelist']: + if 'vlan_id' not in interface: + return 0 + else: + return interface['vlan_id'] return None diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/utils.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/utils.py index b0cc94dcf9..e4f506e2e3 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/utils.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/utils.py @@ -326,6 +326,28 @@ def lookup_static_ip_address(name, networktype): return None +def update_address_mode(interface, family, mode, pool): + interface_id = interface['id'] + pool_id = pecan.request.dbapi.address_pool_get(pool)['id'] if pool else None + try: + # retrieve the existing value and compare + existing = pecan.request.dbapi.address_mode_query( + interface_id, family) + if existing.mode == mode: + if (mode != 'pool' or existing.pool_uuid == pool): + return + if existing.mode == 'pool' or (not mode or mode == 'disabled'): + pecan.request.dbapi.routes_destroy_by_interface( + interface_id, family) + pecan.request.dbapi.addresses_destroy_by_interface( + interface_id, family) + except exception.AddressModeNotFoundByFamily: + # continue and update DB with new record + pass + updates = {'family': family, 'mode': mode, 'address_pool_id': pool_id} + pecan.request.dbapi.address_mode_update(interface_id, updates) + + class SystemHelper(object): @staticmethod def get_product_build(): diff --git a/sysinv/sysinv/sysinv/sysinv/common/constants.py b/sysinv/sysinv/sysinv/sysinv/common/constants.py index f119bf8ac4..eec56af000 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/constants.py +++ b/sysinv/sysinv/sysinv/sysinv/common/constants.py @@ -651,6 +651,13 @@ INTERFACE_CLASS_DATA = 'data' INTERFACE_CLASS_PCI_PASSTHROUGH = 'pci-passthrough' INTERFACE_CLASS_PCI_SRIOV = 'pci-sriov' +AE_MODE_ACTIVE_STANDBY = 'active_standby' +AE_MODE_BALANCED = 'balanced' +AE_MODE_LACP = '802.3ad' +VALID_AEMODE_LIST = [AE_MODE_ACTIVE_STANDBY, + AE_MODE_BALANCED, + AE_MODE_LACP] + SM_MULTICAST_MGMT_IP_NAME = "sm-mgmt-ip" MTCE_MULTICAST_MGMT_IP_NAME = "mtce-mgmt-ip" PATCH_CONTROLLER_MULTICAST_MGMT_IP_NAME = "patch-controller-mgmt-ip" diff --git a/sysinv/sysinv/sysinv/sysinv/common/exception.py b/sysinv/sysinv/sysinv/sysinv/common/exception.py index 969ef5c4a8..fc753e09a8 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/exception.py +++ b/sysinv/sysinv/sysinv/sysinv/common/exception.py @@ -331,9 +331,8 @@ class InterfaceNameAlreadyExists(Conflict): message = _("Interface with name %(name)s already exists.") -class InterfaceNetworkTypeNotSet(Conflict): - message = _("The Interface must have a networktype configured to " - "support addresses. (data or infra)") +class InterfaceNetworkNotSet(Conflict): + message = _("The Interface does not have any network assigned to it.") class AddressInUseByRouteGateway(Conflict): @@ -372,7 +371,7 @@ class RouteGatewayCannotBeLocal(Conflict): class RoutesNotSupportedOnInterfaces(Conflict): message = _("Routes may not be configured against interfaces with network " - "type '%(iftype)s'") + "type '%(type)s'") class DefaultRouteNotAllowedOnVRSInterface(Conflict): @@ -699,6 +698,11 @@ class AddressNotFoundByName(NotFound): message = _("Address could not be found for %(name)s") +class AddressNotFoundByInterfacePool(NotFound): + message = _("Address could not be found for interface %(interface)s " + "pool %(pool)s") + + class AddressModeAlreadyExists(Conflict): message = _("An AddressMode with UUID %(uuid)s already exists.") diff --git a/sysinv/sysinv/sysinv/sysinv/common/utils.py b/sysinv/sysinv/sysinv/sysinv/common/utils.py index f817ec46c5..f94e85175a 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/utils.py +++ b/sysinv/sysinv/sysinv/sysinv/common/utils.py @@ -1027,51 +1027,6 @@ def get_required_platform_reserved_memory(ihost, numa_node, low_core=False): return required_reserved -def get_network_type_list(interface): - if interface['networktype']: - return [n.strip() for n in interface['networktype'].split(",")] - else: - return [] - - -def is_pci_network_types(networktypelist): - """ - Check if the network type consists of the combined PCI passthrough - and SRIOV network types. - """ - return (len(constants.PCI_NETWORK_TYPES) == len(networktypelist) and - all(i in networktypelist for i in constants.PCI_NETWORK_TYPES)) - - -def get_primary_network_type(interface): - """ - An interface can be associated with up to 2 network types but it can only - have 1 primary network type. The additional network type can only be - 'data' and is used as a placeholder to indicate that there is at least one - VLAN based neutron provider network associated to the interface. This - information is used to determine whether the vswitch on the worker needs - to control the interface or not. This function examines the list of - network types, discards the secondary type (if any) and returns the primary - network type. - """ - if not interface['ifclass'] or interface['ifclass'] == constants.INTERFACE_CLASS_NONE: - return None - primary_network_type = None - if interface['ifclass'] == constants.INTERFACE_CLASS_DATA: - primary_network_type = constants.NETWORK_TYPE_DATA - elif interface['ifclass'] == constants.INTERFACE_CLASS_PCI_PASSTHROUGH: - primary_network_type = constants.NETWORK_TYPE_PCI_PASSTHROUGH - elif interface['ifclass'] == constants.INTERFACE_CLASS_PCI_SRIOV: - primary_network_type = constants.NETWORK_TYPE_PCI_SRIOV - elif interface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM: - if not interface['networktype'] or interface[ - 'networktype'] == constants.NETWORK_TYPE_NONE: - return None - primary_network_type = interface['networktype'] - - return primary_network_type - - def get_sw_version(): return SW_VERSION @@ -1502,7 +1457,7 @@ def perform_distributed_cloud_config(dbapi, mgmt_iface_id): mate_interfaces = dbapi.iinterface_get_all( forihostid=mate_controller_id) for interface in mate_interfaces: - if interface.networktype == constants.NETWORK_TYPE_MGMT: + if constants.NETWORK_TYPE_MGMT in interface.networktypelist: mate_mgmt_iface = interface break else: diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py index f732cc4ead..f52008b740 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py @@ -1196,7 +1196,7 @@ class ConductorManager(service.PeriodicService): port_list = self.dbapi.port_get_all(host_id) ports = dict((p['interface_id'], p) for p in port_list) for interface in interface_list: - if interface.networktype == network_type: + if network_type in interface.networktypelist: return cutils.get_interface_os_ifname(interface, ifaces, ports) def _find_local_mgmt_interface_vlan_id(self): @@ -1204,7 +1204,7 @@ class ConductorManager(service.PeriodicService): host_id = self.get_my_host_id() interface_list = self.dbapi.iinterface_get_all(host_id, expunge=True) for interface in interface_list: - if interface.networktype == constants.NETWORK_TYPE_MGMT: + if constants.NETWORK_TYPE_MGMT in interface.networktypelist: if 'vlan_id' not in interface: return 0 else: @@ -1820,7 +1820,7 @@ class ConductorManager(service.PeriodicService): expunge=True) for i in iinterfaces: - if i.networktype == constants.NETWORK_TYPE_MGMT: + if constants.NETWORK_TYPE_MGMT in i.networktypelist: break cloning = False @@ -1923,7 +1923,6 @@ class ConductorManager(service.PeriodicService): 'imtu': mtu, 'iftype': 'ethernet', 'ifclass': ifclass, - 'networktype': networktype } # autocreate untagged interface @@ -1940,6 +1939,7 @@ class ConductorManager(service.PeriodicService): }) if networktype in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_PXEBOOT]: + new_interface_networktype = networktype network = self.dbapi.network_get_by_type(networktype) # create interface network association ifnet_dict = { @@ -1960,6 +1960,7 @@ class ConductorManager(service.PeriodicService): if create_tagged_interface: # autocreate tagged management interface + network = self.dbapi.network_get_by_type(constants.NETWORK_TYPE_MGMT) interface_dict = { 'forihostid': ihost['id'], 'ifname': 'mgmt0', @@ -1967,7 +1968,6 @@ class ConductorManager(service.PeriodicService): 'imtu': constants.DEFAULT_MTU, 'iftype': 'vlan', 'ifclass': constants.INTERFACE_CLASS_PLATFORM, - 'networktype': constants.NETWORK_TYPE_MGMT, 'uses': [ifname], 'vlan_id': vlan_id, } @@ -1978,6 +1978,7 @@ class ConductorManager(service.PeriodicService): new_interface = self.dbapi.iinterface_create( ihost['id'], interface_dict ) + new_interface_networktype = constants.NETWORK_TYPE_MGMT network = self.dbapi.network_get_by_type( constants.NETWORK_TYPE_MGMT ) @@ -2057,7 +2058,7 @@ class ConductorManager(service.PeriodicService): values = {'interface_id': new_interface['id']} try: addr_name = cutils.format_address_name( - ihost.hostname, new_interface['networktype']) + ihost.hostname, new_interface_networktype) address = self.dbapi.address_get_by_name(addr_name) self.dbapi.address_update(address['uuid'], values) except exception.AddressNotFoundByName: @@ -8449,7 +8450,8 @@ class ConductorManager(service.PeriodicService): if nettype: iinterfaces[:] = [i for i in iinterfaces if - i.networktype == nettype] + nettype in i.networktypelist] + return iinterfaces def mgmt_ip_set_by_ihost(self, @@ -8604,13 +8606,12 @@ class ConductorManager(service.PeriodicService): return ilvgs - def _add_port_to_list(self, interface_id, networktype, port_list): + def _add_port_to_list(self, interface_id, port_list): info = {} ports = self.dbapi.port_get_all(interfaceid=interface_id) if ports: info['name'] = ports[0]['name'] info['numa_node'] = ports[0]['numa_node'] - info['networktype'] = networktype if info not in port_list: port_list.append(info) return port_list @@ -8622,30 +8623,22 @@ class ConductorManager(service.PeriodicService): info_list = [] interface_list = self.dbapi.iinterface_get_all(ihost_id, expunge=True) for interface in interface_list: - ntype = interface['networktype'] - if (ntype == constants.NETWORK_TYPE_CLUSTER_HOST or - ntype == constants.NETWORK_TYPE_MGMT): - if interface['iftype'] == 'vlan' or \ - interface['iftype'] == 'ae': + if interface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM: + if interface['iftype'] == constants.INTERFACE_TYPE_VLAN or \ + interface['iftype'] == constants.INTERFACE_TYPE_AE: for uses_if in interface['uses']: - for i in interface_list: - if i['ifname'] == str(uses_if): - if i['iftype'] == 'ethernet': - info_list = self._add_port_to_list(i['id'], - ntype, + lower_iface = self.dbapi.iinterface_get(uses_if, ihost_id) + if lower_iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET: + info_list = self._add_port_to_list(lower_iface['id'], + info_list) + elif lower_iface['iftype'] == constants.INTERFACE_TYPE_AE: + for lower_uses_if in lower_iface['uses']: + ll_iface = self.dbapi.iinterface_get(lower_uses_if, ihost_id) + if ll_iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET: + info_list = self._add_port_to_list(ll_iface['id'], info_list) - elif i['iftype'] == 'ae': - for uses in i['uses']: - for a in interface_list: - if a['ifname'] == str(uses) and \ - a['iftype'] == 'ethernet': - info_list = self._add_port_to_list( - a['id'], - ntype, - info_list) - elif interface['iftype'] == 'ethernet': + elif interface['iftype'] == constants.INTERFACE_TYPE_ETHERNET: info_list = self._add_port_to_list(interface['id'], - ntype, info_list) LOG.info("platform_interfaces host_id=%s info_list=%s" % diff --git a/sysinv/sysinv/sysinv/sysinv/db/api.py b/sysinv/sysinv/sysinv/sysinv/db/api.py index ec7865896b..c524aa60ff 100644 --- a/sysinv/sysinv/sysinv/sysinv/db/api.py +++ b/sysinv/sysinv/sysinv/sysinv/db/api.py @@ -842,7 +842,6 @@ class Connection(object): { 'uuid': uuidutils.generate_uuid(), 'ifname': 'bond1', - 'networktype': constants.NETWORK_TYPE_DATA, 'aemode': 'balanced', 'schedpolicy': 'xor', 'txhashpolicy': 'L2', @@ -934,7 +933,6 @@ class Connection(object): { 'uuid': uuidutils.generate_uuid(), 'ifname': 'eth1', - 'networktype': constants.NETWORK_TYPE_MGMT, 'extra': { ... }, } :returns: An EthernetInterface. diff --git a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/api.py b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/api.py index 454b0f35fe..12d1b3b726 100644 --- a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/api.py +++ b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/api.py @@ -2085,14 +2085,13 @@ class Connection(api.Connection): query, models.ihost, [forihostid]) return query.all() - def _iinterface_get(self, iinterface_id, ihost=None, network=None): + def _iinterface_get(self, iinterface_id, ihost=None): entity = with_polymorphic(models.Interfaces, '*') query = model_query(entity) query = add_interface_filter(query, iinterface_id) if ihost is not None: query = add_interface_filter_by_ihost(query, ihost) - if network is not None: - query = query.filter_by(networktype=network) + try: result = query.one() except NoResultFound: @@ -2106,7 +2105,7 @@ class Connection(api.Connection): @objects.objectify(objects.interface) def iinterface_get(self, iinterface_id, ihost=None, network=None): - return self._iinterface_get(iinterface_id, ihost, network) + return self._iinterface_get(iinterface_id, ihost) @objects.objectify(objects.interface) def iinterface_get_list(self, limit=None, marker=None, @@ -2155,16 +2154,6 @@ class Connection(api.Connection): return _paginate_query(models.Interfaces, limit, marker, sort_key, sort_dir, query) - @objects.objectify(objects.interface) - def iinterface_get_by_network(self, network, - limit=None, marker=None, - sort_key=None, sort_dir=None): - entity = with_polymorphic(models.Interfaces, '*') - query = model_query(entity) - query = query.filter_by(networktype=network) - return _paginate_query(models.Interfaces, limit, marker, - sort_key, sort_dir, query) - @objects.objectify(objects.interface) def iinterface_update(self, iinterface_id, values): with _session_for_write() as session: @@ -2237,13 +2226,6 @@ class Connection(api.Connection): if obj.id is None: obj.id = temp_id - # Ensure networktype results are None when they - # are specified as 'none'. Otherwise the 'none' value is written to - # the database which causes issues with checks that expects it to be - # the None type - if getattr(obj, 'networktype', None) == constants.NETWORK_TYPE_NONE: - setattr(obj, 'networktype', None) - try: session.add(obj) session.flush() @@ -2318,8 +2300,6 @@ class Connection(api.Connection): obj = self._interface_get(models.Interfaces, interface_id) for k, v in list(values.items()): - if k == 'networktype' and v == constants.NETWORK_TYPE_NONE: - v = None if k == 'datanetworks' and v == 'none': v = None if k == 'uses': @@ -4952,6 +4932,26 @@ class Connection(api.Connection): limit, marker, sort_key, sort_dir) + @objects.objectify(objects.address) + def addresses_get_by_interface_pool(self, interface_uuid, pool_uuid, + limit=None, marker=None, + sort_key=None, sort_dir=None): + interface_id = self.iinterface_get(interface_uuid).id + pool_id = self.address_pool_get(pool_uuid).id + query = model_query(models.Addresses) + query = (query. + join(models.AddressPools, + models.AddressPools.id == pool_id). + join(models.Interfaces, + models.Interfaces.id == interface_id). + filter(models.Addresses.interface_id == interface_id). + filter(models.Addresses.address_pool_id == pool_id)) + try: + result = query.one() + except NoResultFound: + raise exception.AddressNotFoundByInterfacePool(interface=interface_uuid, pool=pool_uuid) + return result + def address_destroy(self, address_uuid): query = model_query(models.Addresses) query = add_identity_filter(query, address_uuid) diff --git a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/088_networktype_remove.py b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/088_networktype_remove.py new file mode 100644 index 0000000000..9d8c4ce736 --- /dev/null +++ b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/migrate_repo/versions/088_networktype_remove.py @@ -0,0 +1,26 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright (c) 2019 Wind River Systems, Inc. +# +# The right to copy, distribute, modify, or otherwise make use +# of this software may be licensed only pursuant to the terms +# of an applicable Wind River license agreement. +# + +from sqlalchemy import MetaData, Table + +ENGINE = 'InnoDB' +CHARSET = 'utf8' + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + interface = Table('interfaces', meta, autoload=True) + interface.drop_column('networktype') + return True + + +def downgrade(migrate_engine): + raise NotImplementedError('SysInv database downgrade is unsupported.') diff --git a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py index 490e6c0dfa..d02445e7fc 100644 --- a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py +++ b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/models.py @@ -339,7 +339,6 @@ class Interfaces(Base): ifname = Column(String(255)) ifclass = Column(String(255)) - networktype = Column(String(255)) ifcapabilities = Column(JSONEncodedDict) farend = Column(JSONEncodedDict) sriov_numvfs = Column(Integer) diff --git a/sysinv/sysinv/sysinv/sysinv/helm/neutron.py b/sysinv/sysinv/sysinv/sysinv/helm/neutron.py index 39d355e83a..6d950b9af0 100644 --- a/sysinv/sysinv/sysinv/sysinv/helm/neutron.py +++ b/sysinv/sysinv/sysinv/sysinv/helm/neutron.py @@ -355,12 +355,10 @@ class NeutronHelm(openstack.OpenstackBaseHelm): return ml2_config def _is_data_network_type(self, iface): - networktypelist = utils.get_network_type_list(iface) - return bool(any(n in DATA_NETWORK_TYPES for n in networktypelist)) + return iface.ifclass == constants.INTERFACE_CLASS_DATA def _is_sriov_network_type(self, iface): - networktypelist = utils.get_network_type_list(iface) - return bool(any(n in SRIOV_NETWORK_TYPES for n in networktypelist)) + return iface.ifclass == constants.INTERFACE_CLASS_PCI_SRIOV def _get_interface_datanets(self, iface): """ diff --git a/sysinv/sysinv/sysinv/sysinv/objects/address.py b/sysinv/sysinv/sysinv/sysinv/objects/address.py index 12df11344b..fa3720e020 100644 --- a/sysinv/sysinv/sysinv/sysinv/objects/address.py +++ b/sysinv/sysinv/sysinv/sysinv/objects/address.py @@ -24,7 +24,6 @@ class Address(base.SysinvObject): 'forihostid': utils.int_or_none, 'interface_uuid': utils.uuid_or_none, 'pool_uuid': utils.uuid_or_none, - 'networktype': utils.str_or_none, 'ifname': utils.str_or_none, 'family': utils.int_or_none, 'address': utils.ip_str_or_none(), @@ -36,8 +35,7 @@ class Address(base.SysinvObject): _foreign_fields = {'interface_uuid': 'interface:uuid', 'pool_uuid': 'address_pool:uuid', 'ifname': 'interface:ifname', - 'forihostid': 'interface:forihostid', - 'networktype': 'interface:networktype'} + 'forihostid': 'interface:forihostid'} @base.remotable_classmethod def get_by_uuid(cls, context, uuid): diff --git a/sysinv/sysinv/sysinv/sysinv/objects/interface.py b/sysinv/sysinv/sysinv/sysinv/objects/interface.py index 9f510f2f73..1d82831b33 100644 --- a/sysinv/sysinv/sysinv/sysinv/objects/interface.py +++ b/sysinv/sysinv/sysinv/sysinv/objects/interface.py @@ -78,12 +78,12 @@ def get_host_uuid(field, db_server): return host_uuid -def get_networks(field, db_object): +def get_networktypes(field, db_object): result = [] try: if getattr(db_object, 'interface_networks', None): for entry in getattr(db_object, 'interface_networks', []): - id_str = str(entry.network_id) + id_str = str(entry.network.type) result.append(id_str) except exc.DetachedInstanceError: # instrument and return empty network @@ -126,11 +126,10 @@ class Interface(base.SysinvObject): 'ifclass': utils.str_or_none, 'imac': utils.str_or_none, 'imtu': utils.int_or_none, - 'networktype': utils.str_or_none, 'aemode': utils.str_or_none, 'schedpolicy': utils.str_or_none, 'txhashpolicy': utils.str_or_none, - 'networks': utils.list_of_strings_or_none, + 'networktypelist': utils.list_of_strings_or_none, 'datanetworks': utils.list_of_strings_or_none, 'ifcapabilities': utils.dict_or_none, @@ -156,7 +155,7 @@ class Interface(base.SysinvObject): 'ipv4_pool': get_ipv4_address_pool, 'ipv6_pool': get_ipv6_address_pool, 'ihost_uuid': get_host_uuid, - 'networks': get_networks, + 'networktypelist': get_networktypes, 'datanetworks': get_datanetworks} _optional_fields = ['aemode', 'txhashpolicy', 'schedpolicy', diff --git a/sysinv/sysinv/sysinv/sysinv/objects/route.py b/sysinv/sysinv/sysinv/sysinv/objects/route.py index f3b7a919e9..eb5aa0e54a 100644 --- a/sysinv/sysinv/sysinv/sysinv/objects/route.py +++ b/sysinv/sysinv/sysinv/sysinv/objects/route.py @@ -24,7 +24,6 @@ class Route(base.SysinvObject): 'forihostid': utils.int_or_none, 'interface_uuid': utils.uuid_or_none, 'interface_id': int, - 'networktype': utils.str_or_none, 'ifname': utils.str_or_none, 'family': utils.str_or_none, 'network': utils.ip_str_or_none(), @@ -36,8 +35,7 @@ class Route(base.SysinvObject): _foreign_fields = {'interface_uuid': 'interface:uuid', 'interface_id': 'interface:id', 'ifname': 'interface:ifname', - 'forihostid': 'interface:forihostid', - 'networktype': 'interface:networktype'} + 'forihostid': 'interface:forihostid'} @base.remotable_classmethod def get_by_uuid(cls, context, uuid): diff --git a/sysinv/sysinv/sysinv/sysinv/objects/utils.py b/sysinv/sysinv/sysinv/sysinv/objects/utils.py index 66965da642..c68ea46373 100644 --- a/sysinv/sysinv/sysinv/sysinv/objects/utils.py +++ b/sysinv/sysinv/sysinv/sysinv/objects/utils.py @@ -99,8 +99,7 @@ def list_of_strings_or_none(val): if not isinstance(val, list): raise ValueError(_('A list of strings is required here')) if not all([isinstance(x, six.string_types) for x in val]): - raise ValueError(_('Invalid values found in list ' - '(strings are required)')) + raise ValueError(_('Invalid values %s found in list (strings are required)') % val) return val diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/interface.py b/sysinv/sysinv/sysinv/sysinv/puppet/interface.py index c17d899ea5..86c7fc1e3a 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/interface.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/interface.py @@ -934,7 +934,7 @@ def get_common_network_config(context, iface, config, network_id=None): layer interface (i.e., an interface that is used to terminate IP traffic). """ LOG.debug("get_common_network_config %s %s network_id=%s" % - (iface.ifname, iface.networks, network_id)) + (iface.ifname, iface.networktypelist, network_id)) traffic_classifier = get_interface_traffic_classifier(context, iface, network_id) if traffic_classifier: @@ -967,7 +967,7 @@ def get_interface_network_config(context, iface, network_id=None): # setup an alias interface if there are multiple addresses assigned # NOTE: DHCP will only operate over a non-alias interface - if len(iface.networks) > 1 and network_id and method != DHCP_METHOD: + if len(iface.networktypelist) > 1 and network_id and method != DHCP_METHOD: ifname = "%s:%d" % (os_ifname, network_id) else: ifname = os_ifname @@ -1012,8 +1012,9 @@ def generate_network_config(context, config, iface): net_config['ifname']: format_network_config(net_config) }) - for net_id in iface.networks: - net_config = get_interface_network_config(context, iface, int(net_id)) + for net_type in iface.networktypelist: + net_id = find_network_id_by_networktype(context, net_type) + net_config = get_interface_network_config(context, iface, net_id) config[NETWORK_CONFIG_RESOURCE].update({ net_config['ifname']: format_network_config(net_config) }) @@ -1060,8 +1061,7 @@ def find_interface_by_type(context, networktype): mgmt, cluster-host, pxeboot, bmc). """ for ifname, iface in six.iteritems(context['interfaces']): - for net_id in iface.networks: - net_type = find_networktype_by_network_id(context, int(net_id)) + for net_type in iface.networktypelist: if networktype == net_type: return iface diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/platform.py b/sysinv/sysinv/sysinv/sysinv/puppet/platform.py index c21416b6a8..a1399bbfef 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/platform.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/platform.py @@ -723,16 +723,12 @@ class PlatformPuppet(base.BasePuppet): # Calculate the optimal NFS r/w size based on the network mtu based # on the configured network(s) mtu = constants.DEFAULT_MTU - mgmt_network = self.dbapi.network_get_by_type( - constants.NETWORK_TYPE_MGMT) - network_id = mgmt_network.id interfaces = self.dbapi.iinterface_get_by_ihost(host.uuid) for interface in interfaces: if interface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM: - for net_id in interface['networks']: - if int(net_id) == network_id: - mtu = interface.imtu - break + if constants.NETWORK_TYPE_MGMT in interface['networktypelist']: + mtu = interface.imtu + break if self._get_address_by_name( constants.CONTROLLER_PLATFORM_NFS, diff --git a/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface.py b/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface.py index 0deeb425c1..148af2324b 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface.py @@ -148,6 +148,7 @@ class InterfaceTestCase(base.FunctionalTest): def setUp(self): super(InterfaceTestCase, self).setUp() self.dbapi = db_api.get_instance() + p = mock.patch.object(api_if_v1, '_get_lower_interface_macs') self.mock_lower_macs = p.start() self.mock_lower_macs.return_value = {'enp0s18': '08:00:27:8a:87:48', @@ -281,29 +282,13 @@ class InterfaceTestCase(base.FunctionalTest): def _create_ethernet(self, ifname=None, networktype=None, ifclass=None, datanetworks=None, host=None, expect_errors=False): - if not isinstance(networktype, list): - networktypelist = [networktype] - else: - networktypelist = networktype - networktype = ','.join(networktype) interface_id = len(self.profile['interfaces']) + 1 - networks = [] if not ifname: ifname = (networktype or 'eth') + str(interface_id) if not host: host = self.controller - if all(network_type in constants.PLATFORM_NETWORK_TYPES - for network_type in networktypelist): + if not ifclass and networktype in constants.PLATFORM_NETWORK_TYPES: ifclass = constants.INTERFACE_CLASS_PLATFORM - for network_type in networktypelist: - network = self.dbapi.network_get_by_type(network_type) - networks.append(str(network.id)) - elif ifclass == constants.INTERFACE_CLASS_PLATFORM and \ - any(network_type not in constants.PLATFORM_NETWORK_TYPES - for network_type in networktypelist): - ifclass = networktype - if not ifclass and networktype: - ifclass = networktype port_id = len(self.profile['ports']) port = dbutils.create_test_ethernet_port( id=port_id, @@ -317,22 +302,23 @@ class InterfaceTestCase(base.FunctionalTest): if not networktype: interface = dbutils.create_test_interface(ifname=ifname, forihostid=host.id, - ihost_uuid=host.uuid, - networks=networks) + ihost_uuid=host.uuid) interface_uuid = interface.uuid else: interface = dbutils.post_get_test_interface( ifname=ifname, ifclass=ifclass, - networktype=networktype, - networks=networks, datanetworks=datanetworks, forihostid=host.id, ihost_uuid=host.uuid) - response = self._post_and_check(interface, expect_errors) if expect_errors is False: interface_uuid = response.json['uuid'] interface['uuid'] = interface_uuid + if ifclass == constants.INTERFACE_CLASS_PLATFORM and networktype: + network = self.dbapi.network_get_by_type(networktype) + dbutils.create_test_interface_network( + interface_id=interface_uuid, + network_id=network.id) self.profile['interfaces'].append(interface) self.profile['ports'].append(port) @@ -341,11 +327,6 @@ class InterfaceTestCase(base.FunctionalTest): def _create_bond(self, ifname, networktype=None, ifclass=None, datanetworks=None, host=None, expect_errors=False): - if not isinstance(networktype, list): - networktypelist = [networktype] - else: - networktypelist = networktype - networktype = ','.join(networktype) if not host: host = self.controller port1, iface1 = self._create_ethernet(host=host) @@ -353,26 +334,13 @@ class InterfaceTestCase(base.FunctionalTest): interface_id = len(self.profile['interfaces']) if not ifname: ifname = (networktype or 'eth') + str(interface_id) - networks = [] - if all(network_type in constants.PLATFORM_NETWORK_TYPES - for network_type in networktypelist): + if not ifclass and networktype in constants.PLATFORM_NETWORK_TYPES: ifclass = constants.INTERFACE_CLASS_PLATFORM - for network_type in networktypelist: - network = self.dbapi.network_get_by_type(network_type) - networks.append(str(network.id)) - elif ifclass == constants.INTERFACE_CLASS_PLATFORM and \ - any(network_type not in constants.PLATFORM_NETWORK_TYPES - for network_type in networktypelist): - ifclass = networktype - if not ifclass and networktype: - ifclass = networktype interface = dbutils.post_get_test_interface( id=interface_id, ifname=ifname, iftype=constants.INTERFACE_TYPE_AE, ifclass=ifclass, - networktype=networktype, - networks=networks, uses=[iface1['ifname'], iface2['ifname']], txhashpolicy='layer2', datanetworks=datanetworks, @@ -403,42 +371,30 @@ class InterfaceTestCase(base.FunctionalTest): def _create_vlan(self, ifname, networktype, ifclass, vlan_id, lower_iface=None, datanetworks=None, host=None, expect_errors=False): - if not isinstance(networktype, list): - networktypelist = [networktype] - else: - networktypelist = networktype - networktype = ','.join(networktype) if not host: host = self.controller if not lower_iface: lower_port, lower_iface = self._create_ethernet(host=host) if not ifname: ifname = 'vlan' + str(vlan_id) - networks = [] - if all(network_type in constants.PLATFORM_NETWORK_TYPES - for network_type in networktypelist): + if not ifclass and networktype in constants.PLATFORM_NETWORK_TYPES: ifclass = constants.INTERFACE_CLASS_PLATFORM - for network_type in networktypelist: - network = self.dbapi.network_get_by_type(network_type) - networks.append(str(network.id)) - elif ifclass == constants.INTERFACE_CLASS_PLATFORM and \ - any(network_type not in constants.PLATFORM_NETWORK_TYPES - for network_type in networktypelist): - ifclass = networktype - if not ifclass and networktype: - ifclass = networktype interface = dbutils.post_get_test_interface( ifname=ifname, iftype=constants.INTERFACE_TYPE_VLAN, ifclass=ifclass, - networktype=networktype, - networks=networks, vlan_id=vlan_id, uses=[lower_iface['ifname']], datanetworks=datanetworks, forihostid=host.id, ihost_uuid=host.uuid) - - self._post_and_check(interface, expect_errors) + response = self._post_and_check(interface, expect_errors) + if expect_errors is False: + if ifclass == constants.INTERFACE_CLASS_PLATFORM and networktype: + interface['uuid'] = response.json['uuid'] + network = self.dbapi.network_get_by_type(networktype) + dbutils.create_test_interface_network( + interface_id=interface['uuid'], + network_id=network.id) self.profile['interfaces'].append(interface) return interface @@ -1131,7 +1087,6 @@ class TestPatch(InterfaceTestCase): sriov_vf_driver='i40evf') response = self.patch_dict_json( '%s' % self._get_path(interface['uuid']), - networktype=constants.NETWORK_TYPE_PCI_SRIOV, ifclass=constants.INTERFACE_CLASS_PCI_SRIOV, sriov_numvfs=1, sriov_vf_driver=vf_driver, @@ -1174,12 +1129,6 @@ class TestPost(InterfaceTestCase): self._create_host(constants.WORKER, admin=constants.ADMIN_LOCKED) self._create_datanetworks() - # Expected error: The oam network type is only supported on controller nodes - def test_invalid_oam_on_worker(self): - self._create_ethernet('oam', constants.NETWORK_TYPE_OAM, - constants.INTERFACE_CLASS_PLATFORM, - host=self.worker, expect_errors=True) - # Expected error: The pci-passthrough, pci-sriov network types are only # valid on Ethernet interfaces def test_invalid_iftype_for_pci_network_type(self): @@ -1237,8 +1186,6 @@ class TestPost(InterfaceTestCase): ndict = dbutils.post_get_test_interface( ihost_uuid=self.controller.uuid, ifname='name', - networktype=constants.NETWORK_TYPE_MGMT, - networks=['1'], ifclass=constants.INTERFACE_CLASS_PLATFORM, iftype=constants.INTERFACE_TYPE_ETHERNET, ipv4_mode=constants.IPV4_POOL, @@ -1251,7 +1198,6 @@ class TestPost(InterfaceTestCase): ndict = dbutils.post_get_test_interface( ihost_uuid=self.worker.uuid, ifname='name', - networktype=constants.NETWORK_TYPE_PCI_PASSTHROUGH, ifclass=constants.INTERFACE_CLASS_PCI_PASSTHROUGH, iftype=constants.INTERFACE_TYPE_ETHERNET, ipv4_mode=constants.IPV4_STATIC, @@ -1266,8 +1212,6 @@ class TestPost(InterfaceTestCase): ndict = dbutils.post_get_test_interface( ihost_uuid=self.controller.uuid, ifname='name', - networktype=constants.NETWORK_TYPE_CLUSTER_HOST, - networks=['2'], ifclass=constants.INTERFACE_CLASS_PLATFORM, iftype=constants.INTERFACE_TYPE_ETHERNET, ipv4_mode=constants.IPV4_DISABLED, @@ -1281,8 +1225,6 @@ class TestPost(InterfaceTestCase): ndict = dbutils.post_get_test_interface( ihost_uuid=self.controller.uuid, ifname='name', - networktype=constants.NETWORK_TYPE_MGMT, - networks=['1'], ifclass=constants.INTERFACE_CLASS_PLATFORM, iftype=constants.INTERFACE_TYPE_ETHERNET, ipv4_mode=constants.IPV4_DISABLED, @@ -1296,7 +1238,6 @@ class TestPost(InterfaceTestCase): ihost_uuid=self.controller.uuid, ifname='name', networktype=constants.NETWORK_TYPE_MGMT, - networks=['1'], ifclass=constants.INTERFACE_CLASS_PLATFORM, iftype=constants.INTERFACE_TYPE_ETHERNET, ipv6_mode=constants.IPV6_DISABLED, @@ -1308,8 +1249,6 @@ class TestPost(InterfaceTestCase): ndict = dbutils.post_get_test_interface( ihost_uuid=self.worker.uuid, ifname='name', - networktype=constants.NETWORK_TYPE_MGMT, - networks=['1'], ifclass=constants.INTERFACE_CLASS_PLATFORM, iftype=constants.INTERFACE_TYPE_ETHERNET, ipv4_mode=constants.IPV4_POOL, @@ -1321,8 +1260,6 @@ class TestPost(InterfaceTestCase): ndict = dbutils.post_get_test_interface( ihost_uuid=self.worker.uuid, ifname='name', - networktype=constants.NETWORK_TYPE_MGMT, - networks=['1'], ifclass=constants.INTERFACE_CLASS_PLATFORM, iftype=constants.INTERFACE_TYPE_ETHERNET, ipv4_mode=constants.IPV4_POOL, @@ -1335,8 +1272,6 @@ class TestPost(InterfaceTestCase): ndict = dbutils.post_get_test_interface( ihost_uuid=self.worker.uuid, ifname='name', - networktype=constants.NETWORK_TYPE_MGMT, - networks=['1'], ifclass=constants.INTERFACE_CLASS_PLATFORM, iftype=constants.INTERFACE_TYPE_ETHERNET, ipv4_mode=constants.IPV4_POOL, @@ -1350,8 +1285,6 @@ class TestPost(InterfaceTestCase): ndict = dbutils.post_get_test_interface( ihost_uuid=self.worker.uuid, ifname='name', - networktype=constants.NETWORK_TYPE_MGMT, - networks=['1'], ifclass=constants.INTERFACE_CLASS_PLATFORM, iftype=constants.INTERFACE_TYPE_ETHERNET, ipv4_mode=constants.IPV4_POOL, @@ -1367,7 +1300,6 @@ class TestPost(InterfaceTestCase): ihost_uuid=self.worker.uuid, datanetworks='group0-data0', ifname='name', - networktype=constants.NETWORK_TYPE_DATA, ifclass=constants.INTERFACE_CLASS_DATA, iftype='AE', aemode='active_standby', @@ -1381,7 +1313,6 @@ class TestPost(InterfaceTestCase): ihost_uuid=self.worker.uuid, datanetworks='group0-data0', ifname='name', - networktype=constants.NETWORK_TYPE_DATA, ifclass=constants.INTERFACE_CLASS_DATA, iftype=constants.INTERFACE_TYPE_AE, aemode='active_standby', @@ -1394,7 +1325,6 @@ class TestPost(InterfaceTestCase): ndict = dbutils.post_get_test_interface( ihost_uuid=self.worker.uuid, ifname='name', - networktype=constants.NETWORK_TYPE_DATA, ifclass=constants.INTERFACE_CLASS_DATA, iftype=constants.INTERFACE_TYPE_AE, aemode='balanced', @@ -1408,7 +1338,6 @@ class TestPost(InterfaceTestCase): ihost_uuid=self.worker.uuid, datanetworks='group0-data0', ifname='name', - networktype=constants.NETWORK_TYPE_DATA, ifclass=constants.INTERFACE_CLASS_DATA, iftype=constants.INTERFACE_TYPE_AE, aemode='802.3ad', @@ -1419,28 +1348,12 @@ class TestPost(InterfaceTestCase): ihost_uuid=self.worker.uuid, datanetworks='group0-data0', ifname='name', - networktype=constants.NETWORK_TYPE_DATA, ifclass=constants.INTERFACE_CLASS_DATA, iftype=constants.INTERFACE_TYPE_AE, aemode='balanced', txhashpolicy=None) self._post_and_check_failure(ndict) - # Expected error: Device interface with network type ___, and interface type - # 'aggregated ethernet' must be in mode '802.3ad' - def test_aemode_invalid_mgmt(self): - ndict = dbutils.post_get_test_interface( - ihost_uuid=self.worker.uuid, - datanetworks='group0-data0', - ifname='name', - networktype=constants.NETWORK_TYPE_MGMT, - networks=['1'], - ifclass=constants.INTERFACE_CLASS_PLATFORM, - iftype=constants.INTERFACE_TYPE_AE, - aemode='balanced', - txhashpolicy='layer2') - self._post_and_check_failure(ndict) - # Device interface with network type ___, and interface type # 'aggregated ethernet' must be in mode 'active_standby' or 'balanced' or # '802.3ad'. @@ -1449,57 +1362,26 @@ class TestPost(InterfaceTestCase): ihost_uuid=self.worker.uuid, datanetworks='group0-data0', ifname='name', - networktype=constants.NETWORK_TYPE_DATA, ifclass=constants.INTERFACE_CLASS_DATA, iftype=constants.INTERFACE_TYPE_AE, aemode='bad_aemode', txhashpolicy='layer2') self._post_and_check_failure(ndict) - def test_aemode_invalid_oam(self): + def test_aemode_invalid_platform(self): ndict = dbutils.post_get_test_interface( ihost_uuid=self.controller.uuid, ifname='name', - networktype=constants.NETWORK_TYPE_OAM, - networks=['3'], ifclass=constants.INTERFACE_CLASS_PLATFORM, iftype=constants.INTERFACE_TYPE_AE, aemode='bad_aemode', txhashpolicy='layer2') self._post_and_check_failure(ndict) - def test_aemode_invalid_cluster_host(self): - ndict = dbutils.post_get_test_interface( - ihost_uuid=self.worker.uuid, - ifname='name', - networktype=constants.NETWORK_TYPE_CLUSTER_HOST, - networks=['2'], - ifclass=constants.INTERFACE_CLASS_PLATFORM, - iftype=constants.INTERFACE_TYPE_AE, - aemode='bad_aemode', - txhashpolicy='layer2') - self._post_and_check_failure(ndict) - - # Expected error: Interface ___ does not have associated cluster-host - # interface on controller. - def test_no_cluster_host_on_controller(self): - ndict = dbutils.post_get_test_interface( - ihost_uuid=self.worker.uuid, - ifname='name', - networktype=constants.NETWORK_TYPE_CLUSTER_HOST, - networks=['2'], - ifclass=constants.INTERFACE_CLASS_PLATFORM, - iftype=constants.INTERFACE_TYPE_ETHERNET, - aemode='balanced', - txhashpolicy='layer2') - self._post_and_check_failure(ndict) - def test_setting_mgmt_mtu_allowed(self): ndict = dbutils.post_get_test_interface( ihost_uuid=self.controller.uuid, ifname='mgmt0', - networktype=constants.NETWORK_TYPE_MGMT, - networks=['1'], ifclass=constants.INTERFACE_CLASS_PLATFORM, iftype=constants.INTERFACE_TYPE_ETHERNET, imtu=1600) @@ -1509,8 +1391,6 @@ class TestPost(InterfaceTestCase): ndict = dbutils.post_get_test_interface( ihost_uuid=self.controller.uuid, ifname='cluster0', - networktype=constants.NETWORK_TYPE_CLUSTER_HOST, - networks=['2'], ifclass=constants.INTERFACE_CLASS_PLATFORM, iftype=constants.INTERFACE_TYPE_ETHERNET, imtu=1600) @@ -1529,7 +1409,6 @@ class TestPost(InterfaceTestCase): ihost_uuid=self.worker.uuid, datanetworks='group0-ext1', ifname='bond1', - networktype=constants.NETWORK_TYPE_DATA, ifclass=constants.INTERFACE_CLASS_DATA, iftype=constants.INTERFACE_TYPE_AE, aemode='balanced', @@ -1559,7 +1438,6 @@ class TestPost(InterfaceTestCase): ihost_uuid=self.worker.uuid, datanetworks='group0-ext1', ifname='bond0', - networktype=constants.NETWORK_TYPE_DATA, ifclass=constants.INTERFACE_CLASS_DATA, iftype=constants.INTERFACE_TYPE_AE, aemode='balanced', @@ -1579,7 +1457,6 @@ class TestPost(InterfaceTestCase): ihost_uuid=self.worker.uuid, datanetworks='group0-ext1', ifname='bond1', - networktype=constants.NETWORK_TYPE_DATA, ifclass=constants.INTERFACE_CLASS_DATA, iftype=constants.INTERFACE_TYPE_VLAN, aemode='balanced', @@ -1658,7 +1535,9 @@ class TestPost(InterfaceTestCase): def test_create_invalid_oam_data_ethernet(self): self._create_ethernet('shared', networktype=constants.NETWORK_TYPE_DATA, - ifclass=constants.INTERFACE_CLASS_PLATFORM, + ifclass=constants.INTERFACE_CLASS_DATA, + datanetworks='group0-data0', + host=self.controller, expect_errors=True) # Expected message: @@ -1715,13 +1594,6 @@ class TestCpePost(InterfaceTestCase): lower_iface=iface, datanetworks='group0-ext1', expect_errors=True) - # Expected message: An interface with \'oam\' network type is already - # provisioned on this node - def test_create_invalid_duplicate_networktype(self): - self._create_ethernet('oam', constants.NETWORK_TYPE_OAM) - self._create_ethernet('bad', constants.NETWORK_TYPE_OAM, - expect_errors=True) - # Expected message: VLAN id ___ already in use on interface ___ def test_create_vlan_id_already_in_use(self): port, iface = self._create_ethernet('eth1', constants.NETWORK_TYPE_NONE) @@ -1733,13 +1605,6 @@ class TestCpePost(InterfaceTestCase): lower_iface=iface, datanetworks='group0-ext1', expect_errors=True) - # Expected message: VLAN interfaces cannot have an interface class of none - def test_create_invalid_vlan_networktype_none(self): - port, lower = self._create_ethernet('eth1', constants.NETWORK_TYPE_NONE) - self._create_vlan('vlan2', networktype='none', - ifclass=constants.INTERFACE_CLASS_NONE, - vlan_id=2, lower_iface=lower, expect_errors=True) - # Expected error: VLAN based provider network group0-data0 cannot be # assigned to a VLAN interface def test_create_invalid_vlan_with_vlan_data_network(self): @@ -1775,12 +1640,6 @@ class TestCpePost(InterfaceTestCase): mock.ANY, mock.ANY, vlans=mock.ANY, test=mock.ANY) mock_iinterface_destroy.assert_called_once_with(mock.ANY) - # Expected error: At least one provider network must be selected. - def test_create_invalid_no_data_network(self): - self._create_ethernet('data', - networktype=constants.NETWORK_TYPE_DATA, - expect_errors=True) - # Expected error: Data interface data0 is already attached to this # Data Network: group0-data0. def test_create_invalid_data_network_used(self): @@ -1819,13 +1678,6 @@ class TestCpePatch(InterfaceTestCase): admin=constants.ADMIN_LOCKED) self._create_datanetworks() - def test_create_invalid_cluster_host_data_ethernet(self): - self._create_ethernet('shared', - networktype=[constants.NETWORK_TYPE_CLUSTER_HOST, - constants.NETWORK_TYPE_DATA], - datanetworks='group0-data0', - expect_errors=True) - @testtools.skip("deprecate neutron bind interface") @mock.patch.object(rpcapi.ConductorAPI, 'neutron_bind_interface') def test_patch_neutron_bind_failed(self, mock_neutron_bind_interface): @@ -1853,23 +1705,11 @@ class TestCpePatch(InterfaceTestCase): constants.NETWORK_TYPE_NONE) response = self.patch_dict_json( '%s' % self._get_path(interface['uuid']), - networktype=constants.NETWORK_TYPE_PCI_SRIOV, ifclass=constants.INTERFACE_CLASS_PCI_SRIOV, expect_errors=True) self.assertEqual(http_client.BAD_REQUEST, response.status_int) self.assertEqual('application/json', response.content_type) - # Expected error: At most one port must be enabled. - def test_invalid_sriov_no_port(self): - interface = dbutils.create_test_interface(forihostid='1') - response = self.patch_dict_json( - '%s' % self._get_path(interface['uuid']), sriov_numvfs=1, - networktype=constants.NETWORK_TYPE_PCI_SRIOV, - ifclass=constants.INTERFACE_CLASS_DATA, - expect_errors=True) - self.assertEqual(http_client.BAD_REQUEST, response.status_int) - self.assertEqual('application/json', response.content_type) - # Expected error: SR-IOV can't be configured on this interface def test_invalid_sriov_totalvfs_zero(self): interface = dbutils.create_test_interface(forihostid='1') @@ -1878,7 +1718,6 @@ class TestCpePatch(InterfaceTestCase): pciaddr='0000:00:00.11', dev_id=0, sriov_totalvfs=0, sriov_numvfs=1) response = self.patch_dict_json( '%s' % self._get_path(interface['uuid']), - networktype=constants.NETWORK_TYPE_PCI_SRIOV, ifclass=constants.INTERFACE_CLASS_PCI_SRIOV, sriov_numvfs=1, expect_errors=True) diff --git a/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface_network.py b/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface_network.py index 872606e4b4..6f6c1c1975 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface_network.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface_network.py @@ -7,8 +7,10 @@ # SPDX-License-Identifier: Apache-2.0 # +import mock from six.moves import http_client +from sysinv.api.controllers.v1 import interface as api_if_v1 from sysinv.common import constants from sysinv.tests.api import base from sysinv.tests.db import utils as dbutils @@ -17,6 +19,13 @@ from sysinv.tests.db import utils as dbutils class InterfaceNetworkTestCase(base.FunctionalTest): def setUp(self): super(InterfaceNetworkTestCase, self).setUp() + + p = mock.patch.object(api_if_v1, '_get_lower_interface_macs') + self.mock_lower_macs = p.start() + self.mock_lower_macs.return_value = {'enp0s18': '08:00:27:8a:87:48', + 'enp0s19': '08:00:27:ea:93:8e'} + self.addCleanup(p.stop) + self.system = dbutils.create_test_isystem() self.load = dbutils.create_test_load() self.controller = dbutils.create_test_ihost( @@ -76,6 +85,12 @@ class InterfaceNetworkTestCase(base.FunctionalTest): name='oam', type=constants.NETWORK_TYPE_OAM, address_pool_id=self.address_pool_oam.id) + self.oam_address = dbutils.create_test_address( + family=2, + address='10.10.10.3', + prefix=24, + name='controller-0-oam', + address_pool_id=self.address_pool_oam.id) self.address_pool_pxeboot = dbutils.create_test_address_pool( id=4, network='192.168.202.0', @@ -86,6 +101,12 @@ class InterfaceNetworkTestCase(base.FunctionalTest): id=4, type=constants.NETWORK_TYPE_PXEBOOT, address_pool_id=self.address_pool_pxeboot.id) + self.pxeboot_address = dbutils.create_test_address( + family=2, + address='192.168.202.3', + prefix=24, + name='controller-0-pxeboot', + address_pool_id=self.address_pool_pxeboot.id) def _post_and_check(self, ndict, expect_errors=False): response = self.post_json('%s' % self._get_path(), ndict, @@ -162,7 +183,7 @@ class InterfaceNetworkCreateTestCase(InterfaceNetworkTestCase): worker_interface_network = dbutils.post_get_test_interface_network( interface_uuid=worker_interface.uuid, network_uuid=self.oam_network.uuid) - self._post_and_check(worker_interface_network, expect_errors=False) + self._post_and_check(worker_interface_network, expect_errors=True) def test_create_pxeboot_interface_network(self): controller_interface = dbutils.create_test_interface( @@ -290,3 +311,68 @@ class InterfaceNetworkCreateTestCase(InterfaceNetworkTestCase): interface_uuid=worker_interface.uuid, network_uuid=self.mgmt_network.uuid) self._post_and_check(worker_interface_network, expect_errors=True) + + # Expected error: The oam network type is only supported on controller nodes + def test_invalid_oam_on_worker(self): + worker_interface = dbutils.create_test_interface( + ifname='enp0s3', + forihostid=self.worker.id) + worker_interface_network = dbutils.post_get_test_interface_network( + interface_uuid=worker_interface.uuid, + network_uuid=self.oam_network.uuid) + self._post_and_check(worker_interface_network, expect_errors=True) + + # Expected message: An interface with \'oam\' network type is already + # provisioned on this node + def test_create_invalid_duplicate_networktype(self): + controller_interface1 = dbutils.create_test_interface( + ifname='enp0s3', + forihostid=self.controller.id) + dbutils.create_test_interface_network( + interface_id=controller_interface1.id, + network_id=self.oam_network.id) + controller_interface2 = dbutils.create_test_interface( + ifname='enp0s8', + forihostid=self.controller.id) + controller_interface_network = dbutils.post_get_test_interface_network( + interface_uuid=controller_interface2.uuid, + network_uuid=self.oam_network.uuid) + self._post_and_check(controller_interface_network, expect_errors=True) + + # Expected error: Interface ___ does not have associated cluster-host + # interface on controller. + def test_no_cluster_host_on_controller(self): + worker_interface = dbutils.create_test_interface( + ifname='enp0s3', + forihostid=self.worker.id) + worker_interface_network = dbutils.post_get_test_interface_network( + interface_uuid=worker_interface.uuid, + network_uuid=self.cluster_host_network.uuid) + self._post_and_check(worker_interface_network, expect_errors=True) + + # Expected error: An interface with interface class data cannot + # assign platform networks. + def test_create_invalid_network_on_data_interface(self): + controller_interface = dbutils.create_test_interface( + ifname='enp0s3', + ifclass=constants.NETWORK_TYPE_DATA, + forihostid=self.controller.id) + controller_interface_network = dbutils.post_get_test_interface_network( + interface_uuid=controller_interface.uuid, + network_uuid=self.cluster_host_network.uuid) + self._post_and_check(controller_interface_network, expect_errors=True) + + # Expected error: Device interface with network type ___, and interface type + # 'aggregated ethernet' must be in mode '802.3ad' + def test_aemode_invalid_mgmt(self): + controller_interface = dbutils.create_test_interface( + ifname='name', + forihostid=self.controller.id, + ifclass=constants.INTERFACE_CLASS_PLATFORM, + iftype=constants.INTERFACE_TYPE_AE, + aemode='balanced', + txhashpolicy='layer2') + controller_interface_network = dbutils.post_get_test_interface_network( + interface_uuid=controller_interface.uuid, + network_uuid=self.mgmt_network.uuid) + self._post_and_check(controller_interface_network, expect_errors=True) diff --git a/sysinv/sysinv/sysinv/sysinv/tests/db/utils.py b/sysinv/sysinv/sysinv/sysinv/tests/db/utils.py index 50bcf835f2..99a4d1e168 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/db/utils.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/db/utils.py @@ -674,8 +674,6 @@ def post_get_test_interface(**kw): 'imac': kw.get('imac', '11:22:33:44:55:66'), 'imtu': kw.get('imtu', 1500), 'ifclass': kw.get("ifclass"), - 'networktype': kw.get('networktype'), - 'networks': kw.get('networks', []), 'aemode': kw.get('aemode', 'balanced'), 'txhashpolicy': kw.get('txhashpolicy', 'layer2'), 'datanetworks': datanetworks_list, @@ -710,8 +708,7 @@ def get_test_interface(**kw): 'imac': kw.get('imac', '11:22:33:44:55:66'), 'imtu': kw.get('imtu', 1500), 'ifclass': kw.get('ifclass', None), - 'networktype': kw.get('networktype'), - 'networks': kw.get('networks', []), + 'networktypelist': kw.get('networktypelist', []), 'aemode': kw.get('aemode'), 'txhashpolicy': kw.get('txhashpolicy', None), 'datanetworks': datanetworks_list, @@ -737,6 +734,7 @@ def create_test_interface(**kw): interface = get_test_interface(**kw) datanetworks_list = interface.get('datanetworks') or [] + networks_list = interface.get('networks') or [] # Let DB generate ID if it isn't specified explicitly if 'id' not in kw: @@ -745,10 +743,22 @@ def create_test_interface(**kw): if 'datanetworks' in interface: del interface['datanetworks'] + if 'networks' in interface: + del interface['networks'] + dbapi = db_api.get_instance() forihostid = kw.get('forihostid') interface_obj = dbapi.iinterface_create(forihostid, interface) + # assign the network to the interface + for network in networks_list: + if not network: + continue + net = dbapi.network_get(network) + values = {'interface_id': interface_obj.id, + 'network_id': net.id} + dbapi.interface_network_create(values) + # assign the interface to the datanetwork for datanetwork in datanetworks_list: if not datanetwork: diff --git a/sysinv/sysinv/sysinv/sysinv/tests/puppet/test_interface.py b/sysinv/sysinv/sysinv/sysinv/tests/puppet/test_interface.py index df15793ec5..2ee07d68c1 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/puppet/test_interface.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/puppet/test_interface.py @@ -64,20 +64,21 @@ class BaseTestCase(dbbase.DbTestCase): super(BaseTestCase, self).assertEqual(expected, observed, message) def _setup_address_and_routes(self, iface): - networktype = utils.get_primary_network_type(iface) - if networktype in NETWORKTYPES_WITH_V4_ADDRESSES: + if not iface['ifclass'] or iface['ifclass'] == constants.INTERFACE_CLASS_NONE: + return None + if iface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM: address = {'interface_id': iface['id'], 'family': 4, 'prefix': 24, 'address': '192.168.1.2'} self.addresses.append(dbutils.create_test_address(**address)) - elif networktype in NETWORKTYPES_WITH_V6_ADDRESSES: + elif iface['ifclass'] == constants.INTERFACE_CLASS_DATA: address = {'interface_id': iface['id'], 'family': 6, 'prefix': 64, 'address': '2001:1::2'} self.addresses.append(dbutils.create_test_address(**address)) - if networktype in NETWORKTYPES_WITH_V4_ROUTES: + if iface['ifclass'] == constants.INTERFACE_CLASS_DATA: route = {'interface_id': iface['id'], 'family': 4, 'prefix': 24, @@ -92,7 +93,7 @@ class BaseTestCase(dbbase.DbTestCase): 'gateway': '192.168.1.1', 'metric': '1'} self.routes.append(dbutils.create_test_route(**route)) - if networktype in NETWORKTYPES_WITH_V6_ROUTES: + if iface['ifclass'] == constants.INTERFACE_CLASS_DATA: route = {'interface_id': iface['id'], 'family': 6, 'prefix': 64,