X-Git-Url: https://code.grnet.gr/git/ncclient/blobdiff_plain/d668826426f1c2db7a77295321692234fca99b97..c15671aa73d6c3d4824cbd5dfa5dc754dc727430:/ncclient/operations/edit.py diff --git a/ncclient/operations/edit.py b/ncclient/operations/edit.py index 1d66afb..a2dbd94 100644 --- a/ncclient/operations/edit.py +++ b/ncclient/operations/edit.py @@ -12,127 +12,132 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ncclient.capabilities import URI -from ncclient.rpc import RPC +from ncclient.xml_ import * + +from rpc import RPC import util +import logging + +logger = logging.getLogger("ncclient.operations.edit") + +"Operations related to changing device configuration" + class EditConfig(RPC): - - SPEC = { - 'tag': 'edit-config', - 'children': [ - { 'target': None } - ] - } - - def request(self): - pass - - -class DeleteConfig(RPC): # x - - SPEC = { - 'tag': 'delete-config', - 'children': [ { 'tag': 'target', 'children': None } ] - } - - def request(self, target=None, target_url=None): - spec = DeleteConfig.SPEC.copy() - spec['children'][0]['children'] = util.store_or_url(target, target_url) - return self._request(spec) - - -class CopyConfig(RPC): # x - - SPEC = { - 'tag': 'copy-config', - 'children': [ - { 'tag': 'source', 'children': {'tag': None } }, - { 'tag': 'target', 'children': {'tag': None } } - ] - } - - def request(self, source=None, source_url=None, target=None, target_url=None): - spec = CopyConfig.SPEC.copy() - spec['children'][0]['children'] = util.store_or_url(source, source_url) - spec['children'][1]['children'] = util.store_or_url(target, target_url) - return self._request(spec) - - -class Validate(RPC): # xxxxx - + "`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') + 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): + "`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): + "`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): + "`validate` RPC. Depends on the `:validate` capability." + DEPENDS = [':validate'] - - SPEC = { - 'tag': 'validate', - 'children': [] - } - - def request(self, source=None, config=None): - #self.either_or(source, config) - # - #if source is None and config is None: - # raise OperationError('Insufficient parameters') - #if source is not None and config is not None: - # raise OperationError('Too many parameters') - #spec = Validate.SPEC.copy() - # - util.one_of(source, capability) - if source is not None: - spec['children'].append({ - 'tag': 'source', - 'children': {'tag': source} - }) - # - #else: - # if isinstance(config, dict): - # if config['tag'] != 'config': - # child['tag'] = 'config' - # child['children'] = config - # else: - # child = config - # elif isinstance(config, Element): - # pass - # else: - # from xml.etree import cElementTree as ET - # ele = ET.XML(unicode(config)) - # if __(ele.tag) != 'config': - # pass - # else: - # pass - # spec['children'].append(child) - # - return self._request(spec) - -class Commit(RPC): # x - + + 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): + "`commit` RPC. Depends on the `:candidate` capability, and the `:confirmed-commit`." + DEPENDS = [':candidate'] - - SPEC = {'tag': 'commit', 'children': [] } - - 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') - children = spec['children'] - children.append({'tag': 'confirmed'}) + self._assert(":confirmed-commit") + sub_ele(node, "confirmed") if timeout is not None: - children.append({ - 'tag': 'confirm-timeout', - 'text': timeout - }) - return self._request(Commit.SPEC) + sub_ele(node, "confirm-timeout").text = timeout + return self._request(node) -class DiscardChanges(RPC): # x - - DEPENDS = [':candidate'] - - SPEC = {'tag': 'discard-changes'} - +class DiscardChanges(RPC): + "`discard-changes` RPC. Depends on the `:candidate` capability." + + DEPENDS = [":candidate"] + def request(self): - return self._request(DiscardChanges.SPEC) + """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