Exemplo n.º 1
0
def handle_exception(ex):  # pylint: disable=too-many-return-statements
    # For error code, follow guidelines at https://docs.python.org/2/library/sys.html#sys.exit,
    from jmespath.exceptions import JMESPathTypeError
    from msrestazure.azure_exceptions import CloudError
    from msrest.exceptions import HttpOperationError, ValidationError, ClientRequestError
    from azure.cli.core.azlogging import CommandLoggerContext
    from azure.common import AzureException
    from azure.core.exceptions import AzureError

    with CommandLoggerContext(logger):
        if isinstance(ex, JMESPathTypeError):
            logger.error("\nInvalid jmespath query supplied for `--query`:\n%s", ex)
            logger.error("To learn more about --query, please visit: "
                         "https://docs.microsoft.com/cli/azure/query-azure-cli?view=azure-cli-latest")
            return 1
        if isinstance(ex, (CLIError, CloudError, AzureException, AzureError)):
            logger.error(ex.args[0])
            try:
                for detail in ex.args[0].error.details:
                    logger.error(detail)
            except (AttributeError, TypeError):
                pass
            except:  # pylint: disable=bare-except
                pass
            return ex.args[1] if len(ex.args) >= 2 else 1
        if isinstance(ex, ValidationError):
            logger.error('validation error: %s', ex)
            return 1
        if isinstance(ex, ClientRequestError):
            msg = str(ex)
            if 'SSLError' in msg:
                logger.error("request failed: %s", SSLERROR_TEMPLATE.format(msg))
            else:
                logger.error("request failed: %s", ex)
            return 1
        if isinstance(ex, KeyboardInterrupt):
            return 1
        if isinstance(ex, HttpOperationError):
            try:
                response_dict = json.loads(ex.response.text)
                error = response_dict['error']

                # ARM should use ODATA v4. So should try this first.
                # http://docs.oasis-open.org/odata/odata-json-format/v4.0/os/odata-json-format-v4.0-os.html#_Toc372793091
                if isinstance(error, dict):
                    code = "{} - ".format(error.get('code', 'Unknown Code'))
                    message = error.get('message', ex)
                    logger.error("%s%s", code, message)
                else:
                    logger.error(error)

            except (ValueError, KeyError):
                logger.error(ex)
            return 1

        logger.error("The command failed with an unexpected error. Here is the traceback:\n")
        logger.exception(ex)
        logger.warning("\nTo open an issue, please run: 'az feedback'")

        return 1
Exemplo n.º 2
0
    def _check_value(self, action, value):
        # Override to customize the error message when a argument is not among the available choices
        # converted value must be one of the choices (if specified)
        if action.choices is not None and value not in action.choices:
            if not self.command_source:
                # parser has no `command_source`, value is part of command itself
                error_msg = "{prog}: '{value}' is not in the '{prog}' command group. See '{prog} --help'.".format(
                    prog=self.prog, value=value)
            else:
                # `command_source` indicates command values have been parsed, value is an argument
                parameter = action.option_strings[
                    0] if action.option_strings else action.dest
                error_msg = "{prog}: '{value}' is not a valid value for '{param}'. See '{prog} --help'.".format(
                    prog=self.prog, value=value, param=parameter)
            telemetry.set_user_fault(error_msg)
            with CommandLoggerContext(logger):
                logger.error(error_msg)
            candidates = difflib.get_close_matches(value,
                                                   action.choices,
                                                   cutoff=0.7)
            if candidates:
                print_args = {
                    's': 's' if len(candidates) > 1 else '',
                    'verb': 'are' if len(candidates) > 1 else 'is',
                    'value': value
                }
                suggestion_msg = "\nThe most similar choice{s} to '{value}' {verb}:\n".format(
                    **print_args)
                suggestion_msg += '\n'.join(
                    ['\t' + candidate for candidate in candidates])
                print(suggestion_msg, file=sys.stderr)

            self.exit(2)
