~nskaggs/juju-ci-tools/add-assess-terms

385.1.1 by Aaron Bentley
Support upgrades.
1
from contextlib import contextmanager
777.1.1 by Aaron Bentley
Move find_candidates, get under test.
2
from datetime import (
3
    datetime,
4
    timedelta,
5
    )
379.1.1 by Aaron Bentley
Move portions of deploy job to Python.
6
import errno
1071.1.1 by Aaron Bentley
Implement find_latest_branch_candidates.
7
import json
751.2.1 by Aaron Bentley
Use timestamped logs.
8
import logging
385.1.1 by Aaron Bentley
Support upgrades.
9
import os
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
10
import re
11
import subprocess
379.1.1 by Aaron Bentley
Move portions of deploy job to Python.
12
import socket
386.1.8 by Aaron Bentley
Add missing dependency.
13
import sys
777.1.1 by Aaron Bentley
Move find_candidates, get under test.
14
from time import (
15
    sleep,
16
    time,
17
    )
1354.1.1 by Martin Packman
Sane warnings from _clean_dir helper for common arg parsing
18
import warnings
1889.2.2 by Aaron Bentley
Extract jujupy.utility from utility.
19
from jujupy.utility import (
20
    ensure_deleted,
21
    ensure_dir,
22
    get_timeout_path,
23
    is_ipv6_address,
24
    print_now,
25
    qualified_model_name,
26
    quote,
27
    scoped_environ,
28
    skip_on_missing_file,
29
    temp_dir,
30
    temp_yaml_file,
31
    until_timeout
32
    )
994.3.1 by Martin Packman
Make Remote an abstract class and implement winrm based version
33
1889.2.2 by Aaron Bentley
Extract jujupy.utility from utility.
34
# Imported for other call sites to use.
35
__all__ = [
36
    'ensure_deleted',
37
    'ensure_dir',
38
    'get_timeout_path',
39
    'qualified_model_name',
40
    'quote',
41
    'scoped_environ',
42
    'skip_on_missing_file',
43
    'temp_dir',
44
    'temp_yaml_file',
45
    ]
1409.1.5 by Martin
Lint on transitional import in utility
46
385.1.1 by Aaron Bentley
Support upgrades.
47
1167.2.1 by Martin Packman
Support IPv6 in wait_for_port and move handling of literal addresses closer to final use
48
# Equivalent of socket.EAI_NODATA when using windows sockets
49
# <https://msdn.microsoft.com/ms740668#WSANO_DATA>
50
WSANO_DATA = 11004
1873.6.6 by Nicholas Skaggs
revert 1878
51
1167.2.1 by Martin Packman
Support IPv6 in wait_for_port and move handling of literal addresses closer to final use
52
385.1.1 by Aaron Bentley
Support upgrades.
53
@contextmanager
1575.1.7 by Aaron Bentley
Add timeout handling to wait_for_model.
54
def noop_context():
55
    """A context manager that does nothing."""
56
    yield
57
58
457 by Aaron Bentley
Don't raise an exception when logs cannot be dumped.
59
class PortTimeoutError(Exception):
60
    pass
61
62
1211.2.4 by Aaron Bentley
Move LoggedException to utility.
63
class LoggedException(BaseException):
64
    """Raised in place of an exception that has already been logged.
65
66
    This is a wrapper to avoid double-printing real Exceptions while still
67
    unwinding the stack appropriately.
68
    """
69
    def __init__(self, exception):
70
        self.exception = exception
71
72
1410.1.1 by Seman
Added CI test for deploying a charm with resources.
73
class JujuAssertionError(AssertionError):
74
    """Exception for juju assertion failures."""
75
76
1303.1.2 by Martin Packman
Warn if log_dir has pre-existing contents
77
def _clean_dir(maybe_dir):
78
    """Pseudo-type that validates an argument to be a clean directory path.
79
80
    For safety, this function will not attempt to remove existing directory
81
    contents but will just report a warning.
82
    """
83
    try:
84
        contents = os.listdir(maybe_dir)
85
    except OSError as e:
1449.4.1 by Nicholas Skaggs
modify utility to allow for positional arguments to be optional, with sane defaults. fixes bug #1587925
86
        if e.errno == errno.ENOENT:
