コード例 #1
0
    def _shell_command(self, command):
        """Execute an underlying shell command

        The _shell_command() is an internal API that is invoked as
        part of the public command() API should the user context
        be that of an underlying shell (e.g. non-ArcOS CLI).

        Args:
            command: A string value representing a single shell
                command.
        Returns:
            A Reply() object indicating success/failure along with a
            message.  If the reply is successful, the appropriate
            data payload is returned in the message field.  If the
            reply is a failure, an appropriate error code is set
            along with a message description indicating the failure.
        """
        if self.session.local is False:
            stdin, stdout, stderr = self.session.exec_command(command)
            response = Reply()
            if stdout.channel.recv_exit_status() != 0:
                response.error = Error.SHELL_ERROR
                response.message = stderr.read().rstrip()
            else:
                response.error = None
                response.message = stdout.read()
            return response
        else:
            self.child = pexpect.spawn(command, encoding='UTF-8')
            self.child.expect(pexpect.EOF, timeout=self.timeout)
            result = self.child.before
            response = Reply()
            response.error = None
            response.message = result
            return response
コード例 #2
0
    def _check_expect_response(self,
                               command,
                               match,
                               timeout=None,
                               close_child=True):
        """Check a command response against a match string

        Alternative function to _wait which uses expect to match
        an argument against the return buffer as a result of a
        previous sendline.

        Args:
            command: A string representing the command previously
                sent that corresponds to the buffer returned.
            match: A string indicating a word to match in the
                buffer response.
            timeout: A integer respresenting the time pexpect waits for
                command to return.  If the caller doesn't set this value
                the timeout value will be equal to the object level timeout
            close_child: Boolean value that instructs if the child
                should be terminated
        Returns:
            A tuple indicating success (True) or failure (False)
            along with a Reply() object.  If the reply is successful,
            the appropriate data payload is returned in the message
            field.  If the reply is a failure, an appropriate error
            code is set along with a message description indicating
            the failure.
        """
        response = Reply()
        timeout = self.timeout if timeout is None else timeout
        try:
            self.child.expect(match, timeout)
            return (True, None)
        except pexpect.EOF:
            # we caught a pexpect.EOF
            response.error = Error.PROCESS_UNAVAILABLE
            response.message = 'Management daemon not available'
            if close_child:
                if self.child.isalive():
                    self.child.close(force=True)
            return (False, response)

        except pexpect.TIMEOUT:
            # we caught a pexpect.TIMEOUT
            response.error = Error.PROCESS_TIMEOUT
            response.message = 'Command [{}] timed out.'.format(command)
            if close_child:
                if self.child.isalive():
                    self.child.close(force=True)
            return (False, response)
コード例 #3
0
    def _validate_buffer(self, command, buf):
        """Validate the contents of command responses

        Given an input buffer message as a response to a previously
        sent command, validate the contents of the message and pack
        an appropriate error and message within a Reply() object.

        Args:
            command: A string representing the command issued
            buf: A string representing the return buffer
        Returns:
            A Reply() object indicating success/failure along with a
            message.  If the reply is successful, the appropriate
            data payload is returned in the message field.  If the
            reply is a failure, an appropriate error code is set
            along with a message description indicating the failure.
        """
        error = None
        message = ''

        if '----^' in buf:
            message = 'Syntax Error: [{}]'.format(command.strip())
            error = Error.INVALID_COMMAND
        elif 'Commit complete' in buf:
            message = 'Commit Successful'
        elif 'No modifications to commit' in buf:
            message = 'No modifications to commit'
            error = Error.COMMIT_NO_MODIFICATIONS
        elif 'Aborted' in buf:
            message = '{} [{}]'.format(self._cleanse_buffer(buf), command)
            error = Error.OPERATION_ABORTED
        elif 'Error' in buf:
            message = '{}'.format(self._cleanse_buffer(buf))
            error = Error.OPERATION_ERROR
        elif 'Subsystem started' in buf:
            message = '{}'.format(self._cleanse_buffer(buf))
            error = Error.OPERATION_ERROR
        elif 'Validation complete' in buf:
            message = 'Validation Successful'
        else:
            message = buf

        response = Reply()
        response.error = error
        response.message = message
        return response
