diff --git a/neutron/agent/linux/ip_lib.py b/neutron/agent/linux/ip_lib.py index 0b8e3ac5142..c53786f05c1 100644 --- a/neutron/agent/linux/ip_lib.py +++ b/neutron/agent/linux/ip_lib.py @@ -601,6 +601,10 @@ class IpLinkCommand(IpDeviceCommandBase): return privileged.get_link_attributes(self.name, self._parent.namespace) + @property + def exists(self): + return privileged.interface_exists(self.name, self._parent.namespace) + class IpAddrCommand(IpDeviceCommandBase): COMMAND = 'addr' @@ -1147,8 +1151,8 @@ def ensure_device_is_ready(device_name, namespace=None): dev = IPDevice(device_name, namespace=namespace) try: # Ensure the device has a MAC address and is up, even if it is already - # up. If the device doesn't exist, a RuntimeError will be raised. - if not dev.link.address: + # up. + if not dev.link.exists or not dev.link.address: LOG.error("Device %s cannot be used as it has no MAC " "address", device_name) return False diff --git a/neutron/privileged/agent/linux/ip_lib.py b/neutron/privileged/agent/linux/ip_lib.py index b5f0b72a382..b015f6d178f 100644 --- a/neutron/privileged/agent/linux/ip_lib.py +++ b/neutron/privileged/agent/linux/ip_lib.py @@ -14,6 +14,7 @@ import errno import socket from neutron_lib import constants +from oslo_log import log as logging import pyroute2 from pyroute2.netlink import rtnl from pyroute2.netlink.rtnl import ifinfmsg @@ -25,6 +26,8 @@ from neutron._i18n import _ from neutron import privileged +LOG = logging.getLogger(__name__) + _IP_VERSION_FAMILY_MAP = {4: socket.AF_INET, 6: socket.AF_INET6} @@ -146,12 +149,17 @@ def _translate_ip_device_exception(e, device=None, namespace=None): namespace=namespace) -def _get_link_id(device, namespace): - try: - with _get_iproute(namespace) as ip: - return ip.link_lookup(ifname=device)[0] - except IndexError: - raise NetworkInterfaceNotFound(device=device, namespace=namespace) +def _get_link_id(device, namespace, raise_exception=True): + with _get_iproute(namespace) as ip: + link_id = ip.link_lookup(ifname=device) + if not link_id or len(link_id) < 1: + if raise_exception: + raise NetworkInterfaceNotFound(device=device, namespace=namespace) + else: + LOG.debug('Interface %(dev)s not found in namespace %(namespace)s', + {'dev': device, 'namespace': namespace}) + return None + return link_id[0] def _run_iproute_link(command, device, namespace=None, **kwargs): @@ -272,10 +280,8 @@ def delete_interface(ifname, namespace, **kwargs): @privileged.default.entrypoint def interface_exists(ifname, namespace): try: - idx = _get_link_id(ifname, namespace) + idx = _get_link_id(ifname, namespace, raise_exception=False) return bool(idx) - except NetworkInterfaceNotFound: - return False except OSError as e: if e.errno == errno.ENOENT: return False diff --git a/neutron/tests/unit/agent/linux/test_ip_lib.py b/neutron/tests/unit/agent/linux/test_ip_lib.py index 7f6b41621d6..6df8fca9013 100644 --- a/neutron/tests/unit/agent/linux/test_ip_lib.py +++ b/neutron/tests/unit/agent/linux/test_ip_lib.py @@ -1336,15 +1336,21 @@ class TestDeviceExists(base.BaseTestCase): def test_ensure_device_is_ready_no_link_address(self): with mock.patch.object( - priv_lib, 'get_link_attributes' - ) as get_link_attributes, mock.patch.object( - priv_lib, 'set_link_attribute' - ) as set_link_attribute: + priv_lib, 'get_link_attributes') as get_link_attributes, \ + mock.patch.object(priv_lib, 'set_link_attribute') as \ + set_link_attribute, \ + mock.patch.object(priv_lib, 'interface_exists', + return_value=True): get_link_attributes.return_value = {} self.assertFalse(ip_lib.ensure_device_is_ready("lo")) get_link_attributes.assert_called_once_with("lo", None) set_link_attribute.assert_not_called() + def test_ensure_device_is_ready_no_device(self): + with mock.patch.object(priv_lib, 'interface_exists', + return_value=False): + self.assertFalse(ip_lib.ensure_device_is_ready("lo")) + class TestGetRoutingTable(base.BaseTestCase): ip_db_interfaces = {