1449.4.14 by Nicholas Skaggs
add docstring and comment
87
            # we don't raise this error due to tests abusing /tmp/logs
1449.4.17 by Nicholas Skaggs
update old-school string formatting to new
88
            warnings.warn('Not a directory {}'.format(maybe_dir))
1449.4.13 by Nicholas Skaggs
add _generate_default_binary, update tests, deal with /tmp/logs issue
89
        if e.errno == errno.EEXIST:
1449.4.17 by Nicholas Skaggs
update old-school string formatting to new
90
            warnings.warn('Directory {} already exists'.format(maybe_dir))
1303.1.2 by Martin Packman
Warn if log_dir has pre-existing contents
91
    else:
1354.1.1 by Martin Packman
Sane warnings from _clean_dir helper for common arg parsing
92
        if contents and contents != ["empty"]:
1449.4.17 by Nicholas Skaggs
update old-school string formatting to new
93
            warnings.warn(
1449.4.18 by Nicholas Skaggs
restore repr expression
94
                'Directory {!r} has existing contents.'.format(maybe_dir))
1303.1.2 by Martin Packman
Warn if log_dir has pre-existing contents
95
    return maybe_dir
96
97
1167.2.1 by Martin Packman
Support IPv6 in wait_for_port and move handling of literal addresses closer to final use
98
def as_literal_address(address):
99
    """Returns address in form suitable for embedding in URL or similar.
100
101
    In practice, this just puts square brackets round IPv6 addresses which
102
    avoids conflict with port seperators and other uses of colons.
103
    """
104
    if is_ipv6_address(address):
105
        return address.join("[]")
106
    return address
107
108
379.1.1 by Aaron Bentley
Move portions of deploy job to Python.
109
def wait_for_port(host, port, closed=False, timeout=30):
1167.2.1 by Martin Packman
Support IPv6 in wait_for_port and move handling of literal addresses closer to final use
110
    family = socket.AF_INET6 if is_ipv6_address(host) else socket.AF_INET
379.1.1 by Aaron Bentley
Move portions of deploy job to Python.
111
    for remaining in until_timeout(timeout):
808.2.12 by Aaron Bentley
Handle 'No address associated with hostname'.
112
        try:
1167.2.1 by Martin Packman
Support IPv6 in wait_for_port and move handling of literal addresses closer to final use
113
            addrinfo = socket.getaddrinfo(host, port, family,
808.2.12 by Aaron Bentley
Handle 'No address associated with hostname'.
114
                                          socket.SOCK_STREAM)
115
        except socket.error as e:
1167.2.1 by Martin Packman
Support IPv6 in wait_for_port and move handling of literal addresses closer to final use
116
            if e.errno not in (socket.EAI_NODATA, WSANO_DATA):
808.2.12 by Aaron Bentley
Handle 'No address associated with hostname'.
117
                raise
118
            if closed:
119
                return
120
            else:
121
                continue
808.2.5 by Aaron Bentley
Treat 0.0.0.0 address as a closed port.
122
        sockaddr = addrinfo[0][4]
123
        # Treat Azure messed-up address lookup as a closed port.
124
        if sockaddr[0] == '0.0.0.0':
125
            if closed:
126
                return
127
            else:
128
                continue
129
        conn = socket.socket(*addrinfo[0][:3])
1771.1.5 by Seman Said
Updated after a code review.
130
        conn.settimeout(max(remaining or 0, 5))
379.1.1 by Aaron Bentley
Move portions of deploy job to Python.
131
        try:
808.2.5 by Aaron Bentley
Treat 0.0.0.0 address as a closed port.
132
            conn.connect(sockaddr)
379.1.1 by Aaron Bentley
Move portions of deploy job to Python.
133
        except socket.timeout:
134
            if closed:
135
                return
136
        except socket.error as e:
1167.2.2 by Martin Packman
Expect ENETUNREACH as a posible socket connection error in wait_for_port
137
            if e.errno not in (errno.ECONNREFUSED, errno.ENETUNREACH,
1296 by Curtis Hovey
errno.EHOSTUNREACH is evidence that the port is closed; the host is deleted.
138
                               errno.ETIMEDOUT, errno.EHOSTUNREACH):
