예제 #1
0
 def send(
     self,
     command,
     prompt=None,
     answer=None,
     newline=True,
     sendonly=False,
     prompt_retry_check=False,
     check_all=False,
 ):
     """
     Sends the command to the device in the opened shell
     """
     if check_all:
         prompt_len = len(to_list(prompt))
         answer_len = len(to_list(answer))
         if prompt_len != answer_len:
             raise AssibleConnectionFailure(
                 "Number of prompts (%s) is not same as that of answers (%s)"
                 % (prompt_len, answer_len))
     try:
         cmd = b"%s\r" % command
         self._history.append(cmd)
         self._ssh_shell.sendall(cmd)
         self._log_messages("send command: %s" % cmd)
         if sendonly:
             return
         response = self.receive(command, prompt, answer, newline,
                                 prompt_retry_check, check_all)
         return to_text(response, errors="surrogate_then_replace")
     except (socket.timeout, AttributeError):
         self.queue_message("error", traceback.format_exc())
         raise AssibleConnectionFailure(
             "timeout value %s seconds reached while trying to send command: %s"
             % (self._ssh_shell.gettimeout(), command.strip()))
예제 #2
0
    def __init__(self, play_context, new_stdin, *args, **kwargs):
        super(Connection, self).__init__(play_context, new_stdin, *args,
                                         **kwargs)

        self._url = None
        self._auth = None

        if self._network_os:

            self.httpapi = httpapi_loader.get(self._network_os, self)
            if self.httpapi:
                self._sub_plugin = {
                    "type": "httpapi",
                    "name": self.httpapi._load_name,
                    "obj": self.httpapi,
                }
                self.queue_message(
                    "vvvv",
                    "loaded API plugin %s from path %s for network_os %s" % (
                        self.httpapi._load_name,
                        self.httpapi._original_path,
                        self._network_os,
                    ),
                )
            else:
                raise AssibleConnectionFailure(
                    "unable to load API plugin for network_os %s" %
                    self._network_os)

        else:
            raise AssibleConnectionFailure(
                "Unable to automatically determine host network os. Please "
                "manually configure assible_network_os value for this host")
        self.queue_message("log", "network_os is set to %s" % self._network_os)
예제 #3
0
파일: ios.py 프로젝트: katiestar99/ansible
    def on_become(self, passwd=None):
        if self._get_prompt().endswith(b"#"):
            return

        cmd = {u"command": u"enable"}
        if passwd:
            # Note: python-3.5 cannot combine u"" and r"" together.  Thus make
            # an r string and use to_text to ensure it's text on both py2 and py3.
            cmd[u"prompt"] = to_text(r"[\r\n]?(?:.*)?[Pp]assword: ?$",
                                     errors="surrogate_or_strict")
            cmd[u"answer"] = passwd
            cmd[u"prompt_retry_check"] = True
        try:
            self._exec_cli_command(
                to_bytes(json.dumps(cmd), errors="surrogate_or_strict"))
            prompt = self._get_prompt()
            if prompt is None or not prompt.endswith(b"#"):
                raise AssibleConnectionFailure(
                    "failed to elevate privilege to enable mode still at prompt [%s]"
                    % prompt)
        except AssibleConnectionFailure as e:
            prompt = self._get_prompt()
            raise AssibleConnectionFailure(
                "unable to elevate privilege to enable mode, at prompt [%s] with error: %s"
                % (prompt, e.message))
예제 #4
0
    def __init__(self, play_context, new_stdin, *args, **kwargs):
        super(Connection, self).__init__(play_context, new_stdin, *args,
                                         **kwargs)
        self._ssh_shell = None

        self._matched_prompt = None
        self._matched_cmd_prompt = None
        self._matched_pattern = None
        self._last_response = None
        self._history = list()
        self._command_response = None
        self._last_recv_window = None

        self._terminal = None
        self.cliconf = None
        self._paramiko_conn = None

        # Managing prompt context
        self._check_prompt = False
        self._task_uuid = to_text(kwargs.get("task_uuid", ""))

        if self._play_context.verbosity > 3:
            logging.getLogger("paramiko").setLevel(logging.DEBUG)

        if self._network_os:
            self._terminal = terminal_loader.get(self._network_os, self)
            if not self._terminal:
                raise AssibleConnectionFailure(
                    "network os %s is not supported" % self._network_os)

            self.cliconf = cliconf_loader.get(self._network_os, self)
            if self.cliconf:
                self._sub_plugin = {
                    "type": "cliconf",
                    "name": self.cliconf._load_name,
                    "obj": self.cliconf,
                }
                self.queue_message(
                    "vvvv",
                    "loaded cliconf plugin %s from path %s for network_os %s" %
                    (
                        self.cliconf._load_name,
                        self.cliconf._original_path,
                        self._network_os,
                    ),
                )
            else:
                self.queue_message(
                    "vvvv",
                    "unable to load cliconf for network_os %s" %
                    self._network_os,
                )
        else:
            raise AssibleConnectionFailure(
                "Unable to automatically determine host network os. Please "
                "manually configure assible_network_os value for this host")
        self.queue_message("log", "network_os is set to %s" % self._network_os)
