~veebers/juju-ci-tools/model_migration_check_all_units_of_charm

17 by Aaron Bentley
Split out jujupy library.
1
#!/usr/bin/env python
162 by Curtis Hovey
Windows and py3 love.
2
from __future__ import print_function
17 by Aaron Bentley
Split out jujupy library.
3
4
155.1.1 by Aaron Bentley
Support a local prefix and repository for deployment.
5
from argparse import ArgumentParser
1036.1.1 by John George
Correct variable name passed to background_chaos.
6
from contextlib import (
7
    contextmanager,
8
    nested,
9
)
1449.4.24 by Nicholas Skaggs
move directory creation to bootstrap manager
10
from datetime import (
11
    datetime,
12
)
13
14
import errno
611.1.3 by Menno Smits
use glob() to simplify log dumping code
15
import glob
424.1.4 by Aaron Bentley
Implement bootstrapping with specific agent-version.
16
import logging
379.1.1 by Aaron Bentley
Move portions of deploy job to Python.
17
import os
365.1.3 by Aaron Bentley
Use a random token in dummy deploy.
18
import random
365.1.2 by Aaron Bentley
Remove trailing line feeds.
19
import re
365.1.3 by Aaron Bentley
Use a random token in dummy deploy.
20
import string
379.1.1 by Aaron Bentley
Move portions of deploy job to Python.
21
import subprocess
155.1.1 by Aaron Bentley
Support a local prefix and repository for deployment.
22
import sys
931.1.2 by Martin Packman
Retry check_token for desired result for 120 seconds in all cases
23
import time
881.1.1 by Seman Said
Added a test for juju run
24
import json
908.1.1 by seman.said at canonical
Added script to retain jenv file during a failure.
25
import shutil
155.1.1 by Aaron Bentley
Support a local prefix and repository for deployment.
26
1014.2.5 by John George
deploy_dummy_stack with chaos.
27
from chaos import background_chaos
1515.2.1 by Martin Packman
Move fake_juju_client and related code into a new top level fakejuju file
28
from fakejuju import (
29
    FakeBackend,
30
    fake_juju_client,
31
)
1409.1.1 by Martin
First pass at new jujucharm module.
32
from jujucharm import (
33
    local_charm_path,
34
)
422.1.4 by Aaron Bentley
Fix import.
35
from jujuconfig import (
36
    get_jenv_path,
424.1.4 by Aaron Bentley
Implement bootstrapping with specific agent-version.
37
    get_juju_home,
717.2.2 by Aaron Bentley
Checkpoint with assess_recovery working.
38
    translate_to_env,
422.1.4 by Aaron Bentley
Fix import.
39
)
89.1.2 by Aaron Bentley
Fix imports
40
from jujupy import (
1465.5.2 by Aaron Bentley
Implement client_from_config, delete by_version.
41
    client_from_config,
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
42
    get_local_root,
1153.4.3 by Martin Packman
Changes from review by abentley
43
    get_machine_dns_name,
1049.1.1 by Aaron Bentley
Add --pre-destroy option to deploy_job script.
44
    jes_home_path,
1674.1.4 by Aaron Bentley
More conversion.
45
    NoProvider,
880.1.1 by Aaron Bentley
Remove Environment from deploy_stack.py
46
    SimpleEnvironment,
884 by John George
Add juju-deployer test support.
47
    temp_bootstrap_env,
719.1.3 by John George
Move libvirt_domain functions from jujupy to substrate.
48
)
994.3.1 by Martin Packman
Make Remote an abstract class and implement winrm based version
49
from remote import (
50
    remote_from_address,
51
    remote_from_unit,
1289 by Curtis Hovey
copy_remote_logs cannot ever throw an error because winrm is fucked.
52
    winrm,
994.3.1 by Martin Packman
Make Remote an abstract class and implement winrm based version
53
)
719.1.3 by John George
Move libvirt_domain functions from jujupy to substrate.
54
from substrate import (
982.1.2 by Curtis Hovey
Remove euca_dump_logs because it has not been used this year.
55
    destroy_job_instances,
719.1.4 by John George
Add LIBVIRT_DOMAIN state constants and sleep statements, to increase the interval between virsh calls.
56
    LIBVIRT_DOMAIN_RUNNING,
994.4.4 by Martin Packman
Update with extra tests and addresses review comments by sinzui
57
    resolve_remote_dns_names,
982.1.1 by Curtis Hovey
Move substrate (not juju) functions to substrate.py.
58
    run_instances,
687.1.1 by John George
Add use of KVM start and stop functions, when env type is maas, to deploy_stack.py and cloud_deploy.py
59
    start_libvirt_domain,
60
    stop_libvirt_domain,
719.1.3 by John George
Move libvirt_domain functions from jujupy to substrate.
61
    verify_libvirt_domain,
385.1.1 by Aaron Bentley
Support upgrades.
62
)
63
from utility import (
1028.2.2 by Curtis Hovey
deploy_stack.py uses add_basic_testing_arguments.
64
    add_basic_testing_arguments,
751.2.2 by Aaron Bentley
Log success/failure for each step (of SteppedStageAttempt).
65
    configure_logging,
777.2.1 by Aaron Bentley
Always delete jenv in industrial test.
66
    ensure_deleted,
1303.1.3 by Martin Packman
Handle creation of log dirs correctly for hosted environments
67
    ensure_dir,
1735.1.1 by Martin Packman
New utility.logged_exception factored out from deploy_stack
68
    logged_exception,
1211.2.5 by Aaron Bentley
LoggedException in BootstrapManager, convert to sys.exit in booted_context
69
    LoggedException,
457 by Aaron Bentley
Don't raise an exception when logs cannot be dumped.
70
    PortTimeoutError,
717.2.2 by Aaron Bentley
Checkpoint with assess_recovery working.
71
    print_now,
422.1.1 by Aaron Bentley
Support bootstrap with customized options.
72
    until_timeout,
385.1.1 by Aaron Bentley
Support upgrades.
73
    wait_for_port,
89.1.2 by Aaron Bentley
Fix imports
74
)
17 by Aaron Bentley
Split out jujupy library.
75
155.1.1 by Aaron Bentley
Support a local prefix and repository for deployment.
76
1092.2.2 by Aaron Bentley
Fix lint.
77
__metaclass__ = type
78
79
717.2.1 by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery.
80
def destroy_environment(client, instance_tag):
834.2.6 by Curtis Hovey
Added rule to destroy manual envs, then remove instances (which may not exist).
81
    client.destroy_environment()
1674.1.4 by Aaron Bentley
More conversion.
82
    if (client.env.provider == 'manual' and
1092.2.2 by Aaron Bentley
Fix lint.
83
            'AWS_ACCESS_KEY' in os.environ):
717.2.1 by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery.
84
        destroy_job_instances(instance_tag)
419.1.6 by Aaron Bentley
Use python path for ec2-terminate-job-instances.
85
86
1345.1.6 by Seman
Upated variable name.
87
def deploy_dummy_stack(client, charm_series):
1289 by Curtis Hovey
copy_remote_logs cannot ever throw an error because winrm is fucked.
88
    """"Deploy a dummy stack in the specified environment."""
1091.4.1 by James Tunnicliffe
Merged upstream
89
    # Centos requires specific machine configuration (i.e. network device
90
    # order).
1355.1.1 by Seman
Updated deploy_dummy_stack to support CentOS and Windows charms.
91
    if charm_series.startswith("centos") and client.env.maas:
1251.1.1 by Aaron Bentley
Implement and use set_model_constraints.
92
        client.set_model_constraints({'tags': 'MAAS_NIC_1'})
1345.1.5 by Seman
Deploy charm by path #2.
93
    platform = 'ubuntu'
1355.1.1 by Seman
Updated deploy_dummy_stack to support CentOS and Windows charms.
94
    if charm_series.startswith('win'):
95
        platform = 'win'
96
    elif charm_series.startswith('centos'):
97
        platform = 'centos'
1345.1.5 by Seman
Deploy charm by path #2.
98
    charm = local_charm_path(charm='dummy-source', juju_ver=client.version,
1345.1.6 by Seman
Upated variable name.
99
                             series=charm_series, platform=platform)
100
    client.deploy(charm, series=charm_series)
1345.1.5 by Seman
Deploy charm by path #2.
101
    charm = local_charm_path(charm='dummy-sink', juju_ver=client.version,
1345.1.6 by Seman
Upated variable name.
102
                             series=charm_series, platform=platform)
103
    client.deploy(charm, series=charm_series)
880.1.17 by Aaron Bentley
Fake merge of trunk into no-environment.
104
    client.juju('add-relation', ('dummy-source', 'dummy-sink'))
105
    client.juju('expose', ('dummy-sink',))
993.1.1 by John George
Give deploy_stack extra time to complete on MAAS.
106
    if client.env.kvm or client.env.maas:
650.1.18 by Aaron Bentley
Reduce diff.
107
        # A single virtual machine may need up to 30 minutes before
108
        # "apt-get update" and other initialisation steps are
109
        # finished; two machines initializing concurrently may
993.1.1 by John George
Give deploy_stack extra time to complete on MAAS.
110
        # need even 40 minutes. In addition Windows image blobs or
111
        # any system deployment using MAAS requires extra time.
1506 by Nicholas Skaggs
bump MAAS timeout to 7200 before killing
112
        client.wait_for_started(7200)
650.1.18 by Aaron Bentley
Reduce diff.
113
    else:
1507 by Nicholas Skaggs
set timeout to 3600 for deploy_dummy_stack (precise, windows can take some time)
114
        client.wait_for_started(3600)
1169.1.1 by John George
Factor out relation check, so assess_upgrade can be re-used by run_deployer.py
115
116
117
def assess_juju_relations(client):
118
    token = get_random_string()
1239.1.1 by Aaron Bentley
Implement and use EnvJujuClient.set_config.
119
    client.set_config('dummy-source', {'token': token})
