From 7458575cfbc00a9bedf4d514a95e9b891639d5e8 Mon Sep 17 00:00:00 2001 From: Miguel Angel Ajo Date: Tue, 12 Jun 2018 14:35:39 +0200 Subject: [PATCH] Convert missing exception on device.link.delete() Once we started using oslo.privsep the call to device.link.delete() should return RuntimeError when the device can't be handled by ip link for example, when it's an ovs internal device. Closes-Bug: #1776469 Change-Id: Ibf4b0bbb54aef38fc569036880668c745cb5c096 --- neutron/privileged/agent/linux/ip_lib.py | 36 ++++++++++++++----- .../privileged/agent/linux/test_ip_lib.py | 11 ++++++ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/neutron/privileged/agent/linux/ip_lib.py b/neutron/privileged/agent/linux/ip_lib.py index 14157f277c8..ab7181b9b55 100644 --- a/neutron/privileged/agent/linux/ip_lib.py +++ b/neutron/privileged/agent/linux/ip_lib.py @@ -62,6 +62,21 @@ class NetworkInterfaceNotFound(RuntimeError): super(NetworkInterfaceNotFound, self).__init__(message) +class InterfaceOperationNotSupported(RuntimeError): + message = _("Operation not supported on interface %(device)s, namespace " + "%(namespace)s.") + + def __init__(self, message=None, device=None, namespace=None): + # NOTE(slaweq): 'message' can be passed as an optional argument + # because of how privsep daemon works. If exception is raised in + # function called by privsep daemon, it will then try to reraise it + # and will call it always with passing only message from originally + # raised exception. + message = message or self.message % { + 'device': device, 'namespace': namespace} + super(InterfaceOperationNotSupported, self).__init__(message) + + class IpAddressAlreadyExists(RuntimeError): message = _("IP address %(ip)s already configured on %(device)s.") @@ -116,6 +131,15 @@ def _get_iproute(namespace): return pyroute2.IPRoute() +def _translate_ip_device_exception(e, device=None, namespace=None): + if e.code == errno.ENODEV: + raise NetworkInterfaceNotFound(device=device, namespace=namespace) + if e.code == errno.EOPNOTSUPP: + raise InterfaceOperationNotSupported(device=device, + namespace=namespace) + raise + + def _get_link_id(device, namespace): try: with _get_iproute(namespace) as ip: @@ -130,9 +154,7 @@ def _run_iproute_link(command, device, namespace=None, **kwargs): idx = _get_link_id(device, namespace) return ip.link(command, index=idx, **kwargs) except NetlinkError as e: - if e.code == errno.ENODEV: - raise NetworkInterfaceNotFound(device=device, namespace=namespace) - raise + _translate_ip_device_exception(e, device, namespace) except OSError as e: if e.errno == errno.ENOENT: raise NetworkNamespaceNotFound(netns_name=namespace) @@ -145,9 +167,7 @@ def _run_iproute_neigh(command, device, namespace, **kwargs): idx = _get_link_id(device, namespace) return ip.neigh(command, ifindex=idx, **kwargs) except NetlinkError as e: - if e.code == errno.ENODEV: - raise NetworkInterfaceNotFound(device=device, namespace=namespace) - raise + _translate_ip_device_exception(e, device, namespace) except OSError as e: if e.errno == errno.ENOENT: raise NetworkNamespaceNotFound(netns_name=namespace) @@ -160,9 +180,7 @@ def _run_iproute_addr(command, device, namespace, **kwargs): idx = _get_link_id(device, namespace) return ip.addr(command, index=idx, **kwargs) except NetlinkError as e: - if e.code == errno.ENODEV: - raise NetworkInterfaceNotFound(device=device, namespace=namespace) - raise + _translate_ip_device_exception(e, device, namespace) except OSError as e: if e.errno == errno.ENOENT: raise NetworkNamespaceNotFound(netns_name=namespace) diff --git a/neutron/tests/unit/privileged/agent/linux/test_ip_lib.py b/neutron/tests/unit/privileged/agent/linux/test_ip_lib.py index 6c85128903b..a4845dc16ef 100644 --- a/neutron/tests/unit/privileged/agent/linux/test_ip_lib.py +++ b/neutron/tests/unit/privileged/agent/linux/test_ip_lib.py @@ -62,6 +62,17 @@ class IpLibTestCase(base.BaseTestCase): priv_lib._run_iproute_link, "test_cmd", "eth0", None, test_param="test_value") + def test_run_iproute_link_op_not_supported(self): + with mock.patch.object(pyroute2, "IPRoute") as iproute_mock: + ip_mock = iproute_mock() + ip_mock.__enter__().link_lookup.return_value = [2] + ip_mock.__enter__().link.side_effect = pyroute2.NetlinkError( + code=errno.EOPNOTSUPP) + self.assertRaises( + priv_lib.InterfaceOperationNotSupported, + priv_lib._run_iproute_link, + "test_cmd", "eth0", None, test_param="test_value") + def test_run_iproute_link_namespace_not_exists(self): with mock.patch.object(pyroute2, "IPRoute") as iproute_mock: iproute_mock.side_effect = OSError(