blob: fd4e6d1e9f280269e6edc04651055847a69e0598 [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
42from pyvoltha.protos import openolt_pb2
43from pyvoltha.protos import third_party
44from pyvoltha.protos.common_pb2 import OperStatus, ConnectStatus
45from pyvoltha.protos.common_pb2 import LogLevel
46from pyvoltha.protos.common_pb2 import OperationResp
47from pyvoltha.protos.inter_container_pb2 import SwitchCapability, PortCapability, \
William Kurkian6f436d02019-02-06 16:25:01 -050048 InterAdapterMessageType, InterAdapterResponseBody
William Kurkian44cd7bb2019-02-11 16:39:12 -050049from pyvoltha.protos.device_pb2 import Port, PmConfig, PmConfigs, \
William Kurkian6f436d02019-02-06 16:25:01 -050050 DeviceType, DeviceTypes
William Kurkian44cd7bb2019-02-11 16:39:12 -050051from pyvoltha.protos.adapter_pb2 import Adapter
52from pyvoltha.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
William Kurkian6f436d02019-02-06 16:25:01 -050060
William Kurkian44cd7bb2019-02-11 16:39:12 -050061from pyvoltha.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs
62from pyvoltha.protos.logical_device_pb2 import LogicalPort
63from pyvoltha.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 Kurkian44cd7bb2019-02-11 16:39:12 -050067from pyvoltha.protos.openflow_13_pb2 import ofp_port
William Kurkian6f436d02019-02-06 16:25:01 -050068
69_ = third_party
70log = structlog.get_logger()
William Kurkianfefd4642019-02-07 15:30:03 -050071OpenOltDefaults = {
72 'support_classes': {
73 'platform': OpenOltPlatform,
74 'resource_mgr': OpenOltResourceMgr,
75 'flow_mgr': OpenOltFlowMgr,
76 'alarm_mgr': OpenOltAlarmMgr,
77 'stats_mgr': OpenOltStatisticsMgr,
78 'bw_mgr': OpenOltBW
79 }
80}
William Kurkian6f436d02019-02-06 16:25:01 -050081
William Kurkian6f436d02019-02-06 16:25:01 -050082
Matt Jeanneret7906d232019-02-14 14:57:38 -050083@implementer(IAdapterInterface)
84class OpenoltAdapter(object):
William Kurkian6f436d02019-02-06 16:25:01 -050085 name = 'openolt'
86
87 supported_device_types = [
88 DeviceType(
89 id=name,
90 adapter=name,
91 accepts_bulk_flow_update=True,
92 accepts_add_remove_flow_updates=True
93 )
94 ]
95
96 # System Init Methods #
97 def __init__(self, core_proxy, adapter_proxy, config):
William Kurkian6f436d02019-02-06 16:25:01 -050098 self.adapter_proxy = adapter_proxy
Matt Jeanneret7906d232019-02-14 14:57:38 -050099 self.adapter_agent = core_proxy
William Kurkian6f436d02019-02-06 16:25:01 -0500100 self.config = config
101 self.descriptor = Adapter(
102 id=self.name,
103 vendor='OLT white box vendor',
104 version='0.1',
Matt Jeanneret7906d232019-02-14 14:57:38 -0500105 config=AdapterConfig(log_level=LogLevel.INFO)
William Kurkian6f436d02019-02-06 16:25:01 -0500106 )
Matt Jeanneret7906d232019-02-14 14:57:38 -0500107 log.debug('openolt.__init__', adapter_agent=adapter_proxy)
William Kurkian6f436d02019-02-06 16:25:01 -0500108 self.devices = dict() # device_id -> OpenoltDevice()
109 self.interface = registry('main').get_args().interface
110 self.logical_device_id_to_root_device_id = dict()
111 self.num_devices = 0
112
113 def start(self):
114 log.info('started', interface=self.interface)
115
116 def stop(self):
117 log.info('stopped', interface=self.interface)
118
William Kurkian6f436d02019-02-06 16:25:01 -0500119 def adapter_descriptor(self):
120 log.debug('get descriptor', interface=self.interface)
121 return self.descriptor
122
123 def device_types(self):
124 log.debug('get device_types', interface=self.interface,
125 items=self.supported_device_types)
126 return DeviceTypes(items=self.supported_device_types)
127
128 def health(self):
129 log.debug('get health', interface=self.interface)
130 raise NotImplementedError()
131
William Kurkian6f436d02019-02-06 16:25:01 -0500132 def change_master_state(self, master):
133 log.debug('change_master_state', interface=self.interface,
134 master=master)
135 raise NotImplementedError()
136
Matt Jeanneret7906d232019-02-14 14:57:38 -0500137 def adopt_device(self, device):
138 log.info('adopt-device', device=device)
139
140 kwargs = {
141 'support_classes': OpenOltDefaults['support_classes'],
142 'adapter_proxy': self.adapter_proxy,
143 'adapter_agent': self.adapter_agent,
144 'device': device,
145 'device_num': self.num_devices + 1
146 }
147 try:
148 self.devices[device.id] = OpenoltDevice(**kwargs)
149 except Exception as e:
150 log.error('Failed to adopt OpenOLT device', error=e)
151 # TODO set status to ERROR so that is clear something went wrong
152 del self.devices[device.id]
153 raise
154 else:
155 self.num_devices += 1
156
157 def reconcile_device(self, device):
158 log.info('reconcile-device', device=device)
159 kwargs = {
160 'support_classes': OpenOltDefaults['support_classes'],
161 'adapter_agent': self.adapter_agent,
162 'device': device,
163 'device_num': self.num_devices + 1,
164 'reconciliation': True
165 }
166 try:
167 reconciled_device = OpenoltDevice(**kwargs)
168 log.debug('reconciled-device-recreated',
169 device_id=reconciled_device.device_id)
170 self.devices[device.id] = reconciled_device
171 except Exception as e:
172 log.error('Failed to reconcile OpenOLT device', error=e,
173 exception_type=type(e).__name__)
174 del self.devices[device.id]
175 raise
176 else:
177 self.num_devices += 1
178 # Invoke the children reconciliation which would setup the
179 # basic children data structures
180 self.adapter_agent.reconcile_child_devices(device.id)
181 return device
182
William Kurkian6f436d02019-02-06 16:25:01 -0500183 def abandon_device(self, device):
184 log.info('abandon-device', device=device)
185 raise NotImplementedError()
186
Matt Jeanneret7906d232019-02-14 14:57:38 -0500187 def disable_device(self, device):
188 log.info('disable-device', device=device)
189 handler = self.devices[device.id]
190 handler.disable()
William Kurkian6f436d02019-02-06 16:25:01 -0500191
Matt Jeanneret7906d232019-02-14 14:57:38 -0500192 def reenable_device(self, device):
193 log.info('reenable-device', device=device)
194 handler = self.devices[device.id]
195 handler.reenable()
William Kurkian6f436d02019-02-06 16:25:01 -0500196
Matt Jeanneret7906d232019-02-14 14:57:38 -0500197 def reboot_device(self, device):
198 log.info('reboot_device', device=device)
199 handler = self.devices[device.id]
200 handler.reboot()
William Kurkian6f436d02019-02-06 16:25:01 -0500201
William Kurkian6f436d02019-02-06 16:25:01 -0500202 def download_image(self, device, request):
203 log.info('image_download - Not implemented yet', device=device,
204 request=request)
205 raise NotImplementedError()
206
207 def get_image_download_status(self, device, request):
208 log.info('get_image_download - Not implemented yet', device=device,
209 request=request)
210 raise NotImplementedError()
211
212 def cancel_image_download(self, device, request):
213 log.info('cancel_image_download - Not implemented yet', device=device)
214 raise NotImplementedError()
215
216 def activate_image_update(self, device, request):
217 log.info('activate_image_update - Not implemented yet',
218 device=device, request=request)
219 raise NotImplementedError()
220
221 def revert_image_update(self, device, request):
222 log.info('revert_image_update - Not implemented yet',
223 device=device, request=request)
224 raise NotImplementedError()
225
226 def self_test_device(self, device):
William Kurkian6f436d02019-02-06 16:25:01 -0500227 log.info('Not implemented yet')
228 raise NotImplementedError()
229
Matt Jeanneret7906d232019-02-14 14:57:38 -0500230 def delete_device(self, device):
231 log.info('delete-device', device=device)
232 handler = self.devices[device.id]
233 handler.delete()
234 del self.devices[device.id]
235 del self.logical_device_id_to_root_device_id[device.parent_id]
236 return device
William Kurkian6f436d02019-02-06 16:25:01 -0500237
Matt Jeanneret7906d232019-02-14 14:57:38 -0500238 def get_device_details(self, device):
239 log.debug('get_device_details', device=device)
240 raise NotImplementedError()
241
242 def update_flows_bulk(self, device, flows, groups):
243 log.info('bulk-flow-update', device_id=device.id,
244 number_of_flows=len(flows.items), number_of_groups=len(
245 groups.items))
246 log.debug('flows and grousp details', flows=flows, groups=groups)
247 assert len(groups.items) == 0, "Cannot yet deal with groups"
248 handler = self.devices[device.id]
249 return handler.update_flow_table(flows.items)
250
251 def update_flows_incrementally(self, device, flow_changes, group_changes):
252 log.debug('update_flows_incrementally', device=device,
253 flow_changes=flow_changes, group_changes=group_changes)
254 log.info('This device does not allow this, therefore it is Not '
255 'implemented')
256 raise NotImplementedError()
257
258 def update_logical_flows(self, device_id, flows_to_add, flows_to_remove,
259 groups, device_rules_map):
260
261 log.info('logical-flows-update', flows_to_add=len(flows_to_add),
262 flows_to_remove=len(flows_to_remove))
263 log.debug('logical-flows-details', flows_to_add=flows_to_add,
264 flows_to_remove=flows_to_remove)
265 assert len(groups) == 0, "Cannot yet deal with groups"
266 handler = self.devices[device_id]
267 handler.update_logical_flows(flows_to_add, flows_to_remove,
268 device_rules_map)
269
270 def update_pm_config(self, device, pm_configs):
271 log.info('update_pm_config - Not implemented yet', device=device,
272 pm_configs=pm_configs)
273 raise NotImplementedError()
274
275 def send_proxied_message(self, proxy_address, msg):
276 log.debug('send-proxied-message',
277 proxy_address=proxy_address,
278 proxied_msg=msg)
279 handler = self.devices[proxy_address.device_id]
280 handler.send_proxied_message(proxy_address, msg)
281
282 def receive_proxied_message(self, proxy_address, msg):
283 log.debug('receive_proxied_message - Not implemented',
284 proxy_address=proxy_address,
285 proxied_msg=msg)
286 raise NotImplementedError()
287
288 def receive_packet_out(self, logical_device_id, egress_port_no, msg):
289 log.debug('packet-out', logical_device_id=logical_device_id,
290 egress_port_no=egress_port_no, msg_len=len(msg))
291
292 def ldi_to_di(ldi):
293 di = self.logical_device_id_to_root_device_id.get(ldi)
294 if di is None:
295 logical_device = self.adapter_agent.get_logical_device(ldi)
296 di = logical_device.root_device_id
297 self.logical_device_id_to_root_device_id[ldi] = di
298 return di
299
300 try:
301 device_id = ldi_to_di(logical_device_id)
302 handler = self.devices[device_id]
303 handler.packet_out(egress_port_no, msg)
304 except Exception as e:
305 log.error('packet-out:exception', e=e.message)
306
307 def receive_inter_adapter_message(self, msg):
308 log.info('rx_inter_adapter_msg - Not implemented')
309 raise NotImplementedError()
310
311 def suppress_alarm(self, filter):
312 log.info('suppress_alarm - Not implemented yet', filter=filter)
313 raise NotImplementedError()
314
315 def unsuppress_alarm(self, filter):
316 log.info('unsuppress_alarm - Not implemented yet', filter=filter)
317 raise NotImplementedError()
318
319 # PON Mgnt APIs #
William Kurkian6f436d02019-02-06 16:25:01 -0500320 def create_interface(self, device, data):
321 log.debug('create-interface - Not implemented - We do not use this',
322 data=data)
323 raise NotImplementedError()
324
325 def update_interface(self, device, data):
326 log.debug('update-interface - Not implemented - We do not use this',
327 data=data)
328 raise NotImplementedError()
329
330 def remove_interface(self, device, data):
331 log.debug('remove-interface - Not implemented - We do not use this',
332 data=data)
333 raise NotImplementedError()
334
335 def receive_onu_detect_state(self, proxy_address, state):
336 log.debug('receive-onu-detect-state - Not implemented - We do not '
337 'use this', proxy_address=proxy_address,
338 state=state)
339 raise NotImplementedError()
340
341 def create_tcont(self, device, tcont_data, traffic_descriptor_data):
342 log.info('create-tcont - Not implemented - We do not use this',
343 tcont_data=tcont_data,
344 traffic_descriptor_data=traffic_descriptor_data)
345 raise NotImplementedError()
346
347 def update_tcont(self, device, tcont_data, traffic_descriptor_data):
348 log.info('update-tcont - Not implemented - We do not use this',
349 tcont_data=tcont_data,
350 traffic_descriptor_data=traffic_descriptor_data)
351 raise NotImplementedError()
352
353 def remove_tcont(self, device, tcont_data, traffic_descriptor_data):
354 log.info('remove-tcont - Not implemented - We do not use this',
355 tcont_data=tcont_data,
356 traffic_descriptor_data=traffic_descriptor_data)
357 raise NotImplementedError()
358
359 def create_gemport(self, device, data):
360 log.info('create-gemport - Not implemented - We do not use this',
361 data=data)
362 raise NotImplementedError()
363
364 def update_gemport(self, device, data):
365 log.info('update-gemport - Not implemented - We do not use this',
366 data=data)
367 raise NotImplementedError()
368
369 def remove_gemport(self, device, data):
370 log.info('remove-gemport - Not implemented - We do not use this',
371 data=data)
372 raise NotImplementedError()
373
374 def create_multicast_gemport(self, device, data):
375 log.info('create-mcast-gemport - Not implemented - We do not use '
376 'this', data=data)
377 raise NotImplementedError()
378
379 def update_multicast_gemport(self, device, data):
380 log.info('update-mcast-gemport - Not implemented - We do not use '
381 'this', data=data)
382 raise NotImplementedError()
383
384 def remove_multicast_gemport(self, device, data):
385 log.info('remove-mcast-gemport - Not implemented - We do not use '
386 'this', data=data)
387 raise NotImplementedError()
388
389 def create_multicast_distribution_set(self, device, data):
390 log.info('create-mcast-distribution-set - Not implemented - We do '
391 'not use this', data=data)
392 raise NotImplementedError()
393
394 def update_multicast_distribution_set(self, device, data):
395 log.info('update-mcast-distribution-set - Not implemented - We do '
396 'not use this', data=data)
397 raise NotImplementedError()
398
399 def remove_multicast_distribution_set(self, device, data):
400 log.info('remove-mcast-distribution-set - Not implemented - We do '
401 'not use this', data=data)
402 raise NotImplementedError()
403
Matt Jeanneret7906d232019-02-14 14:57:38 -0500404 def delete_child_device(self, parent_device_id, child_device):
405 log.info('delete-child_device', parent_device_id=parent_device_id,
406 child_device=child_device)
407 handler = self.devices[parent_device_id]
408 if handler is not None:
409 handler.delete_child_device(child_device)
William Kurkian6f436d02019-02-06 16:25:01 -0500410 else:
Matt Jeanneret7906d232019-02-14 14:57:38 -0500411 log.error('Could not find matching handler',
412 looking_for_device_id=parent_device_id,
413 available_handlers=self.devices.keys())
William Kurkian6f436d02019-02-06 16:25:01 -0500414
Matt Jeanneret7906d232019-02-14 14:57:38 -0500415 # This is currently not part of the Iadapter interface
416 def collect_stats(self, device_id):
417 log.info('collect_stats', device_id=device_id)
418 handler = self.devices[device_id]
419 if handler is not None:
420 handler.trigger_statistics_collection()
421 else:
422 log.error('Could not find matching handler',
423 looking_for_device_id=device_id,
424 available_handlers=self.devices.keys())
William Kurkian6f436d02019-02-06 16:25:01 -0500425
Matt Jeanneret7906d232019-02-14 14:57:38 -0500426 def simulate_alarm(self, device, request):
427 log.info('simulate_alarm', device=device, request=request)
William Kurkian6f436d02019-02-06 16:25:01 -0500428
Matt Jeanneret7906d232019-02-14 14:57:38 -0500429 if device.id not in self.devices:
430 log.error("Device does not exist", device_id=device.id)
431 return OperationResp(code=OperationResp.OPERATION_FAILURE,
432 additional_info="Device %s does not exist"
433 % device.id)
William Kurkian6f436d02019-02-06 16:25:01 -0500434
Matt Jeanneret7906d232019-02-14 14:57:38 -0500435 handler = self.devices[device.id]
William Kurkian6f436d02019-02-06 16:25:01 -0500436
Matt Jeanneret7906d232019-02-14 14:57:38 -0500437 handler.simulate_alarm(request)
William Kurkian6f436d02019-02-06 16:25:01 -0500438
Matt Jeanneret7906d232019-02-14 14:57:38 -0500439 return OperationResp(code=OperationResp.OPERATION_SUCCESS)