~nskaggs/juju-ci-tools/add-essential-operations

1091.3.1 by James Tunnicliffe
Supporting functions for testing networking between containers in a Juju environment.
1
#!/usr/bin/env python
2
from __future__ import print_function
1091.5.19 by Aaron Bentley
Fix imports
3
from argparse import ArgumentParser
1226.1.1 by Martin Packman
Fix assess_spaces_subnets by adapting to new BootstrapManager code from container networking test
4
import contextlib
1091.3.2 by James Tunnicliffe
clean_environment bails early if there is no environment to clean.
5
from copy import (
6
    copy,
7
    deepcopy,
8
)
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
9
import logging
10
import re
11
import os
1091.5.4 by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors.
12
import subprocess
1215.1.1 by Martin Packman
Switch assess_container_networking to using BootstrapManager
13
import sys
1091.5.19 by Aaron Bentley
Fix imports
14
import tempfile
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
15
from textwrap import dedent
1091.5.19 by Aaron Bentley
Fix imports
16
import time
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
17
1091.5.19 by Aaron Bentley
Fix imports
18
from deploy_stack import (
1215.1.1 by Martin Packman
Switch assess_container_networking to using BootstrapManager
19
    BootstrapManager,
20
    get_random_string,
1091.5.19 by Aaron Bentley
Fix imports
21
    update_env,
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
22
)
1341.2.1 by Aaron Bentley
Move container support knowledge to jujupy.
23
from jujupy import (
24
    KVM_MACHINE,
25
    LXC_MACHINE,
26
    LXD_MACHINE,
27
    )
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
28
from utility import (
1215.1.1 by Martin Packman
Switch assess_container_networking to using BootstrapManager
29
    add_basic_testing_arguments,
30
    configure_logging,
1091.5.13 by James Tunnicliffe
Fixed up tests.
31
    wait_for_port,
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
32
)
1091.5.19 by Aaron Bentley
Fix imports
33
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
34
1091.5.2 by James Tunnicliffe
retry ssh commands if they fail with code 255, which seems to happen on heavily loaded machines.
35
__metaclass__ = type
36
1091.3.1 by James Tunnicliffe
Supporting functions for testing networking between containers in a Juju environment.
37
1215.1.1 by Martin Packman
Switch assess_container_networking to using BootstrapManager
38
log = logging.getLogger("assess_container_networking")
39
40
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
41
def parse_args(argv=None):
42
    """Parse all arguments."""
43
44
    description = dedent("""\
45
    Test container address allocation.
46
    For LXC and KVM, create machines of each type and test the network
47
    between LXC <--> LXC, KVM <--> KVM and LXC <--> KVM. Also test machine
48
    to outside world, DNS and that these tests still pass after a reboot. In
49
    case of failure pull logs and configuration files from the machine that
50
    we detected a problem on for later analysis.
51
    """)
52
    parser = add_basic_testing_arguments(ArgumentParser(
53
        description=description
54
    ))
55
    parser.add_argument(
56
        '--machine-type',
57
        help='Which virtual machine/container type to test. Defaults to all.',
1319.1.1 by Martin Packman
Allow testing of lxd with assess_container_networking
58
        choices=[KVM_MACHINE, LXC_MACHINE, LXD_MACHINE])
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
59
    parser.add_argument(
60
        '--clean-environment', action='store_true', help=dedent("""\
61
        Attempts to re-use an existing environment rather than destroying it
62
        and creating a new one.
63
64
        On launch, if an environment exists, clean out services and machines
65
        from it rather than destroying it. If an environment doesn't exist,
66
        create one and use it.
67
68
        At termination, clean out services and machines from the environment
69
        rather than destroying it."""))
1215.1.1 by Martin Packman
Switch assess_container_networking to using BootstrapManager
70
    args = parser.parse_args(argv)
71
    # Passing --clean-environment implies --keep-env
72
    if args.clean_environment:
73
        args.keep_env = True
