Flesh out netowrk config list

Add support for indicating default_interface. Also, add some validation
and normalization code, some interface methods and, shockingly,
documentation.

Change-Id: Ib45b68894585ac02821d5d2376510fd7a8e8ee40
This commit is contained in:
Monty Taylor 2016-04-02 09:09:54 -05:00
parent 278a761df6
commit 7c439073f3
7 changed files with 147 additions and 8 deletions

View File

@ -277,7 +277,7 @@ To support this, the region list can actually be a list of dicts, and any
setting that can be set at the cloud level can be overridden for that
region.
::
.. code-block:: yaml
clouds:
internap:

View File

@ -6,6 +6,7 @@
vendor-support
contributing
installation
network-config
api-reference
releasenotes

View File

@ -0,0 +1,47 @@
==============
Network Config
==============
There are several different qualities that networks in OpenStack might have
that might not be able to be automatically inferred from the available
metadata. To help users navigate more complex setups, `os-client-config`
allows configuring a list of network metadata.
.. code-block:: yaml
clouds:
amazing:
networks:
- name: blue
routes_externally: true
- name: purple
routes_externally: true
default_interface: true
- name: green
routes_externally: false
- name: purple
routes_externally: false
nat_destination: true
Every entry must have a name field, which can hold either the name or the id
of the network.
`routes_externally` is a boolean field that labels the network as handling
north/south traffic off of the cloud. In a public cloud this might be thought
of as the "public" network, but in private clouds it's possible it might
be an RFC1918 address. In either case, it's provides IPs to servers that
things not on the cloud can use. This value defaults to `false`, which
indicates only servers on the same network can talk to it.
`default_interface` is a boolean field that indicates that the network is the
one that programs should use. It defaults to false. An example of needing to
use this value is a cloud with two private networks, and where a user is
running ansible in one of the servers to talk to other servers on the private
network. Because both networks are private, there would otherwise be no way
to determine which one should be used for the traffic.
`nat_destination` is a boolean field that indicates which network floating
ips should be attached to. It defaults to false. Normally this can be inferred
by looking for a network that has subnets that have a gateway_ip. But it's
possible to have more than one network that satisfies that condition, so the
user might want to tell programs which one to pick.

View File

@ -438,3 +438,27 @@ class CloudConfig(object):
if resource not in expiration:
return default
return float(expiration[resource])
def get_external_networks(self):
"""Get list of network names for external networks."""
return [
net['name'] for net in self._openstack_config['networks']
if net['routes_externally']]
def get_internal_networks(self):
"""Get list of network names for internal networks."""
return [
net['name'] for net in self._openstack_config['networks']
if not net['routes_externally']]
def get_default_network(self):
"""Get network used for default interactions."""
for net in self._openstack_config['networks']:
if net['default_interface']:
return net
def get_nat_destination(self):
"""Get network used for NAT destination."""
for net in self._openstack_config['networks']:
if net['nat_destination']:
return net

View File

@ -83,6 +83,8 @@ def set_default(key, value):
def get_boolean(value):
if value is None:
return False
if type(value) is bool:
return value
if value.lower() == 'true':
@ -486,10 +488,37 @@ class OpenStackConfig(object):
or 'project_id' in cloud['auth']
or 'project_name' in cloud['auth'])
def _validate_networks(self, networks, key):
value = None
for net in networks:
if value and net[key]:
raise exceptions.OpenStackConfigException(
"Duplicate network entries for {key}: {net1} and {net2}."
" Only one network can be flagged with {key}".format(
key=key,
net1=value['name'],
net2=net['name']))
if not value and net[key]:
value = net
def _fix_backwards_networks(self, cloud):
# Leave the external_network and internal_network keys in the
# dict because consuming code might be expecting them.
networks = cloud.get('networks', [])
networks = []
# Normalize existing network entries
for net in cloud.get('networks', []):
name = net.get('name')
if not name:
raise exceptions.OpenStackConfigException(
'Entry in network list is missing required field "name".')
network = dict(
name=name,
routes_externally=get_boolean(net.get('routes_externally')),
nat_destination=get_boolean(net.get('nat_destination')),
default_interface=get_boolean(net.get('default_interface')),
)
networks.append(network)
for key in ('external_network', 'internal_network'):
external = key.startswith('external')
if key in cloud and 'networks' in cloud:
@ -505,7 +534,14 @@ class OpenStackConfig(object):
key=key, name=cloud[key], external=external))
networks.append(dict(
name=cloud[key],
routes_externally=external))
routes_externally=external,
nat_destination=not external,
default_interface=external))
# Validate that we don't have duplicates
self._validate_networks(networks, 'nat_destination')
self._validate_networks(networks, 'default_interface')
cloud['networks'] = networks
return cloud

View File

@ -779,8 +779,40 @@ class TestBackwardsCompatibility(base.TestCase):
'external_network': 'public',
'internal_network': 'private',
'networks': [
{'name': 'public', 'routes_externally': True},
{'name': 'private', 'routes_externally': False},
{'name': 'public', 'routes_externally': True,
'nat_destination': False, 'default_interface': True},
{'name': 'private', 'routes_externally': False,
'nat_destination': True, 'default_interface': False},
]
}
self.assertEqual(expected, result)
def test_normalize_network(self):
c = config.OpenStackConfig(config_files=[self.cloud_yaml],
vendor_files=[self.vendor_yaml])
cloud = {
'networks': [
{'name': 'private'}
]
}
result = c._fix_backwards_networks(cloud)
expected = {
'networks': [
{'name': 'private', 'routes_externally': False,
'nat_destination': False, 'default_interface': False},
]
}
self.assertEqual(expected, result)
def test_single_default_interface(self):
c = config.OpenStackConfig(config_files=[self.cloud_yaml],
vendor_files=[self.vendor_yaml])
cloud = {
'networks': [
{'name': 'blue', 'default_interface': True},
{'name': 'purple', 'default_interface': True},
]
}
self.assertRaises(
exceptions.OpenStackConfigException,
c._fix_backwards_networks, cloud)

View File

@ -3,9 +3,8 @@ features:
- Support added for configuring metadata about networks
for a cloud in a list of dicts, rather than in the
external_network and internal_network entries. The dicts
support a name and a routes_externally field, as well as
any other arbitrary metadata needed by consuming
applications.
support a name, a routes_externally field, a nat_destination
field and a default_interface field.
deprecations:
- external_network and internal_network are deprecated and
should be replaced with the list of network dicts.