blob: c64c2c129b06e460d5f2076e6ff8629ce7dbcb35 [file] [log] [blame]
Khen Nursimulua7b842a2016-12-03 23:28:42 -05001#!/usr/bin/env python
2#
3# Copyright 2016 the original author or authors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17import structlog
18import io
19from lxml import etree
20from lxml.builder import E
21import netconf.nc_common.error as ncerror
22from netconf import NSMAP, qmap
23from utils import elm
24from twisted.internet.defer import inlineCallbacks, returnValue, Deferred
25from capabilities import Capabilities
26from netconf.nc_rpc.rpc_factory import get_rpc_factory_instance
27from netconf.constants import Constants as C
28
29log = structlog.get_logger()
30
31
32class NetconfProtocolError(Exception): pass
33
34
35class NetconfProtocolHandler:
Khen Nursimuluaaac7ee2016-12-11 22:03:52 -050036 def __init__(self, nc_server, nc_conn, session, grpc_client):
Khen Nursimulua7b842a2016-12-03 23:28:42 -050037 self.started = True
38 self.conn = nc_conn
39 self.nc_server = nc_server
Khen Nursimuluaaac7ee2016-12-11 22:03:52 -050040 self.grpc_client = grpc_client
Khen Nursimulua7b842a2016-12-03 23:28:42 -050041 self.new_framing = False
42 self.capabilities = Capabilities()
43 self.session = session
44 self.exiting = False
45 self.connected = Deferred()
46 self.connected.addCallback(self.nc_server.client_disconnected,
47 self, None)
48
49 def send_message(self, msg):
50 self.conn.send_msg(C.XML_HEADER + msg, self.new_framing)
51
52 def receive_message(self):
53 return self.conn.receive_msg_any(self.new_framing)
54
55 def send_hello(self, caplist, session=None):
56 msg = elm(C.HELLO, attrib={C.XMLNS: NSMAP[C.NC]})
57 caps = E.capabilities(*[E.capability(x) for x in caplist])
58 msg.append(caps)
59
60 if session is not None:
61 msg.append(E(C.SESSION_ID, str(session.session_id)))
62 msg = etree.tostring(msg)
63 log.info("Sending HELLO", msg=msg)
64 msg = msg.decode('utf-8')
65 self.send_message(msg)
66
67 def send_rpc_reply(self, rpc_reply, origmsg):
68 reply = etree.Element(qmap(C.NC) + C.RPC_REPLY, attrib=origmsg.attrib,
69 nsmap=origmsg.nsmap)
70 try:
71 rpc_reply.getchildren
72 reply.append(rpc_reply)
73 except AttributeError:
74 reply.extend(rpc_reply)
75 ucode = etree.tounicode(reply, pretty_print=True)
76 log.info("RPC-Reply", reply=ucode)
77 self.send_message(ucode)
78
79 def set_framing_version(self):
80 if C.NETCONF_BASE_11 in self.capabilities.client_caps:
81 self.new_framing = True
82 elif C.NETCONF_BASE_10 not in self.capabilities.client_caps:
83 raise SessionError(
84 "Client doesn't implement 1.0 or 1.1 of netconf")
85
86 @inlineCallbacks
87 def open_session(self):
88 # The transport should be connected at this point.
89 try:
90 # Send hello message.
91 yield self.send_hello(self.capabilities.server_caps, self.session)
92 # Get reply
93 reply = yield self.receive_message()
94 log.info("reply-received", reply=reply)
95
96 # Parse reply
97 tree = etree.parse(io.BytesIO(reply.encode('utf-8')))
98 root = tree.getroot()
99 caps = root.xpath(C.CAPABILITY_XPATH, namespaces=NSMAP)
100
101 # Store capabilities
102 for cap in caps:
103 self.capabilities.add_client_capability(cap.text)
104
105 self.set_framing_version()
106 self.session.session_opened = True
107
108 log.info('session-opened', session_id=self.session.session_id,
109 framing="1.1" if self.new_framing else "1.0")
110 except Exception as e:
111 log.error('hello-failure', exception=repr(e))
112 self.stop(repr(e))
113 raise
114
115 @inlineCallbacks
116 def start(self):
117 log.info('starting')
118
119 try:
120 yield self.open_session()
121 while True:
122 if not self.session.session_opened:
123 break;
124 msg = yield self.receive_message()
125 yield self.handle_request(msg)
126
127 except Exception as e:
128 log.exception('exception', exception=repr(e))
129 self.stop(repr(e))
130
131 log.info('shutdown')
132 returnValue(self)
133
134 @inlineCallbacks
135 def handle_request(self, msg):
136 if not self.session.session_opened:
137 return
138
139 # Any error with XML encoding here is going to cause a session close
140 try:
141 tree = etree.parse(io.BytesIO(msg.encode('utf-8')))
142 if not tree:
143 raise ncerror.SessionError(msg, "Invalid XML from client.")
144 except etree.XMLSyntaxError:
145 log.error("malformed-message", msg=msg)
146 try:
147 error = ncerror.BadMsg(msg)
148 self.send_message(error.get_reply_msg())
149 except AttributeError:
150 log.error("attribute-error", msg=msg)
151 # close session
152 self.close()
153 return
154
155 rpcs = tree.xpath(C.RPC_XPATH, namespaces=NSMAP)
156 if not rpcs:
157 raise ncerror.SessionError(msg, "No rpc found")
158
159 # A message can have multiple rpc requests
160 rpc_factory = get_rpc_factory_instance()
161 for rpc in rpcs:
162 try:
163 # Validate message id is received
164 try:
165 msg_id = rpc.get(C.MESSAGE_ID)
166 log.info("Received-rpc-message-id", msg_id=msg_id)
167 except (TypeError, ValueError):
168 log.error('no-message-id', rpc=rpc)
169 raise ncerror.MissingElement(msg, C.MESSAGE_ID)
170
171 # Get a rpc handler
172 rpc_handler = rpc_factory.get_rpc_handler(rpc,
173 msg,
Khen Nursimuluaaac7ee2016-12-11 22:03:52 -0500174 self.grpc_client,
Khen Nursimulua7b842a2016-12-03 23:28:42 -0500175 self.session)
176 if rpc_handler:
177 # set the parameters for this handler
178 response = yield rpc_handler.execute()
179 log.info('handler',
180 rpc_handler=rpc_handler,
181 is_error=response.is_error,
182 response=response)
Khen Nursimulu7626ce12016-12-21 11:51:46 -0500183 # self.send_rpc_reply(response.node, rpc)
184 self.send_rpc_reply(self.get_instance(), rpc)
185
Khen Nursimulua7b842a2016-12-03 23:28:42 -0500186 if response.close_session:
187 log.info('response-closing-session', response=response)
188 self.close()
189 else:
190 log.error('no-rpc-handler',
191 request=msg,
192 session_id=self.session.session_id)
193 raise ncerror.NotImpl(msg)
194
195 except ncerror.BadMsg as err:
196 log.info('ncerror.BadMsg')
197 if self.new_framing:
198 self.send_message(err.get_reply_msg())
199 else:
200 # If we are 1.0 we have to simply close the connection
201 # as we are not allowed to send this error
202 log.error("Closing-1-0-session--malformed-message")
203 self.close()
204 except (ncerror.NotImpl, ncerror.MissingElement) as e:
205 log.info('error', repr(e))
206 self.send_message(e.get_reply_msg())
207 except Exception as ex:
208 log.info('Exception', repr(ex))
209 error = ncerror.ServerException(rpc, ex)
210 self.send_message(error.get_reply_msg())
211
Khen Nursimulua7b842a2016-12-03 23:28:42 -0500212
213 def stop(self, reason):
214 if not self.exiting:
215 log.debug('stopping')
216 self.exiting = True
217 if self.session.session_opened:
218 # TODO: send a closing message to the far end
219 self.conn.close_connection()
220 self.nc_server.session_mgr.remove_session(self.session)
221 self.session.session_opened = False
222 self.connected.callback(None)
223 log.info('stopped')
224
225 def close(self):
226 if not self.exiting:
227 log.debug('closing-client')
228 self.exiting = True
229 if self.session.session_opened:
230 self.conn.close_connection()
231 self.nc_server.session_mgr.remove_session(self.session)
232 self.session.session_opened = False
233 self.connected.callback(None)
234 log.info('closing-client')
Khen Nursimulu7626ce12016-12-21 11:51:46 -0500235
236 # Example of a properly formatted Yang-XML message
237 def get_instance(self):
238 xml_string = """
239 <data>
240 <Voltha xmlns="urn:opencord:params:xml:ns:voltha:ietf-voltha">
241 <instances>
242 <log_level>INFO</log_level>
243 <device_types>
244 <adapter>simulated_onu</adapter>
245 <accepts_bulk_flow_update>True</accepts_bulk_flow_update>
246 <id>simulated_onu</id>
247 <accepts_add_remove_flow_updates>False</accepts_add_remove_flow_updates>
248 </device_types>
249 <device_types>
250 <adapter>tibit_onu</adapter>
251 <accepts_bulk_flow_update>True</accepts_bulk_flow_update>
252 <id>tibit_onu</id>
253 <accepts_add_remove_flow_updates>False</accepts_add_remove_flow_updates>
254 </device_types>
255 <device_types>
256 <adapter>maple_olt</adapter>
257 <accepts_bulk_flow_update>True</accepts_bulk_flow_update>
258 <id>maple_olt</id>
259 <accepts_add_remove_flow_updates>False</accepts_add_remove_flow_updates>
260 </device_types>
261 <device_types>
262 <adapter>tibit_olt</adapter>
263 <accepts_bulk_flow_update>True</accepts_bulk_flow_update>
264 <id>tibit_olt</id>
265 <accepts_add_remove_flow_updates>False</accepts_add_remove_flow_updates>
266 </device_types>
267 <device_types>
268 <adapter>broadcom_onu</adapter>
269 <accepts_bulk_flow_update>True</accepts_bulk_flow_update>
270 <id>broadcom_onu</id>
271 <accepts_add_remove_flow_updates>False</accepts_add_remove_flow_updates>
272 </device_types>
273 <device_types>
274 <adapter>simulated_olt</adapter>
275 <accepts_bulk_flow_update>True</accepts_bulk_flow_update>
276 <id>simulated_olt</id>
277 <accepts_add_remove_flow_updates>False</accepts_add_remove_flow_updates>
278 </device_types>
279 <logical_devices>
280 <datapath_id>1</datapath_id>
281 <root_device_id>simulated_olt_1</root_device_id>
282 <switch_features>
283 <auxiliary_id>0</auxiliary_id>
284 <n_tables>2</n_tables>
285 <datapath_id>0</datapath_id>
286 <capabilities>15</capabilities>
287 <n_buffers>256</n_buffers>
288 </switch_features>
289 <flows/>
290 <id>simulated1</id>
291 <flow_groups/>
292 <ports>
293 <device_port_no>2</device_port_no>
294 <root_port>False</root_port>
295 <device_id>simulated_onu_1</device_id>
296 <id>onu1</id>
297 <ofp_port>
298 <hw_addr>
299 <item>0</item>
300 <item>0</item>
301 <item>0</item>
302 <item>0</item>
303 <item>0</item>
304 <item>1</item>
305 </hw_addr>
306 <curr_speed>32</curr_speed>
307 <curr>4128</curr>
308 <name>onu1</name>
309 <supported>0</supported>
310 <state>4</state>
311 <max_speed>32</max_speed>
312 <advertised>4128</advertised>
313 <peer>4128</peer>
314 <config>0</config>
315 <port_no>1</port_no>
316 </ofp_port>
317 </ports>
318 <ports>
319 <device_port_no>2</device_port_no>
320 <root_port>False</root_port>
321 <device_id>simulated_onu_2</device_id>
322 <id>onu2</id>
323 <ofp_port>
324 <hw_addr>
325 <item>0</item>
326 <item>0</item>
327 <item>0</item>
328 <item>0</item>
329 <item>0</item>
330 <item>2</item>
331 </hw_addr>
332 <curr_speed>32</curr_speed>
333 <curr>4128</curr>
334 <name>onu2</name>
335 <supported>0</supported>
336 <state>4</state>
337 <max_speed>32</max_speed>
338 <advertised>4128</advertised>
339 <peer>4128</peer>
340 <config>0</config>
341 <port_no>2</port_no>
342 </ofp_port>
343 </ports>
344 <ports>
345 <device_port_no>2</device_port_no>
346 <root_port>True</root_port>
347 <device_id>simulated_olt_1</device_id>
348 <id>olt1</id>
349 <ofp_port>
350 <hw_addr>
351 <item>0</item>
352 <item>0</item>
353 <item>0</item>
354 <item>0</item>
355 <item>0</item>
356 <item>129</item>
357 </hw_addr>
358 <curr_speed>32</curr_speed>
359 <curr>4128</curr>
360 <name>olt1</name>
361 <supported>0</supported>
362 <state>4</state>
363 <max_speed>32</max_speed>
364 <advertised>4128</advertised>
365 <peer>4128</peer>
366 <config>0</config>
367 <port_no>129</port_no>
368 </ofp_port>
369 </ports>
370 <desc>
371 <dp_desc>n/a</dp_desc>
372 <sw_desc>simualted pon</sw_desc>
373 <hw_desc>simualted pon</hw_desc>
374 <serial_num>985c4449d50a441ca843401e2f44e682</serial_num>
375 <mfr_desc>cord porject</mfr_desc>
376 </desc>
377 </logical_devices>
378 <devices>
379 <item>
380 <vendor>simulated</vendor>
381 <parent_port_no>0</parent_port_no>
382 <software_version>1.0</software_version>
383 <connect_status>UNKNOWN</connect_status>
384 <type>simulated_olt</type>
385 <adapter>simulated_olt</adapter>
386 <vlan>0</vlan>
387 <hardware_version>n/a</hardware_version>
388 <flows>
389 <items/>
390 </flows>
391 <ports>
392 <item>
393 <peers>
394 <item>
395 <port_no>1</port_no>
396 <device_id>simulated_onu_1</device_id>
397 </item>
398 <item>
399 <port_no>1</port_no>
400 <device_id>simulated_onu_2</device_id>
401 </item>
402 </peers>
403 <label>pon</label>
404 <oper_status>UNKNOWN</oper_status>
405 <admin_state>UNKNOWN</admin_state>
406 <type>PON_OLT</type>
407 <port_no>1</port_no>
408 <device_id>simulated_olt_1</device_id>
409 </item>
410 <item>
411 <peers/>
412 <label>eth</label>
413 <oper_status>UNKNOWN</oper_status>
414 <admin_state>UNKNOWN</admin_state>
415 <type>ETHERNET_NNI</type>
416 <port_no>2</port_no>
417 <device_id>simulated_olt_1</device_id>
418 </item>
419 </ports>
420 <parent_id/>
421 <oper_status>DISCOVERED</oper_status>
422 <flow_groups>
423 <items/>
424 </flow_groups>
425 <admin_state>UNKNOWN</admin_state>
426 <serial_number>19addcd7305d4d4fa90300cb8e4ab9a6</serial_number>
427 <model>n/a</model>
428 <root>True</root>
429 <id>simulated_olt_1</id>
430 <firmware_version>n/a</firmware_version>
431 </item>
432 <item>
433 <vendor>simulated</vendor>
434 <parent_port_no>1</parent_port_no>
435 <software_version>1.0</software_version>
436 <connect_status>UNKNOWN</connect_status>
437 <root>False</root>
438 <adapter>simulated_onu</adapter>
439 <vlan>101</vlan>
440 <hardware_version>n/a</hardware_version>
441 <flows>
442 <items/>
443 </flows>
444 <ports>
445 <item>
446 <peers/>
447 <label>eth</label>
448 <oper_status>UNKNOWN</oper_status>
449 <admin_state>UNKNOWN</admin_state>
450 <type>ETHERNET_UNI</type>
451 <port_no>2</port_no>
452 <device_id>simulated_onu_1</device_id>
453 </item>
454 <item>
455 <peers>
456 <item>
457 <port_no>1</port_no>
458 <device_id>simulated_olt_1</device_id>
459 </item>
460 </peers>
461 <label>pon</label>
462 <oper_status>UNKNOWN</oper_status>
463 <admin_state>UNKNOWN</admin_state>
464 <type>PON_ONU</type>
465 <port_no>1</port_no>
466 <device_id>simulated_onu_1</device_id>
467 </item>
468 </ports>
469 <parent_id>simulated_olt_1</parent_id>
470 <oper_status>DISCOVERED</oper_status>
471 <flow_groups>
472 <items/>
473 </flow_groups>
474 <admin_state>UNKNOWN</admin_state>
475 <serial_number>8ce6514e1b324d349038d9a80af04772</serial_number>
476 <model>n/a</model>
477 <type>simulated_onu</type>
478 <id>simulated_onu_1</id>
479 <firmware_version>n/a</firmware_version>
480 </item>
481 <item>
482 <vendor>simulated</vendor>
483 <parent_port_no>1</parent_port_no>
484 <software_version>1.0</software_version>
485 <connect_status>UNKNOWN</connect_status>
486 <root>False</root>
487 <adapter>simulated_onu</adapter>
488 <vlan>102</vlan>
489 <hardware_version>n/a</hardware_version>
490 <flows>
491 <items/>
492 </flows>
493 <ports>
494 <item>
495 <peers/>
496 <label>eth</label>
497 <oper_status>UNKNOWN</oper_status>
498 <admin_state>UNKNOWN</admin_state>
499 <type>ETHERNET_UNI</type>
500 <port_no>2</port_no>
501 <device_id>simulated_onu_2</device_id>
502 </item>
503 <item>
504 <peers>
505 <item>
506 <port_no>1</port_no>
507 <device_id>simulated_olt_1</device_id>
508 </item>
509 </peers>
510 <label>pon</label>
511 <oper_status>UNKNOWN</oper_status>
512 <admin_state>UNKNOWN</admin_state>
513 <type>PON_ONU</type>
514 <port_no>1</port_no>
515 <device_id>simulated_onu_2</device_id>
516 </item>
517 </ports>
518 <parent_id>simulated_olt_1</parent_id>
519 <oper_status>DISCOVERED</oper_status>
520 <flow_groups>
521 <items/>
522 </flow_groups>
523 <admin_state>UNKNOWN</admin_state>
524 <serial_number>0dfbb5af422044639c0660b518c06519</serial_number>
525 <model>n/a</model>
526 <type>simulated_onu</type>
527 <id>simulated_onu_2</id>
528 <firmware_version>n/a</firmware_version>
529 </item>
530 </devices>
531 <instance_id>compose_voltha_1</instance_id>
532 <version>0.9.0</version>
533 <health>
534 <state>HEALTHY</state>
535 </health>
536 <device_groups>
537 <item>
538 <logical_devices/>
539 <id>1</id>
540 <devices/>
541 </item>
542 </device_groups>
543 <adapters>
544 <item>
545 <config>
546 <log_level>INFO</log_level>
547 </config>
548 <version>0.1</version>
549 <vendor>Voltha project</vendor>
550 <id>simulated_onu</id>
551 <logical_device_ids/>
552 </item>
553 <item>
554 <config>
555 <log_level>INFO</log_level>
556 </config>
557 <version>0.1</version>
558 <vendor>Tibit Communications Inc.</vendor>
559 <id>tibit_onu</id>
560 <logical_device_ids/>
561 </item>
562 <item>
563 <config>
564 <log_level>INFO</log_level>
565 </config>
566 <version>0.1</version>
567 <vendor>Voltha project</vendor>
568 <id>maple_olt</id>
569 <logical_device_ids/>
570 </item>
571 <item>
572 <config>
573 <log_level>INFO</log_level>
574 </config>
575 <version>0.1</version>
576 <vendor>Tibit Communications Inc.</vendor>
577 <id>tibit_olt</id>
578 <logical_device_ids/>
579 </item>
580 <item>
581 <config>
582 <log_level>INFO</log_level>
583 </config>
584 <version>0.1</version>
585 <vendor>Voltha project</vendor>
586 <id>broadcom_onu</id>
587 <logical_device_ids/>
588 </item>
589 <item>
590 <config>
591 <log_level>INFO</log_level>
592 </config>
593 <version>0.1</version>
594 <vendor>Voltha project</vendor>
595 <id>simulated_olt</id>
596 <logical_device_ids/>
597 </item>
598 </adapters>
599 </instances>
600 </Voltha>
601 </data>
602 """
603 return etree.fromstring(xml_string)