Exemplo n.º 3
0
 def error(self, message):
     telemetry.set_user_fault('parse error: {}'.format(message))
     args = {'prog': self.prog, 'message': message}
     with CommandLoggerContext(logger):
         logger.error('%(prog)s: error: %(message)s', args)
     self.print_usage(sys.stderr)
     self.exit(2)
Exemplo n.º 4
0
    def _check_value(self, action, value):
        # Override to customize the error message when a argument is not among the available choices
        # converted value must be one of the choices (if specified)
        if action.choices is not None and value not in action.choices:
            if not self.command_source:
                # parser has no `command_source`, value is part of command itself
                extensions_link = 'https://docs.microsoft.com/en-us/cli/azure/azure-cli-extensions-overview'
                error_msg = ("{prog}: '{value}' is not in the '{prog}' command group. See '{prog} --help'. "
                             "If the command is from an extension, "
                             "please make sure the corresponding extension is installed. "
                             "To learn more about extensions, please visit "
                             "{extensions_link}").format(prog=self.prog, value=value, extensions_link=extensions_link)
            else:
                # `command_source` indicates command values have been parsed, value is an argument
                parameter = action.option_strings[0] if action.option_strings else action.dest
                error_msg = "{prog}: '{value}' is not a valid value for '{param}'. See '{prog} --help'.".format(
                    prog=self.prog, value=value, param=parameter)
            telemetry.set_user_fault(error_msg)
            with CommandLoggerContext(logger):
                logger.error(error_msg)
            candidates = difflib.get_close_matches(value, action.choices, cutoff=0.7)
            if candidates:
                print_args = {
                    's': 's' if len(candidates) > 1 else '',
                    'verb': 'are' if len(candidates) > 1 else 'is',
                    'value': value
                }
                suggestion_msg = "\nThe most similar choice{s} to '{value}' {verb}:\n".format(**print_args)
                suggestion_msg += '\n'.join(['\t' + candidate for candidate in candidates])
                print(suggestion_msg, file=sys.stderr)

            self.exit(2)
Exemplo n.º 5
0
def show_resource_not_found_message(group, account, ams_type, name):
    import sys
    message = build_resource_not_found_message(group, account, ams_type, name)
    from azure.cli.core.azlogging import CommandLoggerContext
    with CommandLoggerContext(logger):
        logger.error(message)
        sys.exit(3)
Exemplo n.º 6
0
def show_exception_handler(ex):
    if getattr(getattr(ex, 'response', ex), 'status_code', None) == 404:
        import sys
        from azure.cli.core.azlogging import CommandLoggerContext
        with CommandLoggerContext(logger):
            logger.error(getattr(ex, 'message', ex))
            sys.exit(3)
    raise ex
Exemplo n.º 7
0
 def print_error(self):
     from azure.cli.core.azlogging import CommandLoggerContext
     with CommandLoggerContext(logger):
         # print only error message (no error type)
         logger.error(self.error_msg)
         # print recommendations to action
         if self.recommendations:
             for recommendation in self.recommendations:
                 print(recommendation, file=sys.stderr)
Exemplo n.º 8
0
 def print_error(self):
     from azure.cli.core.azlogging import CommandLoggerContext
     with CommandLoggerContext(logger):
         message = '{}: {}'.format(self.error_type.value, self.error_msg)
         logger.error(message)
         if self.raw_exception:
             logger.exception(self.raw_exception)
         if self.recommendations:
             for recommendation in self.recommendations:
                 print(recommendation, file=sys.stderr)
Exemplo n.º 9
0
def show_exception_handler(ex):
    if getattr(getattr(ex, 'response', ex), 'status_code', None) == 404:
        import sys
        from azure.cli.core.azlogging import CommandLoggerContext
        from azure.cli.core.azclierror import ResourceNotFoundError
        with CommandLoggerContext(logger):
            az_error = ResourceNotFoundError(getattr(ex, 'message', ex))
            az_error.print_error()
            az_error.send_telemetry()
            sys.exit(3)
    raise ex
