Example #1
0
 def format_help(self):
     telemetry.set_command_details(
         command=self.prog[3:],
         extension_name=self.command_source.extension_name
         if self.command_source else None)
     telemetry.set_success(summary='show help')
     super(AzCliCommandParser, self).format_help()
Example #2
0
    def format_help(self):
        is_group = self.is_group()

        telemetry.set_command_details(command=self.prog[3:])
        telemetry.set_success(summary='show help')

        _help.show_help(self.prog.split()[1:],
                        self._actions[-1] if is_group else self, is_group)
        self.exit()
Example #3
0
    def format_help(self):
        is_group = self.is_group()

        telemetry.set_command_details(command=self.prog[3:])
        telemetry.set_success(summary='show help')

        _help.show_help(self.prog.split()[1:],
                        self._actions[-1] if is_group else self,
                        is_group)
        self.exit()
Example #4
0
    def format_help(self):
        is_group = self.is_group()

        telemetry.set_command_details(
            command=self.prog[3:],
            extension_name=self.command_source.extension_name if self.command_source else None)
        telemetry.set_success(summary='show help')

        _help.show_help(self.prog.split()[1:],
                        self._actions[-1] if is_group else self,
                        is_group)
        self.exit()
Example #5
0
    def format_help(self):
        extension_version = None
        try:
            if self.command_source:
                extension_version = get_extension(self.command_source.extension_name).version
        except Exception:  # pylint: disable=broad-except
            pass

        telemetry.set_command_details(
            command=self.prog[3:],
            extension_name=self.command_source.extension_name if self.command_source else None,
            extension_version=extension_version)
        telemetry.set_success(summary='show help')
        super(AzCliCommandParser, self).format_help()
Example #6
0
    def format_help(self):
        extension_version = None
        try:
            if self.command_source:
                extension_version = get_extension(self.command_source.extension_name).version
        except Exception:  # pylint: disable=broad-except
            pass

        telemetry.set_command_details(
            command=self.prog[3:],
            extension_name=self.command_source.extension_name if self.command_source else None,
            extension_version=extension_version)
        telemetry.set_success(summary='show help')
        super(AzCliCommandParser, self).format_help()
Example #7
0
    def execute(self, args):
        from knack.events import (
            EVENT_INVOKER_PRE_CMD_TBL_CREATE,
            EVENT_INVOKER_POST_CMD_TBL_CREATE, EVENT_INVOKER_CMD_TBL_LOADED,
            EVENT_INVOKER_PRE_PARSE_ARGS, EVENT_INVOKER_POST_PARSE_ARGS,
            EVENT_INVOKER_TRANSFORM_RESULT, EVENT_INVOKER_FILTER_RESULT)
        from knack.util import CommandResultItem, todict
        from azure.cli.core.commands.events import EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE

        # TODO: Can't simply be invoked as an event because args are transformed
        args = _pre_command_table_create(self.cli_ctx, args)

        self.cli_ctx.raise_event(EVENT_INVOKER_PRE_CMD_TBL_CREATE, args=args)
        self.commands_loader.load_command_table(args)
        self.cli_ctx.raise_event(
            EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE,
            load_cmd_tbl_func=self.commands_loader.load_command_table,
            args=args)
        command = self._rudimentary_get_command(args)
        telemetry.set_raw_command_name(command)

        try:
            self.commands_loader.command_table = {
                command: self.commands_loader.command_table[command]
            }
        except KeyError:
            # Trim down the command table to reduce the number of subparsers required to optimize the performance.
            #
            # When given a command table like this:
            #
            # network application-gateway create
            # network application-gateway delete
            # network list-usages
            # storage account create
            # storage account list
            #
            # input:  az
            # output: network application-gateway create
            #         storage account create
            #
            # input:  az network
            # output: network application-gateway create
            #         network list-usages

            cmd_table = {}
            group_names = set()
            for cmd_name, cmd in self.commands_loader.command_table.items():
                if command and not cmd_name.startswith(command):
                    continue

                cmd_stub = cmd_name[len(command):].strip()
                group_name = cmd_stub.split(' ', 1)[0]
                if group_name not in group_names:
                    cmd_table[cmd_name] = cmd
                    group_names.add(group_name)
                self.commands_loader.command_table = cmd_table

        self.commands_loader.command_table = self.commands_loader.command_table  # update with the truncated table
        self.commands_loader.command_name = command
        self.commands_loader.load_arguments(command)
        self.cli_ctx.raise_event(EVENT_INVOKER_POST_CMD_TBL_CREATE,
                                 cmd_tbl=self.commands_loader.command_table)
        self.parser.cli_ctx = self.cli_ctx
        self.parser.load_command_table(self.commands_loader.command_table)

        self.cli_ctx.raise_event(EVENT_INVOKER_CMD_TBL_LOADED,
                                 cmd_tbl=self.commands_loader.command_table,
                                 parser=self.parser)

        if not args:
            self.cli_ctx.completion.enable_autocomplete(self.parser)
            subparser = self.parser.subparsers[tuple()]
            self.help.show_welcome(subparser)

            # TODO: No event in base with which to target
            telemetry.set_command_details('az')
            telemetry.set_success(summary='welcome')
            return None

        if args[0].lower() == 'help':
            args[0] = '--help'

        self.cli_ctx.completion.enable_autocomplete(self.parser)

        self.cli_ctx.raise_event(EVENT_INVOKER_PRE_PARSE_ARGS, args=args)
        parsed_args = self.parser.parse_args(args)
        self.cli_ctx.raise_event(EVENT_INVOKER_POST_PARSE_ARGS,
                                 command=parsed_args.command,
                                 args=parsed_args)

        # TODO: This fundamentally alters the way Knack.invocation works here. Cannot be customized
        # with an event. Would need to be customized via inheritance.
        results = []
        for expanded_arg in _explode_list_args(parsed_args):
            cmd = expanded_arg.func
            if hasattr(expanded_arg, 'cmd'):
                expanded_arg.cmd = cmd

            self.cli_ctx.data['command'] = expanded_arg.command

            self._validation(expanded_arg)

            params = self._filter_params(expanded_arg)

            command_source = self.commands_loader.command_table[
                command].command_source

            extension_version = None
            try:
                if command_source:
                    extension_version = get_extension(
                        command_source.extension_name).version
            except Exception:  # pylint: disable=broad-except
                pass

            telemetry.set_command_details(
                self.cli_ctx.data['command'],
                self.data['output'],
                [(p.split('=', 1)[0] if p.startswith('--') else p[:2])
                 for p in args if (p.startswith('-') and len(p) > 1)],
                extension_name=command_source.extension_name
                if command_source else None,
                extension_version=extension_version)
            if command_source:
                self.data[
                    'command_extension_name'] = command_source.extension_name

            try:
                result = cmd(params)
                if cmd.supports_no_wait and getattr(expanded_arg, 'no_wait',
                                                    False):
                    result = None
                elif cmd.no_wait_param and getattr(expanded_arg,
                                                   cmd.no_wait_param, False):
                    result = None

                # TODO: Not sure how to make this actually work with the TRANSFORM event...
                transform_op = cmd.command_kwargs.get('transform', None)
                if transform_op:
                    result = transform_op(result)

                if _is_poller(result):
                    result = LongRunningOperation(
                        self.cli_ctx, 'Starting {}'.format(cmd.name))(result)
                elif _is_paged(result):
                    result = list(result)

                result = todict(result)
                event_data = {'result': result}
                self.cli_ctx.raise_event(EVENT_INVOKER_TRANSFORM_RESULT,
                                         event_data=event_data)
                self.cli_ctx.raise_event(EVENT_INVOKER_FILTER_RESULT,
                                         event_data=event_data)
                result = event_data['result']
                results.append(result)

            except Exception as ex:  # pylint: disable=broad-except
                if cmd.exception_handler:
                    cmd.exception_handler(ex)
                    return None
                else:
                    six.reraise(*sys.exc_info())

        if results and len(results) == 1:
            results = results[0]

        return CommandResultItem(
            results,
            table_transformer=self.commands_loader.command_table[
                parsed_args.command].table_transformer,
            is_query_active=self.data['query_active'])
