Add support for configuring split-stack networks

Some clouds, like OSIC and v1 of DreamCompute, have a split stack
network. This means that a single Neutron Network has both an IPv4 and
an IPv6 subnet, but that the IPv4 subnet is a private/RFC-1918 and the
IPv6 subnet is a Global network. As any inferrance information is
attached to the Network, it's impossible to properly categorize IP
addresses that are on the Server in such a scenario.

Add support for ipv4 and ipv6 versions of the current routes_externally
config value, with each of them defaulting to the value of the
un-specialized routes_externally.

Change-Id: I1e87a1423d20eac31175f44f5f7b38dfcf3a11cb
This commit is contained in:
Monty Taylor 2016-08-18 11:35:50 -05:00
parent a6840f69ff
commit 9d757b3a9a
No known key found for this signature in database
GPG Key ID: 7BAE94BC7141A594
5 changed files with 65 additions and 4 deletions

View File

@ -19,9 +19,15 @@ allows configuring a list of network metadata.
default_interface: true default_interface: true
- name: green - name: green
routes_externally: false routes_externally: false
- name: purple - name: yellow
routes_externally: false routes_externally: false
nat_destination: true nat_destination: true
- name: chartreuse
routes_externally: false
routes_ipv6_externally: true
- name: aubergine
routes_ipv4_externally: false
routes_ipv6_externally: true
Every entry must have a name field, which can hold either the name or the id Every entry must have a name field, which can hold either the name or the id
of the network. of the network.
@ -33,6 +39,11 @@ 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 things not on the cloud can use. This value defaults to `false`, which
indicates only servers on the same network can talk to it. indicates only servers on the same network can talk to it.
`routes_ipv4_externally` and `routes_ipv6_externally` are boolean fields to
help handle `routes_externally` in the case where a network has a split stack
with different values for IPv4 and IPv6. Either entry, if not given, defaults
to the value of `routes_externally`.
`default_interface` is a boolean field that indicates that the network is the `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 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 use this value is a cloud with two private networks, and where a user is

View File

@ -452,12 +452,36 @@ class CloudConfig(object):
net['name'] for net in self.config['networks'] net['name'] for net in self.config['networks']
if net['routes_externally']] if net['routes_externally']]
def get_external_ipv4_networks(self):
"""Get list of network names for external IPv4 networks."""
return [
net['name'] for net in self.config['networks']
if net['routes_ipv4_externally']]
def get_external_ipv6_networks(self):
"""Get list of network names for external IPv6 networks."""
return [
net['name'] for net in self.config['networks']
if net['routes_ipv6_externally']]
def get_internal_networks(self): def get_internal_networks(self):
"""Get list of network names for internal networks.""" """Get list of network names for internal networks."""
return [ return [
net['name'] for net in self.config['networks'] net['name'] for net in self.config['networks']
if not net['routes_externally']] if not net['routes_externally']]
def get_internal_ipv4_networks(self):
"""Get list of network names for internal IPv4 networks."""
return [
net['name'] for net in self.config['networks']
if not net['routes_ipv4_externally']]
def get_internal_ipv6_networks(self):
"""Get list of network names for internal IPv6 networks."""
return [
net['name'] for net in self.config['networks']
if not net['routes_ipv6_externally']]
def get_default_network(self): def get_default_network(self):
"""Get network used for default interactions.""" """Get network used for default interactions."""
for net in self.config['networks']: for net in self.config['networks']:

View File

@ -520,6 +520,14 @@ class OpenStackConfig(object):
nat_destination=get_boolean(net.get('nat_destination')), nat_destination=get_boolean(net.get('nat_destination')),
default_interface=get_boolean(net.get('default_interface')), default_interface=get_boolean(net.get('default_interface')),
) )
# routes_ipv4_externally defaults to the value of routes_externally
network['routes_ipv4_externally'] = get_boolean(
net.get(
'routes_ipv4_externally', network['routes_externally']))
# routes_ipv6_externally defaults to the value of routes_externally
network['routes_ipv6_externally'] = get_boolean(
net.get(
'routes_ipv6_externally', network['routes_externally']))
networks.append(network) networks.append(network)
for key in ('external_network', 'internal_network'): for key in ('external_network', 'internal_network'):

View File

@ -113,6 +113,14 @@ USER_CONF = {
'name': 'another-private', 'name': 'another-private',
'routes_externally': False, 'routes_externally': False,
'nat_destination': True, 'nat_destination': True,
}, {
'name': 'split-default',
'routes_externally': True,
'routes_ipv4_externally': False,
}, {
'name': 'split-no-default',
'routes_ipv6_externally': False,
'routes_ipv4_externally': True,
}], }],
'region_name': 'test-region', 'region_name': 'test-region',
}, },

View File

@ -194,11 +194,19 @@ class TestConfig(base.TestCase):
vendor_files=[self.vendor_yaml]) vendor_files=[self.vendor_yaml])
cc = c.get_one_cloud('_test-cloud-networks_') cc = c.get_one_cloud('_test-cloud-networks_')
self.assertEqual( self.assertEqual(
['a-public', 'another-public'], cc.get_external_networks()) ['a-public', 'another-public', 'split-default'],
cc.get_external_networks())
self.assertEqual( self.assertEqual(
['a-private', 'another-private'], cc.get_internal_networks()) ['a-private', 'another-private', 'split-no-default'],
cc.get_internal_networks())
self.assertEqual('another-private', cc.get_nat_destination()) self.assertEqual('another-private', cc.get_nat_destination())
self.assertEqual('another-public', cc.get_default_network()) self.assertEqual('another-public', cc.get_default_network())
self.assertEqual(
['a-public', 'another-public', 'split-no-default'],
cc.get_external_ipv4_networks())
self.assertEqual(
['a-public', 'another-public', 'split-default'],
cc.get_external_ipv6_networks())
def test_get_one_cloud_no_networks(self): def test_get_one_cloud_no_networks(self):
c = config.OpenStackConfig(config_files=[self.cloud_yaml], c = config.OpenStackConfig(config_files=[self.cloud_yaml],
@ -875,7 +883,9 @@ class TestBackwardsCompatibility(base.TestCase):
expected = { expected = {
'networks': [ 'networks': [
{'name': 'private', 'routes_externally': False, {'name': 'private', 'routes_externally': False,
'nat_destination': False, 'default_interface': False}, 'nat_destination': False, 'default_interface': False,
'routes_ipv4_externally': False,
'routes_ipv6_externally': False},
] ]
} }
self.assertEqual(expected, result) self.assertEqual(expected, result)