74
    return args
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
75
76
77
def ssh(client, machine, cmd):
78
    """Convenience function: run a juju ssh command and get back the output
79
    :param client: A Juju client
80
    :param machine: ID of the machine on which to run a command
81
    :param cmd: the command to run
82
    :return: text output of the command
83
    """
1091.5.4 by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors.
84
    back_off = 2
1091.5.6 by James Tunnicliffe
A bit more reliability hacking around ssh and reboots.
85
    attempts = 4
86
    for attempt in range(attempts):
1091.5.4 by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors.
87
        try:
1357.1.1 by Curtis Hovey
Use --proxy when using ssh/scp and containers.
88
            return client.get_juju_output('ssh', '--proxy', machine, cmd)
1091.5.4 by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors.
89
        except subprocess.CalledProcessError as e:
90
            # If the connection to the host failed, try again in a couple of
91
            # seconds. This is usually due to heavy load.
1357.1.1 by Curtis Hovey
Use --proxy when using ssh/scp and containers.
92
            if(attempt < attempts - 1 and
1091.5.6 by James Tunnicliffe
A bit more reliability hacking around ssh and reboots.
93
               re.search('ssh_exchange_identification: '
94
                         'Connection closed by remote host', e.stderr)):
1091.5.4 by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors.
95
                time.sleep(back_off)
96
                back_off *= 2
1091.5.6 by James Tunnicliffe
A bit more reliability hacking around ssh and reboots.
97
            else:
98
                raise
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
99
100
1091.3.1 by James Tunnicliffe
Supporting functions for testing networking between containers in a Juju environment.
101
def clean_environment(client, services_only=False):
102
    """Remove all the services and, optionally, machines from an environment.
103
104
    Use as an alternative to destroying an environment and creating a new one
105
    to save a bit of time.
106
107
    :param client: a Juju client
108
    """
1091.5.4 by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors.
109
    # A short timeout is used for get_status here because if we don't get a
110
    # response from  get_status quickly then the environment almost
111
    # certainly doesn't exist or needs recreating.
1215.1.1 by Martin Packman
Switch assess_container_networking to using BootstrapManager
112
    try:
113
        status = client.get_status(5)
114
    except Exception as e:
115
        # TODO(gz): get_status should return a more specific error type.
116
        log.info("Could not clean existing env: %s", e)
117
        return False
1091.3.2 by James Tunnicliffe
clean_environment bails early if there is no environment to clean.
118
1420.2.3 by Aaron Bentley
Use get_applications.
119
    for service in status.get_applications():
120
        client.remove_service(service)
1091.3.1 by James Tunnicliffe
Supporting functions for testing networking between containers in a Juju environment.
121
122
    if not services_only:
123
        # First remove all containers; we can't remove a machine that is
124
        # hosting containers.
125
        for m, _ in status.iter_machines(containers=True, machines=False):
126
            client.juju('remove-machine', m)
127
128
        client.wait_for('containers', 'none')
129
130
        for m, _ in status.iter_machines(containers=False, machines=True):
131
            if m != '0':
1091.5.4 by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors.
132
                try:
133
                    client.juju('remove-machine', m)
134
                except subprocess.CalledProcessError:
135
                    # Sometimes this fails because while we have asked Juju
136
                    # to remove a container and it says that it has, when we
137
                    # ask it to remove the host Juju thinks it still has
138
                    # containers on it. Normally a small pause and trying
139
                    # again is all that is needed to resolve this issue.
140
                    time.sleep(2)
1162.1.3 by Aaron Bentley
Fix lint
141
                    client.wait_for_started()
1091.5.4 by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors.
142
                    client.juju('remove-machine', m)
1091.3.1 by James Tunnicliffe
Supporting functions for testing networking between containers in a Juju environment.
143
144
        client.wait_for('machines-not-0', 'none')
145
146
    client.wait_for_started()