Example #8
0
    def execute(self, unexpanded_argv):  # pylint: disable=too-many-statements
        argv = Application._expand_file_prefixed_files(unexpanded_argv)
        command_table = self.configuration.get_command_table(argv)
        self.raise_event(self.COMMAND_TABLE_LOADED, command_table=command_table)
        self.parser.load_command_table(command_table)
        self.raise_event(self.COMMAND_PARSER_LOADED, parser=self.parser)

        if not argv:
            enable_autocomplete(self.parser)
            az_subparser = self.parser.subparsers[tuple()]
            _help.show_welcome(az_subparser)

            # TODO: Question, is this needed?
            telemetry.set_command_details('az')
            telemetry.set_success(summary='welcome')

            return None

        if argv[0].lower() == 'help':
            argv[0] = '--help'

        # Rudimentary parsing to get the command
        nouns = []
        for i, current in enumerate(argv):
            try:
                if current[0] == '-':
                    break
            except IndexError:
                pass
            argv[i] = current.lower()
            nouns.append(argv[i])

        command = ' '.join(nouns)

        if argv[-1] in ('--help', '-h') or command in command_table:
            self.configuration.load_params(command)
            self.raise_event(self.COMMAND_TABLE_PARAMS_LOADED, command_table=command_table)
            self.parser.load_command_table(command_table)

        if self.session['completer_active']:
            enable_autocomplete(self.parser)

        self.raise_event(self.COMMAND_PARSER_PARSING, argv=argv)
        args = self.parser.parse_args(argv)

        self.raise_event(self.COMMAND_PARSER_PARSED, command=args.command, args=args)
        results = []
        for expanded_arg in _explode_list_args(args):
            self.session['command'] = expanded_arg.command
            try:
                _validate_arguments(expanded_arg)
            except CLIError:
                raise
            except:  # pylint: disable=bare-except
                err = sys.exc_info()[1]
                getattr(expanded_arg, '_parser', self.parser).validation_error(str(err))

            # Consider - we are using any args that start with an underscore (_) as 'private'
            # arguments and remove them from the arguments that we pass to the actual function.
            # This does not feel quite right.
            params = dict([(key, value)
                           for key, value in expanded_arg.__dict__.items()
                           if not key.startswith('_')])
            params.pop('subcommand', None)
            params.pop('func', None)
            params.pop('command', None)

            telemetry.set_command_details(expanded_arg.command,
                                          self.configuration.output_format,
                                          [p for p in unexpanded_argv if p.startswith('-')])

            result = expanded_arg.func(params)
            result = todict(result)
            results.append(result)

        if len(results) == 1:
            results = results[0]

        event_data = {'result': results}
        self.raise_event(self.TRANSFORM_RESULT, event_data=event_data)
        self.raise_event(self.FILTER_RESULT, event_data=event_data)

        return CommandResultItem(event_data['result'],
                                 table_transformer=command_table[args.command].table_transformer,
                                 is_query_active=self.session['query_active'])