379.1.1 by Aaron Bentley
Move portions of deploy job to Python.
139
                raise
140
            if closed:
141
                return
787 by Curtis Hovey
Do not swallow errors retrieving logs.
142
        except socket.gaierror as e:
143
            print_now(str(e))
379.1.1 by Aaron Bentley
Move portions of deploy job to Python.
144
        except Exception as e:
1449.4.17 by Nicholas Skaggs
update old-school string formatting to new
145
            print_now('Unexpected {!r}: {}'.format((type(e), e)))
379.1.1 by Aaron Bentley
Move portions of deploy job to Python.
146
            raise
147
        else:
148
            conn.close()
149
            if not closed:
150
                return
151
            sleep(1)
457 by Aaron Bentley
Don't raise an exception when logs cannot be dumped.
152
    raise PortTimeoutError('Timed out waiting for port.')
379.1.1 by Aaron Bentley
Move portions of deploy job to Python.
153
154
621.1.1 by Abel Deuring
new helper function to find successful build for a given build_revision; build_vagrant_boxes.py uses this function.
155
def get_revision_build(build_info):
156
    for action in build_info['actions']:
157
        if 'parameters' in action:
158
            for parameter in action['parameters']:
159
                if parameter['name'] == 'revision_build':
160
                    return parameter['value']
161
162
163
def builds_for_revision(job, revision_build, jenkins):
164
    """Return the build_info data for the given job and revision_build.
165
166
    Only successful builds are included.
167
168
    :param job: The name of the job.
621.1.2 by Abel Deuring
typo fixed
169
    :param revision_build: The revision_build to searh for. Note that
621.1.1 by Abel Deuring
new helper function to find successful build for a given build_revision; build_vagrant_boxes.py uses this function.
170
        this parameter is a string.
171
    :parameter  jenkins: A Jenkins instance.
172
    """
173
    job_info = jenkins.get_job_info(job)
174
    result = []
175
    for build in job_info['builds']:
176
        build_info = jenkins.get_build_info(job, build['number'])
177
        if (get_revision_build(build_info) == revision_build and
706.1.1 by Aaron Bentley
Switch to flake8 and fix lint.
178
                build_info['result'] == 'SUCCESS'):
621.1.1 by Abel Deuring
new helper function to find successful build for a given build_revision; build_vagrant_boxes.py uses this function.
179
            result.append(build_info)
180
    return result
663.1.2 by Aaron Bentley
Port bootstrap_from_env to EnvJujuClient, add unit tests.
181
182
994.3.1 by Martin Packman
Make Remote an abstract class and implement winrm based version
183
def get_winrm_certs():
184
    """"Returns locations of key and cert files for winrm in cloud-city."""
185
    home = os.environ['HOME']
186
    return (
187
        os.path.join(home, 'cloud-city/winrm_client_cert.key'),
188
        os.path.join(home, 'cloud-city/winrm_client_cert.pem'),
189
    )
190
191
683.1.1 by Abel Deuring
function s3_cmd moved from backup-to-s3.py to utility.py
192
def s3_cmd(params, drop_output=False):
193
    s3cfg_path = os.path.join(
194
        os.environ['HOME'], 'cloud-city/juju-qa.s3cfg')
683.1.3 by Abel Deuring
If two result files exist for one revision, use the timestamp of the older file but the data from the newer file.
195
    command = ['s3cmd', '-c', s3cfg_path, '--no-progress'] + params
683.1.1 by Abel Deuring
function s3_cmd moved from backup-to-s3.py to utility.py
196
    if drop_output:
197
        return subprocess.check_call(
683.1.3 by Abel Deuring
If two result files exist for one revision, use the timestamp of the older file but the data from the newer file.
198
            command, stdout=open('/dev/null', 'w'))
683.1.1 by Abel Deuring
function s3_cmd moved from backup-to-s3.py to utility.py
199
    else:
683.1.3 by Abel Deuring
If two result files exist for one revision, use the timestamp of the older file but the data from the newer file.
200
        return subprocess.check_output(command)
751.2.1 by Aaron Bentley
Use timestamped logs.
201
1449.4.3 by Nicholas Skaggs
flake8
202
1449.4.1 by Nicholas Skaggs
modify utility to allow for positional arguments to be optional, with sane defaults. fixes bug #1587925
203
def _get_test_name_from_filename():
1449.4.5 by Nicholas Skaggs
make env optional too
204
    try:
