summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMonty Taylor <mordred@inaugust.com>2016-04-02 09:09:54 -0500
committerMonty Taylor <mordred@inaugust.com>2016-04-02 09:09:54 -0500
commit7c439073f39010ad3ac937b8c9726da0f27976b7 (patch)
tree3e0d9e1aab4cc2da78514ac8a9067a9332bf9a34
parent278a761df68d1e7d4d93ee2c6fb91f1a0e82e78a (diff)
downloados-client-config-7c439073f39010ad3ac937b8c9726da0f27976b7.tar.gz
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
-rw-r--r--README.rst2
-rw-r--r--doc/source/index.rst1
-rw-r--r--doc/source/network-config.rst47
-rw-r--r--os_client_config/cloud_config.py24
-rw-r--r--os_client_config/config.py40
-rw-r--r--os_client_config/tests/test_config.py36
-rw-r--r--releasenotes/notes/network-list-e6e9dafdd8446263.yaml5
7 files changed, 147 insertions, 8 deletions
diff --git a/README.rst b/README.rst
index 15f4bf0..97a1584 100644
--- a/README.rst
+++ b/README.rst
@@ -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:
diff --git a/doc/source/index.rst b/doc/source/index.rst
index bf667b7..f7263c9 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -6,6 +6,7 @@
vendor-support
contributing
installation
+ network-config
api-reference
releasenotes
diff --git a/doc/source/network-config.rst b/doc/source/network-config.rst
new file mode 100644
index 0000000..9bbbf9d
--- /dev/null
+++ b/doc/source/network-config.rst
@@ -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.
diff --git a/os_client_config/cloud_config.py b/os_client_config/cloud_config.py
index b19607e..e63bd12 100644
--- a/os_client_config/cloud_config.py
+++ b/os_client_config/cloud_config.py
@@ -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
diff --git a/os_client_config/config.py b/os_client_config/config.py
index 98870f6..2dd49c3 100644
--- a/os_client_config/config.py
+++ b/os_client_config/config.py
@@ -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
diff --git a/os_client_config/tests/test_config.py b/os_client_config/tests/test_config.py
index 0db1457..7c2bec0 100644
--- a/os_client_config/tests/test_config.py
+++ b/os_client_config/tests/test_config.py
@@ -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)
diff --git a/releasenotes/notes/network-list-e6e9dafdd8446263.yaml b/releasenotes/notes/network-list-e6e9dafdd8446263.yaml
index 8375488..8f793c2 100644
--- a/releasenotes/notes/network-list-e6e9dafdd8446263.yaml
+++ b/releasenotes/notes/network-list-e6e9dafdd8446263.yaml
@@ -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.