880.1.17 by Aaron Bentley
Fake merge of trunk into no-environment.
120
    check_token(client, token)
121
122
931.1.3 by Martin Packman
Tidy up changes and basic test coverage
123
GET_TOKEN_SCRIPT = """
124
        for x in $(seq 120); do
125
          if [ -f /var/run/dummy-sink/token ]; then
126
            if [ "$(cat /var/run/dummy-sink/token)" != "" ]; then
127
              break
128
            fi
129
          fi
130
          sleep 1
131
        done
132
        cat /var/run/dummy-sink/token
1262.1.1 by Curtis Hovey
Sleep 2s when checking token, and provide more info.
133
        sleep 2
931.1.3 by Martin Packman
Tidy up changes and basic test coverage
134
    """
135
136
1523.1.1 by Curtis Hovey
Added get_token_from_status
137
def get_token_from_status(client):
1523.1.4 by Curtis Hovey
Updated docstrings.
138
    """Return the token from the application status message or None."""
1523.1.1 by Curtis Hovey
Added get_token_from_status
139
    status = client.get_status()
140
    unit = status.get_unit('dummy-sink/0')
141
    app_status = unit.get('workload-status')
142
    if app_status is not None:
143
        message = app_status.get('message', '')
144
        parts = message.split()
145
        if parts:
146
            return parts[-1]
147
    return None
148
149
1262.1.1 by Curtis Hovey
Sleep 2s when checking token, and provide more info.
150
def check_token(client, token, timeout=120):
1523.1.4 by Curtis Hovey
Updated docstrings.
151
    """Check the token found on dummy-sink/0 or raise ValueError."""
1526.1.1 by Curtis Hovey
Wait for applications to be active before checking token.
152
    logging.info('Waiting for applications to reach ready.')
153
    client.wait_for_workloads()
880.1.17 by Aaron Bentley
Fake merge of trunk into no-environment.
154
    # Wait up to 120 seconds for token to be created.
155
    logging.info('Retrieving token.')
994.3.1 by Martin Packman
Make Remote an abstract class and implement winrm based version
156
    remote = remote_from_unit(client, "dummy-sink/0")
994.4.4 by Martin Packman
Update with extra tests and addresses review comments by sinzui
157
    # Update remote with real address if needed.
158
    resolve_remote_dns_names(client.env, [remote])
931.1.2 by Martin Packman
Retry check_token for desired result for 120 seconds in all cases
159
    start = time.time()
160
    while True:
994.3.1 by Martin Packman
Make Remote an abstract class and implement winrm based version
161
        if remote.is_windows():
1523.1.1 by Curtis Hovey
Added get_token_from_status
162
            result = get_token_from_status(client)
163
            if not result:
164
                try:
165
                    result = remote.cat("%ProgramData%\\dummy-sink\\token")
166
                except winrm.exceptions.WinRMTransportError as e:
1523.1.3 by Curtis Hovey
Added tests, log warning when returning early.
167
                    logging.warning(
168
                        "Skipping token check because of: {}".format(str(e)))
1523.1.1 by Curtis Hovey
Added get_token_from_status
169
                    return
994.3.1 by Martin Packman
Make Remote an abstract class and implement winrm based version
170
        else:
171
            result = remote.run(GET_TOKEN_SCRIPT)
1266 by Curtis Hovey
quote shell args.
172
        token_pattern = re.compile(r'([^\n\r]*)\r?\n?')
173
        result = token_pattern.match(result).group(1)
931.1.2 by Martin Packman
Retry check_token for desired result for 120 seconds in all cases
174
        if result == token:
931.1.3 by Martin Packman
Tidy up changes and basic test coverage
175
            logging.info("Token matches expected %r", result)
931.1.2 by Martin Packman
Retry check_token for desired result for 120 seconds in all cases
176
            return
1262.1.1 by Curtis Hovey
Sleep 2s when checking token, and provide more info.
177
        if time.time() - start > timeout:
178
            if not remote.is_windows() and remote.use_juju_ssh:
179
                # 'juju ssh' didn't error, but try raw ssh to verify
180
                # the result is the same.
181
                remote.get_address()
182
                remote.use_juju_ssh = False
183
                result = remote.run(GET_TOKEN_SCRIPT)
1266 by Curtis Hovey
quote shell args.
184
                result = token_pattern.match(result).group(1)
1262.1.1 by Curtis Hovey
Sleep 2s when checking token, and provide more info.
185
                if result == token:
186
                    logging.info("Token matches expected %r", result)
187
                    logging.error("juju ssh to unit is broken.")
931.1.2 by Martin Packman
Retry check_token for desired result for 120 seconds in all cases
188
            raise ValueError('Token is %r' % result)
931.1.3 by Martin Packman
Tidy up changes and basic test coverage
189
        logging.info("Found token %r expected %r", result, token)
931.1.2 by Martin Packman
Retry check_token for desired result for 120 seconds in all cases
190
        time.sleep(5)
357.1.1 by Aaron Bentley
Add option to deploy a dummy stack.
191
706.1.1 by Aaron Bentley
Switch to flake8 and fix lint.
192
650.1.7 by Aaron Bentley
Start testing mixed environs.
193
def get_random_string():
194
    allowed_chars = string.ascii_uppercase + string.digits
195
    return ''.join(random.choice(allowed_chars) for n in range(20))
196
197
1053.1.1 by Martin Packman
Refactor log collection to ensure local logs are only collected once
198
def _can_run_ssh():
1289 by Curtis Hovey
copy_remote_logs cannot ever throw an error because winrm is fucked.
199
    """Return true if local system can use ssh to access remote machines."""
1053.1.1 by Martin Packman
Refactor log collection to ensure local logs are only collected once
200
    # When client is run on a windows machine, we have no local ssh binary.
201
    return sys.platform != "win32"
202
203
1050.2.9 by Aaron Bentley
Merged trunk into copy-cache.
204
def dump_env_logs(client, bootstrap_host, artifacts_dir, runtime_config=None):
1173.2.1 by Aaron Bentley
Extract dump_env_logs_known_hosts.
205
    if bootstrap_host is None:
206
        known_hosts = {}
207
    else:
208
        known_hosts = {'0': bootstrap_host}
209
    dump_env_logs_known_hosts(client, artifacts_dir, runtime_config,
210
                              known_hosts)
211
212
213
def dump_env_logs_known_hosts(client, artifacts_dir, runtime_config=None,
214
                              known_hosts=None):
215
    if known_hosts is None:
216
        known_hosts = {}
1053.1.1 by Martin Packman
Refactor log collection to ensure local logs are only collected once
217
    if client.env.local:
218
        logging.info("Retrieving logs for local environment")
219
        copy_local_logs(client.env, artifacts_dir)
220
    else:
1173.2.1 by Aaron Bentley
Extract dump_env_logs_known_hosts.
221
        remote_machines = get_remote_machines(client, known_hosts)
1053.1.1 by Martin Packman
Refactor log collection to ensure local logs are only collected once
222
223
        for machine_id in sorted(remote_machines, key=int):
224
            remote = remote_machines[machine_id]
225
            if not _can_run_ssh() and not remote.is_windows():
226
                logging.info("No ssh, skipping logs for machine-%s using %r",
1056.1.3 by John George
Lint cleanup.
227
                             machine_id, remote)
1053.1.1 by Martin Packman
Refactor log collection to ensure local logs are only collected once
228
                continue
229
            logging.info("Retrieving logs for machine-%s using %r", machine_id,
230
                         remote)
1056.1.3 by John George
Lint cleanup.
231
            machine_dir = os.path.join(artifacts_dir,
232
                                       "machine-%s" % machine_id)
1303.1.3 by Martin Packman
Handle creation of log dirs correctly for hosted environments
233
            ensure_dir(machine_dir)
1053.1.1 by Martin Packman
Refactor log collection to ensure local logs are only collected once
234
            copy_remote_logs(remote, machine_dir)
235
    archive_logs(artifacts_dir)
1050.2.9 by Aaron Bentley
Merged trunk into copy-cache.
236
    retain_config(runtime_config, artifacts_dir)
1050.2.2 by Aaron Bentley
Retain config.yaml instead of .jenv for JES.
237
238
239
def retain_config(runtime_config, log_directory):
240
    if not runtime_config:
908.1.1 by seman.said at canonical
Added script to retain jenv file during a failure.
241
        return False
242
243
    try:
1050.2.2 by Aaron Bentley
Retain config.yaml instead of .jenv for JES.
244
        shutil.copy(runtime_config, log_directory)
908.1.1 by seman.said at canonical
Added script to retain jenv file during a failure.
245
        return True
246
    except IOError:
1050.2.2 by Aaron Bentley
Retain config.yaml instead of .jenv for JES.
247
        print_now("Failed to copy file. Source: %s Destination: %s" %
248
                  (runtime_config, log_directory))
908.1.1 by seman.said at canonical
Added script to retain jenv file during a failure.
249
    return False
611.1.5 by Menno Smits
Added dump_env_logs: archives all Juju logs possible for all machines in an environment
250
251
1048.2.1 by John George
Capture duration of juju commands from the client perspective.
252
def dump_juju_timings(client, log_directory):
253
    try:
254
        with open(os.path.join(log_directory, 'juju_command_times.json'),
255
                  'w') as timing_file:
256
            json.dump(client.get_juju_timings(), timing_file, indent=2,
257
                      sort_keys=True)
258
            timing_file.write('\n')
259
    except Exception as e:
260
        print_now("Failed to save timings")
261
        print_now(str(e))
