From 7b97d363524fbd471289070557d1b8ffe9c06758 Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Tue, 25 Jun 2013 08:54:53 -0400 Subject: [PATCH] Better default for my_ip if 8.8.8.8 is unreachable Run if route/show, extract ipv4 addresses and pick the first one. Fall back to 127.0.0.1 as before if we don't find anything or if we have running the ip route/show commands Fixes LP# 1193013 Change-Id: If73f0f6bf67f858f7506220debab0f74cc9e3cb4 --- nova/netconf.py | 4 +- nova/tests/test_utils.py | 86 ++++++++++++++++++++++++++++++++++++++++ nova/utils.py | 56 ++++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 1 deletion(-) diff --git a/nova/netconf.py b/nova/netconf.py index 78939d5861f..53130ceabe2 100644 --- a/nova/netconf.py +++ b/nova/netconf.py @@ -21,6 +21,8 @@ from oslo.config import cfg +from nova import utils + CONF = cfg.CONF @@ -40,7 +42,7 @@ def _get_my_ip(): csock.close() return addr except socket.error: - return "127.0.0.1" + return utils.get_my_ipv4_address() netconf_opts = [ diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py index 60928026148..ada6495495a 100644 --- a/nova/tests/test_utils.py +++ b/nova/tests/test_utils.py @@ -193,6 +193,92 @@ def test_accepts_dictionaries(self): self.assertEquals(['b_1'], f(input, "a/b")) +class GetMyIP4AddressTestCase(test.TestCase): + def test_get_my_ipv4_address_with_no_ipv4(self): + response = """172.16.0.0/16 via 172.16.251.13 dev tun1 +172.16.251.1 via 172.16.251.13 dev tun1 +172.16.251.13 dev tun1 proto kernel scope link src 172.16.251.14 +172.24.0.0/16 via 172.16.251.13 dev tun1 +192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1""" + + def fake_execute(*args, **kwargs): + return response, None + + self.stubs.Set(utils, 'execute', fake_execute) + address = utils.get_my_ipv4_address() + self.assertEqual(address, '127.0.0.1') + + def test_get_my_ipv4_address_bad_process(self): + def fake_execute(*args, **kwargs): + raise processutils.ProcessExecutionError() + + self.stubs.Set(utils, 'execute', fake_execute) + address = utils.get_my_ipv4_address() + self.assertEqual(address, '127.0.0.1') + + def test_get_my_ipv4_address_with_single_interface(self): + response_route = """default via 192.168.1.1 dev wlan0 proto static +192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.137 metric 9 +""" + response_addr = """ +1: lo inet 127.0.0.1/8 scope host lo +3: wlan0 inet 192.168.1.137/24 brd 192.168.1.255 scope global wlan0 +""" + + def fake_execute(*args, **kwargs): + if 'route' in args: + return response_route, None + return response_addr, None + + self.stubs.Set(utils, 'execute', fake_execute) + address = utils.get_my_ipv4_address() + self.assertEqual(address, '192.168.1.137') + + def test_get_my_ipv4_address_with_multi_ipv4_on_single_interface(self): + response_route = """ +172.18.56.0/24 dev customer proto kernel scope link src 172.18.56.22 +169.254.0.0/16 dev customer scope link metric 1031 +default via 172.18.56.1 dev customer +""" + response_addr = ("" +"31: customer inet 172.18.56.22/24 brd 172.18.56.255 scope global" +" customer\n" +"31: customer inet 172.18.56.32/24 brd 172.18.56.255 scope global " +"secondary customer") + + def fake_execute(*args, **kwargs): + if 'route' in args: + return response_route, None + return response_addr, None + + self.stubs.Set(utils, 'execute', fake_execute) + address = utils.get_my_ipv4_address() + self.assertEqual(address, '172.18.56.22') + + def test_get_my_ipv4_address_with_multiple_interfaces(self): + response_route = """ +169.1.9.0/24 dev eth1 proto kernel scope link src 169.1.9.10 +172.17.248.0/21 dev eth0 proto kernel scope link src 172.17.255.9 +169.254.0.0/16 dev eth0 scope link metric 1002 +169.254.0.0/16 dev eth1 scope link metric 1003 +default via 172.17.248.1 dev eth0 proto static +""" + response_addr = """ +1: lo inet 127.0.0.1/8 scope host lo +2: eth0 inet 172.17.255.9/21 brd 172.17.255.255 scope global eth0 +3: eth1 inet 169.1.9.10/24 scope global eth1 +""" + + def fake_execute(*args, **kwargs): + if 'route' in args: + return response_route, None + return response_addr, None + + self.stubs.Set(utils, 'execute', fake_execute) + address = utils.get_my_ipv4_address() + self.assertEqual(address, '172.17.255.9') + + class GenericUtilsTestCase(test.TestCase): def test_parse_server_string(self): result = utils.parse_server_string('::1') diff --git a/nova/utils.py b/nova/utils.py index 5e968bd35cf..20dc51a79e7 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -321,6 +321,62 @@ def last_octet(address): return int(address.split('.')[-1]) +def get_my_ipv4_address(): + """Run ip route/addr commands to figure out the best ipv4 + """ + LOCALHOST = '127.0.0.1' + try: + out = execute('ip', '-f', 'inet', '-o', 'route', 'show', + run_as_root=True) + + # Find the default route + regex_default = ('default\s*via\s*' + '(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' + '\s*dev\s*(\w*)\s*') + default_routes = re.findall(regex_default, out[0]) + if not default_routes: + return LOCALHOST + gateway, iface = default_routes[0] + + # Find the right subnet for the gateway/interface for + # the default route + route = ('(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,2})' + '\s*dev\s*(\w*)\s*') + for match in re.finditer(route, out[0]): + subnet = netaddr.IPNetwork(match.group(1) + "/" + match.group(2)) + if (match.group(3) == iface and + netaddr.IPAddress(gateway) in subnet): + try: + return _get_ipv4_address_for_interface(iface) + except exception.NovaException: + pass + except Exception as ex: + LOG.error(_("Couldn't get IPv4 : %(ex)s") % {'ex': ex}) + return LOCALHOST + + +def _get_ipv4_address_for_interface(iface): + """Run ip addr show for an interface and grab its ipv4 addresses + """ + try: + out = execute('ip', '-f', 'inet', '-o', 'addr', 'show', iface, + run_as_root=True) + regexp_address = re.compile('inet\s*' + '(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})') + address = [m.group(1) for m in regexp_address.finditer(out[0]) + if m.group(1) != '127.0.0.1'] + if address: + return address[0] + else: + msg = _('IPv4 address is not found.: %s') % out[0] + raise exception.NovaException(msg) + except Exception as ex: + msg = _("Couldn't get IPv4 of %(interface)s" + " : %(ex)s") % {'interface': iface, 'ex': ex} + LOG.error(msg) + raise exception.NovaException(msg) + + def get_my_linklocal(interface): try: if_str = execute('ip', '-f', 'inet6', '-o', 'addr', 'show', interface)