Esempio n. 1
0
    def get_sub_commands(self, root_cmd):
        """
        Gets a list of commands at the current mode.

        It sends root_cmd with ? and returns everything that is a command.
        This strips out things in <>'s, or other free-form fields the user
        has to enter.

        :param root_cmd: root of the command to get subcommands for
        :return: a list of the full paths to subcommands.  eg,  if root_cmd is
            "web ?", this returns:

        .. code-block:: python

            ['web autologout', 'web auto-refresh', ...]
        """

        self._log.debug('Generating help for "%s"' % root_cmd)
        sub_commands = []
        output, match_res = self._send_and_wait('%s ?' % root_cmd,
                                                self.CLI_ANY_PROMPT)

        # Split the output into a list of lines. The first one will be the
        # command we sent, the last two will be an escape code and the prompt,
        # So remove those.
        lines = output.splitlines()
        lines = lines[1:]

        self._log.debug("raw output: %s" % lines)
        for line in lines:
            command = line.split(' ')[0]

            if command == '%':
                # Remove the command we enter to be back at the empty prompt
                self._send_line_and_wait(DELETE_LINE, self.CLI_ANY_PROMPT)
                try:
                    mode = self.current_cli_mode()
                except exceptions.UnknownCLIMode:
                    mode = '<unrecognized>'
                raise exceptions.CLIError(root_cmd, output=output, mode=mode)

            # If this is a user-input field, skip it. Most are surrounded by
            # <>, but not all. If the command contains anything other than
            # letters or numbers, we assume it is a user field.
            if command.isalnum():
                if root_cmd:
                    sub_commands.append(root_cmd + ' ' + command)
                else:
                    sub_commands.append(command)

        # Remove the command we enter, so we're back at the empty prompt
        self._send_line_and_wait(DELETE_LINE, self.CLI_ANY_PROMPT)
        return sub_commands
Esempio n. 2
0
    def enter_mode_normal(self, force=False):
        """
        Puts the CLI into the 'normal' mode.

        In this mode you can run commands, but you cannot change
        the configuration.

        :param force: Will force enter 'normal' mode, discarding all changes
            that haven't been committed.
        :type force: Boolean

        :raises CLIError: if unable to go from "configure" mode to "normal"
            This happens if "commit" is not applied after config changes
        :raises UnknownCLIMode: if mode is not "normal" or "configure"
        """

        self._log.info('Going to normal mode')
        mode = self.current_cli_mode()

        if mode == cli.CLIMode.NORMAL:
            self._log.debug('Already at normal, doing nothing')

        elif mode == cli.CLIMode.CONFIG:
            if force:
                self._log.debug('Entering normal mode, discarding all commits')
                self._send_line_and_wait(
                    'exit discard',
                    self.CLI_NORMAL_PROMPT)

            else:
                self._log.debug('Entering normal mode')
                (output, match_res) = self._send_line_and_wait(
                    'exit', [self.CLI_ERROR_PROMPT, self.CLI_NORMAL_PROMPT])

                if re.match(self.CLI_ERROR_PROMPT, match_res.string):
                    raise exceptions.CLIError(
                        command="exit", output=match_res.string, mode=mode)
        else:
            raise exceptions.UnknownCLIMode(mode=mode)
