def run_commands(self, commands, encoding='json'): """Sends the commands over the transport to the device This method sends the commands to the device using the nodes transport. This is a lower layer function that shouldn't normally need to be used, prefering instead to use config() or enable(). Args: commands (list): The ordered list of commands to send to the device using the transport encoding (str): The encoding method to use for the request and excpected response. Returns: This method will return the raw response from the connection which is a Python dictionary object. """ commands = make_iterable(commands) if self._enablepwd: commands.insert(0, {'cmd': 'enable', 'input': self._enablepwd}) else: commands.insert(0, 'enable') response = self._connection.execute(commands, encoding) # pop enable command from the response response['result'].pop(0) return response['result']
def config(self, commands): """Configures the node with the specified commands This method is used to send configuration commands to the node. It will take either a string or a list and prepend the necessary commands to put the session into config mode. Args: commands (str, list): The commands to send to the node in config mode. If the commands argument is a string it will be cast to a list. The list of commands will also be prepended with the necessary commands to put the session in config mode. Returns: The config method will return a list of dictionaries with the output from each command. The function will strip the response from any commands it prepends. """ commands = make_iterable(commands) commands = list(commands) # push the configure command onto the command stack commands.insert(0, 'configure terminal') response = self.run_commands(commands) if self.autorefresh: self.refresh() # pop the configure command output off the stack response.pop(0) return response
def autoload(self): """ Loads the eapi.conf file This method will use the module variable CONFIG_SEARCH_PATH to attempt to locate a valid eapi.conf file if a filename is not already configured. This method will load the first eapi.conf file it finds and then return. The CONFIG_SEARCH_PATH can be overridden using an environment variable by setting EAPI_CONF. """ path = list(CONFIG_SEARCH_PATH) if 'EAPI_CONF' in os.environ: path = os.environ['EAPI_CONF'] elif self.filename: path = self.filename path = make_iterable(path) for filename in path: filename = os.path.expanduser(filename) if os.path.exists(filename): self.filename = filename return self.read(filename) self._add_default_connection()
def config(self, commands): """Configures the node with the specified commands This method is used to send configuration commands to the node. It will take either a string or a list and prepend the necessary commands to put the session into config mode. Args: commands (str, list): The commands to send to the node in config mode. If the commands argument is a string it will be cast to a list. The list of commands will also be prepended with the necessary commands to put the session in config mode. Returns: The config method will return a list of dictionaries with the output from each command. The function will strip the response from any commands it prepends. """ commands = make_iterable(commands) commands = list(commands) # push the configure command onto the command stack commands.insert(0, 'configure') response = self.run_commands(commands) if self.autorefresh: self.refresh() # pop the configure command output off the stack response.pop(0) return response
def set_trunk_groups(self, intf, value=None, default=False, disable=False): """Configures the switchport trunk group value Args: intf (str): The interface identifier to configure. value (str): The set of values to configure the trunk group default (bool): Configures the trunk group default value disable (bool): Negates all trunk group settings Returns: True if the config operation succeeds otherwise False """ if default: cmd = 'default switchport trunk group' return self.configure_interface(intf, cmd) if disable: cmd = 'no switchport trunk group' return self.configure_interface(intf, cmd) current_value = self.get(intf)['trunk_groups'] failure = False value = make_iterable(value) for name in set(value).difference(current_value): if not self.add_trunk_group(intf, name): failure = True for name in set(current_value).difference(value): if not self.remove_trunk_group(intf, name): failure = True return not failure
def request(self, commands, encoding=None, reqid=None, **kwargs): """Generates an eAPI request object This method will take a list of EOS commands and generate a valid eAPI request object form them. The eAPI request object is then JSON encoding and returned to the caller. eAPI Request Object .. code-block:: json { "jsonrpc": "2.0", "method": "runCmds", "params": { "version": 1, "cmds": [ <commands> ], "format": [json, text], } "id": <reqid> } Args: commands (list): A list of commands to include in the eAPI request object encoding (string): The encoding method passed as the `format` parameter in the eAPI request reqid (string): A custom value to assign to the request ID field. This value is automatically generated if not passed **kwargs: Additional keyword arguments for expanded eAPI functionality. Only supported eAPI params are used in building the request Returns: A JSON encoding request structure that can be send over eAPI """ commands = make_iterable(commands) reqid = id(self) if reqid is None else reqid params = {'version': 1, 'cmds': commands, 'format': encoding} streaming = False if 'autoComplete' in kwargs: params['autoComplete'] = kwargs['autoComplete'] if 'expandAliases' in kwargs: params['expandAliases'] = kwargs['expandAliases'] if 'streaming' in kwargs: streaming = kwargs['streaming'] return json.dumps({ 'jsonrpc': '2.0', 'method': 'runCmds', 'params': params, 'id': str(reqid), 'streaming': streaming })
def enable(self, commands, encoding="json", strict=False): """Sends the array of commands to the node in enable mode This method will send the commands to the node and evaluate the results. If a command fails due to an encoding error, then the command set will be re-issued individual with text encoding. Args: commands (list): The list of commands to send to the node encoding (str): The requested encoding of the command output. Valid values for encoding are JSON or text strict (bool): If False, this method will attempt to run a command with text encoding if JSON encoding fails Returns: A dict object that includes the response for each command along with the encoding Raises: TypeError: This method does not support sending configure commands and will raise a TypeError if configuration commands are found in the list of commands provided This method will also raise a TypeError if the specified encoding is not one of 'json' or 'text' CommandError: This method will raise a CommandError if any one of the commands fails. """ commands = make_iterable(commands) if "configure" in commands: raise TypeError("config mode commands not supported") results = list() if strict: responses = self.run_commands(commands, encoding) for index, response in enumerate(responses): results.append(dict(command=commands[index], response=response, encoding=encoding)) else: for command in commands: try: resp = self.run_commands(command, encoding) results.append(dict(command=command, result=resp[0], encoding=encoding)) except CommandError as exc: if exc.error_code == 1003: resp = self.run_commands(command, "text") results.append(dict(command=command, result=resp[0], encoding="text")) else: raise return results
def run_commands(self, commands, encoding='json', send_enable=True, **kwargs): """Sends the commands over the transport to the device This method sends the commands to the device using the nodes transport. This is a lower layer function that shouldn't normally need to be used, preferring instead to use config() or enable(). Args: commands (list): The ordered list of commands to send to the device using the transport encoding (str): The encoding method to use for the request and excpected response. send_enable (bool): If True the enable command will be prepended to the command list automatically. **kwargs: Additional keyword arguments for expanded eAPI functionality. Only supported eAPI params are used in building the request Returns: This method will return the raw response from the connection which is a Python dictionary object. """ commands = make_iterable(commands) # Some commands are multiline commands. These are banner commands and # SSL commands. So with this two lines we # can support those by passing commands by doing: # banner login MULTILINE: This is my banner.\nAnd I even support # multiple lines. # Why this? To be able to read a configuration from a file, split it # into lines and pass it as it is # to pyeapi without caring about multiline commands. commands = [{ 'cmd': c.split('MULTILINE:')[0], 'input': '%s\n' % (c.split('MULTILINE:')[1].strip()) } if 'MULTILINE:' in c else c for c in commands] if send_enable: if self._enablepwd: commands.insert(0, {'cmd': 'enable', 'input': self._enablepwd}) else: commands.insert(0, 'enable') response = self._connection.execute(commands, encoding, **kwargs) # pop enable command from the response only if we sent enable if send_enable: response['result'].pop(0) return response['result']
def configure_ospf(self, cmd): """Allows for a list of OSPF subcommands to be configured" Args: cmd: (list or str): Subcommand to be entered Returns: bool: True if all the commands completed successfully """ config = self.get() cmds = ['router ospf {}'.format(config['ospf_process_id'])] cmds.extend(make_iterable(cmd)) return super(Ospf, self).configure(cmds)
def configure_vlan(self, vid, commands): """ Configures the specified Vlan using commands Args: vid (str): The VLAN ID to configure commands: The list of commands to configure Returns: True if the commands completed successfully """ commands = make_iterable(commands) commands.insert(0, 'vlan %s' % vid) return self.configure(commands)
def configure_interface(self, name, commands): """Configures the specified interface with the commands Args: name (str): The interface name to configure commands: The commands to configure in the interface Returns: True if the commands completed successfully """ commands = make_iterable(commands) commands.insert(0, 'interface %s' % name) return self.configure(commands)
def configure_vrf(self, vrf_name, commands): """ Configures the specified VRF using commands Args: vrf_name (str): The VRF name to configure commands: The list of commands to configure Returns: True if the commands completed successfully """ commands = make_iterable(commands) commands.insert(0, 'vrf definition %s' % vrf_name) return self.configure(commands)
def run_commands(self, commands, encoding='json', send_enable=True, **kwargs): """Sends the commands over the transport to the device This method sends the commands to the device using the nodes transport. This is a lower layer function that shouldn't normally need to be used, preferring instead to use config() or enable(). Args: commands (list): The ordered list of commands to send to the device using the transport encoding (str): The encoding method to use for the request and excpected response. send_enable (bool): If True the enable command will be prepended to the command list automatically. **kwargs: Additional keyword arguments for expanded eAPI functionality. Only supported eAPI params are used in building the request Returns: This method will return the raw response from the connection which is a Python dictionary object. """ commands = make_iterable(commands) # Some commands are multiline commands. These are banner commands and # SSL commands. So with this two lines we # can support those by passing commands by doing: # banner login MULTILINE: This is my banner.\nAnd I even support # multiple lines. # Why this? To be able to read a configuration from a file, split it # into lines and pass it as it is # to pyeapi without caring about multiline commands. commands = [{'cmd': c.split('MULTILINE:')[0], 'input': '%s\n' % (c.split('MULTILINE:')[1].strip())} if 'MULTILINE:' in c else c for c in commands] if send_enable: if self._enablepwd: commands.insert(0, {'cmd': 'enable', 'input': self._enablepwd}) else: commands.insert(0, 'enable') response = self._connection.execute(commands, encoding, **kwargs) # pop enable command from the response only if we sent enable if send_enable: response['result'].pop(0) return response['result']
def request(self, commands, encoding=None, reqid=None, **kwargs): """Generates an eAPI request object This method will take a list of EOS commands and generate a valid eAPI request object form them. The eAPI request object is then JSON encoding and returned to the caller. eAPI Request Object .. code-block:: json { "jsonrpc": "2.0", "method": "runCmds", "params": { "version": 1, "cmds": [ <commands> ], "format": [json, text], } "id": <reqid> } Args: commands (list): A list of commands to include in the eAPI request object encoding (string): The encoding method passed as the `format` parameter in the eAPI request reqid (string): A custom value to assign to the request ID field. This value is automatically generated if not passed **kwargs: Additional keyword arguments for expanded eAPI functionality. Only supported eAPI params are used in building the request Returns: A JSON encoding request structure that can be send over eAPI """ commands = make_iterable(commands) reqid = id(self) if reqid is None else reqid params = {'version': 1, 'cmds': commands, 'format': encoding} if 'autoComplete' in kwargs: params['autoComplete'] = kwargs['autoComplete'] if 'expandAliases' in kwargs: params['expandAliases'] = kwargs['expandAliases'] return json.dumps({'jsonrpc': '2.0', 'method': 'runCmds', 'params': params, 'id': str(reqid)})
def _configure_terminal(self, commands, **kwargs): """Configures the node with the specified commands with leading "configure terminal" """ commands = make_iterable(commands) commands = list(commands) # push the configure command onto the command stack commands.insert(0, 'configure terminal') response = self.run_commands(commands, **kwargs) if self.autorefresh: self.refresh() # pop the configure command output off the stack response.pop(0) return response
def _configure_session(self, commands, **kwargs): """Configures the node with the specified commands with leading "configure session <session name>" """ if not self._session_name: raise CommandError('Not currently in a session') commands = make_iterable(commands) commands = list(commands) # push the configure command onto the command stack commands.insert(0, 'configure session %s' % self._session_name) response = self.run_commands(commands, **kwargs) # pop the configure command output off the stack response.pop(0) return response
def request(self, commands, encoding=None, reqid=None): """Generates an eAPI request object This method will take a list of EOS commands and generate a valid eAPI request object form them. The eAPI request object is then JSON encoding and returned to the caller. eAPI Request Object .. code-block:: json { "jsonrpc": "2.0", "method": "runCmds", "params": { "version": 1, "cmds": [ <commands> ], "format": [json, text], } "id": <reqid> } Args: commands (list): A list of commands to include in the eAPI request object encoding (string): The encoding method passed as the `format` parameter in the eAPI request reqid (string): A custom value to assign to the request ID field. This value is automatically generated if not passed Returns: A JSON encoding request structure that can be send over eAPI """ commands = make_iterable(commands) reqid = id(self) if reqid is None else reqid params = {"version": 1, "cmds": commands, "format": encoding} return json.dumps({ "jsonrpc": "2.0", "method": "runCmds", "params": params, "id": str(reqid) })
def run_cmds(self, commands, encoding='json'): """Run Cmds allows low-level access to run any eAPI command against your switch and then process the output using Robot's builtin keywords. Arguments: - commands: This must be the full eAPI command. command may not the short form that works on the CLI. Example: Good: | show version Bad: | sho ver - `encoding` is the format of the response will be returned from the API request. The two options are 'text' and 'json'. Note that EOS does not support a JSON response for all commands. Please refer to your EOS Command API documentation for more details. Examples: | ${json_dict}= | Run Cmds | show version | | | ${raw_text}= | Run Cmds | show interfaces description | encoding=text | """ if isinstance(commands, six.string_types): commands = [str(commands)] elif isinstance(commands, list): # Handle Python2 unicode strings for idx, command in enumerate(commands): if isinstance(command, six.text_type): commands[idx] = str(command) try: commands = make_iterable(commands) client = self.connections[self._connection.current_index]['conn'] return client.execute(commands, encoding) except CommandError as e: error = "" # This just added by Peter in pyeapi 10 Feb 2015 # if self.active_node.connection.error.command_error: # error = self.active_node.connection.error.command_error raise AssertionError('eAPI CommandError: {}\n{}'.format(e, error)) except Exception as e: raise AssertionError('eAPI execute command: {}'.format(e))
def set_trunk_groups(self, vid, value=None, default=False, disable=False): """ Configures the list of trunk groups support on a vlan This method handles configuring the vlan trunk group value to default if the default flag is set to True. If the default flag is set to False, then this method will calculate the set of trunk group names to be added and to be removed. EosVersion: 4.13.7M Args: vid (str): The VLAN ID to configure value (str): The list of trunk groups that should be configured for this vlan id. default (bool): Configures the trunk group value to default if this value is true disable (bool): Negates the trunk group value if set to true Returns: True if the operation was successful otherwise False """ if default: return self.configure_vlan(vid, 'default trunk group') if disable: return self.configure_vlan(vid, 'no trunk group') current_value = self.get(vid)['trunk_groups'] failure = False value = make_iterable(value) for name in set(value).difference(current_value): if not self.add_trunk_group(vid, name): failure = True for name in set(current_value).difference(value): if not self.remove_trunk_group(vid, name): failure = True return not failure
def set_ipv4_routing(self, vrf_name, default=False, disable=False): """ Configures ipv4 routing for the vrf Args: vrf_name (str): The VRF name to configure default (bool): Configures ipv4 routing for the vrf value to default if this value is true disable (bool): Negates the ipv4 routing for the vrf if set to true Returns: True if the operation was successful otherwise False """ cmd = 'ip routing vrf %s' % vrf_name if default: cmd = 'default %s' % cmd elif disable: cmd = 'no %s' % cmd cmd = make_iterable(cmd) return self.configure(cmd)
def request(self, commands, encoding=None, reqid=None): """Generates an eAPI request object This method will take a list of EOS commands and generate a valid eAPI request object form them. The eAPI request object is then JSON encoding and returned to the caller. eAPI Request Object .. code-block:: json { "jsonrpc": "2.0", "method": "runCmds", "params": { "version": 1, "cmds": [ <commands> ], "format": [json, text], } "id": <reqid> } Args: commands (list): A list of commands to include in the eAPI request object encoding (string): The encoding method passed as the `format` parameter in the eAPI request reqid (string): A custom value to assign to the request ID field. This value is automatically generated if not passed Returns: A JSON encoding request structure that can be send over eAPI """ commands = make_iterable(commands) reqid = id(self) if reqid is None else reqid params = {"version": 1, "cmds": commands, "format": encoding} return json.dumps({"jsonrpc": "2.0", "method": "runCmds", "params": params, "id": str(reqid)})
def configure_bgp(self, cmd): config = self.get() cmds = ['router bgp {}'.format(config['bgp_as'])] cmds.extend(make_iterable(cmd)) return super(Bgp, self).configure(cmds)
def enable(self, commands, encoding='json', strict=False): """Sends the array of commands to the node in enable mode This method will send the commands to the node and evaluate the results. If a command fails due to an encoding error, then the command set will be re-issued individual with text encoding. Args: commands (list): The list of commands to send to the node encoding (str): The requested encoding of the command output. Valid values for encoding are JSON or text strict (bool): If False, this method will attempt to run a command with text encoding if JSON encoding fails Returns: A dict object that includes the response for each command along with the encoding Raises: TypeError: This method does not support sending configure commands and will raise a TypeError if configuration commands are found in the list of commands provided This method will also raise a TypeError if the specified encoding is not one of 'json' or 'text' CommandError: This method will raise a CommandError if any one of the commands fails. """ commands = make_iterable(commands) if 'configure' in commands: raise TypeError('config mode commands not supported') results = list() # IMPORTANT: There are two keys (response, result) that both # return the same value. 'response' was originally placed # there in error and both are now present to avoid breaking # existing scripts. 'response' will be removed in a future release. if strict: responses = self.run_commands(commands, encoding) for index, response in enumerate(responses): results.append(dict(command=commands[index], result=response, response=response, encoding=encoding)) else: for command in commands: try: resp = self.run_commands(command, encoding) results.append(dict(command=command, result=resp[0], encoding=encoding)) except CommandError as exc: if exc.error_code == 1003: resp = self.run_commands(command, 'text') results.append(dict(command=command, result=resp[0], encoding='text')) else: raise return results
def enable(self, commands, encoding='json', strict=False): """Sends the array of commands to the node in enable mode This method will send the commands to the node and evaluate the results. If a command fails due to an encoding error, then the command set will be re-issued individual with text encoding. Args: commands (list): The list of commands to send to the node encoding (str): The requested encoding of the command output. Valid values for encoding are JSON or text strict (bool): If False, this method will attempt to run a command with text encoding if JSON encoding fails Returns: A dict object that includes the response for each command along with the encoding Raises: TypeError: This method does not support sending configure commands and will raise a TypeError if configuration commands are found in the list of commands provided This method will also raise a TypeError if the specified encoding is not one of 'json' or 'text' CommandError: This method will raise a CommandError if any one of the commands fails. """ commands = make_iterable(commands) if 'configure' in commands: raise TypeError('config mode commands not supported') results = list() if strict: responses = self.run_commands(commands, encoding) for index, response in enumerate(responses): results.append( dict(command=commands[index], response=response, encoding=encoding)) else: for command in commands: try: resp = self.run_commands(command, encoding) results.append( dict(command=command, result=resp[0], encoding=encoding)) except CommandError as exc: if exc.error_code == 1003: resp = self.run_commands(command, 'text') results.append( dict(command=command, result=resp[0], encoding='text')) else: raise return results
def enable(self, commands, encoding='json', strict=False, send_enable=True, **kwargs): """Sends the array of commands to the node in enable mode This method will send the commands to the node and evaluate the results. If a command fails due to an encoding error, then the command set will be re-issued individual with text encoding. Args: commands (list): The list of commands to send to the node encoding (str): The requested encoding of the command output. Valid values for encoding are JSON or text strict (bool): If False, this method will attempt to run a command with text encoding if JSON encoding fails send_enable (bool): If True the enable command will be prepended to the command list automatically. **kwargs: Additional keyword arguments for expanded eAPI functionality. Only supported eAPI params are used in building the request Returns: A dict object that includes the response for each command along with the encoding Raises: TypeError: This method does not support sending configure commands and will raise a TypeError if configuration commands are found in the list of commands provided This method will also raise a TypeError if the specified encoding is not one of 'json' or 'text' CommandError: This method will raise a CommandError if any one of the commands fails. """ commands = make_iterable(commands) if 'configure' in commands: raise TypeError('config mode commands not supported') results = list() # IMPORTANT: There are two keys (response, result) that both # return the same value. 'response' was originally placed # there in error and both are now present to avoid breaking # existing scripts. 'response' will be removed in a future release. if strict: responses = self.run_commands(commands, encoding, send_enable, **kwargs) for index, response in enumerate(responses): results.append( dict(command=commands[index], result=response, response=response, encoding=encoding)) else: for command in commands: try: resp = self.run_commands(command, encoding, send_enable, **kwargs) results.append( dict(command=command, result=resp[0], encoding=encoding)) except CommandError as exc: if exc.error_code == 1003: resp = self.run_commands(command, 'text', send_enable, **kwargs) results.append( dict(command=command, result=resp[0], encoding='text')) else: raise return results