Example #1
0
class CLIDriver(object):
    def __init__(self):
        self._loader = Loader()
        self._endpoint_creator = EndpointCreator(EndpointResolver())
        self._user_agent_header = self._build_user_agent_header()
        self._response_parser_factory = ResponseParserFactory()
        self._cli_data = self._loader.load_json('cli.json')
        self._retryhandler = self._create_default_retryhandler()
        self._available_services = self._loader.list_available_services()
        self._command_table = self._build_command_table()
        self._argument_table = self._build_argument_table()
        self._client_creator = ClientCreator(self._loader, Context(),
                                             self._endpoint_creator,
                                             self._user_agent_header,
                                             self._response_parser_factory,
                                             self._retryhandler)

    def main(self, args=None):
        if args is None:
            args = sys.argv[1:]
        parser = self._create_parser()
        command_table = self._get_command_table()
        if len(args) == 0 or (len(args) == 1 and args[0] == '--help'):
            args = ['help']
        parsed_args, remaining = parser.parse_known_args(args)
        try:
            self._handle_top_level_args(parsed_args)
            self._warn_for_old_python()
            return command_table[parsed_args.command](self._client_creator,
                                                      remaining, parsed_args)
        except Exception as e:
            LOG.debug("Exception caught in main()", exc_info=True)
            sys.stderr.write("\n")
            sys.stderr.write("%s\n" % six.text_type(e))
            return 255

    def _get_loader(self):
        return self._loader

    def _get_cli_data(self):
        return self._cli_data

    def _get_command_table(self):
        return self._command_table

    def _build_user_agent_header(self):
        return 'CDPCLI/%s Python/%s %s/%s' % (VERSION, platform.python_version(
        ), platform.system(), platform.release())

    def _build_command_table(self):
        commands = OrderedDict()
        services = self._get_available_services()
        for service_name in services:
            commands[service_name] = ServiceCommand(self, service_name)
        ConfigureCommand.add_command(commands)
        return commands

    def _get_argument_table(self):
        return self._argument_table

    def _build_argument_table(self):
        argument_table = OrderedDict()
        cli_data = self._get_cli_data()
        cli_arguments = cli_data.get('options', None)
        for option in cli_arguments:
            option_params = copy_kwargs(cli_arguments[option])
            cli_argument = self._create_cli_argument(option, option_params)
            cli_argument.add_to_arg_table(argument_table)
        return argument_table

    def _get_available_services(self):
        return self._available_services

    def get_service_model(self, service_name):
        service_data = self._loader.load_service_data(service_name)
        return ServiceModel(service_data, service_name=service_name)

    def _create_help_command(self):
        cli_data = self._get_cli_data()

        # We filter service aliases out of the service list at the bottom of the
        # top level help.
        commands = OrderedDict()
        for service_name, command in self._get_command_table().items():
            if not self._loader.is_service_alias(service_name):
                commands[service_name] = command

        return ProviderHelpCommand(commands, self._get_argument_table(),
                                   cli_data.get('description', None),
                                   cli_data.get('synopsis', None),
                                   cli_data.get('help_usage', None))

    def _create_parser(self):
        command_table = self._get_command_table()
        command_table['help'] = self._create_help_command()
        cli_data = self._get_cli_data()
        parser = MainArgParser(command_table, VERSION,
                               cli_data.get('description', None),
                               self._get_argument_table())
        return parser

    def _create_cli_argument(self, option_name, option_params):
        return CustomArgument(option_name,
                              help_text=option_params.get('help', ''),
                              dest=option_params.get('dest'),
                              default=option_params.get('default'),
                              action=option_params.get('action'),
                              required=option_params.get('required'),
                              choices=option_params.get('choices'),
                              hidden=option_params.get('hidden', False))

    def _handle_top_level_args(self, args):
        if args.profile:
            self._client_creator.context.set_config_variable(
                'profile', args.profile)
        if args.auth_config:
            self._client_creator.context.set_config_variable(
                'auth_config', args.auth_config)
        if args.debug:
            self._setup_logger(logging.DEBUG)
            LOG.debug("CLI version: %s", self._user_agent_header)
            LOG.debug("Arguments entered to CLI: %s", sys.argv[1:])
        else:
            self._setup_logger(logging.WARNING)

    def _setup_logger(self, log_level):
        ROOT_LOGGER.setLevel(logging.DEBUG)
        ch = logging.StreamHandler()
        ch.setLevel(log_level)
        formatter = logging.Formatter(LOG_FORMAT)
        ch.setFormatter(formatter)
        ROOT_LOGGER.addHandler(ch)

    def _create_default_retryhandler(self):
        # We create one retryhandler based on the __default__ configuration in
        # the _retry.json (in the 'data' directory). This retryhandler is used
        # by all services.
        config = self._load_retry_config()
        if not config:
            return
        LOG.info("Using retry config: %s" % config)
        return create_retry_handler(config)

    def _load_retry_config(self):
        original_config = self._loader.load_json('_retry.json')
        retry_config = build_retry_config(
            original_config['retry'], original_config.get('definitions', {}))
        return retry_config

    def _warn_for_old_python(self):
        if sys.version_info[0] < 3 or \
                (sys.version_info[0] == 3 and sys.version_info[1] < 5):
            LOG.warn(
                'You are running the CDP CLI under Python %s. The CDP CLI '
                'will require Python 3.5 or higher starting in '
                'January 2021. '
                'Please upgrade now!', sys.version)