205
        calling_file = sys._getframe(2).f_back.f_globals['__file__']
206
        return os.path.splitext(os.path.basename(calling_file))[0]
207
    except:
208
        return 'unknown_test'
1449.4.1 by Nicholas Skaggs
modify utility to allow for positional arguments to be optional, with sane defaults. fixes bug #1587925
209
1873.6.10 by Nicholas Skaggs
lint
210
1873.6.12 by Nicholas Skaggs
make _generate_default_clean_dir public
211
def generate_default_clean_dir(temp_env_name):
1873.6.8 by Nicholas Skaggs
wip
212
    """Creates a new unique directory for logging and returns name"""
213
    logging.debug('Environment {}'.format(temp_env_name))
214
    test_name = temp_env_name.split('-')[0]
215
    timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
216
    log_dir = os.path.join('/tmp', test_name, 'logs', timestamp)
217
218
    try:
219
        os.makedirs(log_dir)
220
        logging.info('Created logging directory {}'.format(log_dir))
221
    except OSError as e:
222
        if e.errno == errno.EEXIST:
223
            logging.warn('"Directory {} already exists'.format(log_dir))
224
        else:
225
            raise('Failed to create logging directory: {} ' +
226
                  log_dir +
227
                  '. Please specify empty folder or try again')
228
    return log_dir
1449.4.3 by Nicholas Skaggs
flake8
229
1873.6.10 by Nicholas Skaggs
lint
230
1449.4.24 by Nicholas Skaggs
move directory creation to bootstrap manager
231
def _generate_default_temp_env_name():
1449.4.1 by Nicholas Skaggs
modify utility to allow for positional arguments to be optional, with sane defaults. fixes bug #1587925
232
    """Creates a new unique name for environment and returns the name"""
1449.4.20 by Nicholas Skaggs
fix for bug #1601987
233
    # we need to sanitize the name
1449.4.24 by Nicholas Skaggs
move directory creation to bootstrap manager
234
    timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
1449.4.20 by Nicholas Skaggs
fix for bug #1601987
235
    test_name = re.sub('[^a-zA-Z]', '', _get_test_name_from_filename())
236
    return '{}-{}-temp-env'.format(test_name, timestamp)
751.2.1 by Aaron Bentley
Use timestamped logs.
237
1449.4.3 by Nicholas Skaggs
flake8
238
1575.2.1 by Aaron Bentley
Shove dealine from arg down to client_from_config.
239
def _to_deadline(timeout):
240
    return datetime.utcnow() + timedelta(seconds=int(timeout))
241
242
1711.6.4 by Aaron Bentley
Initial assess_add_cloud test.
243
def add_arg_juju_bin(parser):
244
    parser.add_argument('juju_bin', nargs='?',
245
                        help='Full path to the Juju binary. By default, this'
1873.6.7 by Nicholas Skaggs
wip
246
                        ' will use $PATH/juju',
247
                        default=None)
1711.6.4 by Aaron Bentley
Initial assess_add_cloud test.
248
249
1745.2.3 by Aaron Bentley
Allow env to be omitted.
250
def add_basic_testing_arguments(parser, using_jes=False, deadline=True,
251
                                env=True):
