Return IPv6 address for interface_ip on request

If the local host looks like it can route IPv6 and we have an IPv6
address, and the user has not indicated that they must use IPv4 for
some reason, return it to them in the interface_ip field.  Also, always
return something in interface_ip if possible.

Change-Id: I3544280cab7abfa6b4154244f4c4588bc65c7347
Depends-On: Ib0c4c4e8b3b7b4bcee5fa3414719969274929b9a
Co-Authored-By: James E. Blair <jeblair@linux.vnet.ibm.com>
This commit is contained in:
Monty Taylor 2015-10-01 09:12:21 -04:00
parent 0a06226863
commit 5a420350db
6 changed files with 46 additions and 6 deletions

View File

@ -3,10 +3,11 @@ pbr>=0.11,<2.0
bunch
decorator
jsonpatch
os-client-config>=1.7.4
os-client-config>=1.8.0
six
keystoneauth1>=1.0.0
netifaces>=0.10.4
python-novaclient>=2.21.0,!=2.27.0
python-keystoneclient>=0.11.0
python-glanceclient>=1.0.0

View File

@ -246,6 +246,7 @@ class OpenStackCloud(object):
self.api_timeout = cloud_config.config['api_timeout']
self.image_api_use_tasks = cloud_config.config['image_api_use_tasks']
self.secgroup_source = cloud_config.config['secgroup_source']
self.force_ipv4 = cloud_config.force_ipv4
self._external_network = None
self._external_network_name_or_id = cloud_config.config.get(
@ -295,6 +296,8 @@ class OpenStackCloud(object):
self._swift_service = None
self._trove_client = None
self._local_ipv6 = _utils.localhost_supports_ipv6()
self.cloud_config = cloud_config
@contextlib.contextmanager

View File

@ -15,6 +15,7 @@
import re
import time
import netifaces
from socket import inet_aton
from struct import unpack
@ -273,3 +274,14 @@ def normalize_neutron_floating_ips(ips):
ip.get('port_id') != ''),
status=ip['status']
) for ip in ips]
def localhost_supports_ipv6():
"""Determine whether the local host supports IPv6
We look for a default route that supports the IPv6 address family,
and assume that if it is present, this host has globally routable
IPv6 connectivity.
"""
return netifaces.AF_INET6 in netifaces.gateways()['default']

View File

@ -265,10 +265,15 @@ def get_hostvars_from_server(cloud, server, mounts=None):
server_vars['public_v4'] = get_server_external_ipv4(cloud, server) or ''
server_vars['public_v6'] = get_server_external_ipv6(server) or ''
server_vars['private_v4'] = get_server_private_ip(server, cloud) or ''
if cloud.private:
interface_ip = None
if cloud.private and server_vars['private_v4']:
interface_ip = server_vars['private_v4']
else:
interface_ip = server_vars['public_v4']
if (server_vars['public_v6'] and cloud._local_ipv6
and not cloud.force_ipv4):
interface_ip = server_vars['public_v6']
else:
interface_ip = server_vars['public_v4']
if interface_ip:
server_vars['interface_ip'] = interface_ip
@ -276,7 +281,10 @@ def get_hostvars_from_server(cloud, server, mounts=None):
# server record. Since we know them, go ahead and set them. In the case
# where they were set previous, we use the values, so this will not break
# clouds that provide the information
server_vars['accessIPv4'] = server_vars['public_v4']
if cloud.private and server_vars['private_v4']:
server_vars['accessIPv4'] = server_vars['private_v4']
else:
server_vars['accessIPv4'] = server_vars['public_v4']
server_vars['accessIPv6'] = server_vars['public_v6']
server_vars['region'] = cloud.region_name

View File

@ -32,8 +32,10 @@ class FakeCloud(object):
region_name = 'test-region'
name = 'test-name'
private = False
force_ipv4 = False
service_val = True
_unused = "useless"
_local_ipv6 = True
def get_flavor_name(self, id):
return 'test-flavor-name'
@ -319,7 +321,7 @@ class TestMeta(testtools.TestCase):
self.assertEqual(PRIVATE_V4, hostvars['private_v4'])
self.assertEqual(PUBLIC_V4, hostvars['public_v4'])
self.assertEqual(PUBLIC_V6, hostvars['public_v6'])
self.assertEqual(PUBLIC_V4, hostvars['interface_ip'])
self.assertEqual(PUBLIC_V6, hostvars['interface_ip'])
self.assertEquals(FakeCloud.region_name, hostvars['region'])
self.assertEquals(FakeCloud.name, hostvars['cloud'])
self.assertEquals("test-image-name", hostvars['image']['name'])
@ -332,6 +334,20 @@ class TestMeta(testtools.TestCase):
# test volume exception
self.assertEquals([], hostvars['volumes'])
@mock.patch.object(shade.meta, 'get_server_external_ipv6')
@mock.patch.object(shade.meta, 'get_server_external_ipv4')
def test_ipv4_hostvars(
self, mock_get_server_external_ipv4,
mock_get_server_external_ipv6):
mock_get_server_external_ipv4.return_value = PUBLIC_V4
mock_get_server_external_ipv6.return_value = PUBLIC_V6
fake_cloud = FakeCloud()
fake_cloud.force_ipv4 = True
hostvars = meta.get_hostvars_from_server(
fake_cloud, meta.obj_to_dict(FakeServer()))
self.assertEqual(PUBLIC_V4, hostvars['interface_ip'])
@mock.patch.object(shade.meta, 'get_server_external_ipv4')
def test_private_interface_ip(self, mock_get_server_external_ipv4):
mock_get_server_external_ipv4.return_value = PUBLIC_V4

View File

@ -32,7 +32,7 @@ commands = python setup.py testr --coverage --testr-args='{posargs}'
[flake8]
# Infra does not follow hacking, nor the broken E12* things
ignore = E123,E125,H
ignore = E123,E125,E129,H
show-source = True
builtins = _
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build