Esempio n. 3
0
    def exec_command(self,
                     command,
                     timeout=60,
                     mode=cli.CLIMode.UNDEF,
                     output_expected=None,
                     error_expected=False,
                     prompt=None):
        """Executes the given command.

        This method handles detecting simple boolean conditions such as
        the presence of output or errors.

        :param command:  command to execute, newline appended automatically
        :param timeout:  maximum time, in seconds, to wait for the command to
            finish. 0 to wait forever.
        :param mode:  mode to enter before running the command. The default
            is :func:`default_mode`.  To skip this step and execute directly
            in the cli's current mode, explicitly set this parameter to None.
        :param output_expected: If not None, indicates whether output is
            expected (True) or no output is expected (False).
            If the opposite occurs, raise UnexpectedOutput. Default is None.
        :type output_expected: bool or None
        :param error_expected: If true, cli error output (with a leading '%')
            is expected and will be returned as regular output instead of
            raising a CLIError.  Default is False, and error_expected always
            overrides output_expected.
        :type error_expected: bool
        :param prompt: Prompt regex for matching unusual prompts.  This should
            almost never be used as the ``mode`` parameter automatically
            handles all typical cases.  This parameter is for unusual
            situations like the install config wizard.

        :return: output of the command, minus the command itself.

        :raises CmdlineTimeout: on timeout
        :raises CLIError: if the output matches the cli's error format, and
            error output was not expected.
        :raises UnexpectedOutput: if output occurs when no output was
            expected, or no output occurs when output was expected
        """

        if not (output_expected is None or isinstance(output_expected, bool)):
            err_text = ("exec_command: output_expected requires a boolean "
                        "value or None. {cmd} called with output_expected "
                        "type:{type}, value:{val}")
            raise TypeError(
                err_text.format(cmd=command,
                                type=type(output_expected),
                                val=output_expected))

        if mode is cli.CLIMode.UNDEF:
            mode = self.default_mode
        if mode is not None:
            self.enter_mode(mode)

        self._log.debug('Executing cmd "%s"' % command)

        if prompt is None:
            prompt = self._prompt

        try:
            (output, match_res) = self._send_line_and_wait(command,
                                                           prompt,
                                                           timeout=timeout)
        except exceptions.ConnectionError as e:
            self._log.info("Connection channel in unexpected state. Flushing "
                           "and restarting. Error was {error}".format(error=e))
            self._cleanup_helper()
            self.start()
            (output, match_res) = self._send_line_and_wait(command,
                                                           prompt,
                                                           timeout=timeout)

        # CLI adds on escape chars and such sometimes and the result is that
        # some part of the command that was entered shows up as an extra
        # initial line of output.  Strip off that initial line.
        output = '\n'.join(output.splitlines()[1:])

        if output and (re.match(self.CLI_ERROR_PROMPT, output)):
            if error_expected:
                # Skip output_expected processing entirely.
                return output
            else:
                try:
                    mode = self.current_cli_mode()
                except exceptions.UnknownCLIMode:
                    mode = '<unrecognized>'
                raise exceptions.CLIError(command, output=output, mode=mode)

        if ((output_expected is not None)
                and (bool(output) != bool(output_expected))):
            raise exceptions.UnexpectedOutput(command=command,
                                              output=output,
                                              expected_output=output_expected)
        return output
Esempio n. 4
0
    def exec_command(self,
                     command,
                     timeout=60,
                     mode=cli.CLIMode.CONFIG,
                     output_expected=None,
                     error_expected=False,
                     interface=None,
                     prompt=None):
        """Executes the given command.

        This method handles detecting simple boolean conditions such as
        the presence of output or errors.

        :param command:  command to execute, newline appended automatically
        :param timeout:  maximum time, in seconds, to wait for the command to
            finish. 0 to wait forever.
        :param mode:  mode to enter before running the command.  To skip this
            step and execute directly in the cli's current mode, explicitly
            set this parameter to None.  The default is "configure"
        :param output_expected: If not None, indicates whether output is
            expected (True) or no output is expected (False).
            If the opposite occurs, raise UnexpectedOutput. Default is None.
        :type output_expected: bool or None
        :param error_expected: If true, cli error output (with a leading '%')
            is expected and will be returned as regular output instead of
            raising a CLIError.  Default is False, and error_expected always
            overrides output_expected.
        :type error_expected: bool
        :param interface: if mode 'subif', interface to configure 'gi0/1.666'
            or 'vlan 691'
        :type interface: string
        :param prompt: Prompt regex for matching unusual prompts.  This should
            almost never be used as the ``mode`` parameter automatically
            handles all typical cases.  This parameter is for unusual
            situations like the install config wizard.

        :raises CmdlineTimeout: on timeout
        :raises CLIError: if the output matches the cli's error format, and
            error output was not expected.
        :raises UnexpectedOutput: if output occurs when no output was
            expected, or no output occurs when output was expected

        :return: output of the command, minus the command itself.
        """

        if output_expected is not None and not isinstance(
                output_expected, bool):
            raise TypeError("exec_command: output_expected requires a boolean "
                            "value or None")

        if mode is not None:
            self.enter_mode(mode, interface)

        self._log.debug('Executing cmd "%s"' % command)

        if prompt is None:
            prompt = self.CLI_ANY_PROMPT
        (output, match_res) = self._send_line_and_wait(command,
                                                       prompt,
                                                       timeout=timeout)

        output = '\n'.join(output.splitlines()[1:])

        if output and (re.match(self.CLI_ERROR_PROMPT, output)):
            if error_expected:
                # Skip output_expected processing entirely.
                return output
            else:
                try:
                    mode = self.current_cli_mode()
                except exceptions.UnknownCLIMode:
                    mode = '<unrecognized>'
                raise exceptions.CLIError(command, output=output, mode=mode)

        if ((output_expected is not None)
                and (bool(output) != bool(output_expected))):
            raise exceptions.UnexpectedOutput(command=command,
                                              output=output,
                                              expected_output=output_expected)
        return output