1028.2.7 by Curtis Hovey
Added docstrings.
252
    """Returns the parser loaded with basic testing arguments.
253
254
    The basic testing arguments, used in conjuction with boot_context ensures
255
    a test can be run in any supported substrate in parallel.
256
1449.4.5 by Nicholas Skaggs
make env optional too
257
    This helper adds 4 positional arguments that defines the minimum needed
258
    to run a test script.
1449.4.1 by Nicholas Skaggs
modify utility to allow for positional arguments to be optional, with sane defaults. fixes bug #1587925
259
1449.4.5 by Nicholas Skaggs
make env optional too
260
    These arguments (env, juju_bin, logs, temp_env_name) allow you to specify
261
    specifics for which env, juju binary, which folder for logging and an
1449.4.1 by Nicholas Skaggs
modify utility to allow for positional arguments to be optional, with sane defaults. fixes bug #1587925
262
    environment name for your test respectively.
1028.2.7 by Curtis Hovey
Added docstrings.
263
264
    There are many optional args that either update the env's config or
265
    manipulate the juju command line options to test in controlled situations
266
    or in uncommon substrates: --debug, --verbose, --agent-url, --agent-stream,
995.1.18 by Martin Packman
Add using_jes flag to add_basic_testing_arguments
267
    --series, --bootstrap-host, --machine, --keep-env. If not using_jes, the
268
    --upload-tools arg will also be added.
1028.2.7 by Curtis Hovey
Added docstrings.
269
270
    :param parser: an ArgumentParser.
995.1.18 by Martin Packman
Add using_jes flag to add_basic_testing_arguments
271
    :param using_jes: whether args should be tailored for JES testing.
1575.3.5 by Aaron Bentley
Fix help text.
272
    :param deadline: If true, support the --timeout option and convert to a
273
        deadline.
1028.2.7 by Curtis Hovey
Added docstrings.
274
    """
1449.4.5 by Nicholas Skaggs
make env optional too
275
276
    # Optional postional arguments
1745.2.3 by Aaron Bentley
Allow env to be omitted.
277
    if env:
278
        parser.add_argument(
279
            'env', nargs='?',
280
            help='The juju environment to base the temp test environment on.',
281
            default='lxd')
1711.6.4 by Aaron Bentley
Initial assess_add_cloud test.
282
    add_arg_juju_bin(parser)
1549.1.1 by Curtis Hovey
Removed get_toekn; all jobs require authentication anyway.
283
    parser.add_argument('logs', nargs='?', type=_clean_dir,
1449.4.15 by Nicholas Skaggs
clarify defaults in help, remove try catch block, switch default logging dir name
284
                        help='A directory in which to store logs. By default,'
1449.4.23 by Nicholas Skaggs
remove directory creation as part of arg parsing. By default if nothing is specified, we will us the current directory
285
                        ' this will use the current directory',
1449.4.24 by Nicholas Skaggs
move directory creation to bootstrap manager
286
                        default=None)
1449.4.1 by Nicholas Skaggs
modify utility to allow for positional arguments to be optional, with sane defaults. fixes bug #1587925
287
    parser.add_argument('temp_env_name', nargs='?',
1449.4.15 by Nicholas Skaggs
clarify defaults in help, remove try catch block, switch default logging dir name
288
                        help='A temporary test environment name. By default, '
289
                        ' this will generate an enviroment name using the '
290
                        ' timestamp and testname. '
291
                        ' test_name_timestamp_temp_env',
1449.4.24 by Nicholas Skaggs
move directory creation to bootstrap manager
292
                        default=_generate_default_temp_env_name())
1449.4.1 by Nicholas Skaggs
modify utility to allow for positional arguments to be optional, with sane defaults. fixes bug #1587925
293
1303.1.1 by Martin Packman
Unroll common positional args in helper
294
    # Optional keyword arguments.
995.1.18 by Martin Packman
Add using_jes flag to add_basic_testing_arguments
295
    parser.add_argument('--debug', action='store_true',
906.2.1 by John George
Added the add_basic_testing_arguments function for setting up arguments needed for quickstart, deployer and charmstore tests.
296
                        help='Pass --debug to Juju.')
297
    parser.add_argument('--verbose', action='store_const',
908.2.1 by John George
Re-write quickstart_deploy.py to use EnvJujuClient.
298
                        default=logging.INFO, const=logging.DEBUG,
906.2.1 by John George
Added the add_basic_testing_arguments function for setting up arguments needed for quickstart, deployer and charmstore tests.
299
                        help='Verbose test harness output.')
1793.4.3 by viswesn
Updated specific to code review comments.
300
    parser.add_argument('--region', help='Override environment region.')
1912.3.1 by Curtis Hovey
Move --to from assess_bootstrap to add_basic_testing_arguments.
301
    parser.add_argument('--to', default=None,
302
                        help='Place the controller at a location.')
1024.1.2 by Curtis Hovey
Added --agent-stream to add_basic_testing_arguments.
303
    parser.add_argument('--agent-url', action='store', default=None,
304
                        help='URL for retrieving agent binaries.')
