blob: 5d55e7913ac98c04db2ffa0dfc50eb1e2b1c33dc [file] [log] [blame]
William Kurkian6f436d02019-02-06 16:25:01 -05001#
2# Copyright 2018 the original author or authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17"""
18Openolt adapter.
19"""
20import arrow
21import grpc
22import structlog
Matt Jeanneret7906d232019-02-14 14:57:38 -050023
24from zope.interface import implementer
William Kurkian6f436d02019-02-06 16:25:01 -050025from google.protobuf.empty_pb2 import Empty
26from google.protobuf.json_format import MessageToDict
27from scapy.layers.inet import Raw
28import json
29from google.protobuf.message import Message
30from grpc._channel import _Rendezvous
31from scapy.layers.l2 import Ether, Dot1Q
32from simplejson import dumps
33from twisted.internet import reactor
34from twisted.internet.defer import inlineCallbacks, returnValue
35from twisted.internet.task import LoopingCall
36
William Kurkian44cd7bb2019-02-11 16:39:12 -050037from pyvoltha.adapters.common.frameio.frameio import BpfProgramFilter, hexify
Matt Jeanneret7906d232019-02-14 14:57:38 -050038from pyvoltha.adapters.iadapter import IAdapterInterface
William Kurkian44cd7bb2019-02-11 16:39:12 -050039from pyvoltha.common.utils.asleep import asleep
40from pyvoltha.common.utils.registry import registry
41from pyvoltha.adapters.kafka.kafka_proxy import get_kafka_proxy
William Kurkian8b1690c2019-03-04 16:53:22 -050042from voltha_protos import openolt_pb2
43#from voltha_protos import third_party
44from voltha_protos.common_pb2 import OperStatus, ConnectStatus
45from voltha_protos.common_pb2 import LogLevel
46from voltha_protos.common_pb2 import OperationResp
47from voltha_protos.inter_container_pb2 import SwitchCapability, PortCapability, \
William Kurkian6f436d02019-02-06 16:25:01 -050048 InterAdapterMessageType, InterAdapterResponseBody
William Kurkian8b1690c2019-03-04 16:53:22 -050049from voltha_protos.device_pb2 import Port, PmConfig, PmConfigs, \
William Kurkian6f436d02019-02-06 16:25:01 -050050 DeviceType, DeviceTypes
William Kurkian8b1690c2019-03-04 16:53:22 -050051from voltha_protos.adapter_pb2 import Adapter
52from voltha_protos.adapter_pb2 import AdapterConfig
William Kurkian33508752019-02-12 14:56:06 -050053from openolt_flow_mgr import OpenOltFlowMgr
54from openolt_alarms import OpenOltAlarmMgr
55from openolt_statistics import OpenOltStatisticsMgr
56from openolt_bw import OpenOltBW
57from openolt_platform import OpenOltPlatform
58from openolt_resource_manager import OpenOltResourceMgr
59from openolt_device import OpenoltDevice
Matt Jeanneret9fd36df2019-02-14 19:14:36 -050060
William Kurkian8b1690c2019-03-04 16:53:22 -050061from voltha_protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs
62from voltha_protos.logical_device_pb2 import LogicalPort
63from voltha_protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
William Kurkian6f436d02019-02-06 16:25:01 -050064 OFPPF_1GB_FD, \
65 OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS, \
66 ofp_switch_features, ofp_desc
William Kurkian8b1690c2019-03-04 16:53:22 -050067from voltha_protos.openflow_13_pb2 import ofp_port
Arun Arora75e6bb22019-03-07 13:57:31 +000068from pyvoltha.common.utils.nethelpers import mac_str_to_tuple
William Kurkian6f436d02019-02-06 16:25:01 -050069
William Kurkian8b1690c2019-03-04 16:53:22 -050070#_ = third_party
William Kurkian6f436d02019-02-06 16:25:01 -050071log = structlog.get_logger()
William Kurkianfefd4642019-02-07 15:30:03 -050072OpenOltDefaults = {
73 'support_classes': {
74 'platform': OpenOltPlatform,
75 'resource_mgr': OpenOltResourceMgr,
76 'flow_mgr': OpenOltFlowMgr,
77 'alarm_mgr': OpenOltAlarmMgr,
78 'stats_mgr': OpenOltStatisticsMgr,
79 'bw_mgr': OpenOltBW
80 }
81}
William Kurkian6f436d02019-02-06 16:25:01 -050082
William Kurkian6f436d02019-02-06 16:25:01 -050083
Matt Jeanneret7906d232019-02-14 14:57:38 -050084@implementer(IAdapterInterface)
85class OpenoltAdapter(object):
William Kurkian6f436d02019-02-06 16:25:01 -050086 name = 'openolt'
87
88 supported_device_types = [
89 DeviceType(
90 id=name,
91 adapter=name,
92 accepts_bulk_flow_update=True,
93 accepts_add_remove_flow_updates=True
94 )
95 ]
96
97 # System Init Methods #
98 def __init__(self, core_proxy, adapter_proxy, config):
Matt Jeanneretbad3d982019-03-11 16:06:10 -040099 self.core_proxy = core_proxy
William Kurkian6f436d02019-02-06 16:25:01 -0500100 self.adapter_proxy = adapter_proxy
William Kurkian6f436d02019-02-06 16:25:01 -0500101 self.config = config
102 self.descriptor = Adapter(
103 id=self.name,
104 vendor='OLT white box vendor',
105 version='0.1',
Matt Jeanneret7906d232019-02-14 14:57:38 -0500106 config=AdapterConfig(log_level=LogLevel.INFO)
William Kurkian6f436d02019-02-06 16:25:01 -0500107 )
Matt Jeanneretbad3d982019-03-11 16:06:10 -0400108 log.debug('openolt.__init__', core_proxy=core_proxy, adapter_proxy=adapter_proxy)
William Kurkian6f436d02019-02-06 16:25:01 -0500109 self.devices = dict() # device_id -> OpenoltDevice()
110 self.interface = registry('main').get_args().interface
111 self.logical_device_id_to_root_device_id = dict()
112 self.num_devices = 0
Arun Arora75e6bb22019-03-07 13:57:31 +0000113 self.ofp_port_no = None
William Kurkian6f436d02019-02-06 16:25:01 -0500114
115 def start(self):
116 log.info('started', interface=self.interface)
117
118 def stop(self):
119 log.info('stopped', interface=self.interface)
120
Arun Arora75e6bb22019-03-07 13:57:31 +0000121 def get_ofp_device_info(self, device):
122 log.info('get_ofp_device_info', device_id=device.id)
123 return SwitchCapability(
124 desc=ofp_desc(
125 hw_desc='white box OLT', # Hardware description
126 sw_desc='openolt', # Software description
127 serial_num=device.serial_number, # Serial number
128 dp_desc='n/a' # Human readable description of datapath
129 ),
130 switch_features=ofp_switch_features(
131 n_buffers=256, # Max packets buffered at once # TODO fake for now
132 n_tables=2, # Number of tables supported by datapath # TODO fake for now
133 capabilities=( #Bitmap of support "ofp_capabilities" # TODO fake for now
134 OFPC_FLOW_STATS
135 | OFPC_TABLE_STATS
136 | OFPC_PORT_STATS
137 | OFPC_GROUP_STATS
138 )
139 )
140 )
141
142 def get_ofp_port_info(self, device, port_no):
143 # Since the adapter created the device port then it has the reference of the port to
144 # return the capability. TODO: Do a lookup on the NNI port number and return the
145 # appropriate attributes
146 log.info('get_ofp_port_info', port_no=port_no,
147 info=self.ofp_port_no, device_id=device.id)
148 cap = OFPPF_1GB_FD | OFPPF_FIBER
149 return PortCapability(
150 port=LogicalPort(
151 ofp_port=ofp_port(
152 hw_addr=mac_str_to_tuple(
153 '00:00:00:00:00:%02x' % port_no),
154 config=0,
155 state=OFPPS_LIVE,
156 curr=cap,
157 advertised=cap,
158 peer=cap,
159 curr_speed=OFPPF_1GB_FD,
160 max_speed=OFPPF_1GB_FD
161 ),
162 device_id=device.id,
163 device_port_no=port_no
164 )
165 )
166
William Kurkian6f436d02019-02-06 16:25:01 -0500167 def adapter_descriptor(self):
168 log.debug('get descriptor', interface=self.interface)
169 return self.descriptor
170
171 def device_types(self):
172 log.debug('get device_types', interface=self.interface,
173 items=self.supported_device_types)
174 return DeviceTypes(items=self.supported_device_types)
175
176 def health(self):
177 log.debug('get health', interface=self.interface)
178 raise NotImplementedError()
179
William Kurkian6f436d02019-02-06 16:25:01 -0500180 def change_master_state(self, master):
181 log.debug('change_master_state', interface=self.interface,
182 master=master)
183 raise NotImplementedError()
184
Matt Jeanneret7906d232019-02-14 14:57:38 -0500185 def adopt_device(self, device):
186 log.info('adopt-device', device=device)
187
188 kwargs = {
189 'support_classes': OpenOltDefaults['support_classes'],
Matt Jeanneretbad3d982019-03-11 16:06:10 -0400190 'core_proxy': self.core_proxy,
Matt Jeanneret7906d232019-02-14 14:57:38 -0500191 'adapter_proxy': self.adapter_proxy,
Matt Jeanneret7906d232019-02-14 14:57:38 -0500192 'device': device,
193 'device_num': self.num_devices + 1
194 }
195 try:
196 self.devices[device.id] = OpenoltDevice(**kwargs)
197 except Exception as e:
198 log.error('Failed to adopt OpenOLT device', error=e)
199 # TODO set status to ERROR so that is clear something went wrong
200 del self.devices[device.id]
201 raise
202 else:
203 self.num_devices += 1
204
205 def reconcile_device(self, device):
206 log.info('reconcile-device', device=device)
207 kwargs = {
208 'support_classes': OpenOltDefaults['support_classes'],
Matt Jeanneretbad3d982019-03-11 16:06:10 -0400209 'core_proxy': self.core_proxy,
210 'adapter_proxy': self.adapter_proxy,
Matt Jeanneret7906d232019-02-14 14:57:38 -0500211 'device': device,
212 'device_num': self.num_devices + 1,
213 'reconciliation': True
214 }
215 try:
216 reconciled_device = OpenoltDevice(**kwargs)
217 log.debug('reconciled-device-recreated',
218 device_id=reconciled_device.device_id)
219 self.devices[device.id] = reconciled_device
220 except Exception as e:
221 log.error('Failed to reconcile OpenOLT device', error=e,
222 exception_type=type(e).__name__)
223 del self.devices[device.id]
224 raise
225 else:
226 self.num_devices += 1
227 # Invoke the children reconciliation which would setup the
228 # basic children data structures
229 self.adapter_agent.reconcile_child_devices(device.id)
230 return device
231
William Kurkian6f436d02019-02-06 16:25:01 -0500232 def abandon_device(self, device):
233 log.info('abandon-device', device=device)
234 raise NotImplementedError()
235
Matt Jeanneret7906d232019-02-14 14:57:38 -0500236 def disable_device(self, device):
237 log.info('disable-device', device=device)
238 handler = self.devices[device.id]
239 handler.disable()
William Kurkian6f436d02019-02-06 16:25:01 -0500240
Matt Jeanneret7906d232019-02-14 14:57:38 -0500241 def reenable_device(self, device):
242 log.info('reenable-device', device=device)
243 handler = self.devices[device.id]
244 handler.reenable()
William Kurkian6f436d02019-02-06 16:25:01 -0500245
Matt Jeanneret7906d232019-02-14 14:57:38 -0500246 def reboot_device(self, device):
247 log.info('reboot_device', device=device)
248 handler = self.devices[device.id]
249 handler.reboot()
William Kurkian6f436d02019-02-06 16:25:01 -0500250
William Kurkian6f436d02019-02-06 16:25:01 -0500251 def download_image(self, device, request):
252 log.info('image_download - Not implemented yet', device=device,
253 request=request)
254 raise NotImplementedError()
255
256 def get_image_download_status(self, device, request):
257 log.info('get_image_download - Not implemented yet', device=device,
258 request=request)
259 raise NotImplementedError()
260
261 def cancel_image_download(self, device, request):
262 log.info('cancel_image_download - Not implemented yet', device=device)
263 raise NotImplementedError()
264
265 def activate_image_update(self, device, request):
266 log.info('activate_image_update - Not implemented yet',
267 device=device, request=request)
268 raise NotImplementedError()
269
270 def revert_image_update(self, device, request):
271 log.info('revert_image_update - Not implemented yet',
272 device=device, request=request)
273 raise NotImplementedError()
274
275 def self_test_device(self, device):
William Kurkian6f436d02019-02-06 16:25:01 -0500276 log.info('Not implemented yet')
277 raise NotImplementedError()
278
Matt Jeanneret7906d232019-02-14 14:57:38 -0500279 def delete_device(self, device):
280 log.info('delete-device', device=device)
281 handler = self.devices[device.id]
282 handler.delete()
283 del self.devices[device.id]
284 del self.logical_device_id_to_root_device_id[device.parent_id]
285 return device
William Kurkian6f436d02019-02-06 16:25:01 -0500286
Matt Jeanneret7906d232019-02-14 14:57:38 -0500287 def get_device_details(self, device):
288 log.debug('get_device_details', device=device)
289 raise NotImplementedError()
290
291 def update_flows_bulk(self, device, flows, groups):
292 log.info('bulk-flow-update', device_id=device.id,
Matt Jeanneret9fd36df2019-02-14 19:14:36 -0500293 number_of_flows=len(flows.items),
294 number_of_groups=len(groups.items))
Matt Jeanneret7906d232019-02-14 14:57:38 -0500295 log.debug('flows and grousp details', flows=flows, groups=groups)
296 assert len(groups.items) == 0, "Cannot yet deal with groups"
297 handler = self.devices[device.id]
298 return handler.update_flow_table(flows.items)
299
300 def update_flows_incrementally(self, device, flow_changes, group_changes):
301 log.debug('update_flows_incrementally', device=device,
302 flow_changes=flow_changes, group_changes=group_changes)
303 log.info('This device does not allow this, therefore it is Not '
304 'implemented')
305 raise NotImplementedError()
306
307 def update_logical_flows(self, device_id, flows_to_add, flows_to_remove,
308 groups, device_rules_map):
309
310 log.info('logical-flows-update', flows_to_add=len(flows_to_add),
311 flows_to_remove=len(flows_to_remove))
312 log.debug('logical-flows-details', flows_to_add=flows_to_add,
313 flows_to_remove=flows_to_remove)
314 assert len(groups) == 0, "Cannot yet deal with groups"
315 handler = self.devices[device_id]
316 handler.update_logical_flows(flows_to_add, flows_to_remove,
317 device_rules_map)
318
319 def update_pm_config(self, device, pm_configs):
320 log.info('update_pm_config - Not implemented yet', device=device,
321 pm_configs=pm_configs)
322 raise NotImplementedError()
323
324 def send_proxied_message(self, proxy_address, msg):
325 log.debug('send-proxied-message',
326 proxy_address=proxy_address,
327 proxied_msg=msg)
328 handler = self.devices[proxy_address.device_id]
329 handler.send_proxied_message(proxy_address, msg)
330
331 def receive_proxied_message(self, proxy_address, msg):
332 log.debug('receive_proxied_message - Not implemented',
333 proxy_address=proxy_address,
334 proxied_msg=msg)
335 raise NotImplementedError()
336
337 def receive_packet_out(self, logical_device_id, egress_port_no, msg):
338 log.debug('packet-out', logical_device_id=logical_device_id,
339 egress_port_no=egress_port_no, msg_len=len(msg))
340
341 def ldi_to_di(ldi):
342 di = self.logical_device_id_to_root_device_id.get(ldi)
343 if di is None:
344 logical_device = self.adapter_agent.get_logical_device(ldi)
345 di = logical_device.root_device_id
346 self.logical_device_id_to_root_device_id[ldi] = di
347 return di
348
349 try:
350 device_id = ldi_to_di(logical_device_id)
351 handler = self.devices[device_id]
352 handler.packet_out(egress_port_no, msg)
353 except Exception as e:
354 log.error('packet-out:exception', e=e.message)
355
Matt Jeanneretb428b952019-03-07 05:14:17 -0500356 def process_inter_adapter_message(self, msg):
357 log.debug('process-inter-adapter-message', msg=msg)
358 # Unpack the header to know which device needs to handle this message
359 handler = None
360 if msg.header.proxy_device_id:
361 # typical request
362 handler = self.devices[msg.header.proxy_device_id]
363 elif msg.header.to_device_id and \
364 msg.header.to_device_id in self.devices_handlers:
365 # typical response
366 handler = self.devices[msg.header.to_device_id]
367 if handler:
368 reactor.callLater(0, handler.process_inter_adapter_message, msg)
369
Matt Jeanneret7906d232019-02-14 14:57:38 -0500370 def receive_inter_adapter_message(self, msg):
371 log.info('rx_inter_adapter_msg - Not implemented')
372 raise NotImplementedError()
373
374 def suppress_alarm(self, filter):
375 log.info('suppress_alarm - Not implemented yet', filter=filter)
376 raise NotImplementedError()
377
378 def unsuppress_alarm(self, filter):
379 log.info('unsuppress_alarm - Not implemented yet', filter=filter)
380 raise NotImplementedError()
381
382 # PON Mgnt APIs #
William Kurkian6f436d02019-02-06 16:25:01 -0500383 def create_interface(self, device, data):
384 log.debug('create-interface - Not implemented - We do not use this',
385 data=data)
386 raise NotImplementedError()
387
388 def update_interface(self, device, data):
389 log.debug('update-interface - Not implemented - We do not use this',
390 data=data)
391 raise NotImplementedError()
392
393 def remove_interface(self, device, data):
394 log.debug('remove-interface - Not implemented - We do not use this',
395 data=data)
396 raise NotImplementedError()
397
398 def receive_onu_detect_state(self, proxy_address, state):
399 log.debug('receive-onu-detect-state - Not implemented - We do not '
400 'use this', proxy_address=proxy_address,
401 state=state)
402 raise NotImplementedError()
403
404 def create_tcont(self, device, tcont_data, traffic_descriptor_data):
405 log.info('create-tcont - Not implemented - We do not use this',
406 tcont_data=tcont_data,
407 traffic_descriptor_data=traffic_descriptor_data)
408 raise NotImplementedError()
409
410 def update_tcont(self, device, tcont_data, traffic_descriptor_data):
411 log.info('update-tcont - Not implemented - We do not use this',
412 tcont_data=tcont_data,
413 traffic_descriptor_data=traffic_descriptor_data)
414 raise NotImplementedError()
415
416 def remove_tcont(self, device, tcont_data, traffic_descriptor_data):
417 log.info('remove-tcont - Not implemented - We do not use this',
418 tcont_data=tcont_data,
419 traffic_descriptor_data=traffic_descriptor_data)
420 raise NotImplementedError()
421
422 def create_gemport(self, device, data):
423 log.info('create-gemport - Not implemented - We do not use this',
424 data=data)
425 raise NotImplementedError()
426
427 def update_gemport(self, device, data):
428 log.info('update-gemport - Not implemented - We do not use this',
429 data=data)
430 raise NotImplementedError()
431
432 def remove_gemport(self, device, data):
433 log.info('remove-gemport - Not implemented - We do not use this',
434 data=data)
435 raise NotImplementedError()
436
437 def create_multicast_gemport(self, device, data):
438 log.info('create-mcast-gemport - Not implemented - We do not use '
439 'this', data=data)
440 raise NotImplementedError()
441
442 def update_multicast_gemport(self, device, data):
443 log.info('update-mcast-gemport - Not implemented - We do not use '
444 'this', data=data)
445 raise NotImplementedError()
446
447 def remove_multicast_gemport(self, device, data):
448 log.info('remove-mcast-gemport - Not implemented - We do not use '
449 'this', data=data)
450 raise NotImplementedError()
451
452 def create_multicast_distribution_set(self, device, data):
453 log.info('create-mcast-distribution-set - Not implemented - We do '
454 'not use this', data=data)
455 raise NotImplementedError()
456
457 def update_multicast_distribution_set(self, device, data):
458 log.info('update-mcast-distribution-set - Not implemented - We do '
459 'not use this', data=data)
460 raise NotImplementedError()
461
462 def remove_multicast_distribution_set(self, device, data):
463 log.info('remove-mcast-distribution-set - Not implemented - We do '
464 'not use this', data=data)
465 raise NotImplementedError()
466
Matt Jeanneret7906d232019-02-14 14:57:38 -0500467 def delete_child_device(self, parent_device_id, child_device):
468 log.info('delete-child_device', parent_device_id=parent_device_id,
469 child_device=child_device)
470 handler = self.devices[parent_device_id]
471 if handler is not None:
472 handler.delete_child_device(child_device)
William Kurkian6f436d02019-02-06 16:25:01 -0500473 else:
Matt Jeanneret7906d232019-02-14 14:57:38 -0500474 log.error('Could not find matching handler',
475 looking_for_device_id=parent_device_id,
476 available_handlers=self.devices.keys())
William Kurkian6f436d02019-02-06 16:25:01 -0500477
Matt Jeanneret7906d232019-02-14 14:57:38 -0500478 # This is currently not part of the Iadapter interface
479 def collect_stats(self, device_id):
480 log.info('collect_stats', device_id=device_id)
481 handler = self.devices[device_id]
482 if handler is not None:
483 handler.trigger_statistics_collection()
484 else:
485 log.error('Could not find matching handler',
486 looking_for_device_id=device_id,
487 available_handlers=self.devices.keys())
William Kurkian6f436d02019-02-06 16:25:01 -0500488
Matt Jeanneret7906d232019-02-14 14:57:38 -0500489 def simulate_alarm(self, device, request):
490 log.info('simulate_alarm', device=device, request=request)
William Kurkian6f436d02019-02-06 16:25:01 -0500491
Matt Jeanneret7906d232019-02-14 14:57:38 -0500492 if device.id not in self.devices:
493 log.error("Device does not exist", device_id=device.id)
494 return OperationResp(code=OperationResp.OPERATION_FAILURE,
495 additional_info="Device %s does not exist"
Matt Jeanneret9fd36df2019-02-14 19:14:36 -0500496 % device.id)
William Kurkian6f436d02019-02-06 16:25:01 -0500497
Matt Jeanneret7906d232019-02-14 14:57:38 -0500498 handler = self.devices[device.id]
William Kurkian6f436d02019-02-06 16:25:01 -0500499
Matt Jeanneret7906d232019-02-14 14:57:38 -0500500 handler.simulate_alarm(request)
William Kurkian6f436d02019-02-06 16:25:01 -0500501
Matt Jeanneret7906d232019-02-14 14:57:38 -0500502 return OperationResp(code=OperationResp.OPERATION_SUCCESS)