Exemplo n.º 10
0
 def error(self, message):
     telemetry.set_user_fault('parse error: {}'.format(message))
     args = {'prog': self.prog, 'message': message}
     with CommandLoggerContext(logger):
         logger.error('%(prog)s: error: %(message)s', args)
     self.print_usage(sys.stderr)
     failure_recovery_recommendations = self._get_failure_recovery_recommendations(
     )
     self._suggestion_msg.extend(failure_recovery_recommendations)
     self._print_suggestion_msg(sys.stderr)
     self.exit(2)
Exemplo n.º 11
0
 def print_error(self):
     from azure.cli.core.azlogging import CommandLoggerContext
     with CommandLoggerContext(logger):
         # print error type and error message
         message = '{}: {}'.format(self.__class__.__name__, self.error_msg)
         logger.error(message)
         # print exception trace if there is
         if self.exception_trace:
             logger.exception(self.exception_trace)
         # print recommendations to action
         if self.recommendations:
             for recommendation in self.recommendations:
                 print(recommendation, file=sys.stderr)
Exemplo n.º 12
0
    def _run_cmd(self, command, checks=None, expect_failure=False):
        cli_ctx = get_dummy_cli()
        cli_ctx.logging.command_log_dir = self.temp_command_log_dir

        result = execute(cli_ctx, command, expect_failure=expect_failure).assert_with_checks(checks)

        # manually handle error logging as azure.cli.core.util's handle_exception function is mocked out in testsdk / patches
        # this logged error tests that we can properly parse errors from command log file.
        with CommandLoggerContext(logger):
            if result.exit_code != 0:
                logger.error("There was an error during execution.")

        cli_ctx.logging.end_cmd_metadata_logging(result.exit_code)

        return result
Exemplo n.º 13
0
def handle_exception(ex):
    # For error code, follow guidelines at https://docs.python.org/2/library/sys.html#sys.exit,
    from msrestazure.azure_exceptions import CloudError
    from msrest.exceptions import HttpOperationError, ValidationError, ClientRequestError
    from azure.cli.core.azlogging import CommandLoggerContext

    with CommandLoggerContext(logger):

        if isinstance(ex, (CLIError, CloudError)):
            logger.error(ex.args[0])
            return ex.args[1] if len(ex.args) >= 2 else 1
        if isinstance(ex, ValidationError):
            logger.error('validation error: %s', ex)
            return 1
        if isinstance(ex, ClientRequestError):
            logger.error("request failed: %s", ex)
            return 1
        if isinstance(ex, KeyboardInterrupt):
            return 1
        if isinstance(ex, HttpOperationError):
            try:
                response_dict = json.loads(ex.response.text)
                error = response_dict['error']

                # ARM should use ODATA v4. So should try this first.
                # http://docs.oasis-open.org/odata/odata-json-format/v4.0/os/odata-json-format-v4.0-os.html#_Toc372793091
                if isinstance(error, dict):
                    code = "{} - ".format(error.get('code', 'Unknown Code'))
                    message = error.get('message', ex)
                    logger.error("%s%s", code, message)
                else:
                    logger.error(error)

            except (ValueError, KeyError):
                logger.error(ex)
            return 1

        logger.error(
            "The command failed with an unexpected error. Here is the traceback:\n"
        )
        logger.exception(ex)
        logger.warning("\nTo open an issue, please run: 'az feedback'")

        return 1
Exemplo n.º 14
0
    def print_error(self):
        from azure.cli.core.azlogging import CommandLoggerContext
        from azure.cli.core.style import print_styled_text
        with CommandLoggerContext(logger):
            # print error message
            logger.error(self.error_msg)

            # print exception trace if there is
            if self.exception_trace:
                logger.exception(self.exception_trace)

            # print recommendations to action
            if self.recommendations:
                for recommendation in self.recommendations:
                    print(recommendation, file=sys.stderr)

            if self.aladdin_recommendations:
                print('\nTRY THIS:', file=sys.stderr)
                for recommendation, description in self.aladdin_recommendations:
                    print_styled_text(recommendation, file=sys.stderr)
                    print_styled_text(description, file=sys.stderr)