예제 #5
0
 def on_open_shell(self):
     try:
         for cmd in (b"set terminal length 0", b"set terminal width 512"):
             self._exec_cli_command(cmd)
         self._exec_cli_command(b"set terminal length %d" %
                                self.terminal_length)
     except AssibleConnectionFailure:
         raise AssibleConnectionFailure("unable to set terminal parameters")
예제 #6
0
파일: psrp.py 프로젝트: katiestar99/ansible
    def _connect(self):
        if not HAS_PYPSRP:
            raise AssibleError("pypsrp or dependencies are not installed: %s" %
                               to_native(PYPSRP_IMP_ERR))
        super(Connection, self)._connect()
        self._build_kwargs()
        display.vvv("ESTABLISH PSRP CONNECTION FOR USER: %s ON PORT %s TO %s" %
                    (self._psrp_user, self._psrp_port, self._psrp_host),
                    host=self._psrp_host)

        if not self.runspace:
            connection = WSMan(**self._psrp_conn_kwargs)

            # create our psuedo host to capture the exit code and host output
            host_ui = PSHostUserInterface()
            self.host = PSHost(None, None, False, "Assible PSRP Host", None,
                               host_ui, None)

            self.runspace = RunspacePool(
                connection,
                host=self.host,
                configuration_name=self._psrp_configuration_name)
            display.vvvvv(
                "PSRP OPEN RUNSPACE: auth=%s configuration=%s endpoint=%s" %
                (self._psrp_auth, self._psrp_configuration_name,
                 connection.transport.endpoint),
                host=self._psrp_host)
            try:
                self.runspace.open()
            except AuthenticationError as e:
                raise AssibleConnectionFailure("failed to authenticate with "
                                               "the server: %s" % to_native(e))
            except WinRMError as e:
                raise AssibleConnectionFailure(
                    "psrp connection failure during runspace open: %s" %
                    to_native(e))
            except (ConnectionError, ConnectTimeout) as e:
                raise AssibleConnectionFailure(
                    "Failed to connect to the host via PSRP: %s" %
                    to_native(e))

            self._connected = True
        return self
예제 #7
0
    def _connect(self):
        ''' verify '''

        if self.get_option('remote_user') == 'invaliduser' and self.get_option(
                'password') == 'badpassword':
            raise AssibleConnectionFailure('Got invaliduser and badpassword')

        if not self._connected:
            display.vvv(u"ESTABLISH FAKELOCAL CONNECTION FOR USER: {0}".format(
                self._play_context.remote_user),
                        host=self._play_context.remote_addr)
            self._connected = True
        return self
예제 #8
0
    def send(self, path, data, **kwargs):
        """
        Sends the command to the device over api
        """
        url_kwargs = dict(
            timeout=self.get_option("persistent_command_timeout"),
            validate_certs=self.get_option("validate_certs"),
            use_proxy=self.get_option("use_proxy"),
            headers={},
        )
        url_kwargs.update(kwargs)
        if self._auth:
            # Avoid modifying passed-in headers
            headers = dict(kwargs.get("headers", {}))
            headers.update(self._auth)
            url_kwargs["headers"] = headers
        else:
            url_kwargs["force_basic_auth"] = True
            url_kwargs["url_username"] = self.get_option("remote_user")
            url_kwargs["url_password"] = self.get_option("password")

        try:
            url = self._url + path
            self._log_messages("send url '%s' with data '%s' and kwargs '%s'" %
                               (url, data, url_kwargs))
            response = open_url(url, data=data, **url_kwargs)
        except HTTPError as exc:
            is_handled = self.handle_httperror(exc)
            if is_handled is True:
                return self.send(path, data, **kwargs)
            elif is_handled is False:
                raise
            else:
                response = is_handled
        except URLError as exc:
            raise AssibleConnectionFailure(
                "Could not connect to {0}: {1}".format(self._url + path,
                                                       exc.reason))

        response_buffer = BytesIO()
        resp_data = response.read()
        self._log_messages("received response: '%s'" % resp_data)
        response_buffer.write(resp_data)

        # Try to assign a new auth token if one is given
        self._auth = self.update_auth(response, response_buffer) or self._auth

        response_buffer.seek(0)

        return response, response_buffer
예제 #9
0
파일: ios.py 프로젝트: katiestar99/ansible
    def on_open_shell(self):
        try:
            self._exec_cli_command(b"terminal length 0")
        except AssibleConnectionFailure:
            raise AssibleConnectionFailure("unable to set terminal parameters")

        try:
            self._exec_cli_command(b"terminal width 512")
            try:
                self._exec_cli_command(b"terminal width 0")
            except AssibleConnectionFailure:
                pass
        except AssibleConnectionFailure:
            display.display(
                "WARNING: Unable to set terminal width, command responses may be truncated"
            )
