no need for weakvaluedict in RPCReplyListener, after all
[ncclient] / ncclient / operations / edit.py
1 # Copyright 2009 Shikhar Bhushan
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 from copy import deepcopy
16
17 from ncclient import content
18
19 from rpc import RPC
20
21 import util
22
23 import logging
24 logger = logging.getLogger('ncclient.operations.edit')
25
26
27
28 "Operations related to changing device configuration"
29
30 class EditConfig(RPC):
31
32     # TESTED
33
34     "*<edit-config>* RPC"
35
36     SPEC = {'tag': 'edit-config', 'subtree': []}
37
38     def request(self, target, config, default_operation=None, test_option=None,
39                 error_option=None):
40         """
41         :arg target: see :ref:`source_target`
42         :type target: string
43
44         :arg config: a config element in :ref:`dtree`
45         :type config: `string` or `dict` or :class:`~xml.etree.ElementTree.Element`
46
47         :arg default_operation: optional; one of {'merge', 'replace', 'none'}
48         :type default_operation: `string`
49
50         :arg test_option: optional; one of {'stop-on-error', 'continue-on-error', 'rollback-on-error'}. Last option depends on the *:rollback-on-error* capability
51         :type test_option: string
52
53         :seealso: :ref:`return`
54         """
55         spec = deepcopy(EditConfig.SPEC)
56         subtree = spec['subtree']
57         subtree.append(util.store_or_url('target', target, self._assert))
58         subtree.append(content.validated_element(config, ('config', content.qualify('config'))))
59         if default_operation is not None:
60             subtree.append({
61                 'tag': 'default-operation',
62                 'text': default_operation
63                 })
64         if test_option is not None:
65             self._assert(':validate')
66             subtree.append({
67                 'tag': 'test-option',
68                 'text': test_option
69                 })
70         if error_option is not None:
71             if error_option == 'rollback-on-error':
72                 self._assert(':rollback-on-error')
73             subtree.append({
74                 'tag': 'error-option',
75                 'text': error_option
76                 })
77         return self._request(spec)
78
79 class DeleteConfig(RPC):
80
81     # TESTED
82
83     "*<delete-config>* RPC"
84
85     SPEC = {'tag': 'delete-config', 'subtree': []}
86
87     def request(self, target):
88         """
89         :arg target: See :ref:`source_target`
90         :type target: `string` or `dict` or :class:`~xml.etree.ElementTree.Element`
91
92         :seealso: :ref:`return`
93         """
94         spec = deepcopy(DeleteConfig.SPEC)
95         spec['subtree'].append(util.store_or_url('target', target, self._assert))
96         return self._request(spec)
97
98
99 class CopyConfig(RPC):
100
101     # TESTED
102
103     "*<copy-config>* RPC"
104
105     SPEC = {'tag': 'copy-config', 'subtree': []}
106
107     def request(self, source, target):
108         """
109         :arg source: See :ref:`source_target`
110         :type source: `string` or `dict` or :class:`~xml.etree.ElementTree.Element`
111
112         :arg target: See :ref:`source_target`
113         :type target: `string` or `dict` or :class:`~xml.etree.ElementTree.Element`
114
115         :seealso: :ref:`return`
116         """
117         spec = deepcopy(CopyConfig.SPEC)
118         spec['subtree'].append(util.store_or_url('target', target, self._assert))
119         spec['subtree'].append(util.store_or_url('source', source, self._assert))
120         return self._request(spec)
121
122
123 class Validate(RPC):
124
125     # TESTED
126
127     "*<validate>* RPC. Depends on the *:validate* capability."
128
129     DEPENDS = [':validate']
130
131     SPEC = {'tag': 'validate', 'subtree': []}
132
133     def request(self, source):
134         """
135         :arg source: See :ref:`source_target`
136         :type source: `string` or `dict` or :class:`~xml.etree.ElementTree.Element`
137
138         :seealso: :ref:`return`
139         """
140         spec = deepcopy(Validate.SPEC)
141         try:
142             src = content.validated_element(source, ('config', content.qualify('config')))
143         except Exception as e:
144             logger.debug(e)
145             src = util.store_or_url('source', source, self._assert)
146         spec['subtree'].append({
147             'tag': 'source',
148             'subtree': src
149             })
150         return self._request(spec)
151
152
153 class Commit(RPC):
154
155     # TESTED
156
157     "*<commit>* RPC. Depends on the *:candidate* capability."
158
159     DEPENDS = [':candidate']
160
161     SPEC = {'tag': 'commit', 'subtree': []}
162
163     def _parse_hook(self):
164         pass
165
166     def request(self, confirmed=False, timeout=None):
167         """
168         Requires *:confirmed-commit* capability if *confirmed* argument is
169         :const:`True`.
170
171         :arg confirmed: optional; request a confirmed commit
172         :type confirmed: `bool`
173
174         :arg timeout: specify timeout for confirmed commit
175         :type timeout: `int`
176
177         :seealso: :ref:`return`
178         """
179         spec = deepcopy(Commit.SPEC)
180         if confirmed:
181             self._assert(':confirmed-commit')
182             spec['subtree'].append({'tag': 'confirmed'})
183             if timeout is not None:
184                 spec['subtree'].append({
185                     'tag': 'confirm-timeout',
186                     'text': timeout
187                 })
188         return self._request(Commit.SPEC)
189
190
191 class DiscardChanges(RPC):
192
193     # TESTED
194
195     "*<discard-changes>* RPC. Depends on the *:candidate* capability."
196
197     DEPENDS = [':candidate']
198
199     SPEC = {'tag': 'discard-changes'}
200
201     def request(self):
202         ":seealso: :ref:`return`"
203         return self._request(DiscardChanges.SPEC)