#!/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()