예제 #10
0
파일: vyos.py 프로젝트: katiestar99/ansible
    def edit_config(self,
                    candidate=None,
                    commit=True,
                    replace=None,
                    comment=None):
        resp = {}
        operations = self.get_device_operations()
        self.check_edit_config_capability(operations, candidate, commit,
                                          replace, comment)

        results = []
        requests = []
        self.send_command("configure")
        for cmd in to_list(candidate):
            if not isinstance(cmd, Mapping):
                cmd = {"command": cmd}

            results.append(self.send_command(**cmd))
            requests.append(cmd["command"])
        out = self.get("compare")
        out = to_text(out, errors="surrogate_or_strict")
        diff_config = out if not out.startswith("No changes") else None

        if diff_config:
            if commit:
                try:
                    self.commit(comment)
                except AssibleConnectionFailure as e:
                    msg = "commit failed: %s" % e.message
                    self.discard_changes()
                    raise AssibleConnectionFailure(msg)
                else:
                    self.send_command("exit")
            else:
                self.discard_changes()
        else:
            self.send_command("exit")
            if (to_text(self._connection.get_prompt(),
                        errors="surrogate_or_strict").strip().endswith("#")):
                self.discard_changes()

        if diff_config:
            resp["diff"] = diff_config
        resp["response"] = results
        resp["request"] = requests
        return resp
예제 #11
0
    def _find_prompt(self, response):
        """Searches the buffered response for a matching command prompt
        """
        errored_response = None
        is_error_message = False

        for regex in self._terminal_stderr_re:
            if regex.search(response):
                is_error_message = True

                # Check if error response ends with command prompt if not
                # receive it buffered prompt
                for regex in self._terminal_stdout_re:
                    match = regex.search(response)
                    if match:
                        errored_response = response
                        self._matched_pattern = regex.pattern
                        self._matched_prompt = match.group()
                        self._log_messages(
                            "matched error regex '%s' from response '%s'" %
                            (self._matched_pattern, errored_response))
                        break

        if not is_error_message:
            for regex in self._terminal_stdout_re:
                match = regex.search(response)
                if match:
                    self._matched_pattern = regex.pattern
                    self._matched_prompt = match.group()
                    self._log_messages(
                        "matched cli prompt '%s' with regex '%s' from response '%s'"
                        % (
                            self._matched_prompt,
                            self._matched_pattern,
                            response,
                        ))
                    if not errored_response:
                        return True

        if errored_response:
            raise AssibleConnectionFailure(errored_response)

        return False
예제 #12
0
    def _get_terminal_std_re(self, option):
        terminal_std_option = self.get_option(option)
        terminal_std_re = []

        if terminal_std_option:
            for item in terminal_std_option:
                if "pattern" not in item:
                    raise AssibleConnectionFailure(
                        "'pattern' is a required key for option '%s',"
                        " received option value is %s" % (option, item))
                pattern = br"%s" % to_bytes(item["pattern"])
                flag = item.get("flags", 0)
                if flag:
                    flag = getattr(re, flag.split(".")[1])
                terminal_std_re.append(re.compile(pattern, flag))
        else:
            # To maintain backward compatibility
            terminal_std_re = getattr(self._terminal, option)

        return terminal_std_re
예제 #13
0
파일: ios.py 프로젝트: katiestar99/ansible
    def set_cli_prompt_context(self):
        """
        Make sure we are in the operational cli mode
        :return: None
        """
        if self._connection.connected:
            out = self._connection.get_prompt()

            if out is None:
                raise AssibleConnectionFailure(
                    message=
                    u"cli prompt is not identified from the last received"
                    u" response window: %s" %
                    self._connection._last_recv_window)

            if re.search(
                    r"config.*\)#",
                    to_text(out, errors="surrogate_then_replace").strip(),
            ):
                self._connection.queue_message(
                    "vvvv", "wrong context, sending end to device")
                self._connection.send_command("end")
예제 #14
0
    def _update_cli_prompt_context(self,
                                   config_context=None,
                                   exit_command='exit'):
        """
        Update the cli prompt context to ensure it is in operational mode
        :param config_context: It is string value to identify if the current cli prompt ends with config mode prompt
        :param exit_command: Command to execute to exit the config mode
        :return: None
        """
        out = self._connection.get_prompt()
        if out is None:
            raise AssibleConnectionFailure(
                message=u'cli prompt is not identified from the last received'
                u' response window: %s' % self._connection._last_recv_window)

        while True:
            out = to_text(out, errors='surrogate_then_replace').strip()
            if config_context and out.endswith(config_context):
                self._connection.queue_message(
                    'vvvv', 'wrong context, sending exit to device')
                self.send_command(exit_command)
                out = self._connection.get_prompt()
            else:
                break