Exemplo n.º 15
0
    def _run_cmd(self, command, checks=None, expect_failure=False):
        cli_ctx = get_dummy_cli()
        cli_ctx.logging.command_log_dir = self.temp_command_log_dir

        # hotfix here for untouch feedback's code to avoid introducing possible break change.
        # unregister the event for auto closing CLI's file logging after execute() is done
        cli_ctx.unregister_event(EVENT_CLI_POST_EXECUTE,
                                 cli_ctx.logging.deinit_cmd_metadata_logging)

        # manually handle error logging as azure.cli.core.util's handle_exception function is mocked out in testsdk / patches
        # this logged error tests that we can properly parse errors from command log file.
        with CommandLoggerContext(logger):
            result = execute(
                cli_ctx, command,
                expect_failure=expect_failure).assert_with_checks(checks)

            if result.exit_code != 0:
                logger.error("There was an error during execution.")

        # close it manually because we unregister the deinit_cmd_metadata_logging
        # callback from EVENT_CLI_POST_EXECUTE event
        cli_ctx.logging.end_cmd_metadata_logging(result.exit_code)

        return result
Exemplo n.º 16
0
def handle_exception(ex):  # pylint: disable=too-many-locals, too-many-statements, too-many-branches
    # For error code, follow guidelines at https://docs.python.org/2/library/sys.html#sys.exit,
    from jmespath.exceptions import JMESPathTypeError
    from msrestazure.azure_exceptions import CloudError
    from msrest.exceptions import HttpOperationError, ValidationError, ClientRequestError
    from azure.cli.core.azlogging import CommandLoggerContext
    from azure.common import AzureException
    from azure.core.exceptions import AzureError
    from requests.exceptions import SSLError, HTTPError
    import azure.cli.core.azclierror as azclierror
    import traceback

    logger.debug(
        "azure.cli.core.util.handle_exception is called with an exception:")
    # Print the traceback and exception message
    logger.debug(traceback.format_exc())

    with CommandLoggerContext(logger):
        error_msg = getattr(ex, 'message', str(ex))
        exit_code = 1

        if isinstance(ex, azclierror.AzCLIError):
            az_error = ex

        elif isinstance(ex, JMESPathTypeError):
            error_msg = "Invalid jmespath query supplied for `--query`: {}".format(
                error_msg)
            az_error = azclierror.InvalidArgumentValueError(error_msg)
            az_error.set_recommendation(QUERY_REFERENCE)

        elif isinstance(ex, SSLError):
            az_error = azclierror.AzureConnectionError(error_msg)
            az_error.set_recommendation(SSLERROR_TEMPLATE)

        elif isinstance(ex, CloudError):
            if extract_common_error_message(ex):
                error_msg = extract_common_error_message(ex)
            status_code = str(getattr(ex, 'status_code', 'Unknown Code'))
            AzCLIErrorType = get_error_type_by_status_code(status_code)
            az_error = AzCLIErrorType(error_msg)

        elif isinstance(ex, ValidationError):
            az_error = azclierror.ValidationError(error_msg)

        elif isinstance(ex, CLIError):
            # TODO: Fine-grained analysis here for Unknown error
            az_error = azclierror.UnknownError(error_msg)

        elif isinstance(ex, AzureError):
            if extract_common_error_message(ex):
                error_msg = extract_common_error_message(ex)
            AzCLIErrorType = get_error_type_by_azure_error(ex)
            az_error = AzCLIErrorType(error_msg)

        elif isinstance(ex, AzureException):
            if is_azure_connection_error(error_msg):
                az_error = azclierror.AzureConnectionError(error_msg)
            else:
                # TODO: Fine-grained analysis here for Unknown error
                az_error = azclierror.UnknownError(error_msg)

        elif isinstance(ex, ClientRequestError):
            if is_azure_connection_error(error_msg):
                az_error = azclierror.AzureConnectionError(error_msg)
            else:
                az_error = azclierror.ClientRequestError(error_msg)

        elif isinstance(ex, HttpOperationError):
            message, status_code = extract_http_operation_error(ex)
            if message:
                error_msg = message
            AzCLIErrorType = get_error_type_by_status_code(status_code)
            az_error = AzCLIErrorType(error_msg)

        elif isinstance(ex, HTTPError):
            status_code = str(
                getattr(ex.response, 'status_code', 'Unknown Code'))
            AzCLIErrorType = get_error_type_by_status_code(status_code)
            az_error = AzCLIErrorType(error_msg)

        elif isinstance(ex, KeyboardInterrupt):
            error_msg = 'Keyboard interrupt is captured.'
            az_error = azclierror.ManualInterrupt(error_msg)

        else:
            error_msg = "The command failed with an unexpected error. Here is the traceback:"
            az_error = azclierror.CLIInternalError(error_msg)
            az_error.set_exception_trace(ex)
            az_error.set_recommendation(
                "To open an issue, please run: 'az feedback'")

        if isinstance(az_error, azclierror.ResourceNotFoundError):
            exit_code = 3

        az_error.print_error()
        az_error.send_telemetry()

        return exit_code