コード例 #4
0
    def _interactive_command(self, commands, **kwargs):
        """Execute a set of interactive CLI commands

        Internal only API that is front-ended by all public facing
        APIs.  This code path is executed for configuration or
        operational commands should the context of the user session
        be within the ArcOS CLI.

        Args:
            commands: A list of commands to be executed.
            kwargs: A dict of keyword arguments passed to respective
                functions.
        Returns:
            A Reply() object indicating success/failure along with a
            message.  If the reply is successful, the appropriate
            data payload is returned in the message field.  If the
            reply is a failure, an appropriate error code is set
            along with a message description indicating the failure.
        """
        response = Reply()

        if 'timeout' in kwargs:
            self.timeout = kwargs['timeout']

        if 'cli' in kwargs:
            if kwargs['cli'] is not None:
                command = 'arcos_cli'
                if self.session.local is True:
                    self.child = pexpect.spawnu(command)
                    self.arcos_shell = True
                else:
                    self._send_command(command)
                    self._wait(self.shell_prompt)
                    self.arcos_shell = True

        if 'shell' in kwargs:
            if kwargs['shell']:
                commands = ['bash ' + command for command in commands]
                self.arcos_shell = True

        if self.arcos_shell:
            prompts = {
                'prompt1': self.oper_prompt,
                'prompt2': self.conf_prompt
            }
            if self.session.local is True:
                retry = 0
                for prompt in prompts:
                    success = False
                    retry = 0
                    ## adding a retry to deal with odd timing issues after arcos_cli
                    ## is initially spawned
                    while retry < 3 and not success:
                        command = '%s %s' % (prompt, prompts[prompt])
                        self.child.sendline(command)
                        (success, response) = self._check_expect_response(
                            command,
                            self.oper_prompt,
                            timeout=5,
                            close_child=False)
                        if not success:
                            retry += 1

                    if not success:
                        return response
                    response = self._validate_buffer(command,
                                                     self.child.before)
                    if response.error is not None:
                        return response

                command = 'paginate false'
                self.child.sendline(command)
                (success, response) = self._check_expect_response(
                    command, self.oper_prompt)
                if not success:
                    return response
                response = self._validate_buffer(command, self.child.before)
                if response.error is not None:
                    return response
            else:
                for prompt in prompts:
                    command = '%s %s' % (prompt, prompts[prompt])
                    self._send_command(command)
                    buf = self._wait(self.oper_prompt)
                    if isinstance(buf, Reply):
                        response = buf
                    else:
                        response = self._validate_buffer(command, buf)
                    if response.error is not None:
                        return response

                command = 'paginate false'
                self._send_command(command)
                buf = self._wait(self.oper_prompt)
                if isinstance(buf, Reply):
                    response = buf
                else:
                    response = self._validate_buffer(command, buf)
                if response.error is not None:
                    return response

        commit = False
        commit_response = None
        exit_commands = ['commit', 'show configuration diff', 'validate']

        if any(x in commands for x in exit_commands):
            commit_response = self._commit_handler(commands)
            commit = True

        if commit is False:
            for command in commands:
                if 'encoding' in kwargs:
                    encoding = kwargs['encoding']
                    if encoding is not None:
                        command = command + ' | display %s' % (encoding)

                if self.arcos_shell:
                    if self.session.local is True:
                        try:
                            self.child.sendline(command)
                            self.child.expect(self.oper_prompt,
                                              timeout=self.timeout)
                            response = self._validate_buffer(
                                command, self.child.before)
                            if response.error is not None:
                                return response
                            buf = response.message
                        except pexpect.TIMEOUT:
                            return Reply(
                                Error.PROCESS_TIMEOUT,
                                'Command [{}] timed out'.format(command))
                    else:
                        self._send_command(command)
                        buf = self._wait(self.oper_prompt)
                else:
                    return self._shell_command(command)

        ## For now, each call to _interactive_command() spawns a
        ## new shell.  When this is the case, ensure there is some
        ## cleanup to ensure that locks are not being held for
        ## future callers
        ##
        ## TODO: Move session closure for handling at the manager
        ## (session) level
        if self.arcos_shell:
            if self.session.local is True:
                self.child.close()

        if commit:
            response.error = commit_response.error
            response.message = commit_response.message
        else:
            response.error = None
            response.message = self._cleanse_buffer(buf)
        return response
コード例 #5
0
    def _noninteractive_command(self, commands, **kwargs):
        """Execute a set of noninteractive CLI commands

        Internal only API that is front-ended by all public facing
        APIs.  This code path is executed for configuration or
        operational commands should the context of the user session
        be outside of the ArcOS CLI.

        Non-interactive commands are written to a temporary file and
        piped directly into the ArcOS CLI shell vs. an interactive
        expect based mechanism.

        Args:
            commands: A list of commands to be executed.
            kwargs: A dict of keyword arguments passed to respective
                functions.
        Returns:
            A Reply() object indicating success/failure along with a
            message.  If the reply is successful, the appropriate
            data payload is returned in the message field.  If the
            reply is a failure, an appropriate error code is set
            along with a message description indicating the failure.
        """
        response = Reply()

        # will handle delete outside the scope of tempfile
        tf = tempfile.NamedTemporaryFile(delete=False)
        for command in commands:
            tf.write('{}\n'.format(command))
        tf.flush()
        tf.close()
        try:
            command_status = run_cmdfile(tf.name)
            os.remove(tf.name)
        except subprocess.CalledProcessError as e:

            respone.error = Error.OPERATION_ERROR
            response.message = json.dumps({
                'rc': 255,
                'message': command_status[1]
            })
            os.remove(tf.name)
            return response

        # returncode non-zero
        if command_status[0] != 0:
            # spawning confd_cli with -s so when a command/commit/validate fails
            # it should return a non-zero exit code
            val_buf = self._validate_buffer('', command_status[1])
            response.error = val_buf.error
            response.message = json.dumps({
                'rc': command_status[0],
                'message': val_buf.message
            })
            return response
        else:
            # should only hit this when a valid command is sent through
            val_buf = self._validate_buffer('', command_status[1])
            response.error = val_buf.error
            response.message = json.dumps({
                'rc': command_status[0],
                'message': val_buf.message
            })

            return response