262
263
1173.2.1 by Aaron Bentley
Extract dump_env_logs_known_hosts.
264
def get_remote_machines(client, known_hosts):
994.4.4 by Martin Packman
Update with extra tests and addresses review comments by sinzui
265
    """Return a dict of machine_id to remote machines.
984.1.8 by Curtis Hovey
Added test for case where status cannot provide machine dns-name.
266
994.4.4 by Martin Packman
Update with extra tests and addresses review comments by sinzui
267
    A bootstrap_host address may be provided as a fallback for machine 0 if
268
    status fails. For some providers such as MAAS the dns-name will be
269
    resolved to a real ip address using the substrate api.
984.1.8 by Curtis Hovey
Added test for case where status cannot provide machine dns-name.
270
    """
611.1.5 by Menno Smits
Added dump_env_logs: archives all Juju logs possible for all machines in an environment
271
    # Try to get machine details from environment if possible.
994.4.2 by Martin Packman
Use winrm copy implementation to get logs from windows machines
272
    machines = dict(iter_remote_machines(client))
994.4.4 by Martin Packman
Update with extra tests and addresses review comments by sinzui
273
    # The bootstrap host is added as a fallback in case status failed.
1173.2.1 by Aaron Bentley
Extract dump_env_logs_known_hosts.
274
    for machine_id, address in known_hosts.items():
275
        if machine_id not in machines:
276
            machines[machine_id] = remote_from_address(address)
994.4.4 by Martin Packman
Update with extra tests and addresses review comments by sinzui
277
    # Update remote machines in place with real addresses if substrate needs.
278
    resolve_remote_dns_names(client.env, machines.itervalues())
994.4.2 by Martin Packman
Use winrm copy implementation to get logs from windows machines
279
    return machines
280
281
282
def iter_remote_machines(client):
611.1.5 by Menno Smits
Added dump_env_logs: archives all Juju logs possible for all machines in an environment
283
    try:
717.2.1 by Aaron Bentley
Extract EnvJujuClient.backup from assess_recovery.
284
        status = client.get_status()
611.1.5 by Menno Smits
Added dump_env_logs: archives all Juju logs possible for all machines in an environment
285
    except Exception as err:
286
        logging.warning("Failed to retrieve status for dumping logs: %s", err)
287
        return
994.4.2 by Martin Packman
Use winrm copy implementation to get logs from windows machines
288
611.1.5 by Menno Smits
Added dump_env_logs: archives all Juju logs possible for all machines in an environment
289
    for machine_id, machine in status.iter_machines():
290
        hostname = machine.get('dns-name')
291
        if hostname:
994.4.2 by Martin Packman
Use winrm copy implementation to get logs from windows machines
292
            remote = remote_from_address(hostname, machine.get('series'))
293
            yield machine_id, remote
294
295
1053.1.1 by Martin Packman
Refactor log collection to ensure local logs are only collected once
296
def archive_logs(log_dir):
297
    """Compress log files in given log_dir using gzip."""
1091.4.1 by James Tunnicliffe
Merged upstream
298
    log_files = []
299
    for r, ds, fs in os.walk(log_dir):
1582.1.1 by Andrew Beach
This might be all we need to get the syslog compressed, have yet to test it.
300
        log_files.extend(os.path.join(r, f) for f in fs if is_log(f))
751.4.1 by Martin Packman
Avoid hanging job when no logs have been gathered for whatever reason
301
    if log_files:
1053.1.1 by Martin Packman
Refactor log collection to ensure local logs are only collected once
302
        subprocess.check_call(['gzip', '--best', '-f'] + log_files)
611.1.5 by Menno Smits
Added dump_env_logs: archives all Juju logs possible for all machines in an environment
303
611.1.2 by Menno Smits
Dump more types of log files
304
1582.1.1 by Andrew Beach
This might be all we need to get the syslog compressed, have yet to test it.
305
def is_log(file_name):
306
    """Check to see if the given file name is the name of a log file."""
307
    return file_name.endswith('.log') or file_name.endswith('syslog')
308
309
919.1.1 by Martin Packman
Add lxc container logs to local artifacts collected
310
lxc_template_glob = '/var/lib/juju/containers/juju-*-lxc-template/*.log'
311
312
994.4.2 by Martin Packman
Use winrm copy implementation to get logs from windows machines
313
def copy_local_logs(env, directory):
1053.1.1 by Martin Packman
Refactor log collection to ensure local logs are only collected once
314
    """Copy logs for all machines in local environment."""
994.4.2 by Martin Packman
Use winrm copy implementation to get logs from windows machines
315
    local = get_local_root(get_juju_home(), env)
611.1.2 by Menno Smits
Dump more types of log files
316
    log_names = [os.path.join(local, 'cloud-init-output.log')]
611.1.3 by Menno Smits
use glob() to simplify log dumping code
317
    log_names.extend(glob.glob(os.path.join(local, 'log', '*.log')))
919.1.1 by Martin Packman
Add lxc container logs to local artifacts collected
318
    log_names.extend(glob.glob(lxc_template_glob))
1053.1.1 by Martin Packman
Refactor log collection to ensure local logs are only collected once
319
    try:
320
        subprocess.check_call(['sudo', 'chmod', 'go+r'] + log_names)
321
        subprocess.check_call(['cp'] + log_names + [directory])
322
    except subprocess.CalledProcessError as e:
323
        logging.warning("Could not retrieve local logs: %s", e)
611.1.2 by Menno Smits
Dump more types of log files
324
325
994.4.2 by Martin Packman
Use winrm copy implementation to get logs from windows machines
326
def copy_remote_logs(remote, directory):
796.1.10 by Curtis Hovey
Added test to verify errors do not stop the attempt to recover logs.
327
    """Copy as many logs from the remote host as possible to the directory."""
328
    # This list of names must be in the order of creation to ensure they
329
    # are retrieved.
994.4.2 by Martin Packman
Use winrm copy implementation to get logs from windows machines
330
    if remote.is_windows():
331
        log_paths = [
332
            "%ProgramFiles(x86)%\\Cloudbase Solutions\\Cloudbase-Init\\log\\*",
994.4.3 by Martin Packman
Selection of fixes needed for passing job
333
            "C:\\Juju\\log\\juju\\*.log",
994.4.2 by Martin Packman
Use winrm copy implementation to get logs from windows machines
334
        ]
335
    else:
336
        log_paths = [
337
            '/var/log/cloud-init*.log',
338
            '/var/log/juju/*.log',
1091.4.1 by James Tunnicliffe
Merged upstream
339
            # TODO(gz): Also capture kvm container logs?
340
            '/var/lib/juju/containers/juju-*-lxc-*/',
1440.1.3 by Aaron Bentley
Add lxd logs.
341
            '/var/log/lxd/juju-*',
1457.1.1 by Aaron Bentley
Add /var/log/lxd/lxd.log to list of collected logs.
342
            '/var/log/lxd/lxd.log',
1361.1.1 by Aaron Bentley
Include syslog and mongodb log when duming logs.
343
            '/var/log/syslog',
344
            '/var/log/mongodb/mongodb.log',
1464.1.6 by Curtis Hovey
create a log of ifconfig.
345
            '/etc/network/interfaces',
1655.1.16 by Curtis Hovey
Collect /etc/environment for analysis.
346
            '/etc/environment',
1464.1.6 by Curtis Hovey
create a log of ifconfig.
347
            '/home/ubuntu/ifconfig.log',
994.4.2 by Martin Packman
Use winrm copy implementation to get logs from windows machines
348
        ]
349
350
        try:
351
            wait_for_port(remote.address, 22, timeout=60)
352
        except PortTimeoutError:
353
            logging.warning("Could not dump logs because port 22 was closed.")
354
            return
355
356
        try:
1091.4.1 by James Tunnicliffe
Merged upstream
357
            remote.run('sudo chmod -Rf go+r ' + ' '.join(log_paths))
1464.1.7 by Curtis Hovey
capture ifconfig state in separate block.
358
        except subprocess.CalledProcessError as e:
359
            # The juju log dir is not created until after cloud-init succeeds.
360
            logging.warning("Could not allow access to the juju logs:")
361
            logging.warning(e.output)
362
        try:
1464.1.6 by Curtis Hovey
create a log of ifconfig.
363
            remote.run('ifconfig > /home/ubuntu/ifconfig.log')
994.4.2 by Martin Packman
Use winrm copy implementation to get logs from windows machines
364
        except subprocess.CalledProcessError as e:
1464.1.7 by Curtis Hovey
capture ifconfig state in separate block.
365
            logging.warning("Could not capture ifconfig state:")
994.4.2 by Martin Packman
Use winrm copy implementation to get logs from windows machines
366
            logging.warning(e.output)
611.1.2 by Menno Smits
Dump more types of log files
367
796.1.1 by Curtis Hovey
Report log retrieval errors and continue. Retrieve remote logs in the proper order.
368
    try:
983.1.1 by Martin Packman
Add new Remote class for accessing juju machines
369
        remote.copy(directory, log_paths)
1289 by Curtis Hovey
copy_remote_logs cannot ever throw an error because winrm is fucked.
370
    except (subprocess.CalledProcessError,
371
            winrm.exceptions.WinRMTransportError) as e:
796.1.10 by Curtis Hovey
Added test to verify errors do not stop the attempt to recover logs.
372
        # The juju logs will not exist if cloud-init failed.
796.1.1 by Curtis Hovey
Report log retrieval errors and continue. Retrieve remote logs in the proper order.
373
        logging.warning("Could not retrieve some or all logs:")
1291 by Curtis Hovey
Return None from getattr.
374
        if getattr(e, 'output', None):
1290 by Curtis Hovey
Let me see the error.
375
            logging.warning(e.output)
376
        else:
1292 by Curtis Hovey
Fix test broken by quick fix for log collection.
377
            logging.warning(repr(e))
379.1.1 by Aaron Bentley
Move portions of deploy job to Python.
378
1302.1.2 by Aaron Bentley
Fix lint.
379
880.1.17 by Aaron Bentley
Fake merge of trunk into no-environment.
380
def assess_juju_run(client):
1631.4.9 by Andrew Beach
Adjusted the check in assess_to to use hostname rather than dns name. (Might have to use both.) Updated EnvJujuClient.run to accomidate.
381
    responses = client.run(('uname',),
382
                           applications=['dummy-source', 'dummy-sink'])