1091.3.2 by James Tunnicliffe
clean_environment bails early if there is no environment to clean.
147
    return True
148
149
150
def make_machines(client, container_types):
151
    """Make a test environment consisting of:
152
       Two host machines.
153
       Two of each container_type on one host machine.
154
       One of each container_type on one host machine.
155
    :param client: An EnvJujuClient
156
    :param container_types: list of containers to create
157
    :return: hosts (list), containers {container_type}{host}[containers]
158
    """
159
    # Find existing host machines
160
    old_hosts = client.get_status().status['machines']
161
    machines_to_add = 2 - len(old_hosts)
162
163
    # Allocate more hosts as needed
164
    if machines_to_add > 0:
165
        client.juju('add-machine', ('-n', str(machines_to_add)))
166
    status = client.wait_for_started()
167
    hosts = sorted(status.status['machines'].keys())[:2]
168
169
    # Find existing containers
170
    required = dict(zip(hosts, [copy(container_types) for h in hosts]))
171
    required[hosts[0]] += container_types
172
    for c in status.iter_machines(containers=True, machines=False):
173
        host, type, id = c[0].split('/')
174
        if type in required[host]:
175
            required[host].remove(type)
176
177
    # Start any new containers we need
178
    for host, containers in required.iteritems():
179
        for container in containers:
180
            client.juju('add-machine', ('{}:{}'.format(container, host)))
181
182
    status = client.wait_for_started()
183
184
    # Build a list of containers, now they have all started
185
    tmp = dict(zip(hosts, [[] for h in hosts]))
186
    containers = dict(zip(container_types,
187
                          [deepcopy(tmp) for t in container_types]))
188
    for c in status.iter_machines(containers=True, machines=False):
189
        host, type, id = c[0].split('/')
190
        if type in containers and host in containers[type]:
191
            containers[type][host].append(c[0])
192
193
    return hosts, containers
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
194
195
196
def find_network(client, machine, addr):
197
    """Find a connected subnet containing the given address.
198
199
    When using this to find the subnet of a container, don't use the container
200
    as the machine to run the ip route show command on ("machine"), use a real
201
    box because lxc will just send everything to its host machine, so it is on
202
    a subnet containing itself. Not much use.
203
    :param client: A Juju client
204
    :param machine: ID of the machine on which to run a command
205
    :param addr: find the connected subnet containing this address
206
    :return: CIDR containing the address if found, else, None
207
    """
208
    ip_cmd = ' '.join(['ip', 'route', 'show', 'to', 'match', addr])
209
    routes = ssh(client, machine, ip_cmd)
210
211
    for route in re.findall(r'^(\S+).*[\d\.]+/\d+', routes, re.MULTILINE):
212
        if route != 'default':
213
            return route
214
215
    raise ValueError("Unable to find route to %r" % addr)
216
217
218
def assess_network_traffic(client, targets):
219
    """Test that all containers in target can talk to target[0]
220
    :param client: Juju client
221
    :param targets: machine IDs of machines to test
222
    :return: None;
223
    """
1464.1.8 by Curtis Hovey
Changed per review and fix error test.
224
    status = client.wait_for_started().status
1464.1.1 by Curtis Hovey
Add clear information about the progress of the container networking test. Fix reboot for xenial and trusty.
225
    log.info('Assessing network traffic.')
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
226
    source = targets[0]
227
    dests = targets[1:]
228
229
    with tempfile.NamedTemporaryFile(delete=False) as f:
230
        f.write('tmux new-session -d -s test "nc -l 6778 > nc_listen.out"')
1357.1.1 by Curtis Hovey
Use --proxy when using ssh/scp and containers.
231
    client.juju('scp', ('--proxy', f.name, source + ':/home/ubuntu/listen.sh'))
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
232
    os.remove(f.name)
233
234
    # Containers are named 'x/type/y' where x is the host of the container. We
235
    host = source.split('/')[0]
