• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python
2# Copyright 2017 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""This tool manages the lxc container pool service."""
7
8import argparse
9import logging
10import os
11import signal
12import time
13from contextlib import contextmanager
14
15import common
16from autotest_lib.client.bin import utils
17from autotest_lib.client.common_lib import logging_config
18from autotest_lib.server import server_logging_config
19from autotest_lib.site_utils import lxc
20from autotest_lib.site_utils.lxc import container_pool
21
22try:
23    from chromite.lib import ts_mon_config
24except ImportError:
25    ts_mon_config = utils.metrics_mock
26
27
28# Location and base name of log files.
29_LOG_LOCATION = '/usr/local/autotest/logs'
30_LOG_NAME = 'lxc_pool.%d' % time.time()
31
32
33def _start(args):
34    """Starts up the container pool service.
35
36    This function instantiates and starts up the pool service on the current
37    thread (i.e. the function will block, and not return until the service is
38    shut down).
39    """
40    # TODO(dshi): crbug.com/459344 Set remove this enforcement when test
41    # container can be unprivileged container.
42    if utils.sudo_require_password():
43        logging.warning('SSP requires root privilege to run commands, please '
44                        'grant root access to this process.')
45        utils.run('sudo true')
46
47    # Configure logging.
48    config = server_logging_config.ServerLoggingConfig()
49    config.configure_logging(verbose=args.verbose)
50    config.add_debug_file_handlers(log_dir=_LOG_LOCATION, log_name=_LOG_NAME)
51    # Pool code is heavily multi-threaded.  This will help debugging.
52    logging_config.add_threadname_in_log()
53
54    host_dir = lxc.SharedHostDir()
55    service = container_pool.Service(host_dir)
56    # Catch signals, and send the appropriate stop request to the service
57    # instead of killing the main thread.
58    # - SIGINT is generated by Ctrl-C
59    # - SIGTERM is generated by an upstart stopping event.
60    for sig in (signal.SIGINT, signal.SIGTERM):
61        signal.signal(sig, lambda s, f: service.stop())
62
63    with ts_mon_config.SetupTsMonGlobalState(service_name='lxc_pool_service',
64                                             indirect=True,
65                                             short_lived=False):
66        # Start the service.  This blocks and does not return till the service
67        # shuts down.
68        service.start(pool_size=args.size)
69
70
71def _status(_args):
72    """Requests status from the running container pool.
73
74    The retrieved status is printed out via logging.
75    """
76    with _create_client() as client:
77        logging.debug('Requesting status...')
78        logging.info(client.get_status())
79
80
81def _stop(_args):
82    """Shuts down the running container pool."""
83    with _create_client() as client:
84        logging.debug('Requesting stop...')
85        logging.info(client.shutdown())
86
87
88@contextmanager
89# TODO(kenobi): Don't hard-code the timeout.
90def _create_client(timeout=3):
91    logging.debug('Creating client...')
92    address = os.path.join(lxc.SharedHostDir().path,
93                           lxc.DEFAULT_CONTAINER_POOL_SOCKET)
94    with container_pool.Client.connect(address, timeout) as connection:
95        yield connection
96
97
98def parse_args():
99    """Parse command line inputs.
100
101    @raise argparse.ArgumentError: If command line arguments are invalid.
102    """
103    parser = argparse.ArgumentParser()
104
105    parser.add_argument('-v', '--verbose',
106                        help='Enable verbose output.',
107                        action='store_true')
108
109    subparsers = parser.add_subparsers(title='Commands')
110
111    parser_start = subparsers.add_parser('start',
112                                         help='Start the LXC container pool.')
113    parser_start.set_defaults(func=_start)
114    parser_start.add_argument('--size',
115                              type=int,
116                              default=lxc.DEFAULT_CONTAINER_POOL_SIZE,
117                              help='Pool size (default=%d)' %
118                                      lxc.DEFAULT_CONTAINER_POOL_SIZE)
119
120    parser_stop = subparsers.add_parser('stop',
121                                        help='Stop the container pool.')
122    parser_stop.set_defaults(func=_stop)
123
124    parser_status = subparsers.add_parser('status',
125                                          help='Query pool status.')
126    parser_status.set_defaults(func=_status)
127
128    options = parser.parse_args()
129    return options
130
131
132def main():
133    """Main function."""
134    # Parse args
135    args = parse_args()
136
137    # Dispatch control to the appropriate helper.
138    args.func(args)
139
140
141if __name__ == '__main__':
142    main()
143