305
    parser.add_argument('--agent-stream', action='store', default=None,
1120.3.3 by Aaron Bentley
Fix help
306
                        help='Stream for retrieving agent binaries.')
1793.4.3 by viswesn
Updated specific to code review comments.
307
    parser.add_argument('--series', action='store', default=None,
1024.1.2 by Curtis Hovey
Added --agent-stream to add_basic_testing_arguments.
308
                        help='Name of the Ubuntu series to use.')
995.1.18 by Martin Packman
Add using_jes flag to add_basic_testing_arguments
309
    if not using_jes:
310
        parser.add_argument('--upload-tools', action='store_true',
311
                            help='upload local version of tools to bootstrap.')
1024.1.3 by Curtis Hovey
Added arguments for manual env tests.
312
    parser.add_argument('--bootstrap-host',
313
                        help='The host to use for bootstrap.')
314
    parser.add_argument('--machine', help='A machine to add or when used with '
315
                        'KVM based MaaS, a KVM image to start.',
316
                        action='append', default=[])
995.1.18 by Martin Packman
Add using_jes flag to add_basic_testing_arguments
317
    parser.add_argument('--keep-env', action='store_true',
1024.1.3 by Curtis Hovey
Added arguments for manual env tests.
318
                        help='Keep the Juju environment after the test'
319
                        ' completes.')
1575.2.2 by Aaron Bentley
Fix special cases where deadline may be ignored.
320
    if deadline:
1575.2.12 by Aaron Bentley
Add --timeout help.
321
        parser.add_argument('--timeout', dest='deadline', type=_to_deadline,
322
                            help="The script timeout, in seconds.")
906.2.1 by John George
Added the add_basic_testing_arguments function for setting up arguments needed for quickstart, deployer and charmstore tests.
323
    return parser
324
325
1315.2.23 by Aaron Bentley
Introduce set_model_name, update tests, check controller on bootstrap.
326
# suppress nosetests
327
add_basic_testing_arguments.__test__ = False
328
329
751.2.1 by Aaron Bentley
Use timestamped logs.
330
def configure_logging(log_level):
331
    logging.basicConfig(
332
        level=log_level, format='%(asctime)s %(levelname)s %(message)s',
333
        datefmt='%Y-%m-%d %H:%M:%S')
777.1.1 by Aaron Bentley
Move find_candidates, get under test.
334
335
336
def get_candidates_path(root_dir):
337
    return os.path.join(root_dir, 'candidate')
338
339
1091.4.1 by James Tunnicliffe
Merged upstream
340
# GZ 2015-10-15: Paths returned in filesystem dependent order, may want sort?
1029.1.1 by seman.said at canonical
Added --all option to schedule all candidates for client-server testing.
341
def find_candidates(root_dir, find_all=False):
1071.1.1 by Aaron Bentley
Implement find_latest_branch_candidates.
342
    return (path for path, buildvars in _find_candidates(root_dir, find_all))
343
344
345
def find_latest_branch_candidates(root_dir):
346
    """Return a list of one candidate per branch.
347
348
    :param root_dir: The root directory to find candidates from.
349
    """
350
    candidates = []
1367.1.1 by Aaron Bentley
Switch schedule_reliability_tests to use artifacts dirs and omit path.
351
    for path, buildvars_path in _find_candidates(root_dir, find_all=False,
352
                                                 artifacts=True):
1071.1.1 by Aaron Bentley
Implement find_latest_branch_candidates.
353
        with open(buildvars_path) as buildvars_file:
354
            buildvars = json.load(buildvars_file)
355
            candidates.append(
356
                (buildvars['branch'], int(buildvars['revision_build']), path))
1302.1.1 by Aaron Bentley
Include revision_build in industrial-test job params.
357
    latest = dict(
358
        (branch, (path, build)) for branch, build, path in sorted(candidates))
1071.1.1 by Aaron Bentley
Implement find_latest_branch_candidates.
359
    return latest.values()
360
361
1367.1.1 by Aaron Bentley
Switch schedule_reliability_tests to use artifacts dirs and omit path.
362
def _find_candidates(root_dir, find_all=False, artifacts=False):
777.1.1 by Aaron Bentley
Move find_candidates, get under test.
363
    candidates_path = get_candidates_path(root_dir)