Example #9
0
    def execute(self, args):
        from knack.events import (EVENT_INVOKER_PRE_CMD_TBL_CREATE, EVENT_INVOKER_POST_CMD_TBL_CREATE,
                                  EVENT_INVOKER_CMD_TBL_LOADED, EVENT_INVOKER_PRE_PARSE_ARGS,
                                  EVENT_INVOKER_POST_PARSE_ARGS, EVENT_INVOKER_TRANSFORM_RESULT,
                                  EVENT_INVOKER_FILTER_RESULT)
        from knack.util import CommandResultItem, todict
        from azure.cli.core.commands.events import EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE

        # TODO: Can't simply be invoked as an event because args are transformed
        args = _pre_command_table_create(self.cli_ctx, args)

        self.cli_ctx.raise_event(EVENT_INVOKER_PRE_CMD_TBL_CREATE, args=args)
        self.commands_loader.load_command_table(args)
        self.cli_ctx.raise_event(EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE,
                                 load_cmd_tbl_func=self.commands_loader.load_command_table, args=args)
        command = self._rudimentary_get_command(args)
        telemetry.set_raw_command_name(command)

        try:
            self.commands_loader.command_table = {command: self.commands_loader.command_table[command]}
        except KeyError:
            # Trim down the command table to reduce the number of subparsers required to optimize the performance.
            #
            # When given a command table like this:
            #
            # network application-gateway create
            # network application-gateway delete
            # network list-usages
            # storage account create
            # storage account list
            #
            # input:  az
            # output: network application-gateway create
            #         storage account create
            #
            # input:  az network
            # output: network application-gateway create
            #         network list-usages

            cmd_table = {}
            group_names = set()
            for cmd_name, cmd in self.commands_loader.command_table.items():
                if command and not cmd_name.startswith(command):
                    continue

                cmd_stub = cmd_name[len(command):].strip()
                group_name = cmd_stub.split(' ', 1)[0]
                if group_name not in group_names:
                    cmd_table[cmd_name] = cmd
                    group_names.add(group_name)
                self.commands_loader.command_table = cmd_table

        self.commands_loader.command_table = self.commands_loader.command_table  # update with the truncated table
        self.commands_loader.command_name = command
        self.commands_loader.load_arguments(command)
        self.cli_ctx.raise_event(EVENT_INVOKER_POST_CMD_TBL_CREATE, commands_loader=self.commands_loader)
        self.parser.cli_ctx = self.cli_ctx
        self.parser.load_command_table(self.commands_loader)

        self.cli_ctx.raise_event(EVENT_INVOKER_CMD_TBL_LOADED, cmd_tbl=self.commands_loader.command_table,
                                 parser=self.parser)

        if not args:
            self.parser.enable_autocomplete()
            subparser = self.parser.subparsers[tuple()]
            self.help.show_welcome(subparser)

            # TODO: No event in base with which to target
            telemetry.set_command_details('az')
            telemetry.set_success(summary='welcome')
            return None

        if args[0].lower() == 'help':
            args[0] = '--help'

        self.parser.enable_autocomplete()

        self.cli_ctx.raise_event(EVENT_INVOKER_PRE_PARSE_ARGS, args=args)
        parsed_args = self.parser.parse_args(args)
        self.cli_ctx.raise_event(EVENT_INVOKER_POST_PARSE_ARGS, command=parsed_args.command, args=parsed_args)

        # TODO: This fundamentally alters the way Knack.invocation works here. Cannot be customized
        # with an event. Would need to be customized via inheritance.
        results = []
        for expanded_arg in _explode_list_args(parsed_args):
            cmd = expanded_arg.func
            if hasattr(expanded_arg, 'cmd'):
                expanded_arg.cmd = cmd

            self.cli_ctx.data['command'] = expanded_arg.command

            self._validation(expanded_arg)

            params = self._filter_params(expanded_arg)

            command_source = self.commands_loader.command_table[command].command_source

            extension_version = None
            extension_name = None
            try:
                if isinstance(command_source, ExtensionCommandSource):
                    extension_name = command_source.extension_name
                    extension_version = get_extension(command_source.extension_name).version
            except Exception:  # pylint: disable=broad-except
                pass

            telemetry.set_command_details(self.cli_ctx.data['command'], self.data['output'],
                                          [(p.split('=', 1)[0] if p.startswith('--') else p[:2]) for p in args if
                                           (p.startswith('-') and len(p) > 1)],
                                          extension_name=extension_name, extension_version=extension_version)
            if extension_name:
                self.data['command_extension_name'] = extension_name

            deprecations = [] + getattr(expanded_arg, '_argument_deprecations', [])
            if cmd.deprecate_info:
                deprecations.append(cmd.deprecate_info)

            # search for implicit deprecation
            path_comps = cmd.name.split()[:-1]
            implicit_deprecate_info = None
            while path_comps and not implicit_deprecate_info:
                implicit_deprecate_info = resolve_deprecate_info(self.cli_ctx, ' '.join(path_comps))
                del path_comps[-1]

            if implicit_deprecate_info:
                deprecate_kwargs = implicit_deprecate_info.__dict__.copy()
                deprecate_kwargs['object_type'] = 'command'
                del deprecate_kwargs['_get_tag']
                del deprecate_kwargs['_get_message']
                deprecations.append(ImplicitDeprecated(**deprecate_kwargs))

            for d in deprecations:
                logger.warning(d.message)

            try:
                result = cmd(params)
                if cmd.supports_no_wait and getattr(expanded_arg, 'no_wait', False):
                    result = None
                elif cmd.no_wait_param and getattr(expanded_arg, cmd.no_wait_param, False):
                    result = None

                transform_op = cmd.command_kwargs.get('transform', None)
                if transform_op:
                    result = transform_op(result)

                if _is_poller(result):
                    result = LongRunningOperation(self.cli_ctx, 'Starting {}'.format(cmd.name))(result)
                elif _is_paged(result):
                    result = list(result)

                result = todict(result, AzCliCommandInvoker.remove_additional_prop_layer)
                event_data = {'result': result}
                self.cli_ctx.raise_event(EVENT_INVOKER_TRANSFORM_RESULT, event_data=event_data)
                result = event_data['result']
                results.append(result)

            except Exception as ex:  # pylint: disable=broad-except
                if cmd.exception_handler:
                    cmd.exception_handler(ex)
                    return None
                else:
                    six.reraise(*sys.exc_info())

        if results and len(results) == 1:
            results = results[0]

        event_data = {'result': results}
        self.cli_ctx.raise_event(EVENT_INVOKER_FILTER_RESULT, event_data=event_data)

        return CommandResultItem(
            event_data['result'],
            table_transformer=self.commands_loader.command_table[parsed_args.command].table_transformer,
            is_query_active=self.data['query_active'])
