From aa2236a8bf4e7d2bc1d8cf38a18ea8de067fb7c6 Mon Sep 17 00:00:00 2001 From: Roman Bogorodskiy Date: Thu, 16 Apr 2015 09:07:55 +0200 Subject: [PATCH] sriov: implement spoofchecking configuration - Make sriov agent to set the spoofchecking on VFs according to port_security_enabled attribute of the port. - Extend vf management sanity check to probe spoof checking capability Implements: blueprint sriov-spoofchk Change-Id: I4d1060be26ee6cbd7d766c2bde364f694533de69 --- neutron/cmd/sanity/checks.py | 14 +++++++---- .../mech_sriov/agent/eswitch_manager.py | 24 +++++++++++++++++++ .../ml2/drivers/mech_sriov/agent/pci_lib.py | 16 +++++++++++++ .../mech_sriov/agent/sriov_nic_agent.py | 17 ++++++++++--- .../unit/agent/linux/test_ip_link_support.py | 8 +++++++ .../mech_sriov/agent/test_eswitch_manager.py | 14 +++++++++++ .../drivers/mech_sriov/agent/test_pci_lib.py | 15 ++++++++++++ .../mech_sriov/agent/test_sriov_nic_agent.py | 8 ++++++- 8 files changed, 107 insertions(+), 9 deletions(-) diff --git a/neutron/cmd/sanity/checks.py b/neutron/cmd/sanity/checks.py index 5d90ad9c3..37f0947f2 100644 --- a/neutron/cmd/sanity/checks.py +++ b/neutron/cmd/sanity/checks.py @@ -127,13 +127,17 @@ def arp_header_match_supported(): def vf_management_supported(): + required_caps = ( + ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_STATE, + ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_SPOOFCHK) try: vf_section = ip_link_support.IpLinkSupport.get_vf_mgmt_section() - if not ip_link_support.IpLinkSupport.vf_mgmt_capability_supported( - vf_section, - ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_STATE): - LOG.debug("ip link command does not support vf capability") - return False + for cap in required_caps: + if not ip_link_support.IpLinkSupport.vf_mgmt_capability_supported( + vf_section, cap): + LOG.debug("ip link command does not support " + "vf capability '%(cap)s'", cap) + return False except ip_link_support.UnsupportedIpLinkCommand: LOG.exception(_LE("Unexpected exception while checking supported " "ip link command")) diff --git a/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py b/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py index f8f4bd846..866476977 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py @@ -164,6 +164,17 @@ class EmbSwitch(object): raise exc.InvalidPciSlotError(pci_slot=pci_slot) return self.pci_dev_wrapper.set_vf_state(vf_index, state) + def set_device_spoofcheck(self, pci_slot, enabled): + """Set device spoofchecking + + @param pci_slot: Virtual Function address + @param enabled: True to enable spoofcheck, False to disable + """ + vf_index = self.pci_slot_map.get(pci_slot) + if vf_index is None: + raise exc.InvalidPciSlotError(pci_slot=pci_slot) + return self.pci_dev_wrapper.set_vf_spoofcheck(vf_index, enabled) + def get_pci_device(self, pci_slot): """Get mac address for given Virtual Function address @@ -252,6 +263,19 @@ class ESwitchManager(object): embedded_switch.set_device_state(pci_slot, admin_state_up) + def set_device_spoofcheck(self, device_mac, pci_slot, enabled): + """Set device spoofcheck + + Sets device spoofchecking (enabled or disabled) + @param device_mac: device mac + @param pci_slot: pci slot + @param enabled: device spoofchecking + """ + embedded_switch = self._get_emb_eswitch(device_mac, pci_slot) + if embedded_switch: + embedded_switch.set_device_spoofcheck(pci_slot, + enabled) + def _discover_devices(self, device_mappings, exclude_devices): """Discover which Virtual functions to manage. diff --git a/neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py b/neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py index 05fc0d2f8..0869b549a 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py @@ -106,6 +106,22 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper): raise exc.IpCommandError(dev_name=self.dev_name, reason=e) + def set_vf_spoofcheck(self, vf_index, enabled): + """sets vf spoofcheck + + @param vf_index: vf index + @param enabled: True to enable spoof checking, + False to disable + """ + setting = "on" if enabled else "off" + + try: + self._as_root('', "link", ("set", self.dev_name, "vf", + str(vf_index), "spoofchk", setting)) + except Exception as e: + raise exc.IpCommandError(dev_name=self.dev_name, + reason=str(e)) + def _get_vf_link_show(self, vf_list, link_show_out): """Get link show output for VFs diff --git a/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py b/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py index 54ff293e8..e1dd7247b 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py @@ -33,7 +33,7 @@ from neutron.common import constants as n_constants from neutron.common import topics from neutron.common import utils as n_utils from neutron import context -from neutron.i18n import _LE, _LI +from neutron.i18n import _LE, _LI, _LW from neutron.plugins.ml2.drivers.mech_sriov.agent.common import config # noqa from neutron.plugins.ml2.drivers.mech_sriov.agent.common \ import exceptions as exc @@ -169,8 +169,17 @@ class SriovNicSwitchAgent(object): # If one of the above operations fails => resync with plugin return (resync_a | resync_b) - def treat_device(self, device, pci_slot, admin_state_up): + def treat_device(self, device, pci_slot, admin_state_up, spoofcheck=True): if self.eswitch_mgr.device_exists(device, pci_slot): + try: + self.eswitch_mgr.set_device_spoofcheck(device, pci_slot, + spoofcheck) + except Exception: + LOG.warning(_LW("Failed to set spoofcheck for device %s"), + device) + LOG.info(_LI("Device %(device)s spoofcheck %(spoofcheck)s"), + {"device": device, "spoofcheck": spoofcheck}) + try: self.eswitch_mgr.set_device_state(device, pci_slot, admin_state_up) @@ -210,9 +219,11 @@ class SriovNicSwitchAgent(object): LOG.info(_LI("Port %(device)s updated. Details: %(details)s"), {'device': device, 'details': device_details}) profile = device_details['profile'] + spoofcheck = device_details.get('port_security_enabled', True) self.treat_device(device_details['device'], profile.get('pci_slot'), - device_details['admin_state_up']) + device_details['admin_state_up'], + spoofcheck) else: LOG.info(_LI("Device with MAC %s not defined on plugin"), device) diff --git a/neutron/tests/unit/agent/linux/test_ip_link_support.py b/neutron/tests/unit/agent/linux/test_ip_link_support.py index 0a843e7d2..cf29530b2 100644 --- a/neutron/tests/unit/agent/linux/test_ip_link_support.py +++ b/neutron/tests/unit/agent/linux/test_ip_link_support.py @@ -90,6 +90,8 @@ TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap | TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can } """ + IP_LINK_HELP_NO_SPOOFCHK = IP_LINK_HELP_NO_STATE + IP_LINK_HELP_NO_VF = """Usage: ip link set DEVICE { up | down | arp { on | off } | dynamic { on | off } | @@ -134,6 +136,12 @@ TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can } expected=False, stderr=self.IP_LINK_HELP_NO_STATE) + def test_vf_mgmt_no_spoofchk(self): + self._test_capability( + ip_link.IpLinkConstants.IP_LINK_CAPABILITY_SPOOFCHK, + expected=False, + stderr=self.IP_LINK_HELP_NO_SPOOFCHK) + def test_vf_mgmt_no_vf(self): self._test_capability( ip_link.IpLinkConstants.IP_LINK_CAPABILITY_STATE, diff --git a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py index 72c5da0c3..a9a5b3a67 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py +++ b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py @@ -246,6 +246,20 @@ class TestEmbSwitch(base.BaseTestCase): self.emb_switch.set_device_state, self.WRONG_PCI_SLOT, True) + def test_set_device_spoofcheck_ok(self): + with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." + "PciDeviceIPWrapper.set_vf_spoofcheck") as \ + set_vf_spoofcheck_mock: + self.emb_switch.set_device_spoofcheck(self.PCI_SLOT, True) + self.assertTrue(set_vf_spoofcheck_mock.called) + + def test_set_device_spoofcheck_fail(self): + with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." + "PciDeviceIPWrapper.set_vf_spoofcheck"): + self.assertRaises(exc.InvalidPciSlotError, + self.emb_switch.set_device_spoofcheck, + self.WRONG_PCI_SLOT, True) + def test_get_pci_device(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.get_assigned_macs", diff --git a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_pci_lib.py b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_pci_lib.py index 62a10f0fb..de8a6652b 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_pci_lib.py +++ b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_pci_lib.py @@ -99,3 +99,18 @@ class TestPciLib(base.BaseTestCase): self.pci_wrapper.set_vf_state, self.VF_INDEX, True) + + def test_set_vf_spoofcheck(self): + with mock.patch.object(self.pci_wrapper, "_execute"): + result = self.pci_wrapper.set_vf_spoofcheck(self.VF_INDEX, + True) + self.assertIsNone(result) + + def test_set_vf_spoofcheck_fail(self): + with mock.patch.object(self.pci_wrapper, + "_execute") as mock_exec: + mock_exec.side_effect = Exception() + self.assertRaises(exc.IpCommandError, + self.pci_wrapper.set_vf_spoofcheck, + self.VF_INDEX, + True) diff --git a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py index 9729d3fb7..ccbb04435 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py +++ b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py @@ -178,12 +178,14 @@ class TestSriovAgent(base.BaseTestCase): 'network_type': 'vlan', 'segmentation_id': 100, 'profile': {'pci_slot': '1:2:3.0'}, - 'physical_network': 'physnet1'} + 'physical_network': 'physnet1', + 'port_security_enabled': False} agent.plugin_rpc = mock.Mock() agent.plugin_rpc.get_devices_details_list.return_value = [mock_details] agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr.device_exists.return_value = True agent.set_device_state = mock.Mock() + agent.set_device_spoofcheck = mock.Mock() resync_needed = agent.treat_devices_added_updated( set(['aa:bb:cc:dd:ee:ff'])) @@ -194,6 +196,10 @@ class TestSriovAgent(base.BaseTestCase): 'aa:bb:cc:dd:ee:ff', '1:2:3.0', True) + agent.eswitch_mgr.set_device_spoofcheck.assert_called_with( + 'aa:bb:cc:dd:ee:ff', + '1:2:3.0', + False) self.assertTrue(agent.plugin_rpc.update_device_up.called) def test_treat_devices_added_updated_admin_state_up_false(self): -- 2.45.2