881.1.1 by Seman Said
Added a test for juju run
383
    for machine in responses:
384
        if machine.get('ReturnCode', 0) != 0:
881.1.3 by Seman Said
Added unit test for test_juju_run
385
            raise ValueError('juju run on machine %s returned %d: %s' % (
881.1.4 by Seman Said
Renamed the function to assess_juju_run and added return value. Also, updated the unit test to check for the return value
386
                             machine.get('MachineId'),
387
                             machine.get('ReturnCode'),
388
                             machine.get('Stderr')))
953 by Curtis Hovey
Remove extra ")" causing a syntax error and make test failure.
389
    logging.info(
390
        "juju run succeeded on machines: %r",
391
        [str(machine.get("MachineId")) for machine in responses])
881.1.4 by Seman Said
Renamed the function to assess_juju_run and added return value. Also, updated the unit test to check for the return value
392
    return responses
881.1.1 by Seman Said
Added a test for juju run
393
394
1169.1.1 by John George
Factor out relation check, so assess_upgrade can be re-used by run_deployer.py
395
def assess_upgrade(old_client, juju_path):
1612.2.11 by Christopher Lee
Cleanup.
396
    all_clients = _get_clients_to_upgrade(old_client, juju_path)
397
398
    # all clients have the same provider type, work this out once.
1674.1.4 by Aaron Bentley
More conversion.
399
    if all_clients[0].env.provider == 'maas':
1612.2.11 by Christopher Lee
Cleanup.
400
        timeout = 1200
401
    else:
402
        timeout = 600
403
404
    for client in all_clients:
1612.2.1 by Christopher Lee
Make assess_upgrade 2.0 safe; upgrade the controller first.
405
        upgrade_juju(client)
406
        client.wait_for_version(client.get_matching_agent_version(), timeout)
880.1.17 by Aaron Bentley
Fake merge of trunk into no-environment.
407
408
1612.2.10 by Christopher Lee
Fix for deploy_stack/upgrade test
409
def _get_clients_to_upgrade(old_client, juju_path):
410
    """Return a list of cloned clients to upgrade.
411
412
    Ensure that the controller (if available) is the first client in the list.
413
    """
414
    new_client = old_client.clone_path_cls(juju_path)
415
    all_clients = sorted(
416
        new_client.iter_model_clients(),
1612.2.17 by Christopher Lee
Update commit.
417
        key=lambda m: m.model_name == 'controller',
418
        reverse=True)
1612.2.10 by Christopher Lee
Fix for deploy_stack/upgrade test
419
420
    return all_clients
421
422
880.1.17 by Aaron Bentley
Fake merge of trunk into no-environment.
423
def upgrade_juju(client):
1540.3.4 by Christopher Lee
Actually rename the method in places it's used.
424
    client.set_testing_agent_metadata_url()
1540.3.2 by Christopher Lee
Change tools_ to agent_
425
    tools_metadata_url = client.get_agent_metadata_url()
1612.2.6 by Christopher Lee
Update log message to use the right url name.
426
    logging.info(
427
        'The {url_type} is {url}'.format(
428
            url_type=client.agent_metadata_url,
429
            url=tools_metadata_url))
880.1.17 by Aaron Bentley
Fake merge of trunk into no-environment.
430
    client.upgrade_juju()
385.1.1 by Aaron Bentley
Support upgrades.
431
432
927.1.1 by Aaron Bentley
Add --upload-tools to deploy_job.py
433
def deploy_job_parse_args(argv=None):
884 by John George
Add juju-deployer test support.
434
    parser = ArgumentParser('deploy_job')
1028.2.2 by Curtis Hovey
deploy_stack.py uses add_basic_testing_arguments.
435
    add_basic_testing_arguments(parser)
884 by John George
Add juju-deployer test support.
436
    parser.add_argument('--upgrade', action="store_true", default=False,
437
                        help='Perform an upgrade test.')
1014.2.5 by John George
deploy_dummy_stack with chaos.
438
    parser.add_argument('--with-chaos', default=0, type=int,
439
                        help='Deploy and run Chaos Monkey in the background.')
1049.1.1 by Aaron Bentley
Add --pre-destroy option to deploy_job script.
440
    parser.add_argument('--jes', action='store_true',
441
                        help='Use JES to control environments.')
1711.3.10 by Aaron Bentley
Accept --controller-host to use public controller.
442
    parser.add_argument(
1711.3.12 by Aaron Bentley
Fix lint.
443
        '--controller-host', help=(
444
            'Host with a controller to use.  If supplied, SSO_EMAIL and'
445
            ' SSO_PASSWORD environment variables will be used for oauth'
446
            ' authentication.'))
927.1.1 by Aaron Bentley
Add --upload-tools to deploy_job.py
447
    return parser.parse_args(argv)
448
449
450
def deploy_job():
451
    args = deploy_job_parse_args()
1028.2.2 by Curtis Hovey
deploy_stack.py uses add_basic_testing_arguments.
452
    configure_logging(args.verbose)
639 by Aaron Bentley
Remove charm_prefix argument.
453
    series = args.series
454
    if series is None:
455
        series = 'precise'
1345.1.5 by Seman
Deploy charm by path #2.
456
    charm_series = series
1091.4.1 by James Tunnicliffe
Merged upstream
457
    # Don't need windows or centos state servers.
458
    if series.startswith("win") or series.startswith("centos"):
459
        logging.info('Setting default series to trusty for win and centos.')
994.2.1 by Martin Packman
Use a trusty state server when testing windows charms
460
        series = 'trusty'
1345.1.5 by Seman
Deploy charm by path #2.
461
    return _deploy_job(args, charm_series, series)
710.1.1 by Aaron Bentley
Support deploy_job --agent-url.
462
463
464
def update_env(env, new_env_name, series=None, bootstrap_host=None,
1092.2.3 by Aaron Bentley
Add --region support.
465
               agent_url=None, agent_stream=None, region=None):
650.1.2 by Aaron Bentley
Extract update_env from _deploy_job
466
    # Rename to the new name.
1315.2.23 by Aaron Bentley
Introduce set_model_name, update tests, check controller on bootstrap.
467
    env.set_model_name(new_env_name)
1674.1.16 by Aaron Bentley
Switch call sites to update_config.
468
    new_config = {}
650.1.2 by Aaron Bentley
Extract update_env from _deploy_job
469
    if series is not None:
1674.1.16 by Aaron Bentley
Switch call sites to update_config.
470
        new_config['default-series'] = series
650.1.2 by Aaron Bentley
Extract update_env from _deploy_job
471
    if bootstrap_host is not None:
1674.1.16 by Aaron Bentley
Switch call sites to update_config.
472
        new_config['bootstrap-host'] = bootstrap_host
710.1.1 by Aaron Bentley
Support deploy_job --agent-url.
473
    if agent_url is not None:
1674.1.16 by Aaron Bentley
Switch call sites to update_config.
474
        new_config['tools-metadata-url'] = agent_url
945.1.2 by Curtis Hovey
Pass agent_stream down to update_env.
475
    if agent_stream is not None:
1674.1.16 by Aaron Bentley
Switch call sites to update_config.
476
        new_config['agent-stream'] = agent_stream
477
    env.update_config(new_config)
1092.2.3 by Aaron Bentley
Add --region support.
478
    if region is not None:
1674.1.9 by Aaron Bentley
Switch deploy_stack to set_region.
479
        env.set_region(region)
650.1.2 by Aaron Bentley
Extract update_env from _deploy_job
480
481
1173.2.12 by Aaron Bentley
Tear down JES environments more carefully.
482
@contextmanager
483
def temp_juju_home(client, new_home):
484
    """Temporarily override the client's home directory."""
1193.2.4 by Aaron Bentley
Switch FakeJujuClient to raise on accessing juju_home.
485
    old_home = client.env.juju_home
1193.2.1 by Aaron Bentley
Make EnvJujuClient.juju_home a reference to EnvJUjuClient.env.juju_home.
486
    client.env.juju_home = new_home
1173.2.12 by Aaron Bentley
Tear down JES environments more carefully.
487
    try:
488
        yield
489
    finally:
1193.2.1 by Aaron Bentley
Make EnvJujuClient.juju_home a reference to EnvJUjuClient.env.juju_home.
490
        client.env.juju_home = old_home
1173.2.12 by Aaron Bentley
Tear down JES environments more carefully.
491
492
1711.3.9 by Aaron Bentley
Implement make_controller_strategy.
493
def make_controller_strategy(client, tear_down_client, controller_host):
494
    if controller_host is None:
495
        return CreateController(client, tear_down_client)
496
    else:
497
        return PublicController(
498
            controller_host, os.environ['SSO_EMAIL'],
499
            os.environ['SSO_PASSWORD'], client, tear_down_client)
500
501
1711.3.1 by Aaron Bentley
Introduce CreateController strategy.
502
class CreateController:
503
    """A Controller strategy where the controller is created.
504
505
    Intended for use with BootstrapManager.
506
    """
507
508
    def __init__(self, client, tear_down_client):
509
        self.client = client
510
        self.tear_down_client = tear_down_client
511
512
    def prepare(self):
513
        """Prepare client for use by killing the existing controller."""
514
        self.tear_down_client.kill_controller()
515
516
    def create_initial_model(self, upload_tools, series, boot_kwargs):
517
        """Create the initial model by bootstrapping."""
518
        self.client.bootstrap(
519
            upload_tools=upload_tools, bootstrap_series=series,
520
            **boot_kwargs)
521
522
    def get_hosts(self):
523
        """Provide the controller host."""
524
        host = get_machine_dns_name(
525
            self.client.get_controller_client(), '0')
526
        if host is None:
527
            raise ValueError('Could not get machine 0 host')
