~andrewjbeach/juju-ci-tools/make-local-patcher

1091.6.1 by James Tunnicliffe
Addded test for spaces and subnets. Currently only works with fixed subnets that exist in the Canonical EC2 account in the eu-central-1.
1
#!/usr/bin/env python
2
from __future__ import print_function
1226.1.1 by Martin Packman
Fix assess_spaces_subnets by adapting to new BootstrapManager code from container networking test
3
from argparse import ArgumentParser
1091.6.1 by James Tunnicliffe
Addded test for spaces and subnets. Currently only works with fixed subnets that exist in the Canonical EC2 account in the eu-central-1.
4
import re
1226.1.1 by Martin Packman
Fix assess_spaces_subnets by adapting to new BootstrapManager code from container networking test
5
import sys
6
from textwrap import dedent
1091.6.1 by James Tunnicliffe
Addded test for spaces and subnets. Currently only works with fixed subnets that exist in the Canonical EC2 account in the eu-central-1.
7
8
from utility import (
9
    add_basic_testing_arguments,
1226.1.1 by Martin Packman
Fix assess_spaces_subnets by adapting to new BootstrapManager code from container networking test
10
    configure_logging,
1091.6.1 by James Tunnicliffe
Addded test for spaces and subnets. Currently only works with fixed subnets that exist in the Canonical EC2 account in the eu-central-1.
11
)
12
from deploy_stack import (
1226.1.1 by Martin Packman
Fix assess_spaces_subnets by adapting to new BootstrapManager code from container networking test
13
    BootstrapManager,
1091.6.1 by James Tunnicliffe
Addded test for spaces and subnets. Currently only works with fixed subnets that exist in the Canonical EC2 account in the eu-central-1.
14
)
15
from assess_container_networking import (
1226.1.1 by Martin Packman
Fix assess_spaces_subnets by adapting to new BootstrapManager code from container networking test
16
    cleaned_bootstrap_context,
1091.6.1 by James Tunnicliffe
Addded test for spaces and subnets. Currently only works with fixed subnets that exist in the Canonical EC2 account in the eu-central-1.
17
    ssh,
18
)
19
20
__metaclass__ = type
21
22
23
def parse_args(argv=None):
24
    """Parse all arguments."""
25
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.
33
    """)
34
    parser = add_basic_testing_arguments(ArgumentParser(
35
        description=description
36
    ))
37
    parser.add_argument(
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.
41
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.
45
46
        At termination, clean out services and machines from the environment
47
        rather than destroying it."""))
48
    return parser.parse_args(argv)
49
50
51
def assess_spaces_subnets(client):
1091.6.3 by James Tunnicliffe
Added a bit of documentation.
52
    """Check that space and subnet functionality works as expected
53
    :param client: EnvJujuClient
54
    """
1091.6.2 by James Tunnicliffe
Updated test to match planned demo. Now tests if units are added to the correct space after the first has been directed to one.
55
    network_config = {
56
        'default': ['subnet-0fb97566', 'subnet-d27d91a9'],
57
        'dmz': ['subnet-604dcd09', 'subnet-882d8cf3'],
58
        'apps': ['subnet-c13fbfa8', 'subnet-53da7a28'],
59
        'backend': ['subnet-5e4dcd37', 'subnet-7c2c8d07'],
60
    }
61
62
    charms_to_space = {
63
        'haproxy': {'space': 'dmz'},
64
        'mediawiki': {'space': 'apps'},
65
        'memcached': {'space': 'apps'},
66
        'mysql': {'space': 'backend'},
67
        'mysql-slave': {
68
            'space': 'backend',
69
            'charm': 'mysql',
70
        },
71
    }
72
73
    _assess_spaces_subnets(client, network_config, charms_to_space)