예제 #15
0
    def _connect(self):
        if not HAS_NCCLIENT:
            raise AssibleError("%s: %s" % (
                missing_required_lib("ncclient"),
                to_native(NCCLIENT_IMP_ERR),
            ))

        self.queue_message("log", "ssh connection done, starting ncclient")

        allow_agent = True
        if self._play_context.password is not None:
            allow_agent = False
        setattr(self._play_context, "allow_agent", allow_agent)

        self.key_filename = (self._play_context.private_key_file
                             or self.get_option("private_key_file"))
        if self.key_filename:
            self.key_filename = str(os.path.expanduser(self.key_filename))

        self._ssh_config = self.get_option("netconf_ssh_config")
        if self._ssh_config in BOOLEANS_TRUE:
            self._ssh_config = True
        elif self._ssh_config in BOOLEANS_FALSE:
            self._ssh_config = None

        # Try to guess the network_os if the network_os is set to auto
        if self._network_os == "auto":
            for cls in netconf_loader.all(class_only=True):
                network_os = cls.guess_network_os(self)
                if network_os:
                    self.queue_message("vvv",
                                       "discovered network_os %s" % network_os)
                    self._network_os = network_os

        # If we have tried to detect the network_os but were unable to i.e. network_os is still 'auto'
        # then use default as the network_os

        if self._network_os == "auto":
            # Network os not discovered. Set it to default
            self.queue_message(
                "vvv",
                "Unable to discover network_os. Falling back to default.",
            )
            self._network_os = "default"
        try:
            ncclient_device_handler = self.netconf.get_option(
                "ncclient_device_handler")
        except KeyError:
            ncclient_device_handler = "default"
        self.queue_message(
            "vvv",
            "identified ncclient device handler: %s." %
            ncclient_device_handler,
        )
        device_params = {"name": ncclient_device_handler}

        try:
            port = self._play_context.port or 830
            self.queue_message(
                "vvv",
                "ESTABLISH NETCONF SSH CONNECTION FOR USER: %s on PORT %s TO %s WITH SSH_CONFIG = %s"
                % (
                    self._play_context.remote_user,
                    port,
                    self._play_context.remote_addr,
                    self._ssh_config,
                ),
            )
            self._manager = manager.connect(
                host=self._play_context.remote_addr,
                port=port,
                username=self._play_context.remote_user,
                password=self._play_context.password,
                key_filename=self.key_filename,
                hostkey_verify=self.get_option("host_key_checking"),
                look_for_keys=self.get_option("look_for_keys"),
                device_params=device_params,
                allow_agent=self._play_context.allow_agent,
                timeout=self.get_option("persistent_connect_timeout"),
                ssh_config=self._ssh_config,
            )

            self._manager._timeout = self.get_option(
                "persistent_command_timeout")
        except SSHUnknownHostError as exc:
            raise AssibleConnectionFailure(to_native(exc))
        except ImportError:
            raise AssibleError(
                "connection=netconf is not supported on {0}".format(
                    self._network_os))

        if not self._manager.connected:
            return 1, b"", b"not connected"

        self.queue_message("log",
                           "ncclient manager object created successfully")

        self._connected = True

        super(Connection, self)._connect()

        return (
            0,
            to_bytes(self._manager.session_id, errors="surrogate_or_strict"),
            b"",
        )
예제 #16
0
    def _connect_uncached(self):
        ''' activates the connection object '''

        if paramiko is None:
            raise AssibleError("paramiko is not installed: %s" %
                               to_native(PARAMIKO_IMPORT_ERR))

        port = self._play_context.port or 22
        display.vvv(
            "ESTABLISH PARAMIKO SSH CONNECTION FOR USER: %s on PORT %s TO %s" %
            (self._play_context.remote_user, port,
             self._play_context.remote_addr),
            host=self._play_context.remote_addr)

        ssh = paramiko.SSHClient()

        # override paramiko's default logger name
        if self._log_channel is not None:
            ssh.set_log_channel(self._log_channel)

        self.keyfile = os.path.expanduser("~/.ssh/known_hosts")

        if self.get_option('host_key_checking'):
            for ssh_known_hosts in ("/etc/ssh/ssh_known_hosts",
                                    "/etc/openssh/ssh_known_hosts"):
                try:
                    # TODO: check if we need to look at several possible locations, possible for loop
                    ssh.load_system_host_keys(ssh_known_hosts)
                    break
                except IOError:
                    pass  # file was not found, but not required to function
            ssh.load_system_host_keys()

        ssh_connect_kwargs = self._parse_proxy_command(port)

        ssh.set_missing_host_key_policy(MyAddPolicy(self._new_stdin, self))

        conn_password = self.get_option(
            'password') or self._play_context.password

        allow_agent = True

        if conn_password is not None:
            allow_agent = False

        try:
            key_filename = None
            if self._play_context.private_key_file:
                key_filename = os.path.expanduser(
                    self._play_context.private_key_file)

            # paramiko 2.2 introduced auth_timeout parameter
            if LooseVersion(paramiko.__version__) >= LooseVersion('2.2.0'):
                ssh_connect_kwargs['auth_timeout'] = self._play_context.timeout

            ssh.connect(self._play_context.remote_addr.lower(),
                        username=self._play_context.remote_user,
                        allow_agent=allow_agent,
                        look_for_keys=self.get_option('look_for_keys'),
                        key_filename=key_filename,
                        password=conn_password,
                        timeout=self._play_context.timeout,
                        port=port,
                        **ssh_connect_kwargs)
        except paramiko.ssh_exception.BadHostKeyException as e:
            raise AssibleConnectionFailure('host key mismatch for %s' %
                                           e.hostname)
        except paramiko.ssh_exception.AuthenticationException as e:
            msg = 'Failed to authenticate: {0}'.format(to_text(e))
            raise AssibleAuthenticationFailure(msg)
        except Exception as e:
            msg = to_text(e)
            if u"PID check failed" in msg:
                raise AssibleError(
                    "paramiko version issue, please upgrade paramiko on the machine running assible"
                )
            elif u"Private key file is encrypted" in msg:
                msg = 'ssh %s@%s:%s : %s\nTo connect as a different user, use -u <username>.' % (
                    self._play_context.remote_user,
                    self._play_context.remote_addr, port, msg)
                raise AssibleConnectionFailure(msg)
            else:
                raise AssibleConnectionFailure(msg)

        return ssh
