Ignore IPv6 addresses if force_ipv4 is set

Currently if a cloud config sets force_ipv4, it will still have the
public_v6 field populated if the provider API gives an IPv6 address.

I can not see why you would want this address populated if you are
telling the cloud to force IPv4.  As a concrete example;
If6e1a0402b9b7f93cc76623c01049764abc68b2a proposes in zuul-jobs adding
the IPv6 address to /etc/hosts for multinode jobs.  It does this by
walking the nodepool interface values, which on some clouds with
force_ipv4 set will have invalid/unconfigured IPv6 entries.

I've updated the documentation to expand a bit more on what situations
this flag might be useful, which AIUI is really mostly about clouds
that return you an IPv6 address in the API but don't give you a
practical way to auto-configure it.

Change-Id: I7aaaf44ab1a1d4d25225843227ef6ab6d8564063
This commit is contained in:
Ian Wienand 2020-07-03 11:30:00 +10:00
parent 3b693c2b91
commit 661a0eb4b5
4 changed files with 40 additions and 14 deletions

View File

@ -254,18 +254,22 @@ are connecting to OpenStack can share a cache should you desire.
IPv6 IPv6
---- ----
IPv6 is the future, and you should always use it if your cloud supports it and IPv6 is the future, and you should always use it if your cloud
if your local network supports it. Both of those are easily detectable and all supports it and if your local network supports it. Both of those are
friendly software should do the right thing. However, sometimes you might easily detectable and all friendly software should do the right thing.
exist in a location where you have an IPv6 stack, but something evil has
caused it to not actually function. In that case, there is a config option However, sometimes a cloud API may return IPv6 information that is not
you can set to unbreak you `force_ipv4`, or `OS_FORCE_IPV4` boolean useful to a production deployment. For example, the API may provide
environment variable. an IPv6 address for a server, but not provide that to the host
instance via metadata (configdrive) or standard IPv6 autoconfiguration
methods (i.e. the host either needs to make a bespoke API call, or
otherwise statically configure itself).
For such situations, you can set the ``force_ipv4``, or ``OS_FORCE_IPV4``
boolean environment variable. For example:
.. code-block:: yaml .. code-block:: yaml
client:
force_ipv4: true
clouds: clouds:
mtvexx: mtvexx:
profile: vexxhost profile: vexxhost
@ -276,15 +280,24 @@ environment variable.
region_name: ca-ymq-1 region_name: ca-ymq-1
dns_api_version: 1 dns_api_version: 1
monty: monty:
profile: rax profile: fooprovider
force_ipv4: true
auth: auth:
username: mordred@inaugust.com username: mordred@inaugust.com
password: XXXXXXXXX password: XXXXXXXXX
project_name: mordred@inaugust.com project_name: mordred@inaugust.com
region_name: DFW region_name: RegionFoo
The above snippet will tell client programs to prefer the IPv4 address
and leave the ``public_v6`` field of the `Server` object blank for the
``fooprovider`` cloud . You can also set this with a client flag for
all clouds:
.. code-block:: yaml
client:
force_ipv4: true
The above snippet will tell client programs to prefer returning an IPv4
address.
Per-region settings Per-region settings
------------------- -------------------

View File

@ -280,6 +280,7 @@ def get_server_external_ipv6(server):
:param server: the server from which we want to get an IPv6 address :param server: the server from which we want to get an IPv6 address
:return: a string containing the IPv6 address or None :return: a string containing the IPv6 address or None
""" """
# Don't return ipv6 interfaces if forcing IPv4
if server['accessIPv6']: if server['accessIPv6']:
return server['accessIPv6'] return server['accessIPv6']
addresses = find_nova_addresses(addresses=server['addresses'], version=6) addresses = find_nova_addresses(addresses=server['addresses'], version=6)
@ -448,7 +449,12 @@ def add_server_interfaces(cloud, server):
# not exist to remain consistent with the pre-existing missing values # not exist to remain consistent with the pre-existing missing values
server['addresses'] = _get_supplemental_addresses(cloud, server) server['addresses'] = _get_supplemental_addresses(cloud, server)
server['public_v4'] = get_server_external_ipv4(cloud, server) or '' server['public_v4'] = get_server_external_ipv4(cloud, server) or ''
server['public_v6'] = get_server_external_ipv6(server) or '' # If we're forcing IPv4, then don't report IPv6 interfaces which
# are likely to be unconfigured.
if cloud.force_ipv4:
server['public_v6'] = ''
else:
server['public_v6'] = get_server_external_ipv6(server) or ''
server['private_v4'] = get_server_private_ip(server, cloud) or '' server['private_v4'] = get_server_private_ip(server, cloud) or ''
server['interface_ip'] = _get_interface_ip(cloud, server) or '' server['interface_ip'] = _get_interface_ip(cloud, server) or ''

View File

@ -1018,6 +1018,7 @@ class TestMeta(base.TestCase):
hostvars = meta.get_hostvars_from_server( hostvars = meta.get_hostvars_from_server(
fake_cloud, meta.obj_to_munch(standard_fake_server)) fake_cloud, meta.obj_to_munch(standard_fake_server))
self.assertEqual(PUBLIC_V4, hostvars['interface_ip']) self.assertEqual(PUBLIC_V4, hostvars['interface_ip'])
self.assertEqual('', hostvars['public_v6'])
@mock.patch.object(meta, 'get_server_external_ipv4') @mock.patch.object(meta, 'get_server_external_ipv4')
def test_private_interface_ip(self, mock_get_server_external_ipv4): def test_private_interface_ip(self, mock_get_server_external_ipv4):

View File

@ -0,0 +1,6 @@
---
upgrade:
- |
Cloud with the `force_ipv4` flag will no longer return a
`public_v6` value, even if one is provided by the cloud. This is
to avoid having entries for unconfigured interfaces.