Example #10
0
def upgrade_version(cmd, update_all=None, yes=None):  # pylint: disable=too-many-locals, too-many-statements, too-many-branches, no-member, unused-argument
    import os
    import platform
    import sys
    import subprocess
    import azure.cli.core.telemetry as telemetry
    from azure.cli.core import __version__ as local_version
    from azure.cli.core._environment import _ENV_AZ_INSTALLER
    from azure.cli.core.extension import get_extensions, WheelExtension
    from distutils.version import LooseVersion
    from knack.util import CLIError

    update_cli = True
    from azure.cli.core.util import get_latest_from_github
    try:
        latest_version = get_latest_from_github()
        if latest_version and LooseVersion(latest_version) <= LooseVersion(
                local_version):
            logger.warning("You already have the latest azure-cli version: %s",
                           local_version)
            update_cli = False
            if not update_all:
                return
    except Exception as ex:  # pylint: disable=broad-except
        logger.debug("Failed to get the latest version. %s", str(ex))
    exts = [ext.name for ext in get_extensions(
        ext_type=WheelExtension)] if update_all else []

    exit_code = 0
    installer = os.getenv(_ENV_AZ_INSTALLER) or ''
    installer = installer.upper()
    if update_cli:
        latest_version_msg = 'It will be updated to {}.'.format(latest_version) if yes \
            else 'Latest version available is {}.'.format(latest_version)
        logger.warning("Your current Azure CLI version is %s. %s",
                       local_version, latest_version_msg)
        from knack.prompting import prompt_y_n
        if not yes:
            confirmation = prompt_y_n(
                "Please check the release notes first: https://docs.microsoft.com/"
                "cli/azure/release-notes-azure-cli\nDo you want to continue?",
                default='y')
            if not confirmation:
                telemetry.set_success("Upgrade stopped by user")
                return

        if installer == 'DEB':
            from azure.cli.core.util import in_cloud_console
            if in_cloud_console():
                raise CLIError("az upgrade is not supported in Cloud Shell.")
            apt_update_cmd = 'apt-get update'.split()
            az_update_cmd = 'apt-get install --only-upgrade -y azure-cli'.split(
            )
            if os.geteuid() != 0:  # pylint: disable=no-member
                apt_update_cmd.insert(0, 'sudo')
                az_update_cmd.insert(0, 'sudo')
            exit_code = subprocess.call(apt_update_cmd)
            if exit_code == 0:
                logger.debug("Update azure cli with '%s'",
                             " ".join(apt_update_cmd))
                exit_code = subprocess.call(az_update_cmd)
        elif installer == 'RPM':
            from azure.cli.core.util import get_linux_distro
            distname, _ = get_linux_distro()
            if not distname:
                logger.warning(UPGRADE_MSG)
            else:
                distname = distname.lower().strip()
                if any(x in distname
                       for x in ['centos', 'rhel', 'red hat', 'fedora']):
                    update_cmd = 'yum update -y azure-cli'.split()
                    if os.geteuid() != 0:  # pylint: disable=no-member
                        update_cmd.insert(0, 'sudo')
                    logger.debug("Update azure cli with '%s'",
                                 " ".join(update_cmd))
                    exit_code = subprocess.call(update_cmd)
                elif any(x in distname for x in ['opensuse', 'suse', 'sles']):
                    zypper_refresh_cmd = ['zypper', 'refresh']
                    az_update_cmd = 'zypper update -y azure-cli'.split()
                    if os.geteuid() != 0:  # pylint: disable=no-member
                        zypper_refresh_cmd.insert(0, 'sudo')
                        az_update_cmd.insert(0, 'sudo')
                    exit_code = subprocess.call(zypper_refresh_cmd)
                    if exit_code == 0:
                        logger.debug("Update azure cli with '%s'",
                                     " ".join(az_update_cmd))
                        exit_code = subprocess.call(az_update_cmd)
                else:
                    logger.warning(UPGRADE_MSG)
        elif installer == 'HOMEBREW':
            logger.warning("Update homebrew formulae")
            exit_code = subprocess.call(['brew', 'update'])
            if exit_code == 0:
                update_cmd = ['brew', 'upgrade', 'azure-cli']
                logger.debug("Update azure cli with '%s'",
                             " ".join(update_cmd))
                exit_code = subprocess.call(update_cmd)
        elif installer == 'PIP':
            pip_args = [
                sys.executable, '-m', 'pip', 'install', '--upgrade',
                'azure-cli', '-vv', '--disable-pip-version-check',
                '--no-cache-dir'
            ]
            logger.debug("Update azure cli with '%s'", " ".join(pip_args))
            exit_code = subprocess.call(pip_args,
                                        shell=platform.system() == 'Windows')
        elif installer == 'DOCKER':
            logger.warning(
                "Exit the container to pull latest image with 'docker pull mcr.microsoft.com/azure-cli' "
                "or run 'pip install --upgrade azure-cli' in this container")
        elif installer == 'MSI':
            logger.debug(
                "Update azure cli with MSI from https://aka.ms/installazurecliwindows"
            )
            exit_code = subprocess.call(['powershell.exe', '-NoProfile', "Start-Process msiexec.exe -Wait -ArgumentList '/i https://aka.ms/installazurecliwindows'"])  # pylint: disable=line-too-long
        else:
            logger.warning(UPGRADE_MSG)
    if exit_code:
        telemetry.set_failure("CLI upgrade failed.")
        sys.exit(exit_code)
    # Updating Azure CLI and extension together is not supported in homebrewe package.
    if installer == 'HOMEBREW' and exts:
        logger.warning("Please rerun 'az upgrade' to update all extensions.")
    else:
        for ext_name in exts:
            try:
                logger.warning("Checking update for %s", ext_name)
                subprocess.call(['az', 'extension', 'update', '-n', ext_name],
                                shell=platform.system() == 'Windows')
            except Exception as ex:  # pylint: disable=broad-except
                msg = "Extension {} update failed during az upgrade. {}".format(
                    ext_name, str(ex))
                raise CLIError(msg)

    logger.warning("Upgrade finished.")