528
        return {'0': host}
529
530
    def tear_down(self):
531
        """Tear down via client.tear_down."""
532
        self.tear_down_client.tear_down()
533
534
1711.3.8 by Aaron Bentley
Implement PublicController strategy.
535
class PublicController:
536
    """A controller strategy where the controller is public.
537
538
    The user registers with the controller, and adds the initial model.
539
    """
540
    def __init__(self, controller_host, email, password, client,
541
                 tear_down_client):
542
        self.controller_host = controller_host
543
        self.email = email
544
        self.password = password
545
        self.client = client
546
        self.tear_down_client = tear_down_client
547
548
    def prepare(self):
549
        """Prepare by destroying the model and unregistering if possible."""
550
        try:
551
            self.tear_down()
552
        except subprocess.CalledProcessError:
1711.5.3 by Aaron Bentley
Add comment.
553
            # Assume that any error tearing down means that there was nothing
554
            # to tear down.
1711.3.8 by Aaron Bentley
Implement PublicController strategy.
555
            pass
556
557
    def create_initial_model(self, upload_tools, series, boot_kwargs):
558
        """Register controller and add model."""
559
        self.client.register_host(
560
            self.controller_host, self.email, self.password)
561
        self.client.env.controller.explicit_region = True
562
        self.client.add_model(self.client.env)
563
564
    def get_hosts(self):
565
        """There are no user-owned controller hosts, so no-op."""
566
        return {}
567
568
    def tear_down(self):
569
        """Remove the current model and clean up the controller."""
570
        try:
571
            self.tear_down_client.destroy_model()
572
        finally:
573
            controller = self.tear_down_client.env.controller.name
574
            self.tear_down_client.juju('unregister', ('-y', controller),
575
                                       include_e=False)
576
577
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
578
class BootstrapManager:
579
    """
1162.2.10 by Aaron Bentley
Update docs.
580
    Helper class for running juju tests.
581
582
    Enables running tests on the manual provider and on MAAS systems, with
583
    automatic cleanup, logging, etc.  See BootstrapManager.booted_context.
584
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
585
    :ivar temp_env_name: a unique name for the juju env, such as a Jenkins
586
        job name.
587
    :ivar client: an EnvJujuClient.
1173.2.2 by Aaron Bentley
Switch runtime_context to dump using known_hosts.
588
    :ivar tear_down_client: an EnvJujuClient for tearing down the environment
589
        (may be more reliable/capable/compatible than client.)
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
590
    :ivar bootstrap_host: None, or the address of a manual or MAAS host to
591
        bootstrap on.
592
    :ivar machine: [] or a list of machines to use add to a manual env
593
        before deploying services.
594
    :ivar series: None or the default-series for the temp config.
595
    :ivar agent_url: None or the agent-metadata-url for the temp config.
596
    :ivar agent_stream: None or the agent-stream for the temp config.
597
    :ivar log_dir: The path to the directory to store logs.
598
    :ivar keep_env: False or True to not destroy the environment and keep
599
        it alive to do an autopsy.
600
    :ivar upload_tools: False or True to upload the local agent instead of
601
        using streams.
1173.2.2 by Aaron Bentley
Switch runtime_context to dump using known_hosts.
602
    :ivar known_hosts: A dict mapping machine_ids to hosts for
603
        dump_env_logs_known_hosts.
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
604
    """
605
1162.2.24 by Aaron Bentley
Use tear_down_client for teardown.
606
    def __init__(self, temp_env_name, client, tear_down_client, bootstrap_host,
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
607
                 machines, series, agent_url, agent_stream, region, log_dir,
1711.3.2 by Aaron Bentley
Use CreateController in BootstrapManager.
608
                 keep_env, permanent, jes_enabled, controller_strategy=None):
1162.2.11 by Aaron Bentley
Update docs
609
        """Constructor.
610
611
        Please see see `BootstrapManager` for argument descriptions.
1162.2.10 by Aaron Bentley
Update docs.
612
        """
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
613
        self.temp_env_name = temp_env_name
614
        self.bootstrap_host = bootstrap_host
615
        self.machines = machines
616
        self.series = series
617
        self.agent_url = agent_url
618
        self.agent_stream = agent_stream
619
        self.region = region
620
        self.log_dir = log_dir
621
        self.keep_env = keep_env
1201.1.1 by Aaron Bentley
Ensure that permanent is always True when jes_enabled is True.
622
        if jes_enabled and not permanent:
623
            raise ValueError('Cannot set permanent False if jes_enabled is'
624
                             ' True.')
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
625
        self.permanent = permanent
626
        self.jes_enabled = jes_enabled
1173.2.2 by Aaron Bentley
Switch runtime_context to dump using known_hosts.
627
        self.known_hosts = {}
628
        if bootstrap_host is not None:
629
            self.known_hosts['0'] = bootstrap_host
1711.3.2 by Aaron Bentley
Use CreateController in BootstrapManager.
630
        if controller_strategy is None:
631
            controller_strategy = CreateController(client, tear_down_client)
632
        self.controller_strategy = controller_strategy
1724.1.7 by Curtis Hovey
Convert BootstrapMamanger.has_controller into a simple boolean attribute.
633
        self.has_controller = False
1711.3.2 by Aaron Bentley
Use CreateController in BootstrapManager.
634
635
    @property
636
    def client(self):
637
        return self.controller_strategy.client
638
639
    @property
640
    def tear_down_client(self):
641
        return self.controller_strategy.tear_down_client
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
642
1162.2.13 by Aaron Bentley
Switch quickstart to BootstrapManager.
643
    @classmethod
1449.4.27 by Nicholas Skaggs
restore _generate_default_clean_dir
644
    def _generate_default_clean_dir(cls, temp_env_name):
645
        """Creates a new unique directory for logging and returns name"""
646
        logging.info('Environment {}'.format(temp_env_name))
647
        test_name = temp_env_name.split('-')[0]
648
        timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
649
        log_dir = os.path.join('/tmp', test_name, 'logs', timestamp)
650
651
        try:
652
            os.makedirs(log_dir)
653
            logging.info('Created logging directory {}'.format(log_dir))
654
        except OSError as e:
655
            if e.errno == errno.EEXIST:
656
                logging.warn('"Directory {} already exists'.format(log_dir))
657
            else:
658
                raise('Failed to create logging directory: {} ' +
659
                      log_dir +
660
                      '. Please specify empty folder or try again')
661
        return log_dir
662
663
    @classmethod
1162.2.13 by Aaron Bentley
Switch quickstart to BootstrapManager.
664
    def from_args(cls, args):
1449.4.25 by Nicholas Skaggs
flake8
665
        if not args.logs:
1449.4.27 by Nicholas Skaggs
restore _generate_default_clean_dir
666
            args.logs = cls._generate_default_clean_dir(args.temp_env_name)
1449.4.24 by Nicholas Skaggs
move directory creation to bootstrap manager
667
1543.1.2 by Martin Packman
Doc and comments from review by abentley
668
        # GZ 2016-08-11: Move this logic into client_from_config maybe?
1433.1.2 by Aaron Bentley
Support FAKE as a juju binary.
669
        if args.juju_bin == 'FAKE':
1465.5.2 by Aaron Bentley
Implement client_from_config, delete by_version.
670
            env = SimpleEnvironment.from_config(args.env)
1433.1.2 by Aaron Bentley
Support FAKE as a juju binary.
671
            client = fake_juju_client(env=env)
672
        else:
1465.5.2 by Aaron Bentley
Implement client_from_config, delete by_version.
673
            client = client_from_config(args.env, args.juju_bin,
1575.2.7 by Aaron Bentley
Fake merge of soft-timeouts-4 into soft-timeouts-5.
674
                                        debug=args.debug,
675
                                        soft_deadline=args.deadline)
1745.2.1 by Aaron Bentley
Extract BootstrapManager.from_client.
676
        return cls.from_client(args, client)
677
678
    @classmethod
679
    def from_client(cls, args, client):
1162.2.13 by Aaron Bentley
Switch quickstart to BootstrapManager.
680
        jes_enabled = client.is_jes_enabled()
681
        return cls(
1162.2.24 by Aaron Bentley
Use tear_down_client for teardown.
682
            args.temp_env_name, client, client, args.bootstrap_host,
683
            args.machine, args.series, args.agent_url, args.agent_stream,
684
            args.region, args.logs, args.keep_env, permanent=jes_enabled,
1162.2.13 by Aaron Bentley
Switch quickstart to BootstrapManager.
685
            jes_enabled=jes_enabled)
686
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
687
    @contextmanager
688
    def maas_machines(self):
1162.2.8 by Aaron Bentley
Update docs.
689
        """Handle starting/stopping MAAS machines."""
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
690
        running_domains = dict()
691
        try:
1674.1.4 by Aaron Bentley
More conversion.
692
            if self.client.env.provider == 'maas' and self.machines:
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
693
                for machine in self.machines:
694
                    name, URI = machine.split('@')
695
                    # Record already running domains, so we can warn that
696
                    # we're deleting them following the test.
697
                    if verify_libvirt_domain(URI, name,
698
                                             LIBVIRT_DOMAIN_RUNNING):
699
                        running_domains = {machine: True}
700
                        logging.info("%s is already running" % name)
701
                    else:
702
                        running_domains = {machine: False}
703
                        logging.info("Attempting to start %s at %s"
704
                                     % (name, URI))
705
                        status_msg = start_libvirt_domain(URI, name)
706
                        logging.info("%s" % status_msg)
707
                # No further handling of machines down the line is required.
708
                yield []
709
            else:
710
                yield self.machines
711
        finally:
1674.1.4 by Aaron Bentley
More conversion.
712
            if self.client.env.provider == 'maas' and not self.keep_env:
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
713
                logging.info("Waiting for destroy-environment to complete")
714
                time.sleep(90)
715
                for machine, running in running_domains.items():
716
                    name, URI = machine.split('@')
717
                    if running:
718
                        logging.warning(
719
                            "%s at %s was running when deploy_job started."
720
                            " Shutting it down to ensure a clean environment."
721
                            % (name, URI))
722
                    logging.info("Attempting to stop %s at %s" % (name, URI))
723
                    status_msg = stop_libvirt_domain(URI, name)
724
                    logging.info("%s" % status_msg)
725
726
    @contextmanager
727
    def aws_machines(self):
1162.2.8 by Aaron Bentley
Update docs.
728
        """Handle starting/stopping AWS machines.
729
730
        Machines are deliberately killed by tag so that any stray machines
731
        from previous runs will be killed.
732
        """
1162.2.9 by Aaron Bentley
Fix lint.
733
        if (
1674.1.4 by Aaron Bentley
More conversion.
734
                self.client.env.provider != 'manual' or
1162.2.9 by Aaron Bentley
Fix lint.
735
                self.bootstrap_host is not None):
1173.2.6 by Aaron Bentley
aws_machines doesn't yield bootstrap_host.
736
            yield []
1162.2.9 by Aaron Bentley
Fix lint.
737
            return
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
738
        try:
1334 by Curtis Hovey
Pass region to run_instances for manual testings.
739
            instances = run_instances(
740
                3, self.temp_env_name, self.series, region=self.region)
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
741
            new_bootstrap_host = instances[0][1]
1173.2.3 by Aaron Bentley
runtime_context doesn't take bootstrap_host as a parameter.
742
            self.known_hosts['0'] = new_bootstrap_host
1173.2.6 by Aaron Bentley
aws_machines doesn't yield bootstrap_host.
743
            yield [i[1] for i in instances[1:]]
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
744
        finally:
745
            if self.keep_env:
746
                return
747
            destroy_job_instances(self.temp_env_name)
748
1162.2.25 by Aaron Bentley
Call is_jes_enabled on bootstrap_client.
749
    def tear_down(self, try_jes=False):
1688.1.1 by Andrew Beach
Changed the BootstrapManager.tear_down method. Still have to finish intergating it.
750
        """Tear down the client using tear_down_client.
751
752
        Attempts to use the soft method destroy_controller, if that fails
753
        it will use the hard kill_controller.
754
755
        :param try_jes: Ignored."""
1193.2.1 by Aaron Bentley
Make EnvJujuClient.juju_home a reference to EnvJUjuClient.env.juju_home.
756
        if self.tear_down_client.env is not self.client.env:
757
            raise AssertionError('Tear down client needs same env!')
1711.3.2 by Aaron Bentley
Use CreateController in BootstrapManager.
758
        self.controller_strategy.tear_down()
1724.1.7 by Curtis Hovey
Convert BootstrapMamanger.has_controller into a simple boolean attribute.
759
        self.has_controller = False
1162.2.25 by Aaron Bentley
Call is_jes_enabled on bootstrap_client.
760
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
761
    @contextmanager
1242.3.7 by Aaron Bentley
BootstrapManager support for bootstrap option.
762
    def bootstrap_context(self, machines, omit_config=None):
1162.2.8 by Aaron Bentley
Update docs.
763
        """Context for bootstrapping a state server."""
1173.2.4 by Aaron Bentley
bootstrap_context doesn't accept bootstrap_host.
764
        bootstrap_host = self.known_hosts.get('0')
1242.3.7 by Aaron Bentley
BootstrapManager support for bootstrap option.
765
        kwargs = dict(
766
            series=self.series, bootstrap_host=bootstrap_host,
767
            agent_url=self.agent_url, agent_stream=self.agent_stream,
768
            region=self.region)
769
        if omit_config is not None:
770
            for key in omit_config:
771
                kwargs.pop(key.replace('-', '_'), None)
772
        update_env(self.client.env, self.temp_env_name, **kwargs)
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
773
        ssh_machines = list(machines)
774
        if bootstrap_host is not None:
775
            ssh_machines.append(bootstrap_host)
776
        for machine in ssh_machines:
777
            logging.info('Waiting for port 22 on %s' % machine)
778
            wait_for_port(machine, 22, timeout=120)
1193.2.4 by Aaron Bentley
Switch FakeJujuClient to raise on accessing juju_home.
779
        jenv_path = get_jenv_path(self.client.env.juju_home,
1162.2.18 by Aaron Bentley
Use client.juju_home for getting jenv.
780
                                  self.client.env.environment)
1173.2.12 by Aaron Bentley
Tear down JES environments more carefully.
781
        torn_down = False
1162.2.19 by Aaron Bentley
Do automatic teardown in bootstrap_context.
782
        if os.path.isfile(jenv_path):
1162.2.21 by Aaron Bentley
Implement and use tear_down(try_jes=True)
783
            # An existing .jenv implies JES was not used, because when JES is
784
            # enabled, cache.yaml is enabled.
1688.1.10 by Andrew Beach
Changed calls of tear_down for clean-up to kill_controller. Fixed a test.
785
            self.tear_down_client.kill_controller()
1162.2.19 by Aaron Bentley
Do automatic teardown in bootstrap_context.
786
            torn_down = True
787
        else:
1173.2.12 by Aaron Bentley
Tear down JES environments more carefully.
788
            jes_home = jes_home_path(
1193.2.4 by Aaron Bentley
Switch FakeJujuClient to raise on accessing juju_home.
789
                self.client.env.juju_home, self.client.env.environment)
1173.2.12 by Aaron Bentley
Tear down JES environments more carefully.
790
            with temp_juju_home(self.client, jes_home):
1240.2.1 by Aaron Bentley
Support models/cache.yaml
791
                cache_path = self.client.get_cache_path()
1173.2.12 by Aaron Bentley
Tear down JES environments more carefully.
792
                if os.path.isfile(cache_path):
793
                    # An existing .jenv implies JES was used, because when JES
794
                    # is enabled, cache.yaml is enabled.
1711.3.2 by Aaron Bentley
Use CreateController in BootstrapManager.
795
                    self.controller_strategy.prepare()
1173.2.12 by Aaron Bentley
Tear down JES environments more carefully.
796
                    torn_down = True
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
797
        ensure_deleted(jenv_path)
1193.2.4 by Aaron Bentley
Switch FakeJujuClient to raise on accessing juju_home.
798
        with temp_bootstrap_env(self.client.env.juju_home, self.client,
1242.3.16 by Aaron Bentley
Make agent-version omission client-dependent.
799
                                permanent=self.permanent, set_home=False):
1564.3.1 by Aaron Bentley
Extract common exception handling from bootstrap contexts.
800
            with self.handle_bootstrap_exceptions():
801
                if not torn_down:
1711.3.2 by Aaron Bentley
Use CreateController in BootstrapManager.
802
                    self.controller_strategy.prepare()
1724.1.4 by Curtis Hovey
rename lost_controller to has_controller.
803
                self.has_controller = True
1564.3.1 by Aaron Bentley
Extract common exception handling from bootstrap contexts.
804
                yield
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
805
806
    @contextmanager
1427.1.2 by Christopher Lee
Initial run of Model Migration test. Passes with current charm.
807
    def existing_bootstrap_context(self, machines, omit_config=None):
1427.2.6 by Christopher Lee
Improve docstrings.
808
        """ Context for bootstrapping a state server that shares the
809
        environment with an existing bootstrap environment.
810
811
        Using this context makes it possible to boot multiple simultaneous
812
        environments that share a JUJU_HOME.
1427.1.2 by Christopher Lee
Initial run of Model Migration test. Passes with current charm.
813
814
        """
815
        bootstrap_host = self.known_hosts.get('0')
816
        kwargs = dict(
817
            series=self.series, bootstrap_host=bootstrap_host,
818
            agent_url=self.agent_url, agent_stream=self.agent_stream,
819
            region=self.region)
820
        if omit_config is not None:
821
            for key in omit_config:
822
                kwargs.pop(key.replace('-', '_'), None)
823
        update_env(self.client.env, self.temp_env_name, **kwargs)
824
        ssh_machines = list(machines)
825
        if bootstrap_host is not None:
826
            ssh_machines.append(bootstrap_host)
827
        for machine in ssh_machines:
828
            logging.info('Waiting for port 22 on %s' % machine)
829
            wait_for_port(machine, 22, timeout=120)
830
1564.3.1 by Aaron Bentley
Extract common exception handling from bootstrap contexts.
831
        with self.handle_bootstrap_exceptions():
832
            yield
833
834
    @contextmanager
835
    def handle_bootstrap_exceptions(self):
1564.3.3 by Aaron Bentley
Add docs.
836
        """If an exception is raised during bootstrap, handle it.
837
838
        Log the exception, re-raise as a LoggedException.
839
        Copy logs for the bootstrap host
840
        Tear down.  (self.keep_env is ignored.)
841
        """
1427.1.2 by Christopher Lee
Initial run of Model Migration test. Passes with current charm.
842
        try:
843
            # If an exception is raised that indicates an error, log it
844
            # before tearing down so that the error is closely tied to
845
            # the failed operation.
1735.1.1 by Martin Packman
New utility.logged_exception factored out from deploy_stack
846
            with logged_exception(logging):
847
                yield
1427.1.2 by Christopher Lee
Initial run of Model Migration test. Passes with current charm.
848
        except:
849
            # If run from a windows machine may not have ssh to get
850
            # logs
1564.3.1 by Aaron Bentley
Extract common exception handling from bootstrap contexts.
851
            with self.client.ignore_soft_deadline():
852
                with self.tear_down_client.ignore_soft_deadline():
853
                    if self.bootstrap_host is not None and _can_run_ssh():
854
                        remote = remote_from_address(self.bootstrap_host,
855
                                                     series=self.series)
856
                        copy_remote_logs(remote, self.log_dir)
857
                        archive_logs(self.log_dir)