236
    address = status['machines'][host]['containers'][source]['dns-name']
237
238
    for dest in dests:
1464.1.1 by Curtis Hovey
Add clear information about the progress of the container networking test. Fix reboot for xenial and trusty.
239
        log.info('Assessing network traffic for {}.'.format(dest))
1091.5.4 by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors.
240
        msg = get_random_string()
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
241
        ssh(client, source, 'rm nc_listen.out; bash ./listen.sh')
1091.5.4 by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors.
242
        ssh(client, dest,
243
            'echo "{msg}" | nc {addr} 6778'.format(msg=msg, addr=address))
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
244
        result = ssh(client, source, 'more nc_listen.out')
1091.5.4 by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors.
245
        if result.rstrip() != msg:
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
246
            raise ValueError("Wrong or missing message: %r" % result.rstrip())
1464.1.1 by Curtis Hovey
Add clear information about the progress of the container networking test. Fix reboot for xenial and trusty.
247
        log.info('SUCCESS.')
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
248
249
1091.5.6 by James Tunnicliffe
A bit more reliability hacking around ssh and reboots.
250
def private_address(client, host):
251
    default_route = ssh(client, host, 'ip -4 -o route list 0/0')
252
    device = re.search(r'(\w+)\s*$', default_route).group(1)
253
    device_ip = ssh(client, host, 'ip -4 -o addr show {}'.format(device))
254
    return re.search(r'inet\s+(\S+)/\d+\s', device_ip).group(1)
255
256
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
257
def assess_address_range(client, targets):
258
    """Test that two containers are in the same subnet as their host
259
    :param client: Juju client
260
    :param targets: machine IDs of machines to test
1091.5.4 by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors.
261
    :return: None; raises ValueError on failure
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
262
    """
1464.1.1 by Curtis Hovey
Add clear information about the progress of the container networking test. Fix reboot for xenial and trusty.
263
    log.info('Assessing address range.')
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
264
    status = client.wait_for_started().status
265
1091.5.6 by James Tunnicliffe
A bit more reliability hacking around ssh and reboots.
266
    host_subnet_cache = {}
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
267
268
    for target in targets:
1464.1.1 by Curtis Hovey
Add clear information about the progress of the container networking test. Fix reboot for xenial and trusty.
269
        log.info('Assessing address range for {}.'.format(target))
1091.5.6 by James Tunnicliffe
A bit more reliability hacking around ssh and reboots.
270
        host = target.split('/')[0]
271
272
        if host in host_subnet_cache:
273
            host_subnet = host_subnet_cache[host]
274
        else:
275
            host_address = private_address(client, host)
276
            host_subnet = find_network(client, host, host_address)
277
            host_subnet_cache[host] = host_subnet
278
279
        addr = status['machines'][host]['containers'][target]['dns-name']
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
280
        subnet = find_network(client, host, addr)
1091.5.4 by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors.
281
        if host_subnet != subnet:
282
            raise ValueError(
283
                '{} ({}) not on the same subnet as {} ({})'.format(
284
                    target, subnet, host, host_subnet))
1464.1.1 by Curtis Hovey
Add clear information about the progress of the container networking test. Fix reboot for xenial and trusty.
285
        log.info('SUCCESS.')
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
286
287
288
def assess_internet_connection(client, targets):
1091.5.6 by James Tunnicliffe
A bit more reliability hacking around ssh and reboots.
289
    """Test that targets can ping their default route
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
290
    :param client: Juju client
291
    :param targets: machine IDs of machines to test
1091.5.4 by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors.
292
    :return: None; raises ValueError on failure
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
293
    """
1464.1.1 by Curtis Hovey
Add clear information about the progress of the container networking test. Fix reboot for xenial and trusty.
294
    log.info('Assessing internet connection.')
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
295
    for target in targets:
1464.1.1 by Curtis Hovey
Add clear information about the progress of the container networking test. Fix reboot for xenial and trusty.
296
        log.info("Assessing internet connection for {}".format(target))
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
297
        routes = ssh(client, target, 'ip route show')
