blob: 314f2a03d459f6eb3b89979a79fa1dc611d88374 [file] [log] [blame]
Chip Bolingf5af85d2019-02-12 15:36:17 -06001# Copyright 2017-present Adtran, Inc.
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
15import structlog
16import random
17import time
18from adtran_netconf import AdtranNetconfClient
19from pyvoltha.common.utils.asleep import asleep
20from ncclient.operations.rpc import RPCReply, RPCError
21from twisted.internet.defer import inlineCallbacks, returnValue
22
23log = structlog.get_logger()
24
25_dummy_xml = '<rpc-reply message-id="br-549" ' + \
26 'xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" ' + \
27 'xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">' + \
28 '<data/>' + \
29 '</rpc-reply>'
30
31
32class MockNetconfClient(AdtranNetconfClient):
33 """
34 Performs NETCONF requests
35 """
36 def __init__(self, host_ip, port=830, username='', password='', timeout=20):
37 super(MockNetconfClient, self).__init__(host_ip, port=port, username=username,
38 password=password, timeout=timeout)
39 self._connected = False
40 self._locked = {}
41
42 def __str__(self):
43 return "MockNetconfClient {}@{}:{}".format(self._username, self._ip, self._port)
44
45 @property
46 def capabilities(self):
47 """
48 Get the server's NETCONF capabilities
49
50 :return: (ncclient.capabilities.Capabilities) object representing the server's capabilities.
51 """
52 return None
53
54 @property
55 def connected(self):
56 """
57 Is this client connected to a NETCONF server
58 :return: (boolean) True if connected
59 """
60 return self._connected
61
62 @inlineCallbacks
63 def connect(self, connect_timeout=None):
64 """
65 Connect to the NETCONF server
66 o To disable attempting publickey authentication altogether, call with
67 allow_agent and look_for_keys as False.`
68
69 o hostkey_verify enables hostkey verification from ~/.ssh/known_hosts
70
71 :return: (deferred) Deferred request
72 """
73 yield asleep(random.uniform(0.1, 5.0)) # Simulate NETCONF request delay
74 self._connected = True
75 self._locked = {}
76 returnValue(True)
77
78 @inlineCallbacks
79 def close(self):
80 """
81 Close the connection to the NETCONF server
82 :return: (deferred) Deferred request
83 """
84 yield asleep(random.uniform(0.1, 0.5)) # Simulate NETCONF request delay
85 self._connected = False
86 self._locked = {}
87 returnValue(True)
88
89 @inlineCallbacks
90 def get_config(self, source='running'):
91 """
92 Get the configuration from the specified source
93
94 :param source: (string) Configuration source, 'running', 'candidate', ...
95 :return: (deferred) Deferred request that wraps the GetReply class
96 """
97 yield asleep(random.uniform(0.1, 4.0)) # Simulate NETCONF request delay
98
99 # TODO: Customize if needed...
100 xml = _dummy_xml
101 returnValue(RPCReply(xml))
102
103 @inlineCallbacks
104 def get(self, payload):
105 """
106 Get the requested data from the server
107
108 :param payload: Payload/filter
109 :return: (defeered) for GetReply
110 """
111 yield asleep(random.uniform(0.1, 3.0)) # Simulate NETCONF request delay
112
113 # TODO: Customize if needed...
114 xml = _dummy_xml
115 returnValue(RPCReply(xml))
116
117 @inlineCallbacks
118 def lock(self, source, lock_timeout):
119 """
120 Lock the configuration system
121 :param source: is the name of the configuration datastore accessed
122 :param lock_timeout: timeout in seconds for holding the lock
123 :return: (defeered) for RpcReply
124 """
125 expire_time = time.time() + lock_timeout
126
127 if source not in self._locked:
128 self._locked[source] = None
129
130 while self._locked[source] is not None:
131 # Watch for lock timeout
132 if time.time() >= self._locked[source]:
133 self._locked[source] = None
134 break
135 yield asleep(0.1)
136
137 if time.time() < expire_time:
138 yield asleep(random.uniform(0.1, 0.5)) # Simulate NETCONF request delay
139 self._locked[source] = expire_time
140
141 returnValue(RPCReply(_dummy_xml) if expire_time > time.time() else RPCError('TODO'))
142
143 @inlineCallbacks
144 def unlock(self, source):
145 """
146 Get the requested data from the server
147 :param rpc_string: RPC request
148 :param source: is the name of the configuration datastore accessed
149 :return: (defeered) for RpcReply
150 """
151 if source not in self._locked:
152 self._locked[source] = None
153
154 if self._locked[source] is not None:
155 yield asleep(random.uniform(0.1, 0.5)) # Simulate NETCONF request delay
156
157 self._locked[source] = None
158 returnValue(RPCReply(_dummy_xml))
159
160 @inlineCallbacks
161 def edit_config(self, config, target='running', default_operation='merge',
162 test_option=None, error_option=None):
163 """
164 Loads all or part of the specified config to the target configuration datastore with the ability to lock
165 the datastore during the edit.
166
167 :param config is the configuration, which must be rooted in the config element. It can be specified
168 either as a string or an Element.format="xml"
169 :param target is the name of the configuration datastore being edited
170 :param default_operation if specified must be one of { 'merge', 'replace', or 'none' }
171 :param test_option if specified must be one of { 'test_then_set', 'set' }
172 :param error_option if specified must be one of { 'stop-on-error', 'continue-on-error', 'rollback-on-error' }
173 The 'rollback-on-error' error_option depends on the :rollback-on-error capability.
174
175 :return: (defeered) for RpcReply
176 """
177 try:
178 yield asleep(random.uniform(0.1, 2.0)) # Simulate NETCONF request delay
179
180 except Exception as e:
181 log.exception('edit_config', e=e)
182 raise
183
184 # TODO: Customize if needed...
185 xml = _dummy_xml
186 returnValue(RPCReply(xml))
187
188 @inlineCallbacks
189 def rpc(self, rpc_string):
190 """
191 Custom RPC request
192 :param rpc_string: (string) RPC request
193 :return: (defeered) for GetReply
194 """
195 yield asleep(random.uniform(0.1, 2.0)) # Simulate NETCONF request delay
196
197 # TODO: Customize if needed...
198 xml = _dummy_xml
199 returnValue(RPCReply(xml))