Exemplo n.º 17
0
    def _check_value(self, action, value):  # pylint: disable=too-many-statements, too-many-locals
        # Override to customize the error message when a argument is not among the available choices
        # converted value must be one of the choices (if specified)
        if action.choices is not None and value not in action.choices:  # pylint: disable=too-many-nested-blocks
            caused_by_extension_not_installed = False
            if not self.command_source:
                candidates = difflib.get_close_matches(value,
                                                       action.choices,
                                                       cutoff=0.7)
                error_msg = None
                # self.cli_ctx is None when self.prog is beyond 'az', such as 'az iot'.
                # use cli_ctx from cli_help which is not lost.
                cli_ctx = self.cli_ctx or (self.cli_help.cli_ctx
                                           if self.cli_help else None)
                use_dynamic_install = self._get_extension_use_dynamic_install_config(
                )
                if use_dynamic_install != 'no' and not candidates:
                    # Check if the command is from an extension
                    from azure.cli.core.util import roughly_parse_command
                    cmd_list = self.prog.split() + self._raw_arguments
                    command_str = roughly_parse_command(cmd_list[1:])
                    ext_name = self._search_in_extension_commands(command_str)
                    if ext_name:
                        caused_by_extension_not_installed = True
                        telemetry.set_command_details(
                            command_str,
                            parameters=AzCliCommandInvoker.
                            _extract_parameter_names(cmd_list),  # pylint: disable=protected-access
                            extension_name=ext_name)
                        run_after_extension_installed = cli_ctx.config.getboolean(
                            'extension', 'run_after_dynamic_install',
                            False) if cli_ctx else False
                        if use_dynamic_install == 'yes_without_prompt':
                            logger.warning(
                                'The command requires the extension %s. '
                                'It will be installed first.', ext_name)
                            go_on = True
                        else:
                            from knack.prompting import prompt_y_n, NoTTYException
                            prompt_msg = 'The command requires the extension {}. ' \
                                'Do you want to install it now?'.format(ext_name)
                            if run_after_extension_installed:
                                prompt_msg = '{} The command will continue to run after the extension is installed.' \
                                    .format(prompt_msg)
                            NO_PROMPT_CONFIG_MSG = "Run 'az config set extension.use_dynamic_install=" \
                                "yes_without_prompt' to allow installing extensions without prompt."
                            try:
                                go_on = prompt_y_n(prompt_msg, default='y')
                                if go_on:
                                    logger.warning(NO_PROMPT_CONFIG_MSG)
                            except NoTTYException:
                                logger.warning(
                                    "The command requires the extension %s.\n "
                                    "Unable to prompt for extension install confirmation as no tty "
                                    "available. %s", ext_name,
                                    NO_PROMPT_CONFIG_MSG)
                                go_on = False
                        if go_on:
                            from azure.cli.core.extension.operations import add_extension
                            add_extension(cli_ctx=cli_ctx,
                                          extension_name=ext_name)
                            if run_after_extension_installed:
                                import subprocess
                                import platform
                                exit_code = subprocess.call(
                                    cmd_list,
                                    shell=platform.system() == 'Windows')
                                telemetry.set_user_fault(
                                    "Extension {} dynamically installed and commands will be "
                                    "rerun automatically.".format(ext_name))
                                self.exit(exit_code)
                            else:
                                error_msg = 'Extension {} installed. Please rerun your command.'.format(
                                    ext_name)
                        else:
                            error_msg = "The command requires the extension {ext_name}. " \
                                "To install, run 'az extension add -n {ext_name}'.".format(ext_name=ext_name)
                if not error_msg:
                    # parser has no `command_source`, value is part of command itself
                    error_msg = "{prog}: '{value}' is not in the '{prog}' command group. See '{prog} --help'." \
                        .format(prog=self.prog, value=value)
                    if use_dynamic_install.lower() == 'no':
                        extensions_link = 'https://docs.microsoft.com/en-us/cli/azure/azure-cli-extensions-overview'
                        error_msg = (
                            "{msg} "
                            "If the command is from an extension, "
                            "please make sure the corresponding extension is installed. "
                            "To learn more about extensions, please visit "
                            "{extensions_link}").format(
                                msg=error_msg, extensions_link=extensions_link)
            else:
                # `command_source` indicates command values have been parsed, value is an argument
                parameter = action.option_strings[
                    0] if action.option_strings else action.dest
                error_msg = "{prog}: '{value}' is not a valid value for '{param}'. See '{prog} --help'.".format(
                    prog=self.prog, value=value, param=parameter)
                candidates = difflib.get_close_matches(value,
                                                       action.choices,
                                                       cutoff=0.7)

            telemetry.set_user_fault(error_msg)
            with CommandLoggerContext(logger):
                logger.error(error_msg)
            if not caused_by_extension_not_installed:
                if candidates:
                    print_args = {
                        's': 's' if len(candidates) > 1 else '',
                        'verb': 'are' if len(candidates) > 1 else 'is',
                        'value': value
                    }
                    self._suggestion_msg.append(
                        "\nThe most similar choice{s} to '{value}' {verb}:".
                        format(**print_args))
                    self._suggestion_msg.append('\n'.join(
                        ['\t' + candidate for candidate in candidates]))

                failure_recovery_recommendations = self._get_failure_recovery_recommendations(
                    action)
                self._suggestion_msg.extend(failure_recovery_recommendations)
                self._print_suggestion_msg(sys.stderr)
            self.exit(2)