298
299
        d = re.search(r'^default\s+via\s+([\d\.]+)\s+', routes, re.MULTILINE)
300
        if d:
1357.1.1 by Curtis Hovey
Use --proxy when using ssh/scp and containers.
301
            rc = client.juju('ssh', ('--proxy', target,
302
                             'ping -c1 -q ' + d.group(1)), check=False)
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
303
            if rc != 0:
304
                raise ValueError('%s unable to ping default route' % target)
305
        else:
306
            raise ValueError("Default route not found")
1464.1.1 by Curtis Hovey
Add clear information about the progress of the container networking test. Fix reboot for xenial and trusty.
307
        log.info("SUCCESS")
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
308
309
310
def _assessment_iteration(client, containers):
311
    """Run the network tests on this collection of machines and containers
312
    :param client: Juju client
313
    :param hosts: list of hosts of containers
314
    :param containers: list of containers to run tests between
315
    :return: None
316
    """
317
    assess_internet_connection(client, containers)
318
    assess_address_range(client, containers)
319
    assess_network_traffic(client, containers)
320
321
1091.5.2 by James Tunnicliffe
retry ssh commands if they fail with code 255, which seems to happen on heavily loaded machines.
322
def _assess_container_networking(client, types, hosts, containers):
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
323
    """Run _assessment_iteration on all useful combinations of containers
324
    :param client: Juju client
325
    :param args: Parsed command line arguments
326
    :return: None
327
    """
328
    for container_type in types:
329
        # Test with two containers on the same host
1091.5.2 by James Tunnicliffe
retry ssh commands if they fail with code 255, which seems to happen on heavily loaded machines.
330
        _assessment_iteration(client, containers[container_type][hosts[0]])
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
331
332
        # Now test with two containers on two different hosts
1091.5.2 by James Tunnicliffe
retry ssh commands if they fail with code 255, which seems to happen on heavily loaded machines.
333
        test_containers = [
334
            containers[container_type][hosts[0]][0],
335
            containers[container_type][hosts[1]][0],
336
            ]
337
        _assessment_iteration(client, test_containers)
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
338
1091.5.2 by James Tunnicliffe
retry ssh commands if they fail with code 255, which seems to happen on heavily loaded machines.
339
    if KVM_MACHINE in types and LXC_MACHINE in types:
340
        test_containers = [
341
            containers[LXC_MACHINE][hosts[0]][0],
342
            containers[KVM_MACHINE][hosts[0]][0],
343
            ]
344
        _assessment_iteration(client, test_containers)
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
345
346
        # Test with an LXC and a KVM on different machines
1091.5.2 by James Tunnicliffe
retry ssh commands if they fail with code 255, which seems to happen on heavily loaded machines.
347
        test_containers = [
348
            containers[LXC_MACHINE][hosts[0]][0],
349
            containers[KVM_MACHINE][hosts[1]][0],
350
            ]
351
        _assessment_iteration(client, test_containers)
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
352
353
1319.1.1 by Martin Packman
Allow testing of lxd with assess_container_networking
354
def assess_container_networking(client, types):
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
355
    """Runs _assess_address_allocation, reboots hosts, repeat.
356
    :param client: Juju client
1319.1.1 by Martin Packman
Allow testing of lxd with assess_container_networking
357
    :param types: Container types to test
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
358
    :return: None
359
    """
1464.1.1 by Curtis Hovey
Add clear information about the progress of the container networking test. Fix reboot for xenial and trusty.
360
    log.info("Setting up test.")
1091.5.2 by James Tunnicliffe
retry ssh commands if they fail with code 255, which seems to happen on heavily loaded machines.
361
    hosts, containers = make_machines(client, types)
1091.5.13 by James Tunnicliffe
Fixed up tests.
362
    status = client.wait_for_started().status