예제 #17
0
    def exec_command(self, cmd, in_data=None, sudoable=True):
        ''' run a command on the remote host '''

        super(Connection, self).exec_command(cmd,
                                             in_data=in_data,
                                             sudoable=sudoable)

        if in_data:
            raise AssibleError(
                "Internal Error: this module does not support optimized module pipelining"
            )

        bufsize = 4096

        try:
            self.ssh.get_transport().set_keepalive(5)
            chan = self.ssh.get_transport().open_session()
        except Exception as e:
            text_e = to_text(e)
            msg = u"Failed to open session"
            if text_e:
                msg += u": %s" % text_e
            raise AssibleConnectionFailure(to_native(msg))

        # sudo usually requires a PTY (cf. requiretty option), therefore
        # we give it one by default (pty=True in assible.cfg), and we try
        # to initialise from the calling environment when sudoable is enabled
        if self.get_option('pty') and sudoable:
            chan.get_pty(term=os.getenv('TERM', 'vt100'),
                         width=int(os.getenv('COLUMNS', 0)),
                         height=int(os.getenv('LINES', 0)))

        display.vvv("EXEC %s" % cmd, host=self._play_context.remote_addr)

        cmd = to_bytes(cmd, errors='surrogate_or_strict')

        no_prompt_out = b''
        no_prompt_err = b''
        become_output = b''

        try:
            chan.exec_command(cmd)
            if self.become and self.become.expect_prompt():
                passprompt = False
                become_sucess = False
                while not (become_sucess or passprompt):
                    display.debug('Waiting for Privilege Escalation input')

                    chunk = chan.recv(bufsize)
                    display.debug("chunk is: %s" % chunk)
                    if not chunk:
                        if b'unknown user' in become_output:
                            n_become_user = to_native(
                                self.become.get_option(
                                    'become_user',
                                    playcontext=self._play_context))
                            raise AssibleError('user %s does not exist' %
                                               n_become_user)
                        else:
                            break
                            # raise AssibleError('ssh connection closed waiting for password prompt')
                    become_output += chunk

                    # need to check every line because we might get lectured
                    # and we might get the middle of a line in a chunk
                    for l in become_output.splitlines(True):
                        if self.become.check_success(l):
                            become_sucess = True
                            break
                        elif self.become.check_password_prompt(l):
                            passprompt = True
                            break

                if passprompt:
                    if self.become:
                        become_pass = self.become.get_option(
                            'become_pass', playcontext=self._play_context)
                        chan.sendall(
                            to_bytes(become_pass, errors='surrogate_or_strict')
                            + b'\n')
                    else:
                        raise AssibleError(
                            "A password is required but none was supplied")
                else:
                    no_prompt_out += become_output
                    no_prompt_err += become_output
        except socket.timeout:
            raise AssibleError(
                'ssh timed out waiting for privilege escalation.\n' +
                become_output)

        stdout = b''.join(chan.makefile('rb', bufsize))
        stderr = b''.join(chan.makefile_stderr('rb', bufsize))

        return (chan.recv_exit_status(), no_prompt_out + stdout,
                no_prompt_out + stderr)
