X-Git-Url: https://code.grnet.gr/git/ncclient/blobdiff_plain/cc9af1c3172668c53798833697dbeac7d926398b..c15671aa73d6c3d4824cbd5dfa5dc754dc727430:/ncclient/operations/edit.py diff --git a/ncclient/operations/edit.py b/ncclient/operations/edit.py index c7967eb..a2dbd94 100644 --- a/ncclient/operations/edit.py +++ b/ncclient/operations/edit.py @@ -12,160 +12,132 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ncclient.content import iselement +from ncclient.xml_ import * from rpc import RPC import util +import logging -"Operations related to configuration editing" +logger = logging.getLogger("ncclient.operations.edit") +"Operations related to changing device configuration" class EditConfig(RPC): - - # tested: no - # combed: yes - - SPEC = {'tag': 'edit-config', 'subtree': []} - - def request(self, target=None, target_url=None, config=None, - default_operation=None, test_option=None, error_option=None): - util.one_of(target, target_url) - spec = EditConfig.SPEC.copy() - subtree = spec['subtree'] - subtree.append({ - 'tag': 'target', - 'subtree': util.store_or_url(target, target_url, self._assert) - }) - subtree.append(config) - if default_operation is not None: - subtree.append({ - 'tag': 'default-operation', - 'text': default_operation - }) + "`edit-config` RPC" + + def request(self, target, config, default_operation=None, test_option=None, error_option=None): + """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. + """ + node = new_ele("edit-config") + node.append(util.datastore_or_url("target", target, self._assert)) + if error_option is not None: + if error_option == "rollback-on-error": + self._assert(":rollback-on-error") + sub_ele(node, "error-option").text = error_option if test_option is not None: self._assert(':validate') - subtree.append({ - 'tag': 'test-option', - 'text': test_option - }) - if error_option is not None: - if error_option == 'rollback-on-error': - self._assert(':rollback-on-error') - subtree.append({ - 'tag': 'error-option', - 'text': error_option - }) + sub_ele(node, "test-option").text = test_option + if default_operation is not None: + # TODO: check if it is a valid default-operation + sub_ele(node, "default-operation").text = default_operation + node.append(validated_element(config, ("config", qualify("config")))) + return self._request(node) class DeleteConfig(RPC): - - # tested: no - # combed: yes - - SPEC = {'tag': 'delete-config', 'subtree': []} - - def request(self, target=None, target_url=None): - spec = DeleteConfig.SPEC.copy() - spec['subtree'].append({ - 'tag': 'target', - 'subtree': util.store_or_url(target, target_url, self._assert) - }) - return self._request(spec) + "`delete-config` RPC" + + def request(self, target): + """Delete a configuration datastore. + + *target* specifies the name or URL of configuration datastore to delete + + :seealso: :ref:`srctarget_params`""" + node = new_ele("delete-config") + node.append(util.datastore_or_url("target", target, self._assert)) + return self._request(node) class CopyConfig(RPC): - - # tested: no - # combed: yes - - SPEC = {'tag': 'copy-config', 'subtree': []} - - def request(self, source=None, source_url=None, target=None, target_url=None): - spec = CopyConfig.SPEC.copy() - spec['subtree'].append({ - 'tag': 'source', - 'subtree': util.store_or_url(source, source_url, self._assert) - }) - spec['subtree'].append({ - 'tag': 'target', - 'subtree': util.store_or_url(target, target_url, self._assert) - }) - return self._request(spec) + "`copy-config` RPC" + + def request(self, source, target): + """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 + + :seealso: :ref:`srctarget_params`""" + node = new_ele("copy-config") + node.append(util.datastore_or_url("target", target, self._assert)) + node.append(util.datastore_or_url("source", source, self._assert)) + return self._request(node) class Validate(RPC): - - # tested: no - # combed: yes - - 'config attr shd not include root' - + "`validate` RPC. Depends on the `:validate` capability." + DEPENDS = [':validate'] - - SPEC = {'tag': 'validate', 'subtree': []} - - def request(self, source=None, source_url=None, config=None): - util.one_of(source, source_url, config) - spec = Validate.SPEC.copy() - if config is None: - spec['subtree'].append({ - 'tag': 'source', - 'subtree': util.store_or_url(source, source_url, self._assert) - }) - else: - spec['subtree'].append(config) - return self._request(spec) + + def request(self, source): + """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 + + :seealso: :ref:`srctarget_params`""" + node = new_ele("validate") + try: + src = validated_element(source, ("config", qualify("config"))) + except Exception as e: + logger.debug(e) + src = util.datastore_or_url("source", source, self._assert) + (node if src.tag == "source" else sub_ele(node, "source")).append(src) + return self._request(node) class Commit(RPC): - - # tested: no - # combed: yes - + "`commit` RPC. Depends on the `:candidate` capability, and the `:confirmed-commit`." + DEPENDS = [':candidate'] - - SPEC = {'tag': 'commit', 'subtree': []} - - def _parse_hook(self): - pass - + def request(self, confirmed=False, timeout=None): - spec = SPEC.copy() + """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""" + node = new_ele("commit") if confirmed: - self._assert(':confirmed-commit') - spec['subtree'].append({'tag': 'confirmed'}) + self._assert(":confirmed-commit") + sub_ele(node, "confirmed") if timeout is not None: - spec['subtree'].append({ - 'tag': 'confirm-timeout', - 'text': timeout - }) - return self._request(Commit.SPEC) - - -class ConfirmedCommit(Commit): - "psuedo-op" - - # tested: no - # combed: yes - - DEPENDS = [':candidate', ':confirmed-commit'] - - def request(self, timeout=None): - "Commit changes; requireing that a confirming commit follow" - return Commit.request(self, confirmed=True, timeout=timeout) - - def confirm(self): - "Make the confirming commit" - return Commit.request(self, confirmed=True) + sub_ele(node, "confirm-timeout").text = timeout + return self._request(node) class DiscardChanges(RPC): - - # tested: no - # combed: yes - - DEPENDS = [':candidate'] - - SPEC = {'tag': 'discard-changes'} + "`discard-changes` RPC. Depends on the `:candidate` capability." + + DEPENDS = [":candidate"] + + def request(self): + """Revert the candidate configuration to the currently running configuration. Any uncommitted changes are discarded.""" + return self._request(new_ele("discard-changes")) \ No newline at end of file