Example #11
0
    def run(self):
        """ runs the CLI """
        telemetry.start()
        self.cli.buffers['symbols'].reset(
            initial_document=Document(u'%s' %shell_help)
        )
        while True:
            try:
                document = self.cli.run(reset_current_buffer=True)
                text = document.text
                cmd = text
                outside = False
                if text.split() and text.split()[0] == 'az':
                    cmd = ' '.join(text.split()[1:])
                if self.default_command:
                    cmd = self.default_command + " " + cmd
                # if self.default_params:
                #     for param in self.default_params:
                #         cmd += ' ' + param

            except AttributeError:  # when the user pressed Control Q
                break
            else:
                if text.strip() == "quit" or text.strip() == "exit":
                    break
                elif text.strip() == "clear":  # clears the history, but only when you restart
                    outside = True
                    cmd = 'echo -n "" >' +\
                        os.path.join(
                            SHELL_CONFIGURATION.get_config_dir(),
                            SHELL_CONFIGURATION.get_history())
                elif text.strip() == "help":
                    print(help_doc.dump(shell_help))
                if text:
                    if text[0] == SELECT_SYMBOL['outside']:
                        cmd = text[1:]
                        outside = True
                    # elif text.split()[0] == "az":  # dumps the extra az
                    #     cmd = " ".join(text.split()[1:])
                    elif text[0] == SELECT_SYMBOL['exit_code']:
                        print(self.last_exit)
                        self.set_prompt()
                        continue
                    elif SELECT_SYMBOL['query'] in text:  # query previous output
                        if self.last and self.last.result:
                            if hasattr(self.last.result, '__dict__'):
                                input_dict = dict(self.last.result)
                            else:
                                input_dict = self.last.result
                            try:
                                result = jmespath.search(
                                    text.partition(SELECT_SYMBOL['query'])[2], input_dict)
                                if isinstance(result, str):
                                    print(result)
                                else:
                                    print(json.dumps(result, sort_keys=True, indent=2))
                            except jmespath.exceptions.ParseError:
                                print("Invalid Query")

                        self.set_prompt()
                        continue
                    elif "|" in text or ">" in text:  # anything I don't parse, send off
                        outside = True
                        cmd = "az " + cmd
                    elif SELECT_SYMBOL['example'] in text:
                        global NOTIFICATIONS
                        cmd = self.handle_example(text)
                if SELECT_SYMBOL['default'] in text:
                    default = text.partition(SELECT_SYMBOL['default'])[2].split()
                    if default[0].startswith('-'):
                        value = self.handle_default_param(default)
                    else:
                        value = self.handle_default_command(default)
                    print("defaulting: " + value)
                    self.set_prompt()
                    continue
                if SELECT_SYMBOL['undefault'] in text:
                    value = text.partition(SELECT_SYMBOL['undefault'])[2].split()
                    if len(value) == 0:
                        self.default_command = ""
                        set_default_command("", add=False)
                        # self.default_params = []
                        print('undefaulting all')
                    elif len(value) == 1 and value[0] == self.default_command:
                        self.default_command = ""
                        set_default_command("", add=False)
                        print('undefaulting: ' + value[0])
                    # elif len(value) == 2 and ' '.join(value[:2]) in self.default_params:
                    #     self.default_params.remove(' '.join(value[:2]))
                    #     print('undefaulting: ' + ' '.join(value[:2]))

                    self.set_prompt()
                    continue

                if not text: # not input
                    self.set_prompt()
                    continue

                self.history.append(cmd)
                self.set_prompt()
                if outside:
                    subprocess.Popen(cmd, shell=True).communicate()
                else:
                    try:
                        args = [str(command) for command in cmd.split()]
                        azlogging.configure_logging(args)

                        azure_folder = get_config_dir()
                        if not os.path.exists(azure_folder):
                            os.makedirs(azure_folder)
                        ACCOUNT.load(os.path.join(azure_folder, 'azureProfile.json'))
                        CONFIG.load(os.path.join(azure_folder, 'az.json'))
                        SESSION.load(os.path.join(azure_folder, 'az.sess'), max_age=3600)

                        config = Configuration(args)
                        self.app.initialize(config)

                        result = self.app.execute(args)
                        if result and result.result is not None:
                            from azure.cli.core._output import OutputProducer, format_json
                            if self.output:
                                self.output.out(result)
                            else:
                                formatter = OutputProducer.get_formatter(
                                    self.app.configuration.output_format)
                                OutputProducer(formatter=formatter, file=self.input).out(result)
                                self.last = result
                                self.last_exit = 0
                    except Exception as ex:  # pylint: disable=broad-except
                        self.last_exit = handle_exception(ex)
                    except SystemExit as ex:
                        self.last_exit = ex.code
                    if self.last_exit != 0:
                        telemetry.set_failure()
                    else:
                        telemetry.set_success()

        print('Have a lovely day!!')
        telemetry.conclude()
Example #12
0
    def execute(self, args):
        from knack.events import (EVENT_INVOKER_PRE_CMD_TBL_CREATE,
                                  EVENT_INVOKER_POST_CMD_TBL_CREATE,
                                  EVENT_INVOKER_CMD_TBL_LOADED,
                                  EVENT_INVOKER_PRE_PARSE_ARGS,
                                  EVENT_INVOKER_POST_PARSE_ARGS,
                                  EVENT_INVOKER_FILTER_RESULT)
        from azure.cli.core.commands.events import EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE

        # TODO: Can't simply be invoked as an event because args are transformed
        args = _pre_command_table_create(self.cli_ctx, args)

        self.cli_ctx.raise_event(EVENT_INVOKER_PRE_CMD_TBL_CREATE, args=args)
        self.commands_loader.load_command_table(args)
        self.cli_ctx.raise_event(
            EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE,
            load_cmd_tbl_func=self.commands_loader.load_command_table,
            args=args)
        command = self._rudimentary_get_command(args)
        self.cli_ctx.invocation.data['command_string'] = command
        telemetry.set_raw_command_name(command)

        try:
            self.commands_loader.command_table = {
                command: self.commands_loader.command_table[command]
            }
        except KeyError:
            # Trim down the command table to reduce the number of subparsers required to optimize the performance.
            #
            # When given a command table like this:
            #
            # network application-gateway create
            # network application-gateway delete
            # network list-usages
            # storage account create
            # storage account list
            #
            # input:  az
            # output: network application-gateway create
            #         storage account create
            #
            # input:  az network
            # output: network application-gateway create
            #         network list-usages

            cmd_table = {}
            group_names = set()
            for cmd_name, cmd in self.commands_loader.command_table.items():
                if command and not cmd_name.startswith(command):
                    continue

                cmd_stub = cmd_name[len(command):].strip()
                group_name = cmd_stub.split(' ', 1)[0]
                if group_name not in group_names:
                    cmd_table[cmd_name] = cmd
                    group_names.add(group_name)
                self.commands_loader.command_table = cmd_table

        self.commands_loader.command_table = self.commands_loader.command_table  # update with the truncated table
        self.commands_loader.command_name = command
        self.commands_loader.load_arguments(command)
        self.cli_ctx.raise_event(EVENT_INVOKER_POST_CMD_TBL_CREATE,
                                 commands_loader=self.commands_loader)
        self.parser.cli_ctx = self.cli_ctx
        self.parser.load_command_table(self.commands_loader)

        self.cli_ctx.raise_event(EVENT_INVOKER_CMD_TBL_LOADED,
                                 cmd_tbl=self.commands_loader.command_table,
                                 parser=self.parser)

        arg_check = [a for a in args if a not in ['--debug', '--verbose']]
        if not arg_check:
            self.parser.enable_autocomplete()
            subparser = self.parser.subparsers[tuple()]
            self.help.show_welcome(subparser)

            # TODO: No event in base with which to target
            telemetry.set_command_details('az')
            telemetry.set_success(summary='welcome')
            return CommandResultItem(None, exit_code=0)

        if args[0].lower() == 'help':
            args[0] = '--help'

        self.parser.enable_autocomplete()

        self.cli_ctx.raise_event(EVENT_INVOKER_PRE_PARSE_ARGS, args=args)
        parsed_args = self.parser.parse_args(args)
        self.cli_ctx.raise_event(EVENT_INVOKER_POST_PARSE_ARGS,
                                 command=parsed_args.command,
                                 args=parsed_args)

        # TODO: This fundamentally alters the way Knack.invocation works here. Cannot be customized
        # with an event. Would need to be customized via inheritance.

        cmd = parsed_args.func
        self.cli_ctx.data['command'] = parsed_args.command

        self.cli_ctx.data[
            'safe_params'] = AzCliCommandInvoker._extract_parameter_names(args)

        command_source = self.commands_loader.command_table[
            command].command_source

        extension_version = None
        extension_name = None
        try:
            if isinstance(command_source, ExtensionCommandSource):
                extension_name = command_source.extension_name
                extension_version = get_extension(
                    command_source.extension_name).version
        except Exception:  # pylint: disable=broad-except
            pass
        telemetry.set_command_details(self.cli_ctx.data['command'],
                                      self.data['output'],
                                      self.cli_ctx.data['safe_params'],
                                      extension_name=extension_name,
                                      extension_version=extension_version)
        if extension_name:
            self.data['command_extension_name'] = extension_name

        self.resolve_warnings(cmd, parsed_args)
        self.resolve_confirmation(cmd, parsed_args)

        jobs = []
        for expanded_arg in _explode_list_args(parsed_args):
            cmd_copy = copy.copy(cmd)
            cmd_copy.cli_ctx = copy.copy(cmd.cli_ctx)
            cmd_copy.cli_ctx.data = copy.deepcopy(cmd.cli_ctx.data)
            expanded_arg.cmd = cmd_copy

            if hasattr(expanded_arg, '_subscription'):
                cmd_copy.cli_ctx.data[
                    'subscription_id'] = expanded_arg._subscription  # pylint: disable=protected-access

            self._validation(expanded_arg)
            jobs.append((expanded_arg, cmd_copy))

        ids = getattr(parsed_args, '_ids', None) or [None] * len(jobs)
        if self.cli_ctx.config.getboolean('core', 'disable_concurrent_ids',
                                          False) or len(ids) < 2:
            results, exceptions = self._run_jobs_serially(jobs, ids)
        else:
            results, exceptions = self._run_jobs_concurrently(jobs, ids)

        # handle exceptions
        if len(exceptions) == 1 and not results:
            ex, id_arg = exceptions[0]
            raise ex
        elif exceptions:
            for exception, id_arg in exceptions:
                logger.warning('%s: "%s"', id_arg, str(exception))
            if not results:
                return CommandResultItem(
                    None,
                    exit_code=1,
                    error=CLIError('Encountered more than one exception.'))
            else:
                logger.warning('Encountered more than one exception.')

        if results and len(results) == 1:
            results = results[0]

        event_data = {'result': results}
        self.cli_ctx.raise_event(EVENT_INVOKER_FILTER_RESULT,
                                 event_data=event_data)

        return CommandResultItem(
            event_data['result'],
            table_transformer=self.commands_loader.command_table[
                parsed_args.command].table_transformer,
            is_query_active=self.data['query_active'])
Example #13
0
def upgrade_version(cmd, update_all=None, yes=None):  # pylint: disable=too-many-locals, too-many-statements, too-many-branches, no-member, unused-argument
    import os
    import platform
    import sys
    import subprocess
    import azure.cli.core.telemetry as telemetry
    from azure.cli.core import __version__ as local_version
    from azure.cli.core._environment import _ENV_AZ_INSTALLER
    from azure.cli.core.extension import get_extensions, WheelExtension
    from packaging.version import parse
    from knack.util import CLIError

    update_cli = True
    from azure.cli.core.util import get_latest_from_github
    try:
        latest_version = get_latest_from_github()
        if latest_version and parse(latest_version) <= parse(local_version):
            logger.warning("You already have the latest azure-cli version: %s",
                           local_version)
            update_cli = False
            if not update_all:
                return
    except Exception as ex:  # pylint: disable=broad-except
        logger.debug("Failed to get the latest version. %s", str(ex))
    exts = [ext.name for ext in get_extensions(
        ext_type=WheelExtension)] if update_all else []

    exit_code = 0
    installer = os.getenv(_ENV_AZ_INSTALLER) or ''
    installer = installer.upper()
    if update_cli:
        latest_version_msg = 'It will be updated to {}.'.format(latest_version) if yes \
            else 'Latest version available is {}.'.format(latest_version)
        logger.warning("Your current Azure CLI version is %s. %s",
                       local_version, latest_version_msg)
        from knack.prompting import prompt_y_n, NoTTYException
        if not yes:
            logger.warning(
                "Please check the release notes first: https://docs.microsoft.com/"
                "cli/azure/release-notes-azure-cli")
            try:
                confirmation = prompt_y_n("Do you want to continue?",
                                          default='y')
            except NoTTYException:
                from azure.cli.core.azclierror import UnclassifiedUserFault
                raise UnclassifiedUserFault("No tty available.",
                                            "Please run command with --yes.")

            if not confirmation:
                telemetry.set_success("Upgrade stopped by user")
                return

        if installer == 'DEB':
            from azure.cli.core.util import in_cloud_console
            if in_cloud_console():
                raise CLIError("az upgrade is not supported in Cloud Shell.")
            apt_update_cmd = 'apt-get update'.split()
            az_update_cmd = 'apt-get install --only-upgrade -y azure-cli'.split(
            )
            if os.geteuid() != 0:  # pylint: disable=no-member
                apt_update_cmd.insert(0, 'sudo')
                az_update_cmd.insert(0, 'sudo')
            exit_code = subprocess.call(apt_update_cmd)
            if exit_code == 0:
                logger.debug("Update azure cli with '%s'",
                             " ".join(az_update_cmd))
                exit_code = subprocess.call(az_update_cmd)
        elif installer == 'RPM':
            from azure.cli.core.util import get_linux_distro
            distname, _ = get_linux_distro()
            if not distname:
                logger.warning(UPGRADE_MSG)
            else:
                distname = distname.lower().strip()
                if any(x in distname
                       for x in ['centos', 'rhel', 'red hat', 'fedora']):
                    update_cmd = 'yum update -y azure-cli'.split()
                    if os.geteuid() != 0:  # pylint: disable=no-member
                        update_cmd.insert(0, 'sudo')
                    logger.debug("Update azure cli with '%s'",
                                 " ".join(update_cmd))
                    exit_code = subprocess.call(update_cmd)
                elif any(x in distname for x in ['opensuse', 'suse', 'sles']):
                    zypper_refresh_cmd = ['zypper', 'refresh']
                    az_update_cmd = 'zypper update -y azure-cli'.split()
                    if os.geteuid() != 0:  # pylint: disable=no-member
                        zypper_refresh_cmd.insert(0, 'sudo')
                        az_update_cmd.insert(0, 'sudo')
                    exit_code = subprocess.call(zypper_refresh_cmd)
                    if exit_code == 0:
                        logger.debug("Update azure cli with '%s'",
                                     " ".join(az_update_cmd))
                        exit_code = subprocess.call(az_update_cmd)
                else:
                    logger.warning(UPGRADE_MSG)
        elif installer == 'HOMEBREW':
            logger.debug("Update homebrew formulae")
            exit_code = subprocess.call(['brew', 'update'])
            if exit_code == 0:
                update_cmd = ['brew', 'upgrade', 'azure-cli']
                logger.debug("Update azure cli with '%s'",
                             " ".join(update_cmd))
                exit_code = subprocess.call(update_cmd)
        elif installer == 'PIP':
            pip_args = [
                sys.executable, '-m', 'pip', 'install', '--upgrade',
                'azure-cli', '-vv', '--disable-pip-version-check',
                '--no-cache-dir'
            ]
            logger.debug("Update azure cli with '%s'", " ".join(pip_args))
            exit_code = subprocess.call(pip_args,
                                        shell=platform.system() == 'Windows')
        elif installer == 'DOCKER':
            logger.warning(
                "Exit the container to pull latest image with 'docker pull mcr.microsoft.com/azure-cli' "
                "or run 'pip install --upgrade azure-cli' in this container")
        elif installer == 'MSI':
            logger.debug(
                "Update azure cli with MSI from https://aka.ms/installazurecliwindows"
            )
            exit_code = subprocess.call(['powershell.exe', '-NoProfile', "Start-Process msiexec.exe -Wait -ArgumentList '/i https://aka.ms/installazurecliwindows'"])  # pylint: disable=line-too-long
        else:
            logger.warning(UPGRADE_MSG)
    if exit_code:
        err_msg = "CLI upgrade failed."
        logger.warning(err_msg)
        telemetry.set_failure(err_msg)
        sys.exit(exit_code)

    # Avoid using python modules directly as they may have been changed due to upgrade.
    # If you do need to use them, you may need to reload them and their dependent modules.
    # Otherwise you may have such issue https://github.com/Azure/azure-cli/issues/16952
    import importlib
    import json
    importlib.reload(subprocess)
    importlib.reload(json)

    version_result = subprocess.check_output(
        ['az', 'version', '-o', 'json'], shell=platform.system() == 'Windows')
    version_json = json.loads(version_result)
    new_version = version_json['azure-cli-core']

    if update_cli and new_version == local_version:
        err_msg = "CLI upgrade failed or aborted."
        logger.warning(err_msg)
        telemetry.set_failure(err_msg)
        sys.exit(1)

    if exts:
        logger.warning("Upgrading extensions")
    for ext_name in exts:
        try:
            logger.warning("Checking update for %s", ext_name)
            subprocess.call(['az', 'extension', 'update', '-n', ext_name],
                            shell=platform.system() == 'Windows')
        except Exception as ex:  # pylint: disable=broad-except
            msg = "Extension {} update failed during az upgrade. {}".format(
                ext_name, str(ex))
            raise CLIError(msg)
    auto_upgrade_msg = "You can enable auto-upgrade with 'az config set auto-upgrade.enable=yes'. " \
        "More details in https://docs.microsoft.com/cli/azure/update-azure-cli#automatic-update"
    logger.warning(
        "Upgrade finished.%s", "" if cmd.cli_ctx.config.getboolean(
            'auto-upgrade', 'enable', False) else auto_upgrade_msg)