예제 #18
0
    def _kerb_auth(self, principal, password):
        if password is None:
            password = ""

        self._kerb_ccache = tempfile.NamedTemporaryFile()
        display.vvvvv("creating Kerberos CC at %s" % self._kerb_ccache.name)
        krb5ccname = "FILE:%s" % self._kerb_ccache.name
        os.environ["KRB5CCNAME"] = krb5ccname
        krb5env = dict(KRB5CCNAME=krb5ccname)

        # Stores various flags to call with kinit, these could be explicit args set by 'assible_winrm_kinit_args' OR
        # '-f' if kerberos delegation is requested (assible_winrm_kerberos_delegation).
        kinit_cmdline = [self._kinit_cmd]
        kinit_args = self.get_option('kinit_args')
        if kinit_args:
            kinit_args = [
                to_text(a) for a in shlex.split(kinit_args) if a.strip()
            ]
            kinit_cmdline.extend(kinit_args)

        elif boolean(
                self.get_option('_extras').get(
                    'assible_winrm_kerberos_delegation', False)):
            kinit_cmdline.append('-f')

        kinit_cmdline.append(principal)

        # pexpect runs the process in its own pty so it can correctly send
        # the password as input even on MacOS which blocks subprocess from
        # doing so. Unfortunately it is not available on the built in Python
        # so we can only use it if someone has installed it
        if HAS_PEXPECT:
            proc_mechanism = "pexpect"
            command = kinit_cmdline.pop(0)
            password = to_text(password,
                               encoding='utf-8',
                               errors='surrogate_or_strict')

            display.vvvv("calling kinit with pexpect for principal %s" %
                         principal)
            try:
                child = pexpect.spawn(command,
                                      kinit_cmdline,
                                      timeout=60,
                                      env=krb5env,
                                      echo=False)
            except pexpect.ExceptionPexpect as err:
                err_msg = "Kerberos auth failure when calling kinit cmd " \
                          "'%s': %s" % (command, to_native(err))
                raise AssibleConnectionFailure(err_msg)

            try:
                child.expect(".*:")
                child.sendline(password)
            except OSError as err:
                # child exited before the pass was sent, Assible will raise
                # error based on the rc below, just display the error here
                display.vvvv("kinit with pexpect raised OSError: %s" %
                             to_native(err))

            # technically this is the stdout + stderr but to match the
            # subprocess error checking behaviour, we will call it stderr
            stderr = child.read()
            child.wait()
            rc = child.exitstatus
        else:
            proc_mechanism = "subprocess"
            password = to_bytes(password,
                                encoding='utf-8',
                                errors='surrogate_or_strict')

            display.vvvv("calling kinit with subprocess for principal %s" %
                         principal)
            try:
                p = subprocess.Popen(kinit_cmdline,
                                     stdin=subprocess.PIPE,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE,
                                     env=krb5env)

            except OSError as err:
                err_msg = "Kerberos auth failure when calling kinit cmd " \
                          "'%s': %s" % (self._kinit_cmd, to_native(err))
                raise AssibleConnectionFailure(err_msg)

            stdout, stderr = p.communicate(password + b'\n')
            rc = p.returncode != 0

        if rc != 0:
            # one last attempt at making sure the password does not exist
            # in the output
            exp_msg = to_native(stderr.strip())
            exp_msg = exp_msg.replace(to_native(password), "<redacted>")

            err_msg = "Kerberos auth failure for principal %s with %s: %s" \
                      % (principal, proc_mechanism, exp_msg)
            raise AssibleConnectionFailure(err_msg)

        display.vvvvv("kinit succeeded for principal %s" % principal)
예제 #19
0
 def exec_command(self, cmd, in_data=None, sudoable=True):
     display.debug('Intercepted call to exec remote command')
     raise AssibleConnectionFailure(
         'BADLOCAL Error: this is supposed to fail')
예제 #20
0
    def _winrm_connect(self):
        '''
        Establish a WinRM connection over HTTP/HTTPS.
        '''
        display.vvv(
            "ESTABLISH WINRM CONNECTION FOR USER: %s on PORT %s TO %s" %
            (self._winrm_user, self._winrm_port, self._winrm_host),
            host=self._winrm_host)

        winrm_host = self._winrm_host
        if HAS_IPADDRESS:
            display.debug("checking if winrm_host %s is an IPv6 address" %
                          winrm_host)
            try:
                ipaddress.IPv6Address(winrm_host)
            except ipaddress.AddressValueError:
                pass
            else:
                winrm_host = "[%s]" % winrm_host

        netloc = '%s:%d' % (winrm_host, self._winrm_port)
        endpoint = urlunsplit(
            (self._winrm_scheme, netloc, self._winrm_path, '', ''))
        errors = []
        for transport in self._winrm_transport:
            if transport == 'kerberos':
                if not HAVE_KERBEROS:
                    errors.append(
                        'kerberos: the python kerberos library is not installed'
                    )
                    continue
                if self._kerb_managed:
                    self._kerb_auth(self._winrm_user, self._winrm_pass)
            display.vvvvv('WINRM CONNECT: transport=%s endpoint=%s' %
                          (transport, endpoint),
                          host=self._winrm_host)
            try:
                winrm_kwargs = self._winrm_kwargs.copy()
                if self._winrm_connection_timeout:
                    winrm_kwargs[
                        'operation_timeout_sec'] = self._winrm_connection_timeout
                    winrm_kwargs[
                        'read_timeout_sec'] = self._winrm_connection_timeout + 1
                protocol = Protocol(endpoint,
                                    transport=transport,
                                    **winrm_kwargs)

                # open the shell from connect so we know we're able to talk to the server
                if not self.shell_id:
                    self.shell_id = protocol.open_shell(
                        codepage=65001)  # UTF-8
                    display.vvvvv('WINRM OPEN SHELL: %s' % self.shell_id,
                                  host=self._winrm_host)

                return protocol
            except Exception as e:
                err_msg = to_text(e).strip()
                if re.search(to_text(r'Operation\s+?timed\s+?out'), err_msg,
                             re.I):
                    raise AssibleError('the connection attempt timed out')
                m = re.search(to_text(r'Code\s+?(\d{3})'), err_msg)
                if m:
                    code = int(m.groups()[0])
                    if code == 401:
                        err_msg = 'the specified credentials were rejected by the server'
                    elif code == 411:
                        return protocol
                errors.append(u'%s: %s' % (transport, err_msg))
                display.vvvvv(u'WINRM CONNECTION ERROR: %s\n%s' %
                              (err_msg, to_text(traceback.format_exc())),
                              host=self._winrm_host)
        if errors:
            raise AssibleConnectionFailure(', '.join(map(to_native, errors)))
        else:
            raise AssibleError('No transport found for WinRM connection')