1464.1.1 by Curtis Hovey
Add clear information about the progress of the container networking test. Fix reboot for xenial and trusty.
363
    log.info("Setup complete.")
364
    log.info("Test started.")
1091.5.13 by James Tunnicliffe
Fixed up tests.
365
1091.5.2 by James Tunnicliffe
retry ssh commands if they fail with code 255, which seems to happen on heavily loaded machines.
366
    _assess_container_networking(client, types, hosts, containers)
1091.5.13 by James Tunnicliffe
Fixed up tests.
367
1091.5.16 by James Tunnicliffe
Fixed up reboot ordering and waits.
368
    # Reboot all hosts apart from machine 0 because we use machine 0 to jump
369
    # through for some hosts.
1464.1.1 by Curtis Hovey
Add clear information about the progress of the container networking test. Fix reboot for xenial and trusty.
370
    log.info("Instrumenting reboot of all machines.")
1091.5.16 by James Tunnicliffe
Fixed up reboot ordering and waits.
371
    for host in hosts[1:]:
1464.1.3 by Curtis Hovey
Remove 1 minute delay.
372
        ssh(client, host, 'sudo shutdown -r')
1091.5.6 by James Tunnicliffe
A bit more reliability hacking around ssh and reboots.
373
1091.5.16 by James Tunnicliffe
Fixed up reboot ordering and waits.
374
    # Finally reboot machine 0
1464.1.3 by Curtis Hovey
Remove 1 minute delay.
375
    ssh(client, hosts[0], 'sudo shutdown -r')
1091.5.16 by James Tunnicliffe
Fixed up reboot ordering and waits.
376
377
    # Wait for the state server to shut down. This prevents us from calling
378
    # wait_for_started before machine 0 has shut down, which can cause us
379
    # to think that we have finished rebooting before we actually have.
380
    hostname = status['machines'][hosts[0]]['dns-name']
1464.1.5 by Curtis Hovey
Increased timeout. Do not collect interfaces.
381
    wait_for_port(hostname, 22, closed=True, timeout=600)
1091.5.16 by James Tunnicliffe
Fixed up reboot ordering and waits.
382
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
383
    client.wait_for_started()
1091.5.6 by James Tunnicliffe
A bit more reliability hacking around ssh and reboots.
384
1091.5.13 by James Tunnicliffe
Fixed up tests.
385
    # Once Juju is up it can take a little while before ssh responds.
386
    for host in hosts:
387
        hostname = status['machines'][host]['dns-name']
388
        wait_for_port(hostname, 22, timeout=240)
1464.1.1 by Curtis Hovey
Add clear information about the progress of the container networking test. Fix reboot for xenial and trusty.
389
    log.info("Reboot complete and all hosts ready for retest.")
1091.5.13 by James Tunnicliffe
Fixed up tests.
390
1091.5.2 by James Tunnicliffe
retry ssh commands if they fail with code 255, which seems to happen on heavily loaded machines.
391
    _assess_container_networking(client, types, hosts, containers)
1464.1.1 by Curtis Hovey
Add clear information about the progress of the container networking test. Fix reboot for xenial and trusty.
392
    log.info("PASS")
1091.5.2 by James Tunnicliffe
retry ssh commands if they fail with code 255, which seems to happen on heavily loaded machines.
393
394
1226.1.2 by Martin Packman
Make dedicated _CleanedContext object for mutability
395
class _CleanedContext:
396
397
    def __init__(self, client):
398
        self.client = client
399
        self.return_code = None
400
401
1226.1.1 by Martin Packman
Fix assess_spaces_subnets by adapting to new BootstrapManager code from container networking test
402
@contextlib.contextmanager
403
def cleaned_bootstrap_context(bs_manager, args):
1226.1.2 by Martin Packman
Make dedicated _CleanedContext object for mutability
404
    ctx = _CleanedContext(bs_manager.client)
405
    client = ctx.client