Example #14
0
def set_success(summary=None):
    telemetry_core.set_success(summary=summary)
Example #15
0
    uuid.uuid1 = uuid.uuid4


logger = get_logger(__name__)


def cli_main(cli, args):
    return cli.invoke(args)


az_cli = get_default_cli()

telemetry.set_application(az_cli, ARGCOMPLETE_ENV_NAME)

try:
    telemetry.start()

    exit_code = cli_main(az_cli, sys.argv[1:])

    if exit_code and exit_code != 0:
        telemetry.set_failure()
    else:
        telemetry.set_success()

    sys.exit(exit_code)
except KeyboardInterrupt:
    telemetry.set_user_fault('keyboard interrupt')
    sys.exit(1)
finally:
    telemetry.conclude()
Example #16
0
    def execute(self, unexpanded_argv):  # pylint: disable=too-many-statements
        self.refresh_request_id()

        argv = Application._expand_file_prefixed_files(unexpanded_argv)
        command_table = self.configuration.get_command_table(argv)
        self.raise_event(self.COMMAND_TABLE_LOADED, command_table=command_table)
        self.parser.load_command_table(command_table)
        self.raise_event(self.COMMAND_PARSER_LOADED, parser=self.parser)

        if not argv:
            enable_autocomplete(self.parser)
            az_subparser = self.parser.subparsers[tuple()]
            _help.show_welcome(az_subparser)

            # TODO: Question, is this needed?
            telemetry.set_command_details('az')
            telemetry.set_success(summary='welcome')

            return None

        if argv[0].lower() == 'help':
            argv[0] = '--help'

        # Rudimentary parsing to get the command
        nouns = []
        for i, current in enumerate(argv):
            try:
                if current[0] == '-':
                    break
            except IndexError:
                pass
            argv[i] = current.lower()
            nouns.append(argv[i])

        command = ' '.join(nouns)

        if argv[-1] in ('--help', '-h') or command in command_table:
            self.configuration.load_params(command)
            self.raise_event(self.COMMAND_TABLE_PARAMS_LOADED, command_table=command_table)
            self.parser.load_command_table(command_table)

        if self.session['completer_active']:
            enable_autocomplete(self.parser)

        self.raise_event(self.COMMAND_PARSER_PARSING, argv=argv)
        args = self.parser.parse_args(argv)

        self.raise_event(self.COMMAND_PARSER_PARSED, command=args.command, args=args)
        results = []
        for expanded_arg in _explode_list_args(args):
            self.session['command'] = expanded_arg.command
            try:
                _validate_arguments(expanded_arg)
            except CLIError:
                raise
            except:  # pylint: disable=bare-except
                err = sys.exc_info()[1]
                getattr(expanded_arg, '_parser', self.parser).validation_error(str(err))

            # Consider - we are using any args that start with an underscore (_) as 'private'
            # arguments and remove them from the arguments that we pass to the actual function.
            # This does not feel quite right.
            params = dict([(key, value)
                           for key, value in expanded_arg.__dict__.items()
                           if not key.startswith('_')])
            params.pop('subcommand', None)
            params.pop('func', None)
            params.pop('command', None)

            telemetry.set_command_details(expanded_arg.command,
                                          self.configuration.output_format,
                                          [p for p in unexpanded_argv if p.startswith('-')])

            result = expanded_arg.func(params)
            result = todict(result)
            results.append(result)

        if len(results) == 1:
            results = results[0]

        event_data = {'result': results}
        self.raise_event(self.TRANSFORM_RESULT, event_data=event_data)
        self.raise_event(self.FILTER_RESULT, event_data=event_data)

        return CommandResultItem(event_data['result'],
                                 table_transformer=command_table[args.command].table_transformer,
                                 is_query_active=self.session['query_active'])