74
75
76
def _assess_spaces_subnets(client, network_config, charms_to_space):
1091.6.3 by James Tunnicliffe
Added a bit of documentation.
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.
83
    """
1091.6.2 by James Tunnicliffe
Updated test to match planned demo. Now tests if units are added to the correct space after the first has been directed to one.
84
    for space in sorted(network_config.keys()):
1221.5.3 by Aaron Bentley
Extract EnvJujuClient.add_space.
85
        client.add_space(space)
1091.6.2 by James Tunnicliffe
Updated test to match planned demo. Now tests if units are added to the correct space after the first has been directed to one.
86
        for subnet in network_config[space]:
1221.5.5 by Aaron Bentley
Extract add_subnet.
87
            client.add_subnet(subnet, space)
1091.6.2 by James Tunnicliffe
Updated test to match planned demo. Now tests if units are added to the correct space after the first has been directed to one.
88
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']
94
        client.juju('deploy',
95
                    (charm, name, '--constraints', 'spaces=' + space))
96
97
    # Scale up. We don't specify constraints, but they should still be honored
98
    # per charm.
99
    client.juju('add-unit', 'mysql-slave')
100
    client.juju('add-unit', 'mediawiki')
1091.6.1 by James Tunnicliffe
Addded test for spaces and subnets. Currently only works with fixed subnets that exist in the Canonical EC2 account in the eu-central-1.
101
    status = client.wait_for_started()
1091.6.2 by James Tunnicliffe
Updated test to match planned demo. Now tests if units are added to the correct space after the first has been directed to one.
102
1221.5.1 by Aaron Bentley
Extract EnvJujuClient.list_space.
103
    spaces = client.list_space()
1091.6.1 by James Tunnicliffe
Addded test for spaces and subnets. Currently only works with fixed subnets that exist in the Canonical EC2 account in the eu-central-1.
104
105
    unit_priv_address = {}
1091.6.2 by James Tunnicliffe
Updated test to match planned demo. Now tests if units are added to the correct space after the first has been directed to one.
106
    units_found = 0
1091.6.1 by James Tunnicliffe
Addded test for spaces and subnets. Currently only works with fixed subnets that exist in the Canonical EC2 account in the eu-central-1.
107
    for service in sorted(status.status['services'].values()):
108
        for unit_name, unit in service.get('units', {}).items():
1091.6.2 by James Tunnicliffe
Updated test to match planned demo. Now tests if units are added to the correct space after the first has been directed to one.
109
            units_found += 1
1091.6.1 by James Tunnicliffe
Addded test for spaces and subnets. Currently only works with fixed subnets that exist in the Canonical EC2 account in the eu-central-1.
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):
113
                if addr[0] != 'lo':
114
                    unit_priv_address[unit_name] = addr[1]
115
116
    cidrs_in_space = {}
117
    for name, attrs in spaces['spaces'].iteritems():
118
        cidrs_in_space[name] = []
119
        for cidr in attrs:
120
            cidrs_in_space[name].append(cidr)
121
122
    units_checked = 0
123
    for space, cidrs in cidrs_in_space.iteritems():
124
        for cidr in cidrs:
125
            for unit, address in unit_priv_address.iteritems():
126
                if ipv4_in_cidr(address, cidr):
127
                    units_checked += 1
1091.6.2 by James Tunnicliffe
Updated test to match planned demo. Now tests if units are added to the correct space after the first has been directed to one.
128
                    charm = unit.split('/')[0]
129
                    if charms_to_space[charm]['space'] != space:
1091.6.1 by James Tunnicliffe
Addded test for spaces and subnets. Currently only works with fixed subnets that exist in the Canonical EC2 account in the eu-central-1.
130
                        raise ValueError("Found {} in {}, expected {}".format(
1091.6.2 by James Tunnicliffe
Updated test to match planned demo. Now tests if units are added to the correct space after the first has been directed to one.
131
                            unit, space, charms_to_space[charm]['space']))
132
133
    if units_found != units_checked:
134
        raise ValueError("Could not find spaces for all units")
1091.6.1 by James Tunnicliffe
Addded test for spaces and subnets. Currently only works with fixed subnets that exist in the Canonical EC2 account in the eu-central-1.
135
136
    return units_checked
137
138
139
def ipv4_to_int(ipv4):
1091.6.3 by James Tunnicliffe
Added a bit of documentation.
140
    """Convert an IPv4 dotted decimal address to an integer"""
1091.6.1 by James Tunnicliffe
Addded test for spaces and subnets. Currently only works with fixed subnets that exist in the Canonical EC2 account in the eu-central-1.
141
    b = [int(b) for b in ipv4.split('.')]
142
    return b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]
143
144
145
def ipv4_in_cidr(ipv4, cidr):
1091.6.3 by James Tunnicliffe
Added a bit of documentation.
146
    """Returns True if the given address is in the given CIDR"""
1091.6.1 by James Tunnicliffe
Addded test for spaces and subnets. Currently only works with fixed subnets that exist in the Canonical EC2 account in the eu-central-1.
147
    if '/' in ipv4:
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
154
155
1226.1.1 by Martin Packman
Fix assess_spaces_subnets by adapting to new BootstrapManager code from container networking test
156
def main(argv=None):
157
    args = parse_args(argv)
158
    configure_logging(args.verbose)
159
    bs_manager = BootstrapManager.from_args(args)
1263.1.1 by Martin Packman
Refactor how juju feature flags are handled with new enable_feature method
160
    bs_manager.client.enable_feature('address-allocation')
1226.1.1 by Martin Packman
Fix assess_spaces_subnets by adapting to new BootstrapManager code from container networking test
161
    with cleaned_bootstrap_context(bs_manager, args) as ctx:
162
        assess_spaces_subnets(bs_manager.client)
163
    return ctx.return_code
1091.6.1 by James Tunnicliffe
Addded test for spaces and subnets. Currently only works with fixed subnets that exist in the Canonical EC2 account in the eu-central-1.
164
165
166
if __name__ == '__main__':
1226.1.1 by Martin Packman
Fix assess_spaces_subnets by adapting to new BootstrapManager code from container networking test
167
    sys.exit(main())