Exemplo n.º 18
0
    def _check_value(self, action, value):  # pylint: disable=too-many-statements, too-many-locals
        # Override to customize the error message when a argument is not among the available choices
        # converted value must be one of the choices (if specified)
        if action.choices is not None and value not in action.choices:  # pylint: disable=too-many-nested-blocks
            # self.cli_ctx is None when self.prog is beyond 'az', such as 'az iot'.
            # use cli_ctx from cli_help which is not lost.
            cli_ctx = self.cli_ctx or (self.cli_help.cli_ctx if self.cli_help else None)

            caused_by_extension_not_installed = False
            command_name_inferred = self.prog
            error_msg = None
            if not self.command_source:
                candidates = difflib.get_close_matches(value, action.choices, cutoff=0.7)
                if candidates:
                    # use the most likely candidate to replace the misspelled command
                    args = self.prog.split() + self._raw_arguments
                    args_inferred = [item if item != value else candidates[0] for item in args]
                    command_name_inferred = ' '.join(args_inferred).split('-')[0]

                use_dynamic_install = self._get_extension_use_dynamic_install_config()
                if use_dynamic_install != 'no' and not candidates:
                    # Check if the command is from an extension
                    from azure.cli.core.util import roughly_parse_command
                    cmd_list = self.prog.split() + self._raw_arguments
                    command_str = roughly_parse_command(cmd_list[1:])
                    ext_name = self._search_in_extension_commands(command_str)
                    if ext_name:
                        caused_by_extension_not_installed = True
                        telemetry.set_command_details(command_str,
                                                      parameters=AzCliCommandInvoker._extract_parameter_names(cmd_list),  # pylint: disable=protected-access
                                                      extension_name=ext_name)
                        run_after_extension_installed = self._get_extension_run_after_dynamic_install_config()
                        if use_dynamic_install == 'yes_without_prompt':
                            logger.warning('The command requires the extension %s. '
                                           'It will be installed first.', ext_name)
                            go_on = True
                        else:
                            from knack.prompting import prompt_y_n, NoTTYException
                            prompt_msg = 'The command requires the extension {}. ' \
                                'Do you want to install it now?'.format(ext_name)
                            if run_after_extension_installed:
                                prompt_msg = '{} The command will continue to run after the extension is installed.' \
                                    .format(prompt_msg)
                            NO_PROMPT_CONFIG_MSG = "Run 'az config set extension.use_dynamic_install=" \
                                "yes_without_prompt' to allow installing extensions without prompt."
                            try:
                                go_on = prompt_y_n(prompt_msg, default='y')
                                if go_on:
                                    logger.warning(NO_PROMPT_CONFIG_MSG)
                            except NoTTYException:
                                logger.warning("The command requires the extension %s.\n "
                                               "Unable to prompt for extension install confirmation as no tty "
                                               "available. %s", ext_name, NO_PROMPT_CONFIG_MSG)
                                go_on = False
                        if go_on:
                            from azure.cli.core.extension.operations import add_extension
                            add_extension(cli_ctx=cli_ctx, extension_name=ext_name, upgrade=True)
                            if run_after_extension_installed:
                                import subprocess
                                import platform
                                exit_code = subprocess.call(cmd_list, shell=platform.system() == 'Windows')
                                error_msg = ("Extension {} dynamically installed and commands will be "
                                             "rerun automatically.").format(ext_name)
                                telemetry.set_user_fault(error_msg)
                                self.exit(exit_code)
                            else:
                                with CommandLoggerContext(logger):
                                    error_msg = 'Extension {} installed. Please rerun your command.'.format(ext_name)
                                    logger.error(error_msg)
                                    telemetry.set_user_fault(error_msg)
                                self.exit(2)
                        else:
                            error_msg = "The command requires the latest version of extension {ext_name}. " \
                                "To install, run 'az extension add --upgrade -n {ext_name}'.".format(ext_name=ext_name)
                if not error_msg:
                    # parser has no `command_source`, value is part of command itself
                    error_msg = "'{value}' is misspelled or not recognized by the system.".format(value=value)
                az_error = CommandNotFoundError(error_msg)

            else:
                # `command_source` indicates command values have been parsed, value is an argument
                parameter = action.option_strings[0] if action.option_strings else action.dest
                error_msg = "{prog}: '{value}' is not a valid value for '{param}'.".format(
                    prog=self.prog, value=value, param=parameter)
                candidates = difflib.get_close_matches(value, action.choices, cutoff=0.7)
                az_error = InvalidArgumentValueError(error_msg)

            command_arguments = self._get_failure_recovery_arguments(action)
            if candidates:
                az_error.set_recommendation("Did you mean '{}' ?".format(candidates[0]))

            # recommend a command for user
            recommender = CommandRecommender(*command_arguments, error_msg, cli_ctx)
            recommender.set_help_examples(self.get_examples(command_name_inferred))
            recommended_command = recommender.recommend_a_command()
            if recommended_command:
                az_error.set_recommendation("Try this: '{}'".format(recommended_command))

            # remind user to check extensions if we can not find a command to recommend
            if isinstance(az_error, CommandNotFoundError) \
                    and not az_error.recommendations and self.prog == 'az' \
                    and use_dynamic_install == 'no':
                az_error.set_recommendation(EXTENSION_REFERENCE)

            az_error.set_recommendation(OVERVIEW_REFERENCE.format(command=self.prog))

            if not caused_by_extension_not_installed:
                az_error.print_error()
                az_error.send_telemetry()

            self.exit(2)
