From b17aad2384ab1fa50dae9b4f39c860276fddadf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awek=20Kap=C5=82o=C5=84ski?= Date: Thu, 22 Mar 2018 20:15:00 +0100 Subject: [PATCH] Handle adding ip address when it already exists Adding IP address on device is now done with pyroute2 lib. This commit handle the case when pyroute2 function raise exception when same IP address is already configured on device. In such case it will now raise exception which inherits from RuntimeError what is consistent with eariler code when it was done by running "ip addr" command to configue IP. Change-Id: I89f22310782f2f0baf0ce6b20d2ab0e1d68654a0 Related-Bug: #1492714 --- neutron/privileged/agent/linux/ip_lib.py | 34 ++++++++++++++----- .../functional/agent/linux/test_ip_lib.py | 6 ++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/neutron/privileged/agent/linux/ip_lib.py b/neutron/privileged/agent/linux/ip_lib.py index 28da4ac8068..18dd32effd5 100644 --- a/neutron/privileged/agent/linux/ip_lib.py +++ b/neutron/privileged/agent/linux/ip_lib.py @@ -50,6 +50,19 @@ class NetworkInterfaceNotFound(RuntimeError): pass +class IpAddressAlreadyExists(RuntimeError): + message = _("IP address %(ip)s already configured on %(device)s.") + + def __init__(self, message=None, ip=None, device=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 % {'ip': ip, 'device': device} + super(IpAddressAlreadyExists, self).__init__(message) + + @privileged.default.entrypoint def get_routing_table(ip_version, namespace=None): """Return a list of dictionaries, each representing a route. @@ -139,14 +152,19 @@ def _run_iproute_addr(command, device, namespace, **kwargs): def add_ip_address(ip_version, ip, prefixlen, device, namespace, scope, broadcast=None): family = _IP_VERSION_FAMILY_MAP[ip_version] - _run_iproute_addr('add', - device, - namespace, - address=ip, - mask=prefixlen, - family=family, - broadcast=broadcast, - scope=_get_scope_name(scope)) + try: + _run_iproute_addr('add', + device, + namespace, + address=ip, + mask=prefixlen, + family=family, + broadcast=broadcast, + scope=_get_scope_name(scope)) + except NetlinkError as e: + if e.code == errno.EEXIST: + raise IpAddressAlreadyExists(ip=ip, device=device) + raise @privileged.default.entrypoint diff --git a/neutron/tests/functional/agent/linux/test_ip_lib.py b/neutron/tests/functional/agent/linux/test_ip_lib.py index c8aa749b262..479ba3b343c 100644 --- a/neutron/tests/functional/agent/linux/test_ip_lib.py +++ b/neutron/tests/functional/agent/linux/test_ip_lib.py @@ -417,6 +417,12 @@ class IpLibTestCase(IpLibTestFramework): device = self.manage_device(attr) self._add_and_check_ips(device, ip_addresses) + # Now let's check if adding already existing IP address will raise + # RuntimeError + ip_address = ip_addresses[0] + self.assertRaises(RuntimeError, + device.addr.add, str(ip_address[0]), ip_address[1]) + def test_delete_ip_address(self): attr = self.generate_device_details() cidr = attr.ip_cidrs[0]