1711.3.2 by Aaron Bentley
Use CreateController in BootstrapManager.
858
                    self.controller_strategy.prepare()
1427.1.2 by Christopher Lee
Initial run of Model Migration test. Passes with current charm.
859
            raise
860
861
    @contextmanager
1173.2.3 by Aaron Bentley
runtime_context doesn't take bootstrap_host as a parameter.
862
    def runtime_context(self, addable_machines):
1162.2.8 by Aaron Bentley
Update docs.
863
        """Context for running non-bootstrap operations.
864
865
        If any manual machines need to be added, they will be added before
866
        control is yielded.
867
        """
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
868
        try:
1735.1.1 by Martin Packman
New utility.logged_exception factored out from deploy_stack
869
            with logged_exception(logging):
1191.2.1 by Curtis Hovey
Always add addable_machines in runtime_context.
870
                if len(self.known_hosts) == 0:
1711.3.2 by Aaron Bentley
Use CreateController in BootstrapManager.
871
                    self.known_hosts.update(
872
                        self.controller_strategy.get_hosts())
1191.2.1 by Curtis Hovey
Always add addable_machines in runtime_context.
873
                if addable_machines is not None:
874
                    self.client.add_ssh_machines(addable_machines)
1173.2.3 by Aaron Bentley
runtime_context doesn't take bootstrap_host as a parameter.
875
                yield
1386.1.1 by Martin Packman
Propogate errors from show-status in BootstrapManager
876
        except:
1724.1.5 by Curtis Hovey
revised doc
877
            if self.has_controller:
878
                safe_print_status(self.client)
879
            else:
880
                logging.info("Client lost controller, not calling status.")
1386.1.1 by Martin Packman
Propogate errors from show-status in BootstrapManager
881
            raise
882
        else:
1564.2.6 by Aaron Bentley
Ignore soft deadlines for tearing down runtime_context.
883
            with self.client.ignore_soft_deadline():
884
                self.client.list_controllers()
885
                self.client.list_models()
886
                for m_client in self.client.iter_model_clients():
887
                    m_client.show_status()
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
888
        finally:
1564.2.6 by Aaron Bentley
Ignore soft deadlines for tearing down runtime_context.
889
            with self.client.ignore_soft_deadline():
1564.2.9 by Aaron Bentley
Check runtime_context with tear_down_client.
890
                with self.tear_down_client.ignore_soft_deadline():
891
                    try:
892
                        self.dump_all_logs()
893
                    except KeyboardInterrupt:
894
                        pass
895
                    if not self.keep_env:
896
                        self.tear_down(self.jes_enabled)
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
897
1543.1.2 by Martin Packman
Doc and comments from review by abentley
898
    # GZ 2016-08-11: Should this method be elsewhere to avoid poking backend?
1440.1.1 by Aaron Bentley
Fix log dumping tests with relative imports.
899
    def _should_dump(self):
1440.1.2 by Aaron Bentley
Fix cases where dumping makes sense.
900
        return not isinstance(self.client._backend, FakeBackend)
1440.1.1 by Aaron Bentley
Fix log dumping tests with relative imports.
901
1485.6.14 by Christopher Lee
Keep a running storage of the logs between runs.
902
    def dump_all_logs(self, patch_dir=None):
1440.1.1 by Aaron Bentley
Fix log dumping tests with relative imports.
903
        """Dump logs for all models in the bootstrapped controller."""
1315.2.8 by Aaron Bentley
Dump logs for all models, including on JES.
904
        # This is accurate because we bootstrapped self.client.  It might not
905
        # be accurate for a model created by create_environment.
1440.1.1 by Aaron Bentley
Fix log dumping tests with relative imports.
906
        if not self._should_dump():
907
            return
1493.1.1 by Martin
Rename methods and variables refering to admin model to new term controller model
908
        controller_client = self.client.get_controller_client()
1315.2.8 by Aaron Bentley
Dump logs for all models, including on JES.
909
        if not self.jes_enabled:
910
            clients = [self.client]
911
        else:
1315.2.16 by Aaron Bentley
Make iteration support more robust.
912
            try:
913
                clients = list(self.client.iter_model_clients())
914
            except Exception:
1315.2.17 by Aaron Bentley
Add explanation.
915
                # Even if the controller is unreachable, we may still be able
1493.1.1 by Martin
Rename methods and variables refering to admin model to new term controller model
916
                # to gather some logs. The controller_client and self.client
917
                # instances are all we have knowledge of.
918
                clients = [controller_client]
919
                if self.client is not controller_client:
1315.2.16 by Aaron Bentley
Make iteration support more robust.
920
                    clients.append(self.client)
1315.2.8 by Aaron Bentley
Dump logs for all models, including on JES.
921
        for client in clients:
1647.1.3 by Aaron Bentley
Ignore soft deadline when dumping logs.
922
            with client.ignore_soft_deadline():
923
                if client.env.environment == controller_client.env.environment:
924
                    known_hosts = self.known_hosts
925
                    if self.jes_enabled:
926
                        runtime_config = self.client.get_cache_path()
927
                    else:
928
                        runtime_config = get_jenv_path(
929
                            self.client.env.juju_home,
930
                            self.client.env.environment)
1315.2.8 by Aaron Bentley
Dump logs for all models, including on JES.
931
                else:
1647.1.3 by Aaron Bentley
Ignore soft deadline when dumping logs.
932
                    known_hosts = {}
933
                    runtime_config = None
934
                artifacts_dir = os.path.join(self.log_dir,
935
                                             client.env.environment)
936
                os.mkdir(artifacts_dir)
937
                dump_env_logs_known_hosts(
938
                    client, artifacts_dir, runtime_config, known_hosts)
1315.2.8 by Aaron Bentley
Dump logs for all models, including on JES.
939
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
940
    @contextmanager
941
    def top_context(self):
1162.2.8 by Aaron Bentley
Update docs.
942
        """Context for running all juju operations in."""
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
943
        with self.maas_machines() as machines:
1173.2.6 by Aaron Bentley
aws_machines doesn't yield bootstrap_host.
944
            with self.aws_machines() as new_machines:
1629.1.1 by Aaron Bentley
Move log dump to top_context, skip if no log_dir.
945
                try:
946
                    yield machines + new_machines
947
                finally:
948
                    # This is not done in dump_all_logs because it should be
949
                    # done after tear down.
950
                    if self.log_dir is not None:
951
                        dump_juju_timings(self.client, self.log_dir)
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
952
953
    @contextmanager
1614.1.3 by Andrew Beach
Made the adjustements to bootstrap_manage, but can't get passing tests for them.
954
    def booted_context(self, upload_tools, **kwargs):
1162.2.8 by Aaron Bentley
Update docs.
955
        """Create a temporary environment in a context manager to run tests in.
956
957
        Bootstrap a new environment from a temporary config that is suitable
958
        to run tests in. Logs will be collected from the machines. The
959
        environment will be destroyed when the test completes or there is an
960
        unrecoverable error.
961
962
        The temporary environment is created by updating a EnvJujuClient's
963
        config with series, agent_url, agent_stream.
964
965
        :param upload_tools: False or True to upload the local agent instead
966
            of using streams.
1614.1.3 by Andrew Beach
Made the adjustements to bootstrap_manage, but can't get passing tests for them.
967
        :param **kwargs: All remaining keyword arguments are passed to the
968
        client's bootstrap.
1162.2.8 by Aaron Bentley
Update docs.
969
        """
1211.2.5 by Aaron Bentley
LoggedException in BootstrapManager, convert to sys.exit in booted_context
970
        try:
971
            with self.top_context() as machines:
1242.3.7 by Aaron Bentley
BootstrapManager support for bootstrap option.
972
                with self.bootstrap_context(
1242.3.18 by Aaron Bentley
Rename bootstrap_supports to bootstrap_replaces.
973
                        machines, omit_config=self.client.bootstrap_replaces):
1711.3.2 by Aaron Bentley
Use CreateController in BootstrapManager.
974
                    self.controller_strategy.create_initial_model(
975
                        upload_tools, self.series, kwargs)
1211.2.5 by Aaron Bentley
LoggedException in BootstrapManager, convert to sys.exit in booted_context
976
                with self.runtime_context(machines):
1449.2.1 by Curtis Hovey
Fix list-models and show the state of the env.
977
                    self.client.list_controllers()
978
                    self.client.list_models()
1449.2.6 by Curtis Hovey
show_status for all models.
979
                    for m_client in self.client.iter_model_clients():
980
                        m_client.show_status()
1211.2.5 by Aaron Bentley
LoggedException in BootstrapManager, convert to sys.exit in booted_context
981
                    yield machines
1704.1.14 by Andrew Beach
Removed a bit of debugging code that krept in.
982
        except LoggedException:
1211.2.5 by Aaron Bentley
LoggedException in BootstrapManager, convert to sys.exit in booted_context
983
            sys.exit(1)
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
984
1427.1.2 by Christopher Lee
Initial run of Model Migration test. Passes with current charm.
985
    @contextmanager
1614.1.3 by Andrew Beach
Made the adjustements to bootstrap_manage, but can't get passing tests for them.
986
    def existing_booted_context(self, upload_tools, **kwargs):
1427.1.2 by Christopher Lee
Initial run of Model Migration test. Passes with current charm.
987
        try:
988
            with self.top_context() as machines:
989
                # Existing does less things as there is no pre-cleanup needed.
990
                with self.existing_bootstrap_context(
991
                        machines, omit_config=self.client.bootstrap_replaces):
992
                    self.client.bootstrap(
1614.1.1 by Andrew Beach
Saving some changes that make calls to client.bootstrap more robust so I don't have to worry about loosing them.
993
                        upload_tools=upload_tools,
1614.1.3 by Andrew Beach
Made the adjustements to bootstrap_manage, but can't get passing tests for them.
994
                        bootstrap_series=self.series,
995
                        **kwargs)