Example #2
0
class CLIDriver(object):
    def __init__(self):
        self._loader = Loader()
        self._endpoint_creator = EndpointCreator(EndpointResolver())
        self._user_agent_header = self._build_user_agent_header()
        self._response_parser_factory = ResponseParserFactory()
        self._cli_data = self._loader.load_json('cli.json')
        self._retryhandler = self._create_default_retryhandler()
        self._available_services = self._loader.list_available_services()
        self._command_table = self._build_command_table()
        self._argument_table = self._build_argument_table()
        self._context = Context()
        self._client_creator = ClientCreator(self._loader, self._context,
                                             self._endpoint_creator,
                                             self._user_agent_header,
                                             self._response_parser_factory,
                                             self._retryhandler)

    def main(self, args=None):
        if args is None:
            args = sys.argv[1:]
        parser = self._create_parser()
        command_table = self._get_command_table()
        if len(args) == 0 or (len(args) == 1 and args[0] == '--help'):
            args = ['help']
        parsed_args, remaining = parser.parse_known_args(args)
        try:
            self._handle_top_level_args(parsed_args)
            self._filter_command_table_for_form_factor(parsed_args)
            self._warn_for_old_python()
            self._warn_for_non_public_release()
            return command_table[parsed_args.command](self._client_creator,
                                                      remaining, parsed_args)
        except Exception as e:
            LOG.debug("Exception caught in main()", exc_info=True)
            sys.stderr.write("\n")
            sys.stderr.write("%s\n" % six.text_type(e))
            return 255

    def _get_loader(self):
        return self._loader

    def _get_cli_data(self):
        return self._cli_data

    def _get_command_table(self):
        return self._command_table

    def _build_user_agent_header(self):
        return 'CDPCLI/%s Python/%s %s/%s' % (VERSION, platform.python_version(
        ), platform.system(), platform.release())

    def _build_command_table(self):
        commands = OrderedDict()
        services = self._get_available_services()
        for service_name in services:
            commands[service_name] = ServiceCommand(self, service_name)
        ConfigureCommand.add_command(commands)
        LoginCommand.add_command(commands)
        LogoutCommand.add_command(commands)
        RefdocCommand.add_command(commands)
        return commands

    def _filter_command_table_for_form_factor(self, parsed_args):
        """
        Replaces services and operations in the command table that do not apply
        to the current form factor with stubs that error out when called.
        """

        # Find the form factor based on:
        # 1. the form factor explicitly specified by --form-factor, or else
        # 2. the configured form factor, or else
        # 3. the explicit endpoint URL, or else
        # 4. the configured CDP endpoint URL.
        if parsed_args.form_factor:
            form_factor = parsed_args.form_factor
        else:
            try:
                form_factor = self._context.get_scoped_config().get(
                    'form_factor', None)
            except ProfileNotFound:
                form_factor = None
            valid_form_factors = [dt.value for dt in list(DeploymentType)]
            if form_factor and form_factor not in valid_form_factors:
                raise InvalidConfiguredFormFactor(
                    form_factor=form_factor,
                    valid_form_factors=valid_form_factors)
            if not form_factor:
                endpoint_url = parsed_args.endpoint_url
                if not endpoint_url:
                    try:
                        endpoint_url = self._context.get_scoped_config(). \
                            get(EndpointResolver.CDP_ENDPOINT_URL_KEY_NAME, None)
                    except ProfileNotFound:
                        endpoint_url = None
                form_factor = \
                    ClassifyDeployment(endpoint_url).get_deployment_type().value
        LOG.debug("Current form factor is {}".format(form_factor))

        for command in list(self._command_table.keys()):
            try:
                # If the service does not apply to the current form factor,
                # filter it out.
                service_model = self._command_table[command].service_model
                service_form_factors = service_model.form_factors
                if form_factor not in service_form_factors:
                    self._command_table[command] = \
                        FilteredServiceCommand(self, command, form_factor,
                                               service_form_factors)
                else:
                    for operation_name in service_model.operation_names:
                        # If the operation does not apply to the current form
                        # factor, filter it out.
                        operation_model = service_model.operation_model(
                            operation_name)
                        operation_form_factors = operation_model.form_factors
                        if not operation_form_factors:
                            operation_form_factors = service_form_factors
                        if form_factor not in operation_form_factors:
                            self._command_table[command]. \
                                filter_operation(operation_name, form_factor,
                                                 operation_form_factors)

            except AttributeError:
                # not a service model, so available in all form factors
                pass

    def _get_argument_table(self):
        return self._argument_table

    def _build_argument_table(self):
        argument_table = OrderedDict()
        cli_data = self._get_cli_data()
        cli_arguments = cli_data.get('options', None)
        for option in cli_arguments:
            option_params = copy_kwargs(cli_arguments[option])
            cli_argument = self._create_cli_argument(option, option_params)
            cli_argument.add_to_arg_table(argument_table)
        return argument_table

    def _get_available_services(self):
        return self._available_services

    def get_service_model(self, service_name):
        service_data = self._loader.load_service_data(service_name)
        return ServiceModel(service_data, service_name=service_name)

    def _create_help_command(self):
        cli_data = self._get_cli_data()

        # We filter service aliases out of the service list at the bottom of the
        # top level help.
        commands = OrderedDict()
        for service_name, command in self._get_command_table().items():
            if not self._loader.is_service_alias(service_name):
                commands[service_name] = command

        return ProviderHelpCommand(commands, self._get_argument_table(),
                                   cli_data.get('description', None),
                                   cli_data.get('synopsis', None),
                                   cli_data.get('help_usage', None))

    def _create_parser(self):
        command_table = self._get_command_table()
        command_table['help'] = self._create_help_command()
        cli_data = self._get_cli_data()
        parser = MainArgParser(command_table, VERSION,
                               cli_data.get('description', None),
                               self._get_argument_table())
        return parser

    def _create_cli_argument(self, option_name, option_params):
        return CustomArgument(option_name,
                              help_text=option_params.get('help', ''),
                              dest=option_params.get('dest'),
                              default=option_params.get('default'),
                              action=option_params.get('action'),
                              required=option_params.get('required'),
                              choices=option_params.get('choices'),
                              cli_type_name=option_params.get('type'),
                              hidden=option_params.get('hidden', False))

    def _handle_top_level_args(self, args):
        if args.profile:
            self._client_creator.context.set_config_variable(
                'profile', args.profile)
        if args.auth_config:
            self._client_creator.context.set_config_variable(
                'auth_config', args.auth_config)
        if args.debug:
            self._setup_logger(logging.DEBUG)
            LOG.debug("CLI version: %s", self._user_agent_header)
            LOG.debug("Arguments entered to CLI: %s", sys.argv[1:])
        else:
            self._setup_logger(logging.WARNING)

        if args.force_ipv4:
            # Based on SO /a/46972341
            LOG.debug("Forcing IPv4 connections only")

            def _allowed_gai_family():
                return socket.AF_INET

            urllib3_connection.allowed_gai_family = _allowed_gai_family

    def _setup_logger(self, log_level):
        ROOT_LOGGER.setLevel(logging.DEBUG)
        ch = logging.StreamHandler()
        ch.setLevel(log_level)
        formatter = logging.Formatter(LOG_FORMAT)
        ch.setFormatter(formatter)
        ROOT_LOGGER.addHandler(ch)

    def _create_default_retryhandler(self):
        # We create one retryhandler based on the __default__ configuration in
        # the _retry.json (in the 'data' directory). This retryhandler is used
        # by all services.
        config = self._load_retry_config()
        if not config:
            return
        LOG.info("Using retry config: %s" % config)
        return create_retry_handler(config)

    def _load_retry_config(self):
        original_config = self._loader.load_json('_retry.json')
        retry_config = build_retry_config(
            original_config['retry'], original_config.get('definitions', {}))
        return retry_config

    def _warn_for_old_python(self):
        if sys.version_info[0] < 3 or \
                (sys.version_info[0] == 3 and sys.version_info[1] < 6):
            LOG.warn(
                'You are running the CDP CLI under Python %s. The CDP CLI '
                'now requires Python 3.6 or higher. Please upgrade now to '
                'avoid CLI errors.', sys.version)

    def _warn_for_non_public_release(self):
        if RELEASE != 'PUBLIC':
            if RELEASE == 'INTERNAL':
                article = 'an'
            else:
                article = 'a'
            LOG.warn('You are running {0} {1} release of the CDP CLI, which '
                     'has different capabilities from the standard public '
                     'release. Find the public release at: '
                     'https://pypi.org/project/cdpcli/'.format(
                         article, RELEASE))