diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py index b94ef0b436..8e130335a5 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py @@ -1455,6 +1455,11 @@ def _check_interface_data(op, interface, ihost, existing_interface, return interface +def _is_port_sriov_restricted(port): + return port.pvendor in constants.SRIOV_RESTRICTED_NET_DEVICES and \ + port.pdevice in constants.SRIOV_RESTRICTED_NET_DEVICES[port.pvendor] + + def _check_ports(op, interface, ihost, ports): port_list = [] @@ -1474,10 +1479,17 @@ def _check_ports(op, interface, ihost, ports): raise wsme.exc.ClientSideError(_( "For Ethernet, select a single port.")) - # Make sure that no other interface is currently using these ports + # Make sure that the port is not in the SR-IOV restricted list and + # that no other interface is currently using these ports host_ports = pecan.request.dbapi.ethernet_port_get_all(hostid=ihost['id']) for p in host_ports: if p.name in port_list or p.uuid in port_list: + if (interface['ifclass'] == constants.INTERFACE_CLASS_PCI_SRIOV and + _is_port_sriov_restricted(p)): + pif = pecan.request.dbapi.iinterface_get(p.interface_id) + msg = _("Cannot use port. PCI vendor '%s' and device '%s' is not allowed." + % (p.pvendor, p.pdevice)) + raise wsme.exc.ClientSideError(msg) if p.interface_id and p.interface_id != this_interface_id: pif = pecan.request.dbapi.iinterface_get(p.interface_id) msg = _("Another interface %s is already using this port" diff --git a/sysinv/sysinv/sysinv/sysinv/common/constants.py b/sysinv/sysinv/sysinv/sysinv/common/constants.py index 92a910440d..5b97fa2dd1 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/constants.py +++ b/sysinv/sysinv/sysinv/sysinv/common/constants.py @@ -2276,6 +2276,14 @@ DRIVERS_UP_BEFORE_SRIOV = [DRIVER_BNXT_EN] # become operational DRIVERS_NOT_IMMEDIATELY_OPERATIONAL = [DRIVER_BNXT_EN] +# SR-IOV restricted network devices (cannot be used to create VFs) +# Marvell (Cavium) CNF105xx family (CNF10KA and CNF10KB): +PVENDOR_CAVIUM = 'Cavium, Inc. [177d]' +PDEVICE_CAVIUM_BA00 = 'Device [ba00]' +PDEVICE_CAVIUM_BC00 = 'Device [bc00]' +SRIOV_RESTRICTED_NET_DEVICES = {PVENDOR_CAVIUM: (PDEVICE_CAVIUM_BA00, + PDEVICE_CAVIUM_BC00)} + # Traffic control TRAFFIC_CONTROL_SCRIPT = '/usr/local/bin/tc_setup.sh' diff --git a/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface.py b/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface.py index 5f526bf1d9..cbf677398e 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/api/test_interface.py @@ -1938,6 +1938,38 @@ class TestPostMixin(object): self.assertEqual(http_client.OK, patch_result.status_code) self.assertEqual(0, patch_result.json['max_tx_rate']) + def test_modify_sriov_restricted_port(self): + pvendor = constants.PVENDOR_CAVIUM + pdevice = constants.PDEVICE_CAVIUM_BA00 + interface = dbutils.create_test_interface(forihostid=self.worker.id, + datanetworks='group0-data0') + dbutils.create_test_ethernet_port( + id=1, name='enp81s0f0', host_id=self.worker.id, + interface_id=interface.id, + pciaddr='0000:51:00.0', dev_id=0, sriov_totalvfs=32, sriov_numvfs=0, + driver='octeon_ep', + pvendor=pvendor, + pdevice=pdevice) + # ifclass to pci-passthrough is allowed + patch_result = self.patch_dict_json( + '%s' % self._get_path(interface['uuid']), + ifname='sriov0', + ifclass=constants.INTERFACE_CLASS_PCI_PASSTHROUGH, + expect_errors=True) + self.assertEqual('application/json', patch_result.content_type) + self.assertEqual(http_client.OK, patch_result.status_code) + # ifclass to pci-sriov is not allowed + patch_result = self.patch_dict_json( + '%s' % self._get_path(interface['uuid']), + ifclass=constants.INTERFACE_CLASS_PCI_SRIOV, + sriov_numvfs=8, + expect_errors=True) + self.assertEqual(http_client.BAD_REQUEST, patch_result.status_int) + self.assertTrue(patch_result.json['error_message']) + self.assertIn(f"Cannot use port. PCI vendor '{pvendor}' and " + f"device '{pdevice}' is not allowed.", + patch_result.json['error_message']) + class TestAIOPost(InterfaceTestCase): def setUp(self):