예제 #21
0
 def _validate_timeout_value(self, timeout, timer_name):
     if timeout < 0:
         raise AssibleConnectionFailure(
             "'%s' timer value '%s' is invalid, value should be greater than or equal to zero."
             % (timer_name, timeout))
예제 #22
0
 def _handle_command_timeout(self, signum, frame):
     msg = (
         "command timeout triggered, timeout value is %s secs.\nSee the timeout setting options in the Network Debug and Troubleshooting Guide."
         % self.get_option("persistent_command_timeout"))
     self.queue_message("log", msg)
     raise AssibleConnectionFailure(msg)
예제 #23
0
    def _winrm_exec(self,
                    command,
                    args=(),
                    from_exec=False,
                    stdin_iterator=None):
        if not self.protocol:
            self.protocol = self._winrm_connect()
            self._connected = True
        if from_exec:
            display.vvvvv("WINRM EXEC %r %r" % (command, args),
                          host=self._winrm_host)
        else:
            display.vvvvvv("WINRM EXEC %r %r" % (command, args),
                           host=self._winrm_host)
        command_id = None
        try:
            stdin_push_failed = False
            command_id = self.protocol.run_command(
                self.shell_id,
                to_bytes(command),
                map(to_bytes, args),
                console_mode_stdin=(stdin_iterator is None))

            try:
                if stdin_iterator:
                    for (data, is_last) in stdin_iterator:
                        self._winrm_send_input(self.protocol,
                                               self.shell_id,
                                               command_id,
                                               data,
                                               eof=is_last)

            except Exception as ex:
                display.warning(
                    "ERROR DURING WINRM SEND INPUT - attempting to recover: %s %s"
                    % (type(ex).__name__, to_text(ex)))
                display.debug(traceback.format_exc())
                stdin_push_failed = True

            # NB: this can hang if the receiver is still running (eg, network failed a Send request but the server's still happy).
            # FUTURE: Consider adding pywinrm status check/abort operations to see if the target is still running after a failure.
            resptuple = self.protocol.get_command_output(
                self.shell_id, command_id)
            # ensure stdout/stderr are text for py3
            # FUTURE: this should probably be done internally by pywinrm
            response = Response(
                tuple(
                    to_text(v) if isinstance(v, binary_type) else v
                    for v in resptuple))

            # TODO: check result from response and set stdin_push_failed if we have nonzero
            if from_exec:
                display.vvvvv('WINRM RESULT %r' % to_text(response),
                              host=self._winrm_host)
            else:
                display.vvvvvv('WINRM RESULT %r' % to_text(response),
                               host=self._winrm_host)

            display.vvvvvv('WINRM STDOUT %s' % to_text(response.std_out),
                           host=self._winrm_host)
            display.vvvvvv('WINRM STDERR %s' % to_text(response.std_err),
                           host=self._winrm_host)

            if stdin_push_failed:
                # There are cases where the stdin input failed but the WinRM service still processed it. We attempt to
                # see if stdout contains a valid json return value so we can ignore this error
                try:
                    filtered_output, dummy = _filter_non_json_lines(
                        response.std_out)
                    json.loads(filtered_output)
                except ValueError:
                    # stdout does not contain a return response, stdin input was a fatal error
                    stderr = to_bytes(response.std_err, encoding='utf-8')
                    if stderr.startswith(b"#< CLIXML"):
                        stderr = _parse_clixml(stderr)

                    raise AssibleError(
                        'winrm send_input failed; \nstdout: %s\nstderr %s' %
                        (to_native(response.std_out), to_native(stderr)))

            return response
        except requests.exceptions.Timeout as exc:
            raise AssibleConnectionFailure('winrm connection error: %s' %
                                           to_native(exc))
        finally:
            if command_id:
                self.protocol.cleanup_command(self.shell_id, command_id)