1427.1.2 by Christopher Lee
Initial run of Model Migration test. Passes with current charm.
996
                with self.runtime_context(machines):
997
                    yield machines
998
        except LoggedException:
999
            sys.exit(1)
1000
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
1001
925.1.2 by Aaron Bentley
Extract boot_context.
1002
@contextmanager
1024.1.5 by Curtis Hovey
Use args.temp_env_name instead of args.job_name to be clear about the purpose.
1003
def boot_context(temp_env_name, client, bootstrap_host, machines, series,
1044.1.9 by Aaron Bentley
Update and add tests.
1004
                 agent_url, agent_stream, log_dir, keep_env, upload_tools,
1201.1.1 by Aaron Bentley
Ensure that permanent is always True when jes_enabled is True.
1005
                 region=None):
1028.2.7 by Curtis Hovey
Added docstrings.
1006
    """Create a temporary environment in a context manager to run tests in.
1007
1008
    Bootstrap a new environment from a temporary config that is suitable to
1009
    run tests in. Logs will be collected from the machines. The environment
1010
    will be destroyed when the test completes or there is an unrecoverable
1011
    error.
1012
1028.2.8 by Curtis Hovey
Revised docstrings.
1013
    The temporary environment is created by updating a EnvJujuClient's config
1028.2.7 by Curtis Hovey
Added docstrings.
1014
    with series, agent_url, agent_stream.
1015
1028.2.8 by Curtis Hovey
Revised docstrings.
1016
    :param temp_env_name: a unique name for the juju env, such as a Jenkins
1028.2.7 by Curtis Hovey
Added docstrings.
1017
        job name.
1018
    :param client: an EnvJujuClient.
1019
    :param bootstrap_host: None, or the address of a manual or MAAS host to
1020
        bootstrap on.
1028.2.8 by Curtis Hovey
Revised docstrings.
1021
    :param machine: [] or a list of machines to use add to a manual env
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
1022
        before deploying services.  This is mutated to indicate all machines,
1023
        including new instances, that have been manually added.
1028.2.7 by Curtis Hovey
Added docstrings.
1024
    :param series: None or the default-series for the temp config.
1025
    :param agent_url: None or the agent-metadata-url for the temp config.
1026
    :param agent_stream: None or the agent-stream for the temp config.
1027
    :param log_dir: The path to the directory to store logs.
1028.2.8 by Curtis Hovey
Revised docstrings.
1028
    :param keep_env: False or True to not destroy the environment and keep
1028.2.7 by Curtis Hovey
Added docstrings.
1029
        it alive to do an autopsy.
1028.2.8 by Curtis Hovey
Revised docstrings.
1030
    :param upload_tools: False or True to upload the local agent instead of
1028.2.7 by Curtis Hovey
Added docstrings.
1031
        using streams.
1032
    """
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
1033
    jes_enabled = client.is_jes_enabled()
1034
    bs_manager = BootstrapManager(
1162.2.24 by Aaron Bentley
Use tear_down_client for teardown.
1035
        temp_env_name, client, client, bootstrap_host, machines, series,
1201.1.1 by Aaron Bentley
Ensure that permanent is always True when jes_enabled is True.
1036
        agent_url, agent_stream, region, log_dir, keep_env,
1037
        permanent=jes_enabled, jes_enabled=jes_enabled)
1162.2.1 by Aaron Bentley
Extract BootstrapManager.
1038
    with bs_manager.booted_context(upload_tools) as new_machines:
1039
        machines[:] = new_machines
1040
        yield
379.1.1 by Aaron Bentley
Move portions of deploy job to Python.
1041
1042
1345.1.5 by Seman
Deploy charm by path #2.
1043
def _deploy_job(args, charm_series, series):
1236.1.1 by Martin Packman
Refactor _deploy_job and make sane when jes is on by default
1044
    start_juju_path = None if args.upgrade else args.juju_bin
925.1.2 by Aaron Bentley
Extract boot_context.
1045
    if sys.platform == 'win32':
1046
        # Ensure OpenSSH is never in the path for win tests.
1047
        sys.path = [p for p in sys.path if 'OpenSSH' not in p]
1236.1.1 by Martin Packman
Refactor _deploy_job and make sane when jes is on by default
1048
    # GZ 2016-01-22: When upgrading, could make sure to tear down with the
1049
    # newer client instead, this will be required for major version upgrades?
1575.2.7 by Aaron Bentley
Fake merge of soft-timeouts-4 into soft-timeouts-5.
1050
    client = client_from_config(args.env, start_juju_path, args.debug,
1051
                                soft_deadline=args.deadline)
1236.1.1 by Martin Packman
Refactor _deploy_job and make sane when jes is on by default
1052
    if args.jes and not client.is_jes_enabled():
1044.1.9 by Aaron Bentley
Update and add tests.
1053
        client.enable_jes()
1236.1.1 by Martin Packman
Refactor _deploy_job and make sane when jes is on by default
1054
    jes_enabled = client.is_jes_enabled()
1711.3.10 by Aaron Bentley
Accept --controller-host to use public controller.
1055
    controller_strategy = make_controller_strategy(client, client,
1056
                                                   args.controller_host)
1162.2.2 by Aaron Bentley
Switch to direct BootstrapManager.
1057
    bs_manager = BootstrapManager(
1236.1.1 by Martin Packman
Refactor _deploy_job and make sane when jes is on by default
1058
        args.temp_env_name, client, client, args.bootstrap_host, args.machine,
1059
        series, args.agent_url, args.agent_stream, args.region, args.logs,
1711.3.10 by Aaron Bentley
Accept --controller-host to use public controller.
1060
        args.keep_env, permanent=jes_enabled, jes_enabled=jes_enabled,
1061
        controller_strategy=controller_strategy)
1236.1.1 by Martin Packman
Refactor _deploy_job and make sane when jes is on by default
1062
    with bs_manager.booted_context(args.upload_tools):
925.1.2 by Aaron Bentley
Extract boot_context.
1063
        if sys.platform in ('win32', 'darwin'):
1064
            # The win and osx client tests only verify the client
1065
            # can bootstrap and call the state-server.
1066
            return
1236.1.1 by Martin Packman
Refactor _deploy_job and make sane when jes is on by default
1067
        if args.with_chaos > 0:
1068
            manager = background_chaos(args.temp_env_name, client,
1069
                                       args.logs, args.with_chaos)
1014.2.5 by John George
deploy_dummy_stack with chaos.
1070
        else:
1036.1.1 by John George
Correct variable name passed to background_chaos.
1071
            # Create a no-op context manager, to avoid duplicate calls of
1072
            # deploy_dummy_stack(), as was the case prior to this revision.
1073
            manager = nested()
1074
        with manager:
1345.1.5 by Seman
Deploy charm by path #2.
1075
            deploy_dummy_stack(client, charm_series)
1169.1.1 by John George
Factor out relation check, so assess_upgrade can be re-used by run_deployer.py
1076
        assess_juju_relations(client)
1345.1.5 by Seman
Deploy charm by path #2.
1077
        skip_juju_run = charm_series.startswith(("centos", "win"))
1091.4.1 by James Tunnicliffe
Merged upstream
1078
        if not skip_juju_run:
994.4.3 by Martin Packman
Selection of fixes needed for passing job
1079
            assess_juju_run(client)
1236.1.1 by Martin Packman
Refactor _deploy_job and make sane when jes is on by default
1080
        if args.upgrade:
1221.5.15 by Aaron Bentley
Use 'show-status' instead of 'status'.
1081
            client.show_status()
1243 by Aaron Bentley
Fix bad arg.
1082
            assess_upgrade(client, args.juju_bin)
1169.1.1 by John George
Factor out relation check, so assess_upgrade can be re-used by run_deployer.py
1083
            assess_juju_relations(client)
1084
            if not skip_juju_run:
1085
                assess_juju_run(client)
925.1.2 by Aaron Bentley
Extract boot_context.
1086
1087
1593.2.9 by Curtis Hovey
Require get_model() to be fast.
1088
def safe_print_status(client):
893.1.2 by seman.said at canonical
Added safe_print_status() to output juju status without raising exceptions
1089
    """Show the output of juju status without raising exceptions."""
1090
    try:
1647.1.1 by Aaron Bentley
Ignore soft deadline in safe_print_status.
1091
        with client.ignore_soft_deadline():
1092
            for m_client in client.iter_model_clients():
1093
                m_client.show_status()
893.1.2 by seman.said at canonical
Added safe_print_status() to output juju status without raising exceptions
1094
    except Exception as e:
1095
        logging.exception(e)
884 by John George
Add juju-deployer test support.
1096
1097
1452.3.7 by Curtis Hovey
Added a timeout for slow azure.
1098
def wait_for_state_server_to_shutdown(host, client, instance_id, timeout=60):
717.2.2 by Aaron Bentley
Checkpoint with assess_recovery working.
1099
    print_now("Waiting for port to close on %s" % host)
1452.3.7 by Curtis Hovey
Added a timeout for slow azure.
1100
    wait_for_port(host, 17070, closed=True, timeout=timeout)
717.2.2 by Aaron Bentley
Checkpoint with assess_recovery working.
1101
    print_now("Closed.")
1674.1.4 by Aaron Bentley
More conversion.
1102
    try:
1103
        provider_type = client.env.provider
1104
    except NoProvider:
1105
        provider_type = None
717.2.2 by Aaron Bentley
Checkpoint with assess_recovery working.
1106
    if provider_type == 'openstack':
1107
        environ = dict(os.environ)
1108
        environ.update(translate_to_env(client.env.config))
1109
        for ignored in until_timeout(300):
1110
            output = subprocess.check_output(['nova', 'list'], env=environ)
1111
            if instance_id not in output:
1112
                print_now('{} was removed from nova list'.format(instance_id))
1113
                break
1114
        else:
1115
            raise Exception(
1116
                '{} was not deleted:\n{}'.format(instance_id, output))