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