예제 #24
0
    def receive(
        self,
        command=None,
        prompts=None,
        answer=None,
        newline=True,
        prompt_retry_check=False,
        check_all=False,
    ):
        """
        Handles receiving of output from command
        """
        self._matched_prompt = None
        self._matched_cmd_prompt = None
        recv = BytesIO()
        handled = False
        command_prompt_matched = False
        matched_prompt_window = window_count = 0

        # set terminal regex values for command prompt and errors in response
        self._terminal_stderr_re = self._get_terminal_std_re(
            "terminal_stderr_re")
        self._terminal_stdout_re = self._get_terminal_std_re(
            "terminal_stdout_re")

        cache_socket_timeout = self._ssh_shell.gettimeout()
        command_timeout = self.get_option("persistent_command_timeout")
        self._validate_timeout_value(command_timeout,
                                     "persistent_command_timeout")
        if cache_socket_timeout != command_timeout:
            self._ssh_shell.settimeout(command_timeout)

        buffer_read_timeout = self.get_option("persistent_buffer_read_timeout")
        self._validate_timeout_value(buffer_read_timeout,
                                     "persistent_buffer_read_timeout")

        self._log_messages("command: %s" % command)
        while True:
            if command_prompt_matched:
                try:
                    signal.signal(signal.SIGALRM,
                                  self._handle_buffer_read_timeout)
                    signal.setitimer(signal.ITIMER_REAL, buffer_read_timeout)
                    data = self._ssh_shell.recv(256)
                    signal.alarm(0)
                    self._log_messages("response-%s: %s" %
                                       (window_count + 1, data))
                    # if data is still received on channel it indicates the prompt string
                    # is wrongly matched in between response chunks, continue to read
                    # remaining response.
                    command_prompt_matched = False

                    # restart command_timeout timer
                    signal.signal(signal.SIGALRM, self._handle_command_timeout)
                    signal.alarm(command_timeout)

                except AssibleCmdRespRecv:
                    # reset socket timeout to global timeout
                    self._ssh_shell.settimeout(cache_socket_timeout)
                    return self._command_response
            else:
                data = self._ssh_shell.recv(256)
                self._log_messages("response-%s: %s" %
                                   (window_count + 1, data))
            # when a channel stream is closed, received data will be empty
            if not data:
                break

            recv.write(data)
            offset = recv.tell() - 256 if recv.tell() > 256 else 0
            recv.seek(offset)

            window = self._strip(recv.read())
            self._last_recv_window = window
            window_count += 1

            if prompts and not handled:
                handled = self._handle_prompt(window, prompts, answer, newline,
                                              False, check_all)
                matched_prompt_window = window_count
            elif (prompts and handled and prompt_retry_check
                  and matched_prompt_window + 1 == window_count):
                # check again even when handled, if same prompt repeats in next window
                # (like in the case of a wrong enable password, etc) indicates
                # value of answer is wrong, report this as error.
                if self._handle_prompt(
                        window,
                        prompts,
                        answer,
                        newline,
                        prompt_retry_check,
                        check_all,
                ):
                    raise AssibleConnectionFailure(
                        "For matched prompt '%s', answer is not valid" %
                        self._matched_cmd_prompt)

            if self._find_prompt(window):
                self._last_response = recv.getvalue()
                resp = self._strip(self._last_response)
                self._command_response = self._sanitize(resp, command)
                if buffer_read_timeout == 0.0:
                    # reset socket timeout to global timeout
                    self._ssh_shell.settimeout(cache_socket_timeout)
                    return self._command_response
                else:
                    command_prompt_matched = True
예제 #25
0
    def _connect(self):
        """
        Connects to the remote device and starts the terminal
        """
        if not self.connected:
            self.paramiko_conn._set_log_channel(self._get_log_channel())
            self.paramiko_conn.force_persistence = self.force_persistence

            command_timeout = self.get_option("persistent_command_timeout")
            max_pause = min([
                self.get_option("persistent_connect_timeout"),
                command_timeout,
            ])
            retries = self.get_option("network_cli_retries")
            total_pause = 0

            for attempt in range(retries + 1):
                try:
                    ssh = self.paramiko_conn._connect()
                    break
                except Exception as e:
                    pause = 2**(attempt + 1)
                    if attempt == retries or total_pause >= max_pause:
                        raise AssibleConnectionFailure(
                            to_text(e, errors="surrogate_or_strict"))
                    else:
                        msg = (
                            u"network_cli_retry: attempt: %d, caught exception(%s), "
                            u"pausing for %d seconds" % (
                                attempt + 1,
                                to_text(e, errors="surrogate_or_strict"),
                                pause,
                            ))

                        self.queue_message("vv", msg)
                        time.sleep(pause)
                        total_pause += pause
                        continue

            self.queue_message("vvvv", "ssh connection done, setting terminal")
            self._connected = True

            self._ssh_shell = ssh.ssh.invoke_shell()
            self._ssh_shell.settimeout(command_timeout)

            self.queue_message(
                "vvvv",
                "loaded terminal plugin for network_os %s" % self._network_os,
            )

            terminal_initial_prompt = (
                self.get_option("terminal_initial_prompt")
                or self._terminal.terminal_initial_prompt)
            terminal_initial_answer = (
                self.get_option("terminal_initial_answer")
                or self._terminal.terminal_initial_answer)
            newline = (self.get_option("terminal_inital_prompt_newline")
                       or self._terminal.terminal_inital_prompt_newline)
            check_all = (self.get_option("terminal_initial_prompt_checkall")
                         or False)

            self.receive(
                prompts=terminal_initial_prompt,
                answer=terminal_initial_answer,
                newline=newline,
                check_all=check_all,
            )

            if self._play_context.become:
                self.queue_message("vvvv", "firing event: on_become")
                auth_pass = self._play_context.become_pass
                self._terminal.on_become(passwd=auth_pass)

            self.queue_message("vvvv", "firing event: on_open_shell()")
            self._terminal.on_open_shell()

            self.queue_message("vvvv",
                               "ssh connection has completed successfully")

        return self
예제 #26
0
 def put_file(self, in_path, out_path):
     display.debug('Intercepted call to send data')
     raise AssibleConnectionFailure(
         'BADLOCAL Error: this is supposed to fail')