#!/usr/bin/env python3 import logging import sys import importlib import argparse import os from etcd3.exceptions import ConnectionFailedError from uncloud.common import settings from uncloud import UncloudException from uncloud.common.cli import resolve_otp_credentials def exception_hook(exc_type, exc_value, exc_traceback): logging.getLogger(__name__).error( 'Uncaught exception', exc_info=(exc_type, exc_value, exc_traceback) ) sys.excepthook = exception_hook # the components that use etcd ETCD_COMPONENTS = ['api', 'scheduler', 'host', 'filescanner', 'imagescanner', 'metadata', 'configure', 'hack'] ALL_COMPONENTS = ETCD_COMPONENTS.copy() ALL_COMPONENTS.append('cli') if __name__ == '__main__': # Setting up root logger logger = logging.getLogger() logger.setLevel(logging.DEBUG) arg_parser = argparse.ArgumentParser() subparsers = arg_parser.add_subparsers(dest='command') parent_parser = argparse.ArgumentParser(add_help=False) parent_parser.add_argument('--debug', '-d', action='store_true', default=False, help='More verbose logging') parent_parser.add_argument('--conf-dir', '-c', help='Configuration directory', default=os.path.expanduser('~/uncloud')) etcd_parser = argparse.ArgumentParser(add_help=False) etcd_parser.add_argument('--etcd-host', dest='etcd_url') etcd_parser.add_argument('--etcd-port') etcd_parser.add_argument('--etcd-ca-cert', help='CA that signed the etcd certificate') etcd_parser.add_argument('--etcd-cert-cert', help='Path to client certificate') etcd_parser.add_argument('--etcd-cert-key', help='Path to client certificate key') for component in ALL_COMPONENTS: mod = importlib.import_module('uncloud.{}.main'.format(component)) parser = getattr(mod, 'arg_parser') if component in ETCD_COMPONENTS: subparsers.add_parser(name=parser.prog, parents=[parser, parent_parser, etcd_parser]) else: subparsers.add_parser(name=parser.prog, parents=[parser, parent_parser]) arguments = vars(arg_parser.parse_args()) etcd_arguments = [key for key, value in arguments.items() if key.startswith('etcd_') and value] etcd_arguments = { 'etcd': { key.replace('etcd_', ''): arguments[key] for key in etcd_arguments } } if not arguments['command']: arg_parser.print_help() else: # Initializing Settings and resolving otp_credentials # It is neccessary to resolve_otp_credentials after argument parsing is done because # previously we were reading config file which was fixed to ~/uncloud/uncloud.conf and # providing the default values for --name, --realm and --seed arguments from the values # we read from file. But, now we are asking user about where the config file lives. So, # to providing default value is not possible before parsing arguments. So, we are doing # it after.. settings.settings = settings.Settings(arguments['conf_dir'], seed_value=etcd_arguments) resolve_otp_credentials(arguments) name = arguments.pop('command') mod = importlib.import_module('uncloud.{}.main'.format(name)) main = getattr(mod, 'main') try: main(arguments) except UncloudException as err: logger.error(err) except ConnectionFailedError: logger.error('Cannot connect to etcd') except Exception as err: logger.exception(err)