Example #17
0
    def execute(self, args):
        from knack.events import (EVENT_INVOKER_PRE_CMD_TBL_CREATE, EVENT_INVOKER_POST_CMD_TBL_CREATE,
                                  EVENT_INVOKER_CMD_TBL_LOADED, EVENT_INVOKER_PRE_PARSE_ARGS,
                                  EVENT_INVOKER_POST_PARSE_ARGS,
                                  EVENT_INVOKER_FILTER_RESULT)
        from azure.cli.core.commands.events import EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE

        # TODO: Can't simply be invoked as an event because args are transformed
        args = _pre_command_table_create(self.cli_ctx, args)

        self.cli_ctx.raise_event(EVENT_INVOKER_PRE_CMD_TBL_CREATE, args=args)
        self.commands_loader.load_command_table(args)
        self.cli_ctx.raise_event(EVENT_INVOKER_PRE_CMD_TBL_TRUNCATE,
                                 load_cmd_tbl_func=self.commands_loader.load_command_table, args=args)
        command = self._rudimentary_get_command(args)
        self.cli_ctx.invocation.data['command_string'] = command
        telemetry.set_raw_command_name(command)

        try:
            self.commands_loader.command_table = {command: self.commands_loader.command_table[command]}
        except KeyError:
            # Trim down the command table to reduce the number of subparsers required to optimize the performance.
            #
            # When given a command table like this:
            #
            # network application-gateway create
            # network application-gateway delete
            # network list-usages
            # storage account create
            # storage account list
            #
            # input:  az
            # output: network application-gateway create
            #         storage account create
            #
            # input:  az network
            # output: network application-gateway create
            #         network list-usages

            cmd_table = {}
            group_names = set()
            for cmd_name, cmd in self.commands_loader.command_table.items():
                if command and not cmd_name.startswith(command):
                    continue

                cmd_stub = cmd_name[len(command):].strip()
                group_name = cmd_stub.split(' ', 1)[0]
                if group_name not in group_names:
                    cmd_table[cmd_name] = cmd
                    group_names.add(group_name)
                self.commands_loader.command_table = cmd_table

        self.commands_loader.command_table = self.commands_loader.command_table  # update with the truncated table
        self.commands_loader.command_name = command
        self.commands_loader.load_arguments(command)
        self.cli_ctx.raise_event(EVENT_INVOKER_POST_CMD_TBL_CREATE, commands_loader=self.commands_loader)
        self.parser.cli_ctx = self.cli_ctx
        self.parser.load_command_table(self.commands_loader)

        self.cli_ctx.raise_event(EVENT_INVOKER_CMD_TBL_LOADED, cmd_tbl=self.commands_loader.command_table,
                                 parser=self.parser)

        arg_check = [a for a in args if a not in ['--debug', '--verbose']]
        if not arg_check:
            self.parser.enable_autocomplete()
            subparser = self.parser.subparsers[tuple()]
            self.help.show_welcome(subparser)

            # TODO: No event in base with which to target
            telemetry.set_command_details('az')
            telemetry.set_success(summary='welcome')
            return CommandResultItem(None, exit_code=0)

        if args[0].lower() == 'help':
            args[0] = '--help'

        self.parser.enable_autocomplete()

        self.cli_ctx.raise_event(EVENT_INVOKER_PRE_PARSE_ARGS, args=args)
        parsed_args = self.parser.parse_args(args)
        self.cli_ctx.raise_event(EVENT_INVOKER_POST_PARSE_ARGS, command=parsed_args.command, args=parsed_args)

        # TODO: This fundamentally alters the way Knack.invocation works here. Cannot be customized
        # with an event. Would need to be customized via inheritance.

        cmd = parsed_args.func
        self.cli_ctx.data['command'] = parsed_args.command

        self.cli_ctx.data['safe_params'] = AzCliCommandInvoker._extract_parameter_names(args)

        command_source = self.commands_loader.command_table[command].command_source

        extension_version = None
        extension_name = None
        try:
            if isinstance(command_source, ExtensionCommandSource):
                extension_name = command_source.extension_name
                extension_version = get_extension(command_source.extension_name).version
        except Exception:  # pylint: disable=broad-except
            pass
        telemetry.set_command_details(self.cli_ctx.data['command'], self.data['output'],
                                      self.cli_ctx.data['safe_params'],
                                      extension_name=extension_name, extension_version=extension_version)
        if extension_name:
            self.data['command_extension_name'] = extension_name

        self.resolve_warnings(cmd, parsed_args)
        self.resolve_confirmation(cmd, parsed_args)

        jobs = []
        for expanded_arg in _explode_list_args(parsed_args):
            cmd_copy = copy.copy(cmd)
            cmd_copy.cli_ctx = copy.copy(cmd.cli_ctx)
            cmd_copy.cli_ctx.data = copy.deepcopy(cmd.cli_ctx.data)
            expanded_arg.cmd = expanded_arg._cmd = cmd_copy

            if hasattr(expanded_arg, '_subscription'):
                cmd_copy.cli_ctx.data['subscription_id'] = expanded_arg._subscription  # pylint: disable=protected-access

            self._validation(expanded_arg)
            jobs.append((expanded_arg, cmd_copy))

        ids = getattr(parsed_args, '_ids', None) or [None] * len(jobs)
        if self.cli_ctx.config.getboolean('core', 'disable_concurrent_ids', False) or len(ids) < 2:
            results, exceptions = self._run_jobs_serially(jobs, ids)
        else:
            results, exceptions = self._run_jobs_concurrently(jobs, ids)

        # handle exceptions
        if len(exceptions) == 1 and not results:
            ex, id_arg = exceptions[0]
            raise ex
        elif exceptions:
            for exception, id_arg in exceptions:
                logger.warning('%s: "%s"', id_arg, str(exception))
            if not results:
                return CommandResultItem(None, exit_code=1, error=CLIError('Encountered more than one exception.'))
            logger.warning('Encountered more than one exception.')

        if results and len(results) == 1:
            results = results[0]

        event_data = {'result': results}
        self.cli_ctx.raise_event(EVENT_INVOKER_FILTER_RESULT, event_data=event_data)

        return CommandResultItem(
            event_data['result'],
            table_transformer=self.commands_loader.command_table[parsed_args.command].table_transformer,
            is_query_active=self.data['query_active'])