Exemplo n.º 19
0
 def _handle_exception_with_log(ex, *args, **kwargs):
     with CommandLoggerContext(logger):
         logger.error(ex)
     original_handle_exception(*args, **kwargs)
Exemplo n.º 20
0
def handle_exception(ex):  # pylint: disable=too-many-return-statements, too-many-statements
    # For error code, follow guidelines at https://docs.python.org/2/library/sys.html#sys.exit,
    from jmespath.exceptions import JMESPathTypeError
    from msrestazure.azure_exceptions import CloudError
    from msrest.exceptions import HttpOperationError, ValidationError, ClientRequestError
    from azure.cli.core.azlogging import CommandLoggerContext
    from azure.common import AzureException
    from azure.core.exceptions import AzureError
    from requests.exceptions import SSLError
    import traceback

    logger.debug(
        "azure.cli.core.util.handle_exception is called with an exception:")
    # Print the traceback and exception message
    logger.debug(traceback.format_exc())

    with CommandLoggerContext(logger):
        error_msg = getattr(ex, 'message', str(ex))
        exit_code = 1

        if isinstance(ex, AzCLIError):
            az_error = ex

        elif isinstance(ex, JMESPathTypeError):
            error_msg = "Invalid jmespath query supplied for `--query`: {}".format(
                error_msg)
            az_error = AzCLIError(AzCLIErrorType.ArgumentParseError, error_msg)
            az_error.set_recommendation(QUERY_REFERENCE)

        elif isinstance(ex, ValidationError):
            az_error = AzCLIError(AzCLIErrorType.ValidationError, error_msg)

        # TODO: Fine-grained analysis to decide whether they are ValidationErrors
        elif isinstance(ex, (CLIError, CloudError, AzureError)):
            try:
                error_msg = ex.args[0]
                for detail in ex.args[0].error.details:
                    error_msg += ('\n' + detail)
            except Exception:  # pylint: disable=broad-except
                pass
            az_error = AzCLIError(AzCLIErrorType.ValidationError, error_msg)
            exit_code = ex.args[1] if len(ex.args) >= 2 else 1

        # TODO: Fine-grained analysis
        elif isinstance(ex, AzureException):
            az_error = AzCLIError(AzCLIErrorType.ServiceError, error_msg)
            exit_code = ex.args[1] if len(ex.args) >= 2 else 1

        # TODO: Fine-grained analysis
        elif isinstance(ex, (ClientRequestError, SSLError)):
            az_error = AzCLIError(AzCLIErrorType.ClientError, error_msg)
            if 'SSLError' in error_msg:
                az_error.set_recommendation(SSLERROR_TEMPLATE)

        # TODO: Fine-grained analysis
        elif isinstance(ex, HttpOperationError):
            try:
                response = json.loads(ex.response.text)
                if isinstance(response, str):
                    error = response
                else:
                    error = response['error']

                # ARM should use ODATA v4. So should try this first.
                # http://docs.oasis-open.org/odata/odata-json-format/v4.0/os/odata-json-format-v4.0-os.html#_Toc372793091
                if isinstance(error, dict):
                    code = "{} - ".format(error.get('code', 'Unknown Code'))
                    message = error.get('message', ex)
                    error_msg = "code: {}, {}".format(code, message)
                else:
                    error_msg = error

            except (ValueError, KeyError):
                pass

            az_error = AzCLIError(AzCLIErrorType.ServiceError, error_msg)

        elif isinstance(ex, KeyboardInterrupt):
            error_msg = 'Keyboard interrupt is captured.'
            az_error = AzCLIError(AzCLIErrorType.ManualInterrupt, error_msg)

        else:
            error_msg = "The command failed with an unexpected error. Here is the traceback:"
            az_error = AzCLIError(AzCLIErrorType.UnexpectedError, error_msg)
            az_error.set_raw_exception(ex)
            az_error.set_recommendation(
                "To open an issue, please run: 'az feedback'")

        az_error.print_error()
        az_error.send_telemetry()

        return exit_code