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