def __init__(self, play_context, new_stdin, *args, **kwargs): super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs) self._network_os = self._play_context.network_os or 'default' display.display('network_os is set to %s' % self._network_os, log_only=True) self._netconf = None self._manager = None self._connected = False self._local = LocalConnection(play_context, new_stdin, *args, **kwargs)
def create_action_module(name, args=None, task_vars=None): play = Play.load(dict()) play_context = PlayContext(play=play) module = LintActionModule(task=Task.load(data=dict(local_action=name, args=args), block=Block(play=play)), connection=Connection(play_context, new_stdin=False), play_context=play_context, loader=play._loader, templar=Templar(play._loader), shared_loader_obj=None) module.use_display(NullDisplay()) return module
def __init__(self, play_context, new_stdin, *args, **kwargs): super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs) self.ssh = None self._ssh_shell = None self._matched_prompt = None self._matched_pattern = None self._last_response = None self._history = list() self._local = LocalConnection(play_context, new_stdin, *args, **kwargs) self._terminal = None self._cliconf = None if self._play_context.verbosity > 3: logging.getLogger('paramiko').setLevel(logging.DEBUG) # reconstruct the socket_path and set instance values accordingly self._update_connection_state()
class Connection(ConnectionBase): """NetConf connections""" transport = 'netconf' has_pipelining = False force_persistence = True def __init__(self, play_context, new_stdin, *args, **kwargs): super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs) self._network_os = self._play_context.network_os or 'default' display.display('network_os is set to %s' % self._network_os, log_only=True) self._manager = None self._connected = False self._local = LocalConnection(play_context, new_stdin, *args, **kwargs) def exec_command(self, request, in_data=None, sudoable=True): """Sends the request to the node and returns the reply The method accepts two forms of request. The first form is as a byte string that represents xml string be send over netconf session. The second form is a json-rpc (2.0) byte string. """ if self._manager: # to_ele operates on native strings request = to_ele(to_native(request, errors='surrogate_or_strict')) if request is None: return 'unable to parse request' try: reply = self._manager.rpc(request) except RPCError as exc: return to_xml(exc.xml) return reply.data_xml else: return self._local.exec_command(request, in_data, sudoable) def put_file(self, in_path, out_path): """Transfer a file from local to remote""" return self._local.put_file(in_path, out_path) def fetch_file(self, in_path, out_path): """Fetch a file from remote to local""" return self._local.fetch_file(in_path, out_path) def _connect(self): super(Connection, self)._connect() display.display('ssh connection done, starting ncclient', log_only=True) allow_agent = True if self._play_context.password is not None: allow_agent = False key_filename = None if self._play_context.private_key_file: key_filename = os.path.expanduser( self._play_context.private_key_file) network_os = self._play_context.network_os if not network_os: for cls in netconf_loader.all(class_only=True): network_os = cls.guess_network_os(self) if network_os: display.display('discovered network_os %s' % network_os, log_only=True) if not network_os: raise AnsibleConnectionFailure( 'Unable to automatically determine host network os. Please ansible_network_os value' ) ssh_config = os.getenv('ANSIBLE_NETCONF_SSH_CONFIG', False) if ssh_config in BOOLEANS_TRUE: ssh_config = True else: ssh_config = None try: self._manager = manager.connect( host=self._play_context.remote_addr, port=self._play_context.port or 830, username=self._play_context.remote_user, password=self._play_context.password, key_filename=str(key_filename), hostkey_verify=C.HOST_KEY_CHECKING, look_for_keys=C.PARAMIKO_LOOK_FOR_KEYS, allow_agent=allow_agent, timeout=self._play_context.timeout, device_params={'name': network_os}, ssh_config=ssh_config) except SSHUnknownHostError as exc: raise AnsibleConnectionFailure(str(exc)) except ImportError as exc: raise AnsibleError( "connection=netconf is not supported on {0}".format( network_os)) if not self._manager.connected: return 1, b'', b'not connected' display.display('ncclient manager object created successfully', log_only=True) self._connected = True self._netconf = netconf_loader.get(network_os, self) if self._netconf: display.display('loaded netconf plugin for network_os %s' % network_os, log_only=True) else: display.display('unable to load netconf for network_os %s' % network_os) return 0, to_bytes(self._manager.session_id, errors='surrogate_or_strict'), b'' def close(self): if self._manager: self._manager.close_session() self._connected = False super(Connection, self).close()
class Connection(ConnectionBase): """NetConf connections""" transport = 'netconf' has_pipelining = False force_persistence = True # Do not use _remote_is_local in other connections _remote_is_local = True def __init__(self, play_context, new_stdin, *args, **kwargs): super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs) self._network_os = self._play_context.network_os or 'default' display.display('network_os is set to %s' % self._network_os, log_only=True) self._netconf = None self._manager = None self._connected = False self._local = LocalConnection(play_context, new_stdin, *args, **kwargs) def __getattr__(self, name): try: return self.__dict__[name] except KeyError: if name.startswith('_'): raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name)) return getattr(self._netconf, name) def exec_command(self, request, in_data=None, sudoable=True): """Sends the request to the node and returns the reply The method accepts two forms of request. The first form is as a byte string that represents xml string be send over netconf session. The second form is a json-rpc (2.0) byte string. """ if self._manager: # to_ele operates on native strings request = to_ele(to_native(request, errors='surrogate_or_strict')) if request is None: return 'unable to parse request' try: reply = self._manager.rpc(request) except RPCError as exc: error = self.internal_error(data=to_text( to_xml(exc.xml), errors='surrogate_or_strict')) return json.dumps(error) return reply.data_xml else: return self._local.exec_command(request, in_data, sudoable) def put_file(self, in_path, out_path): """Transfer a file from local to remote""" return self._local.put_file(in_path, out_path) def fetch_file(self, in_path, out_path): """Fetch a file from remote to local""" return self._local.fetch_file(in_path, out_path) def _connect(self): super(Connection, self)._connect() display.display('ssh connection done, starting ncclient', log_only=True) allow_agent = True if self._play_context.password is not None: allow_agent = False setattr(self._play_context, 'allow_agent', allow_agent) key_filename = None if self._play_context.private_key_file: key_filename = os.path.expanduser( self._play_context.private_key_file) network_os = self._play_context.network_os if not network_os: for cls in netconf_loader.all(class_only=True): network_os = cls.guess_network_os(self) if network_os: display.display('discovered network_os %s' % network_os, log_only=True) device_params = { 'name': (NETWORK_OS_DEVICE_PARAM_MAP.get(network_os) or network_os or 'default') } ssh_config = os.getenv('ANSIBLE_NETCONF_SSH_CONFIG', False) if ssh_config in BOOLEANS_TRUE: ssh_config = True else: ssh_config = None try: self._manager = manager.connect( host=self._play_context.remote_addr, port=self._play_context.port or 830, username=self._play_context.remote_user, password=self._play_context.password, key_filename=str(key_filename), hostkey_verify=C.HOST_KEY_CHECKING, look_for_keys=C.PARAMIKO_LOOK_FOR_KEYS, device_params=device_params, allow_agent=self._play_context.allow_agent, timeout=self._play_context.timeout, ssh_config=ssh_config) except SSHUnknownHostError as exc: raise AnsibleConnectionFailure(str(exc)) except ImportError as exc: raise AnsibleError( "connection=netconf is not supported on {0}".format( network_os)) if not self._manager.connected: return 1, b'', b'not connected' display.display('ncclient manager object created successfully', log_only=True) self._connected = True self._netconf = netconf_loader.get(network_os, self) if self._netconf: display.display('loaded netconf plugin for network_os %s' % network_os, log_only=True) else: self._netconf = netconf_loader.get("default", self) display.display( 'unable to load netconf plugin for network_os %s, falling back to default plugin' % network_os) return 0, to_bytes(self._manager.session_id, errors='surrogate_or_strict'), b'' def reset(self): ''' Reset the connection ''' if self._socket_path: display.vvvv('resetting persistent connection for socket_path %s' % self._socket_path, host=self._play_context.remote_addr) self.close() display.vvvv('reset call on connection instance', host=self._play_context.remote_addr) def close(self): if self._manager: self._manager.close_session() self._connected = False super(Connection, self).close()
class Connection(ConnectionBase): ''' CLI (shell) SSH connections on Paramiko ''' transport = 'network_cli' has_pipelining = True force_persistence = True def __init__(self, play_context, new_stdin, *args, **kwargs): super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs) self.ssh = None self._ssh_shell = None self._matched_prompt = None self._matched_pattern = None self._last_response = None self._history = list() self._local = LocalConnection(play_context, new_stdin, *args, **kwargs) self._terminal = None self._cliconf = None if self._play_context.verbosity > 3: logging.getLogger('paramiko').setLevel(logging.DEBUG) # reconstruct the socket_path and set instance values accordingly self._update_connection_state() def __getattr__(self, name): try: return self.__dict__[name] except KeyError: if name.startswith('_'): raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name)) return getattr(self._cliconf, name) def exec_command(self, cmd, in_data=None, sudoable=True): # this try..except block is just to handle the transition to supporting # network_cli as a toplevel connection. Once connection=local is gone, # this block can be removed as well and all calls passed directly to # the local connection if self._ssh_shell: try: cmd = json.loads(to_text(cmd, errors='surrogate_or_strict')) kwargs = { 'command': to_bytes(cmd['command'], errors='surrogate_or_strict') } for key in ('prompts', 'answer', 'send_only'): if key in cmd: kwargs[key] = to_bytes(cmd[key], errors='surrogate_or_strict') return self.send(**kwargs) except ValueError: cmd = to_bytes(cmd, errors='surrogate_or_strict') return self.send(command=cmd) else: return self._local.exec_command(cmd, in_data, sudoable) def put_file(self, in_path, out_path): return self._local.put_file(in_path, out_path) def fetch_file(self, in_path, out_path): return self._local.fetch_file(in_path, out_path) def update_play_context(self, play_context): """Updates the play context information for the connection""" display.vvvv('updating play_context for connection', host=self._play_context.remote_addr) if self._play_context.become is False and play_context.become is True: auth_pass = play_context.become_pass self._terminal.on_authorize(passwd=auth_pass) elif self._play_context.become is True and not play_context.become: self._terminal.on_deauthorize() self._play_context = play_context def _connect(self): ''' Connects to the remote device and starts the terminal ''' if self.connected: return if self._play_context.password and not self._play_context.private_key_file: C.PARAMIKO_LOOK_FOR_KEYS = False ssh = ParamikoSshConnection(self._play_context, '/dev/null')._connect() self.ssh = ssh.ssh display.vvvv('ssh connection done, setting terminal', host=self._play_context.remote_addr) self._ssh_shell = self.ssh.invoke_shell() self._ssh_shell.settimeout(self._play_context.timeout) network_os = self._play_context.network_os if not network_os: raise AnsibleConnectionFailure( 'Unable to automatically determine host network os. Please ' 'manually configure ansible_network_os value for this host') self._terminal = terminal_loader.get(network_os, self) if not self._terminal: raise AnsibleConnectionFailure('network os %s is not supported' % network_os) display.vvvv('loaded terminal plugin for network_os %s' % network_os, host=self._play_context.remote_addr) self._cliconf = cliconf_loader.get(network_os, self) if self._cliconf: display.vvvv('loaded cliconf plugin for network_os %s' % network_os, host=self._play_context.remote_addr) else: display.vvvv('unable to load cliconf for network_os %s' % network_os) self.receive() display.vvvv('firing event: on_open_shell()', host=self._play_context.remote_addr) self._terminal.on_open_shell() if self._play_context.become and self._play_context.become_method == 'enable': display.vvvv('firing event: on_authorize', host=self._play_context.remote_addr) auth_pass = self._play_context.become_pass self._terminal.on_authorize(passwd=auth_pass) display.vvvv('ssh connection has completed successfully', host=self._play_context.remote_addr) self._connected = True return self def _update_connection_state(self): ''' Reconstruct the connection socket_path and check if it exists If the socket path exists then the connection is active and set both the _socket_path value to the path and the _connected value to True. If the socket path doesn't exist, leave the socket path value to None and the _connected value to False ''' ssh = connection_loader.get('ssh', class_only=True) cp = ssh._create_control_path(self._play_context.remote_addr, self._play_context.port, self._play_context.remote_user) tmp_path = unfrackpath(C.PERSISTENT_CONTROL_PATH_DIR) socket_path = unfrackpath(cp % dict(directory=tmp_path)) if os.path.exists(socket_path): self._connected = True self._socket_path = socket_path def reset(self): ''' Reset the connection ''' if self._socket_path: display.vvvv('resetting persistent connection for socket_path %s' % self._socket_path, host=self._play_context.remote_addr) self.shutdown() def close(self): ''' Close the active connection to the device ''' # only close the connection if its connected. if self._connected: display.debug("closing ssh connection to device") if self._ssh_shell: display.debug("firing event: on_close_shell()") self._terminal.on_close_shell() self._ssh_shell.close() self._ssh_shell = None display.debug("cli session is now closed") self._connected = False display.debug("ssh connection has been closed successfully") def receive(self, command=None, prompts=None, answer=None): ''' Handles receiving of output from command ''' recv = BytesIO() handled = False self._matched_prompt = None while True: data = self._ssh_shell.recv(256) recv.write(data) offset = recv.tell() - 256 if recv.tell() > 256 else 0 recv.seek(offset) window = self._strip(recv.read()) if prompts and not handled: handled = self._handle_prompt(window, prompts, answer) if self._find_prompt(window): self._last_response = recv.getvalue() resp = self._strip(self._last_response) return self._sanitize(resp, command) def send(self, command, prompts=None, answer=None, send_only=False): ''' Sends the command to the device in the opened shell ''' try: self._history.append(command) self._ssh_shell.sendall(b'%s\r' % command) if send_only: return response = self.receive(command, prompts, answer) return to_text(response, errors='surrogate_or_strict') except (socket.timeout, AttributeError): display.vvvv(traceback.format_exc(), host=self._play_context.remote_addr) raise AnsibleConnectionFailure( "timeout trying to send command: %s" % command.strip()) def _strip(self, data): ''' Removes ANSI codes from device response ''' for regex in self._terminal.ansi_re: data = regex.sub(b'', data) return data def _handle_prompt(self, resp, prompts, answer): ''' Matches the command prompt and responds :arg resp: Byte string containing the raw response from the remote :arg prompts: Sequence of byte strings that we consider prompts for input :arg answer: Byte string to send back to the remote if we find a prompt. A carriage return is automatically appended to this string. :returns: True if a prompt was found in ``resp``. False otherwise ''' if not isinstance(prompts, list): prompts = [prompts] prompts = [re.compile(r, re.I) for r in prompts] for regex in prompts: match = regex.search(resp) if match: self._ssh_shell.sendall(b'%s\r' % answer) return True return False def _sanitize(self, resp, command=None): ''' Removes elements from the response before returning to the caller ''' cleaned = [] for line in resp.splitlines(): if (command and line.strip() == command.strip() ) or self._matched_prompt.strip() in line: continue cleaned.append(line) return b'\n'.join(cleaned).strip() 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.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.terminal_stdout_re: match = regex.search(response) if match: errored_response = response self._matched_prompt = match.group() break if not is_error_message: for regex in self._terminal.terminal_stdout_re: match = regex.search(response) if match: self._matched_pattern = regex.pattern self._matched_prompt = match.group() if not errored_response: return True if errored_response: raise AnsibleConnectionFailure(errored_response) return False
class Connection(ConnectionBase): """NetConf connections""" transport = 'netconf' has_pipelining = False force_persistence = True def __init__(self, play_context, new_stdin, *args, **kwargs): super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs) self._network_os = self._play_context.network_os or 'default' display.display('network_os is set to %s' % self._network_os, log_only=True) self._netconf = None self._manager = None self._connected = False self._local = LocalConnection(play_context, new_stdin, *args, **kwargs) def __getattr__(self, name): try: return self.__dict__[name] except KeyError: if name.startswith('_'): raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name)) return getattr(self._netconf, name) def exec_command(self, request, in_data=None, sudoable=True): """Sends the request to the node and returns the reply The method accepts two forms of request. The first form is as a byte string that represents xml string be send over netconf session. The second form is a json-rpc (2.0) byte string. """ if self._manager: # to_ele operates on native strings request = to_ele(to_native(request, errors='surrogate_or_strict')) if request is None: return 'unable to parse request' try: reply = self._manager.rpc(request) except RPCError as exc: error = self.internal_error(data=to_text(to_xml(exc.xml), errors='surrogate_or_strict')) return json.dumps(error) return reply.data_xml else: return self._local.exec_command(request, in_data, sudoable) def put_file(self, in_path, out_path): """Transfer a file from local to remote""" return self._local.put_file(in_path, out_path) def fetch_file(self, in_path, out_path): """Fetch a file from remote to local""" return self._local.fetch_file(in_path, out_path) def _connect(self): super(Connection, self)._connect() display.display('ssh connection done, starting ncclient', log_only=True) allow_agent = True if self._play_context.password is not None: allow_agent = False setattr(self._play_context, 'allow_agent', allow_agent) key_filename = None if self._play_context.private_key_file: key_filename = os.path.expanduser(self._play_context.private_key_file) network_os = self._play_context.network_os if not network_os: for cls in netconf_loader.all(class_only=True): network_os = cls.guess_network_os(self) if network_os: display.display('discovered network_os %s' % network_os, log_only=True) device_params = {'name': (NETWORK_OS_DEVICE_PARAM_MAP.get(network_os) or network_os or 'default')} ssh_config = os.getenv('ANSIBLE_NETCONF_SSH_CONFIG', False) if ssh_config in BOOLEANS_TRUE: ssh_config = True else: ssh_config = None try: self._manager = manager.connect( host=self._play_context.remote_addr, port=self._play_context.port or 830, username=self._play_context.remote_user, password=self._play_context.password, key_filename=str(key_filename), hostkey_verify=C.HOST_KEY_CHECKING, look_for_keys=C.PARAMIKO_LOOK_FOR_KEYS, device_params=device_params, allow_agent=self._play_context.allow_agent, timeout=self._play_context.timeout, ssh_config=ssh_config ) except SSHUnknownHostError as exc: raise AnsibleConnectionFailure(str(exc)) except ImportError as exc: raise AnsibleError("connection=netconf is not supported on {0}".format(network_os)) if not self._manager.connected: return 1, b'', b'not connected' display.display('ncclient manager object created successfully', log_only=True) self._connected = True self._netconf = netconf_loader.get(network_os, self) if self._netconf: display.display('loaded netconf plugin for network_os %s' % network_os, log_only=True) else: self._netconf = netconf_loader.get("default", self) display.display('unable to load netconf plugin for network_os %s, falling back to default plugin' % network_os) return 0, to_bytes(self._manager.session_id, errors='surrogate_or_strict'), b'' def reset(self): ''' Reset the connection ''' if self._socket_path: display.vvvv('resetting persistent connection for socket_path %s' % self._socket_path, host=self._play_context.remote_addr) self.close() display.vvvv('reset call on connection instance', host=self._play_context.remote_addr) def close(self): if self._manager: self._manager.close_session() self._connected = False super(Connection, self).close()