# See the License for the specific language governing permissions and
# limitations under the License.
-from copy import deepcopy
-
-from ncclient import xml_
+from ncclient.xml_ import *
from rpc import RPC
import util
import logging
-logger = logging.getLogger('ncclient.operations.edit')
+
+logger = logging.getLogger("ncclient.operations.edit")
"Operations related to changing device configuration"
class EditConfig(RPC):
+ "`edit-config` RPC"
- "*<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.
- SPEC = {'tag': 'edit-config', 'subtree': []}
-
- def request(self, target, config, default_operation=None, test_option=None,
- error_option=None):
- """
- :arg target: see :ref:`source_target`
- :type target: string
+ *target* is the name of the configuration datastore being edited
- :arg config: a config element in :ref:`dtree`
- :type config: `string` or `dict` or :class:`~xml.etree.ElementTree.Element`
+ *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`.
- :arg default_operation: optional; one of {'merge', 'replace', 'none'}
- :type default_operation: `string`
+ *default_operation* if specified must be one of { `"merge"`, `"replace"`, or `"none"` }
- :arg error_option: optional; one of {'stop-on-error', 'continue-on-error', 'rollback-on-error'}. Last option depends on the *:rollback-on-error* capability
- :type error_option: string
+ *test_option* if specified must be one of { `"test_then_set"`, `"set"` }
- :arg test_option: optional; one of {'test-then-set', 'set'}. Depends on *:validate* capability.
- :type test_option: string
+ *error_option* if specified must be one of { `"stop-on-error"`, `"continue-on-error"`, `"rollback-on-error"` }
- :seealso: :ref:`return`
+ The `"rollback-on-error"` *error_option* depends on the `:rollback-on-error` capability.
"""
- spec = deepcopy(EditConfig.SPEC)
- subtree = spec['subtree']
- subtree.append(util.store_or_url('target', target, self._assert))
+ 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')
- subtree.append({
- 'tag': 'error-option',
- 'text': error_option
- })
+ 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
- })
+ sub_ele(node, "test-option").text = test_option
if default_operation is not None:
- subtree.append({
- 'tag': 'default-operation',
- 'text': default_operation
- })
- subtree.append(xml_.validated_element(config, ('config', xml_.qualify('config'))))
- return self._request(spec)
+ # 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"
- SPEC = {'tag': 'delete-config', 'subtree': []}
+class DeleteConfig(RPC):
+ "`delete-config` RPC"
def request(self, target):
- """
- :arg target: See :ref:`source_target`
- :type target: `string` or `dict` or :class:`~xml.etree.ElementTree.Element`
-
- :seealso: :ref:`return`
- """
- spec = deepcopy(DeleteConfig.SPEC)
- spec['subtree'].append(util.store_or_url('target', target, self._assert))
- return self._request(spec)
+ """Delete a configuration datastore.
+ *target* specifies the name or URL of configuration datastore to delete
-class CopyConfig(RPC):
+ :seealso: :ref:`srctarget_params`"""
+ node = new_ele("delete-config")
+ node.append(util.datastore_or_url("target", target, self._assert))
+ return self._request(node)
- "*<copy-config>* RPC"
- SPEC = {'tag': 'copy-config', 'subtree': []}
+class CopyConfig(RPC):
+ "`copy-config` RPC"
def request(self, source, target):
- """
- :arg source: See :ref:`source_target`
- :type source: `string` or `dict` or :class:`~xml.etree.ElementTree.Element`
+ """Create or replace an entire configuration datastore with the contents of another complete
+ configuration datastore.
- :arg target: See :ref:`source_target`
- :type target: `string` or `dict` or :class:`~xml.etree.ElementTree.Element`
+ *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
- :seealso: :ref:`return`
- """
- spec = deepcopy(CopyConfig.SPEC)
- spec['subtree'].append(util.store_or_url('target', target, self._assert))
- spec['subtree'].append(util.store_or_url('source', source, self._assert))
- return self._request(spec)
+ *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."
+class Validate(RPC):
+ "`validate` RPC. Depends on the `:validate` capability."
DEPENDS = [':validate']
- SPEC = {'tag': 'validate', 'subtree': []}
-
def request(self, source):
- """
- :arg source: See :ref:`source_target`
- :type source: `string` or `dict` or :class:`~xml.etree.ElementTree.Element`
+ """Validate the contents of the specified configuration.
- :seealso: :ref:`return`
- """
- spec = deepcopy(Validate.SPEC)
+ *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 = xml_.validated_element(source, ('config', xml_.qualify('config')))
+ src = validated_element(source, ("config", qualify("config")))
except Exception as e:
logger.debug(e)
- src = util.store_or_url('source', source, self._assert)
- spec['subtree'].append({
- 'tag': 'source',
- 'subtree': src
- })
- return self._request(spec)
+ 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."
+ "`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):
- """
- Requires *:confirmed-commit* capability if *confirmed* argument is
- :const:`True`.
+ """Commit the candidate configuration as the device's new current configuration. Depends on the `:candidate` capability.
- :arg confirmed: optional; request a confirmed commit
- :type confirmed: `bool`
+ 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.
- :arg timeout: specify timeout for confirmed commit
- :type timeout: `int`
+ *confirmed* whether this is a confirmed commit
- :seealso: :ref:`return`
- """
- spec = deepcopy(Commit.SPEC)
+ *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)
+ sub_ele(node, "confirm-timeout").text = timeout
+ return self._request(node)
class DiscardChanges(RPC):
+ "`discard-changes` RPC. Depends on the `:candidate` capability."
- # TESTED
-
- "*<discard-changes>* RPC. Depends on the *:candidate* capability."
-
- DEPENDS = [':candidate']
-
- SPEC = {'tag': 'discard-changes'}
+ DEPENDS = [":candidate"]
def request(self):
- ":seealso: :ref:`return`"
- 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