2
from __future__ import print_function
3
from argparse import ArgumentParser
6
from textwrap import dedent
9
add_basic_testing_arguments,
12
from deploy_stack import (
15
from assess_container_networking import (
16
cleaned_bootstrap_context,
23
def parse_args(argv=None):
24
"""Parse all arguments."""
26
description = dedent("""\
27
Test container address allocation.
28
For LXC and KVM, create machines of each type and test the network
29
between LXC <--> LXC, KVM <--> KVM and LXC <--> KVM. Also test machine
30
to outside world, DNS and that these tests still pass after a reboot. In
31
case of failure pull logs and configuration files from the machine that
32
we detected a problem on for later analysis.
34
parser = add_basic_testing_arguments(ArgumentParser(
35
description=description
38
'--clean-environment', action='store_true', help=dedent("""\
39
Attempts to re-use an existing environment rather than destroying it
40
and creating a new one.
42
On launch, if an environment exists, clean out services and machines
43
from it rather than destroying it. If an environment doesn't exist,
44
create one and use it.
46
At termination, clean out services and machines from the environment
47
rather than destroying it."""))
48
return parser.parse_args(argv)
51
def assess_spaces_subnets(client):
52
"""Check that space and subnet functionality works as expected
53
:param client: EnvJujuClient
56
'default': ['subnet-0fb97566', 'subnet-d27d91a9'],
57
'dmz': ['subnet-604dcd09', 'subnet-882d8cf3'],
58
'apps': ['subnet-c13fbfa8', 'subnet-53da7a28'],
59
'backend': ['subnet-5e4dcd37', 'subnet-7c2c8d07'],
63
'haproxy': {'space': 'dmz'},
64
'mediawiki': {'space': 'apps'},
65
'memcached': {'space': 'apps'},
66
'mysql': {'space': 'backend'},
73
_assess_spaces_subnets(client, network_config, charms_to_space)
76
def _assess_spaces_subnets(client, network_config, charms_to_space):
77
"""Check that space and subnet functionality works as expected
78
:param client: EnvJujuClient
79
:param network_config: Map of 'space name' to ['subnet', 'list']
80
:param charms_to_space: Map of 'unit name' to
81
{'space': 'space name', 'charm': 'charm name (if not same as unit)}
82
:return: None. Raises exception on failure.
84
for space in sorted(network_config.keys()):
85
client.add_space(space)
86
for subnet in network_config[space]:
87
client.add_subnet(subnet, space)
89
for name in sorted(charms_to_space.keys()):
90
if 'charm' not in charms_to_space[name]:
91
charms_to_space[name]['charm'] = name
92
charm = charms_to_space[name]['charm']
93
space = charms_to_space[name]['space']
95
(charm, name, '--constraints', 'spaces=' + space))
97
# Scale up. We don't specify constraints, but they should still be honored
99
client.juju('add-unit', 'mysql-slave')
100
client.juju('add-unit', 'mediawiki')
101
status = client.wait_for_started()
103
spaces = client.list_space()
105
unit_priv_address = {}
107
for service in sorted(status.status['services'].values()):
108
for unit_name, unit in service.get('units', {}).items():
110
addrs = ssh(client, unit['machine'], 'ip -o addr')
111
for addr in re.findall(r'^\d+:\s+(\w+)\s+inet\s+(\S+)',
112
addrs, re.MULTILINE):
114
unit_priv_address[unit_name] = addr[1]
117
for name, attrs in spaces['spaces'].iteritems():
118
cidrs_in_space[name] = []
120
cidrs_in_space[name].append(cidr)
123
for space, cidrs in cidrs_in_space.iteritems():
125
for unit, address in unit_priv_address.iteritems():
126
if ipv4_in_cidr(address, cidr):
128
charm = unit.split('/')[0]
129
if charms_to_space[charm]['space'] != space:
130
raise ValueError("Found {} in {}, expected {}".format(
131
unit, space, charms_to_space[charm]['space']))
133
if units_found != units_checked:
134
raise ValueError("Could not find spaces for all units")
139
def ipv4_to_int(ipv4):
140
"""Convert an IPv4 dotted decimal address to an integer"""
141
b = [int(b) for b in ipv4.split('.')]
142
return b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]
145
def ipv4_in_cidr(ipv4, cidr):
146
"""Returns True if the given address is in the given CIDR"""
148
ipv4, _ = ipv4.split('/')
149
ipv4 = ipv4_to_int(ipv4)
150
value, bits = cidr.split('/')
151
subnet = ipv4_to_int(value)
152
mask = 0xFFFFFFFF & (0xFFFFFFFF << (32-int(bits)))
153
return (ipv4 & mask) == subnet
157
args = parse_args(argv)
158
configure_logging(args.verbose)
159
bs_manager = BootstrapManager.from_args(args)
160
bs_manager.client.enable_feature('address-allocation')
161
with cleaned_bootstrap_context(bs_manager, args) as ctx:
162
assess_spaces_subnets(bs_manager.client)
163
return ctx.return_code
166
if __name__ == '__main__':