diff --git a/lib/ansible/plugins/netconf/__init__.py b/lib/ansible/plugins/netconf/__init__.py index b2d425884f..a780e5ccae 100644 --- a/lib/ansible/plugins/netconf/__init__.py +++ b/lib/ansible/plugins/netconf/__init__.py @@ -98,12 +98,19 @@ class NetconfBase(AnsiblePlugin): conn.load_configuration(config=[''set system ntp server 1.1.1.1''], action='set', format='text') """ + __rpc__ = ['get_config', 'edit_config', 'get_capabilities', 'get'] + def __init__(self, connection): self._connection = connection self.m = self._connection._manager @ensure_connected def rpc(self, name): + """ + RPC to be execute on remote device + :param name: Name of rpc in string format + :return: Received rpc response from remote host + """ """RPC to be execute on remote device :name: Name of rpc in string format""" try: @@ -116,9 +123,9 @@ class NetconfBase(AnsiblePlugin): @ensure_connected def get_config(self, source=None, filter=None): - """Retrieve all or part of a specified configuration - (by default entire configuration is retrieved). - + """ + Retrieve all or part of a specified configuration + (by default entire configuration is retrieved). :param source: Name of the configuration datastore being queried, defaults to running datastore :param filter: This argument specifies the portion of the configuration data to retrieve :return: Returns xml string containing the RPC response received from remote host @@ -132,88 +139,97 @@ class NetconfBase(AnsiblePlugin): return resp.data_xml if hasattr(resp, 'data_xml') else resp.xml @ensure_connected - def get(self, filter=None): - """Retrieve device configuration and state information. - + def get(self, filter=None, with_defaults=None): + """ + Retrieve device configuration and state information. :param filter: This argument specifies the portion of the state data to retrieve - (by default entire state data is retrieved) + (by default entire state data is retrieved) + :param with_defaults: defines an explicit method of retrieving default values + from the configuration :return: Returns xml string containing the RPC response received from remote host """ if isinstance(filter, list): filter = tuple(filter) - resp = self.m.get(filter=filter) + resp = self.m.get(filter=filter, with_defaults=with_defaults) response = resp.data_xml if hasattr(resp, 'data_xml') else resp.xml return response @ensure_connected - def edit_config(self, *args, **kwargs): - """Loads all or part of the specified *config* to the *target* configuration datastore. - - :target: is the name of the configuration datastore being edited - :config: is the configuration, which must be rooted in the `config` element. - It can be specified either as a string or an :class:`~xml.etree.ElementTree.Element`. - :default_operation: if specified must be one of { `"merge"`, `"replace"`, or `"none"` } - :test_option: if specified must be one of { `"test_then_set"`, `"set"` } - :error_option: if specified must be one of { `"stop-on-error"`, `"continue-on-error"`, `"rollback-on-error"` } - The `"rollback-on-error"` *error_option* depends on the `:rollback-on-error` capability. + def edit_config(self, config, format='xml', target='candidate', default_operation=None, test_option=None, error_option=None): """ - resp = self.m.edit_config(*args, **kwargs) - return resp.data_xml if hasattr(resp, 'data_xml') else resp.xml - - @ensure_connected - def validate(self, *args, **kwargs): - """Validate the contents of the specified configuration. - :source: is the name of the configuration datastore being validated or `config` - element containing the configuration subtree to be validated + Loads all or part of the specified *config* to the *target* configuration datastore. + :param config: Is the configuration, which must be rooted in the `config` element. + It can be specified either as a string or an :class:`~xml.etree.ElementTree.Element`. + :param format: The format of configuration eg. xml, text + :param target: Is the name of the configuration datastore being edited + :param default_operation: If specified must be one of { `"merge"`, `"replace"`, or `"none"` } + :param test_option: If specified must be one of { `"test_then_set"`, `"set"` } + :param error_option: If specified must be one of { `"stop-on-error"`, `"continue-on-error"`, `"rollback-on-error"` } + The `"rollback-on-error"` *error_option* depends on the `:rollback-on-error` capability. + :return: Returns xml string containing the RPC response received from remote host """ - resp = self.m.validate(*args, **kwargs) + resp = self.m.edit_config(config, format=format, target=target, default_operation=default_operation, test_option=test_option, + error_option=error_option) return resp.data_xml if hasattr(resp, 'data_xml') else resp.xml @ensure_connected - def copy_config(self, *args, **kwargs): - """Create or replace an entire configuration datastore with the contents of another complete - configuration datastore. - :source: is the name of the configuration datastore to use as the source of the - copy operation or `config` element containing the configuration subtree to copy - :target: is the name of the configuration datastore to use as the destination of the copy operation""" - resp = self.m.copy_config(*args, **kwargs) + def validate(self, source='candidate'): + """ + Validate the contents of the specified configuration. + :param source: Is the name of the configuration datastore being validated or `config` element + containing the configuration subtree to be validated + :return: Returns xml string containing the RPC response received from remote host + """ + resp = self.m.validate(source=source) return resp.data_xml if hasattr(resp, 'data_xml') else resp.xml @ensure_connected - def dispatch(self, request): + def copy_config(self, source, target): + """ + Create or replace an entire configuration datastore with the contents of another complete configuration datastore. + :param source: Is the name of the configuration datastore to use as the source of the copy operation or `config` + element containing the configuration subtree to copy + :param target: Is the name of the configuration datastore to use as the destination of the copy operation + :return: Returns xml string containing the RPC response received from remote host + """ + resp = self.m.copy_config(source, target) + return resp.data_xml if hasattr(resp, 'data_xml') else resp.xml + + @ensure_connected + def dispatch(self, rpc_command, source=None, filter=None): + """ + Execute rpc on the remote device eg. dispatch('clear-arp-table') + :param rpc_command: specifies rpc command to be dispatched either in plain text or in xml element format (depending on command) + :param source: name of the configuration datastore being queried + :param filter: specifies the portion of the configuration to retrieve (by default entire configuration is retrieved) + :return: Returns xml string containing the RPC response received from remote host + """ """Execute operation on the remote device :request: is the rpc request including attributes as XML string """ - req = fromstring(request) - resp = self.m.dispatch(req) + req = fromstring(rpc_command) + resp = self.m.dispatch(req, source=source, filter=filter) return resp.data_xml if hasattr(resp, 'data_xml') else resp.xml @ensure_connected - def lock(self, target=None): + def lock(self, target="candidate"): """ Allows the client to lock the configuration system of a device. :param target: is the name of the configuration datastore to lock, defaults to candidate datastore :return: Returns xml string containing the RPC response received from remote host """ - if not target: - target = 'candidate' resp = self.m.lock(target=target) return resp.data_xml if hasattr(resp, 'data_xml') else resp.xml @ensure_connected - def unlock(self, target=None): + def unlock(self, target="candidate"): """ Release a configuration lock, previously obtained with the lock operation. :param target: is the name of the configuration datastore to unlock, defaults to candidate datastore :return: Returns xml string containing the RPC response received from remote host """ - """Release a configuration lock, previously obtained with the lock operation. - :target: is the name of the configuration datastore to unlock - """ - if not target: - target = 'candidate' resp = self.m.unlock(target=target) return resp.data_xml if hasattr(resp, 'data_xml') else resp.xml @@ -228,60 +244,95 @@ class NetconfBase(AnsiblePlugin): return resp.data_xml if hasattr(resp, 'data_xml') else resp.xml @ensure_connected - def commit(self, *args, **kwargs): - """Commit the candidate configuration as the device's new current configuration. - Depends on the `:candidate` capability. - A confirmed commit (i.e. if *confirmed* is `True`) is reverted if there is no - followup commit within the *timeout* interval. If no timeout is specified the - confirm timeout defaults to 600 seconds (10 minutes). - A confirming commit may have the *confirmed* parameter but this is not required. - Depends on the `:confirmed-commit` capability. - :confirmed: whether this is a confirmed commit - :timeout: specifies the confirm timeout in seconds + def commit(self, confirmed=False, timeout=None, persist=None): """ - resp = self.m.commit(*args, **kwargs) + Commit the candidate configuration as the device's new current configuration. + Depends on the `:candidate` capability. + A confirmed commit (i.e. if *confirmed* is `True`) is reverted if there is no + followup commit within the *timeout* interval. If no timeout is specified the + confirm timeout defaults to 600 seconds (10 minutes). + A confirming commit may have the *confirmed* parameter but this is not required. + Depends on the `:confirmed-commit` capability. + :param confirmed: whether this is a confirmed commit + :param timeout: specifies the confirm timeout in seconds + :param persist: make the confirmed commit survive a session termination, + and set a token on the ongoing confirmed commit + :return: Returns xml string containing the RPC response received from remote host + """ + resp = self.m.commit(confirmed=confirmed, timeout=timeout, persist=persist) return resp.data_xml if hasattr(resp, 'data_xml') else resp.xml @ensure_connected - def get_schema(self, *args, **kwargs): - """Retrieves the required schema from the device + def get_schema(self, identifier, version=None, format=None): """ - resp = self.m.get_schema(*args, **kwargs) + Retrieve a named schema, with optional revision and type. + :param identifier: name of the schema to be retrieved + :param version: version of schema to get + :param format: format of the schema to be retrieved, yang is the default + :return: Returns xml string containing the RPC response received from remote host + """ + resp = self.m.get_schema(identifier, version=version, format=format) return resp.data_xml if hasattr(resp, 'data_xml') else resp.xml @ensure_connected - def locked(self, *args, **kwargs): - resp = self.m.locked(*args, **kwargs) - return resp.data_xml if hasattr(resp, 'data_xml') else resp.xml + def locked(self, target): + """ + Returns a context manager for a lock on a datastore + :param target: Name of the configuration datastore to lock + :return: Locked context object + """ + return self.m.locked(target) @abstractmethod def get_capabilities(self): - """Retrieves device information and supported + """ + Retrieves device information and supported rpc methods by device platform and return result as a string + :return: Netconf session capability """ pass @staticmethod def guess_network_os(obj): - """Identifies the operating system of - network device. + """ + Identifies the operating system of network device. + :param obj: ncclient manager connection instance + :return: The name of network operating system. """ pass def get_base_rpc(self): - """Returns list of base rpc method supported by remote device""" - return ['get_config', 'edit_config', 'get_capabilities', 'get'] + """ + Returns list of base rpc method supported by remote device + :return: List of RPC supported + """ + return self.__rpc__ def put_file(self, source, destination): - """Copies file over scp to remote device""" + """ + Copies file to remote host + :param source: Source location of file + :param destination: Destination file path + :return: Returns xml string containing the RPC response received from remote host + """ pass def fetch_file(self, source, destination): - """Fetch file over scp from remote device""" + """ + Fetch file from remote host + :param source: Source location of file + :param destination: Source location of file + :return: Returns xml string containing the RPC response received from remote host + """ pass def get_device_operations(self, server_capabilities): + """ + Retrieve remote host capability from Netconf server hello message. + :param server_capabilities: Server capabilities received during Netconf session initialization + :return: Remote host capabilities in dictionary format + """ operations = {} capabilities = '\n'.join(server_capabilities) operations['supports_commit'] = ':candidate' in capabilities diff --git a/lib/ansible/plugins/netconf/iosxr.py b/lib/ansible/plugins/netconf/iosxr.py index 379f334c7f..47dc59baaf 100644 --- a/lib/ansible/plugins/netconf/iosxr.py +++ b/lib/ansible/plugins/netconf/iosxr.py @@ -93,7 +93,11 @@ class Netconf(NetconfBase): @staticmethod def guess_network_os(obj): - + """ + Guess the remote network os name + :param obj: Netconf connection class object + :return: Network OS name + """ try: m = manager.connect( host=obj._play_context.remote_addr, @@ -140,25 +144,26 @@ class Netconf(NetconfBase): raise Exception(to_xml(exc.xml)) @ensure_connected - def edit_config(self, *args, **kwargs): + def edit_config(self, config, format='xml', target='candidate', default_operation=None, test_option=None, error_option=None): try: - response = self.m.edit_config(*args, **kwargs) + response = self.m.edit_config(config, format=format, target=target, default_operation=default_operation, test_option=test_option, + error_option=error_option) return remove_namespaces(response) except RPCError as exc: raise Exception(to_xml(exc.xml)) @ensure_connected - def commit(self, *args, **kwargs): + def commit(self, confirmed=False, timeout=None, persist=None): try: - response = self.m.commit(*args, **kwargs) + response = self.m.commit(confirmed=confirmed, timeout=timeout, persist=persist) return remove_namespaces(response) except RPCError as exc: raise Exception(to_xml(exc.xml)) @ensure_connected - def validate(self, *args, **kwargs): + def validate(self, source="candidate"): try: - response = self.m.validate(*args, **kwargs) + response = self.m.validate(source=source) return remove_namespaces(response) except RPCError as exc: raise Exception(to_xml(exc.xml)) diff --git a/lib/ansible/plugins/netconf/junos.py b/lib/ansible/plugins/netconf/junos.py index 104d2a1ee6..08de537e48 100644 --- a/lib/ansible/plugins/netconf/junos.py +++ b/lib/ansible/plugins/netconf/junos.py @@ -61,23 +61,29 @@ class Netconf(NetconfBase): @ensure_connected def execute_rpc(self, name): - """RPC to be execute on remote device - :name: Name of rpc in string format""" + """ + RPC to be execute on remote device + :param name: Name of rpc in string format + :return: Received rpc response from remote host + """ return self.rpc(name) @ensure_connected - def load_configuration(self, *args, **kwargs): - """Loads given configuration on device - :format: Format of configuration (xml, text, set) - :action: Action to be performed (merge, replace, override, update) - :target: is the name of the configuration datastore being edited - :config: is the configuration in string format.""" - if kwargs.get('config'): - if kwargs.get('format', 'xml') == 'xml': - kwargs['config'] = to_ele(kwargs['config']) + def load_configuration(self, format='xml', action='merge', target='candidate', config=None): + """ + Load given configuration on device + :param format: Format of configuration (xml, text, set) + :param action: Action to be performed (merge, replace, override, update) + :param target: The name of the configuration datastore being edited + :param config: The configuration to be loaded on remote host in string format + :return: Received rpc response from remote host in string format + """ + if config: + if format == 'xml': + config = to_ele(config) try: - return self.m.load_configuration(*args, **kwargs).data_xml + return self.m.load_configuration(format=format, action=action, target=target, config=config).data_xml except RPCError as exc: raise Exception(to_xml(exc.xml)) @@ -96,7 +102,11 @@ class Netconf(NetconfBase): @staticmethod def guess_network_os(obj): - + """ + Guess the remote network os name + :param obj: Netconf connection class object + :return: Network OS name + """ try: m = manager.connect( host=obj._play_context.remote_addr, @@ -121,18 +131,25 @@ class Netconf(NetconfBase): return guessed_os @ensure_connected - def get_configuration(self, *args, **kwargs): - """Retrieve all or part of a specified configuration. - :format: format in configuration should be retrieved - :filter: specifies the portion of the configuration to retrieve - (by default entire configuration is retrieved)""" - return self.m.get_configuration(*args, **kwargs).data_xml + def get_configuration(self, format='xml', filter=None): + """ + Retrieve all or part of a specified configuration. + :param format: format in which configuration should be retrieved + :param filter: specifies the portion of the configuration to retrieve + :return: Received rpc response from remote host in string format + """ + return self.m.get_configuration(format=format, filter=filter).data_xml @ensure_connected - def compare_configuration(self, *args, **kwargs): - """Compare configuration - :rollback: rollback id""" - return self.m.compare_configuration(*args, **kwargs).data_xml + def compare_configuration(self, rollback=0): + """ + Compare the candidate configuration with running configuration + by default. The candidate configuration can be compared with older + committed configuration by providing rollback id. + :param rollback: Rollback id of previously commited configuration + :return: Received rpc response from remote host in string format + """ + return self.m.compare_configuration(rollback=rollback).data_xml @ensure_connected def halt(self): @@ -150,15 +167,21 @@ class Netconf(NetconfBase): # Remove below method after the issue in ncclient is fixed. @ensure_connected def commit(self, confirmed=False, check=False, timeout=None, comment=None, synchronize=False, at_time=None): - """Commit the candidate configuration as the device's new current configuration. - Depends on the `:candidate` capability. - A confirmed commit (i.e. if *confirmed* is `True`) is reverted if there is no - followup commit within the *timeout* interval. If no timeout is specified the - confirm timeout defaults to 600 seconds (10 minutes). - A confirming commit may have the *confirmed* parameter but this is not required. - Depends on the `:confirmed-commit` capability. - :confirmed: whether this is a confirmed commit - :timeout: specifies the confirm timeout in seconds + """ + Commit the candidate configuration as the device's new current configuration. + Depends on the `:candidate` capability. + A confirmed commit (i.e. if *confirmed* is `True`) is reverted if there is no + followup commit within the *timeout* interval. If no timeout is specified the + confirm timeout defaults to 600 seconds (10 minutes). + A confirming commit may have the *confirmed* parameter but this is not required. + Depends on the `:confirmed-commit` capability. + :param confirmed: whether this is a confirmed commit + :param check: Check correctness of syntax + :param timeout: specifies the confirm timeout in seconds + :param comment: Message to write to commit log + :param synchronize: Synchronize commit on remote peers + :param at_time: Time at which to activate configuration changes + :return: Received rpc response from remote host """ obj = new_ele('commit-configuration') if confirmed: