#!/usr/bin/python # Copyright 2017 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """This tool manages the lxc container pool service.""" import argparse import logging import os import signal import time from contextlib import contextmanager import common from autotest_lib.client.bin import utils from autotest_lib.client.common_lib import logging_config from autotest_lib.server import server_logging_config from autotest_lib.site_utils import lxc from autotest_lib.site_utils.lxc import container_pool try: from chromite.lib import ts_mon_config except ImportError: ts_mon_config = utils.metrics_mock # Location and base name of log files. _LOG_LOCATION = '/usr/local/autotest/logs' _LOG_NAME = 'lxc_pool.%d' % time.time() def _start(args): """Starts up the container pool service. This function instantiates and starts up the pool service on the current thread (i.e. the function will block, and not return until the service is shut down). """ # TODO(dshi): crbug.com/459344 Set remove this enforcement when test # container can be unprivileged container. if utils.sudo_require_password(): logging.warning('SSP requires root privilege to run commands, please ' 'grant root access to this process.') utils.run('sudo true') # Configure logging. config = server_logging_config.ServerLoggingConfig() config.configure_logging(verbose=args.verbose) config.add_debug_file_handlers(log_dir=_LOG_LOCATION, log_name=_LOG_NAME) # Pool code is heavily multi-threaded. This will help debugging. logging_config.add_threadname_in_log() host_dir = lxc.SharedHostDir() service = container_pool.Service(host_dir) # Catch signals, and send the appropriate stop request to the service # instead of killing the main thread. # - SIGINT is generated by Ctrl-C # - SIGTERM is generated by an upstart stopping event. for sig in (signal.SIGINT, signal.SIGTERM): signal.signal(sig, lambda s, f: service.stop()) with ts_mon_config.SetupTsMonGlobalState(service_name='lxc_pool_service', indirect=True, short_lived=False): # Start the service. This blocks and does not return till the service # shuts down. service.start(pool_size=args.size) def _status(_args): """Requests status from the running container pool. The retrieved status is printed out via logging. """ with _create_client() as client: logging.debug('Requesting status...') logging.info(client.get_status()) def _stop(_args): """Shuts down the running container pool.""" with _create_client() as client: logging.debug('Requesting stop...') logging.info(client.shutdown()) @contextmanager # TODO(kenobi): Don't hard-code the timeout. def _create_client(timeout=3): logging.debug('Creating client...') address = os.path.join(lxc.SharedHostDir().path, lxc.DEFAULT_CONTAINER_POOL_SOCKET) with container_pool.Client.connect(address, timeout) as connection: yield connection def parse_args(): """Parse command line inputs. @raise argparse.ArgumentError: If command line arguments are invalid. """ parser = argparse.ArgumentParser() parser.add_argument('-v', '--verbose', help='Enable verbose output.', action='store_true') subparsers = parser.add_subparsers(title='Commands') parser_start = subparsers.add_parser('start', help='Start the LXC container pool.') parser_start.set_defaults(func=_start) parser_start.add_argument('--size', type=int, default=lxc.DEFAULT_CONTAINER_POOL_SIZE, help='Pool size (default=%d)' % lxc.DEFAULT_CONTAINER_POOL_SIZE) parser_stop = subparsers.add_parser('stop', help='Stop the container pool.') parser_stop.set_defaults(func=_stop) parser_status = subparsers.add_parser('status', help='Query pool status.') parser_status.set_defaults(func=_status) options = parser.parse_args() return options def main(): """Main function.""" # Parse args args = parse_args() # Dispatch control to the appropriate helper. args.func(args) if __name__ == '__main__': main()