1215.1.2 by Martin Packman
Fix clean-environment by making calling update_env early to switch to the temp env
406
    # TODO(gz): Having to manipulate client env state here to get the temp env
407
    #           is ugly, would ideally be captured in an explicit scope.
408
    update_env(client.env, bs_manager.temp_env_name, series=bs_manager.series,
409
               agent_url=bs_manager.agent_url,
410
               agent_stream=bs_manager.agent_stream, region=bs_manager.region)
411
    with bs_manager.top_context() as machines:
412
        bootstrap_required = True
413
        if args.clean_environment and clean_environment(client):
414
            bootstrap_required = False
415
        if bootstrap_required:
416
            with bs_manager.bootstrap_context(machines):
417
                client.bootstrap(args.upload_tools)
418
        with bs_manager.runtime_context(machines):
1226.1.1 by Martin Packman
Fix assess_spaces_subnets by adapting to new BootstrapManager code from container networking test
419
            yield ctx
1226.1.2 by Martin Packman
Make dedicated _CleanedContext object for mutability
420
        ctx.return_code = 0
1215.1.2 by Martin Packman
Fix clean-environment by making calling update_env early to switch to the temp env
421
        if args.clean_environment and not clean_environment(client):
1226.1.1 by Martin Packman
Fix assess_spaces_subnets by adapting to new BootstrapManager code from container networking test
422
            ctx.return_code = 1
423
424
1341.2.1 by Aaron Bentley
Move container support knowledge to jujupy.
425
def _get_container_types(client, machine_type):
1319.1.1 by Martin Packman
Allow testing of lxd with assess_container_networking
426
    """
427
    Give list of container types to run testing against.
428
429
    If a machine_type was explictly specified, only test against those kind
430
    of containers. Otherwise, test all possible containers for the given
431
    juju version.
432
    """
433
    if machine_type:
1341.2.1 by Aaron Bentley
Move container support knowledge to jujupy.
434
        if machine_type not in client.supported_container_types:
435
            raise Exception(
436
                "no {} support on juju {}".format(machine_type,
437
                                                  client.version))
1319.1.1 by Martin Packman
Allow testing of lxd with assess_container_networking
438
        return [machine_type]
439
    # TODO(gz): Only include LXC for 1.X clients
1341.2.1 by Aaron Bentley
Move container support knowledge to jujupy.
440
    types = list(client.supported_container_types)
441
    types.sort()
1319.1.1 by Martin Packman
Allow testing of lxd with assess_container_networking
442
    return types
443
444
1226.1.1 by Martin Packman
Fix assess_spaces_subnets by adapting to new BootstrapManager code from container networking test
445
def main(argv=None):
446
    args = parse_args(argv)
447
    configure_logging(args.verbose)
448
    bs_manager = BootstrapManager.from_args(args)
1319.1.1 by Martin Packman
Allow testing of lxd with assess_container_networking
449
    client = bs_manager.client
450
    client.enable_feature('address-allocation')
1341.2.1 by Aaron Bentley
Move container support knowledge to jujupy.
451
    machine_types = _get_container_types(client, args.machine_type)
1226.1.1 by Martin Packman
Fix assess_spaces_subnets by adapting to new BootstrapManager code from container networking test
452
    with cleaned_bootstrap_context(bs_manager, args) as ctx:
1319.1.1 by Martin Packman
Allow testing of lxd with assess_container_networking
453
        assess_container_networking(bs_manager.client, machine_types)
1226.1.1 by Martin Packman
Fix assess_spaces_subnets by adapting to new BootstrapManager code from container networking test
454
    return ctx.return_code
1091.5.4 by James Tunnicliffe
SSH wrapper now trying even harder to retry on connection errors.
455
1091.5.1 by James Tunnicliffe
Test communication between containers and the wider network.
456
457
if __name__ == '__main__':
1215.1.1 by Martin Packman
Switch assess_container_networking to using BootstrapManager
458
    sys.exit(main())