364
    a_week_ago = time() - timedelta(days=7).total_seconds()
365
    for candidate_dir in os.listdir(candidates_path):
1367.1.1 by Aaron Bentley
Switch schedule_reliability_tests to use artifacts dirs and omit path.
366
        if candidate_dir.endswith('-artifacts') != artifacts:
777.1.1 by Aaron Bentley
Move find_candidates, get under test.
367
            continue
368
        candidate_path = os.path.join(candidates_path, candidate_dir)
369
        buildvars = os.path.join(candidate_path, 'buildvars.json')
370
        try:
371
            stat = os.stat(buildvars)
372
        except OSError as e:
373
            if e.errno in (errno.ENOENT, errno.ENOTDIR):
374
                continue
375
            raise
1029.1.1 by seman.said at canonical
Added --all option to schedule all candidates for client-server testing.
376
        if not find_all and stat.st_mtime < a_week_ago:
777.1.1 by Aaron Bentley
Move find_candidates, get under test.
377
            continue
1071.1.1 by Aaron Bentley
Implement find_latest_branch_candidates.
378
        yield candidate_path, buildvars
889.6.1 by Aaron Bentley
Make utility for determining architecture.
379
380
381
def get_deb_arch():
382
    """Get the debian machine architecture."""
383
    return subprocess.check_output(['dpkg', '--print-architecture']).strip()
889.6.6 by Aaron Bentley
Extract extract_deb.
384
385
386
def extract_deb(package_path, directory):
387
    """Extract a debian package to a specified directory."""
388
    subprocess.check_call(['dpkg', '-x', package_path, directory])
1001.1.1 by John George
Add openstack_basic_check.py
389
390
391
def run_command(command, dry_run=False, verbose=False):
392
    """Optionally execute a command and maybe print the output."""
393
    if verbose:
394
        print_now('Executing: {}'.format(command))
395
    if not dry_run:
396
        output = subprocess.check_output(command)
397
        if verbose:
398
            print_now(output)
1426.1.7 by Leo Zhang
updates after review
399
400
1718.2.9 by Christopher Lee
Move get_ip_address to utility.py
401
def get_unit_ipaddress(client, unit_name):
402
    status = client.get_status()
403
    return status.get_unit(unit_name)['public-address']
1735.1.1 by Martin Packman
New utility.logged_exception factored out from deploy_stack
404
405
406
def log_and_wrap_exception(logger, exc):
1738.1.1 by Andrew Beach
Pollished the new logged_exception function just a bit.
407
    """Record exc details to logger and return wrapped in LoggedException."""
1735.1.1 by Martin Packman
New utility.logged_exception factored out from deploy_stack
408
    logger.exception(exc)
409
    stdout = getattr(exc, 'output', None)
410
    stderr = getattr(exc, 'stderr', None)
411
    if stdout or stderr:
1738.1.1 by Andrew Beach
Pollished the new logged_exception function just a bit.
412
        logger.info('Output from exception:\nstdout:\n%s\nstderr:\n%s',
413
                    stdout, stderr)
1735.1.1 by Martin Packman
New utility.logged_exception factored out from deploy_stack
414
    return LoggedException(exc)
415
416
417
@contextmanager
418
def logged_exception(logger):
1738.1.1 by Andrew Beach
Pollished the new logged_exception function just a bit.
419
    """\
1735.1.1 by Martin Packman
New utility.logged_exception factored out from deploy_stack
420
    Record exceptions in managed context to logger and reraise LoggedException.
421
1738.1.1 by Andrew Beach
Pollished the new logged_exception function just a bit.
422
    Note that BaseException classes like SystemExit, GeneratorExit and
423
    LoggedException itself are not wrapped, except for KeyboardInterrupt.
1735.1.1 by Martin Packman
New utility.logged_exception factored out from deploy_stack
424
    """
425
    try:
426
        yield
1735.1.2 by Martin Packman
Also wrap KeyboardInterrupt as SIGINT is used for timeout
427
    except (Exception, KeyboardInterrupt) as e:
1735.1.1 by Martin Packman
New utility.logged_exception factored out from deploy_stack
428
        raise log_and_wrap_exception(logger, e)