blob: 09e4440307e694f376c3523cc5ab001efd3595dc [file] [log] [blame]
Zsolt Haraszticc153aa2016-12-14 02:28:59 -08001#
Zsolt Haraszti3eb27a52017-01-03 21:56:48 -08002# Copyright 2017 the original author or authors.
Zsolt Haraszticc153aa2016-12-14 02:28:59 -08003#
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
Steve Crooks3c2c7582017-01-10 15:02:26 -060017"""
18Maple OLT/ONU adapter.
19"""
Zsolt Haraszticc153aa2016-12-14 02:28:59 -080020from uuid import uuid4
21
Peter Shafik412a7fb2017-02-27 12:39:51 -050022import arrow
Steve Crooks8abe8c22017-03-31 10:48:29 -050023import binascii
Steve Crooks3c2c7582017-01-10 15:02:26 -060024from scapy.layers.l2 import Ether, Dot1Q
Zsolt Haraszticc153aa2016-12-14 02:28:59 -080025from twisted.internet import reactor
Steve Crooks1e1c3e22017-03-06 15:24:49 -050026from twisted.internet.protocol import ReconnectingClientFactory
Steve Crooks3c2c7582017-01-10 15:02:26 -060027from twisted.spread import pb
28from twisted.internet.defer import inlineCallbacks, returnValue, DeferredQueue
Zsolt Haraszticc153aa2016-12-14 02:28:59 -080029from zope.interface import implementer
30
Steve Crooks3c2c7582017-01-10 15:02:26 -060031from common.frameio.frameio import BpfProgramFilter, hexify
32
Zsolt Haraszticc153aa2016-12-14 02:28:59 -080033from voltha.adapters.interface import IAdapterInterface
34from voltha.core.logical_device_agent import mac_str_to_tuple
Steve Crooksf248e182017-02-07 10:50:24 -050035import voltha.core.flow_decomposer as fd
Steve Crooks3c2c7582017-01-10 15:02:26 -060036from voltha.protos import third_party
37from voltha.protos.adapter_pb2 import Adapter
38from voltha.protos.adapter_pb2 import AdapterConfig
Zsolt Haraszticc153aa2016-12-14 02:28:59 -080039from voltha.protos.common_pb2 import LogLevel, OperStatus, ConnectStatus, \
40 AdminState
Peter Shafikba9b3d82017-03-15 18:01:22 -040041from voltha.protos.device_pb2 import DeviceType, DeviceTypes, Port, Device, \
42PmConfigs, PmConfig, PmGroupConfig
Steve Crooks3c2c7582017-01-10 15:02:26 -060043from voltha.protos.health_pb2 import HealthStatus
44from google.protobuf.empty_pb2 import Empty
Peter Shafik412a7fb2017-02-27 12:39:51 -050045from voltha.protos.events_pb2 import KpiEvent, MetricValuePairs
46from voltha.protos.events_pb2 import KpiEventType
Peter Shafikff803c42017-03-08 11:24:58 -050047from voltha.protos.events_pb2 import AlarmEvent, AlarmEventType, \
48 AlarmEventSeverity, AlarmEventState, AlarmEventCategory
Zsolt Haraszticc153aa2016-12-14 02:28:59 -080049
Steve Crooks3c2c7582017-01-10 15:02:26 -060050from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice
Peter Shafikff803c42017-03-08 11:24:58 -050051from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
52 OFPPF_1GB_FD, OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, \
53 OFPC_FLOW_STATS, OFPP_CONTROLLER, OFPXMC_OPENFLOW_BASIC, \
54 ofp_switch_features, ofp_desc, ofp_port
Steve Crooks3c2c7582017-01-10 15:02:26 -060055from voltha.registry import registry
56from voltha.extensions.omci.omci import *
57
58_ = third_party
Zsolt Haraszticc153aa2016-12-14 02:28:59 -080059log = structlog.get_logger()
60
Steve Crooks3c2c7582017-01-10 15:02:26 -060061PACKET_IN_VLAN = 4091
62is_inband_frame = BpfProgramFilter('(ether[14:2] & 0xfff) = 0x{:03x}'.format(
63 PACKET_IN_VLAN))
Zsolt Haraszticc153aa2016-12-14 02:28:59 -080064
Steve Crooks3c2c7582017-01-10 15:02:26 -060065
Peter Shafikba9b3d82017-03-15 18:01:22 -040066class MapleOltPmMetrics:
67 class Metrics:
68 def __init__(self, config, value=0, is_group=False):
69 self.config = config
70 self.value = value
71 self.is_group = is_group
72
73 def __init__(self,device):
74 self.pm_names = {'tx_64','tx_65_127', 'tx_128_255', 'tx_256_511',
75 'tx_512_1023', 'tx_1024_1518', 'tx_1519_9k', 'rx_64',
76 'rx_65_127', 'rx_128_255', 'rx_256_511', 'rx_512_1023',
77 'rx_1024_1518', 'rx_1519_9k', 'tx_pkts', 'rx_pkts',
78 'tx_bytes', 'rx_bytes'}
79 self.pm_group_names = {'nni'}
80 self.device = device
81 self.id = device.id
82 self.default_freq = 150
83 self.pon_metrics = dict()
84 self.nni_metrics = dict()
85 for m in self.pm_names:
86 self.pon_metrics[m] = \
87 self.Metrics(config = PmConfig(name=m,
88 type=PmConfig.COUNTER,
89 enabled=True), value = 0)
90 self.nni_metrics[m] = \
91 self.Metrics(config = PmConfig(name=m,
92 type=PmConfig.COUNTER,
93 enabled=True), value = 0)
94 self.pm_group_metrics = dict()
95 for m in self.pm_group_names:
96 self.pm_group_metrics[m] = \
97 self.Metrics(config = PmGroupConfig(group_name=m,
98 group_freq=self.default_freq,
99 enabled=True),
100 is_group = True)
101 for m in sorted(self.nni_metrics):
102 pm=self.nni_metrics[m]
103 self.pm_group_metrics['nni'].config.metrics.extend([PmConfig(
104 name=pm.config.name,
105 type=pm.config.type,
106 enabled=pm.config.enabled)])
107
108 @inlineCallbacks
109 def configure_pm_collection_freq(self, freq, remote):
110 log.info('configuring-pm-collection-freq',
111 freq=freq)
112 try:
113 data = yield remote.callRemote('set_stats_collection_interval', 0,
114 freq)
115 log.info('configured-pm-collection-freq', data=data)
116 except Exception as e:
117 log.exception('configure-pm-collection-freq', exc=str(e))
118
119 def enable_pm_collection(self, pm_group, remote):
120 if pm_group == 'nni':
121 self.configure_pm_collection_freq(self.default_freq/10, remote)
122
123 def disable_pm_collection(self, pm_group, remote):
124 if pm_group == 'nni':
125 self.configure_pm_collection_freq(0, remote)
126
127 def update(self, device, pm_config, remote):
128 if self.default_freq != pm_config.default_freq:
129 self.default_freq = pm_config.default_freq
130
131 if pm_config.grouped is True:
132 for m in pm_config.groups:
133 self.pm_group_metrics[m.group_name].config.enabled = m.enabled
134 if m.enabled is True:
135 self.enable_pm_collection(m.group_name, remote)
136 else:
137 self.disable_pm_collection(m.group_name, remote)
138
139 else:
140 for m in pm_config.metrics:
141 self.pon_metrics[m.name].config.enabled = m.enabled
142 self.nni_metrics[m.name].config.enabled = m.enabled
143
144 def make_proto(self):
145 pm_config = PmConfigs(
146 id=self.id,
147 default_freq=self.default_freq,
148 grouped = True,
149 freq_override = False)
150 for m in self.pm_group_names:
151 pm_config.groups.extend([self.pm_group_metrics[m].config])
152
153 return pm_config
154
155
Peter Shafik412a7fb2017-02-27 12:39:51 -0500156class MapleOltRxHandler(pb.Root):
Steve Crooks8abe8c22017-03-31 10:48:29 -0500157 def __init__(self, device_id, adapter, onu_queue):
Peter Shafik412a7fb2017-02-27 12:39:51 -0500158 self.device_id = device_id
Steve Crooks8abe8c22017-03-31 10:48:29 -0500159 self.adapter = adapter
160 self.onu_discovered_queue = onu_queue
Peter Shafik412a7fb2017-02-27 12:39:51 -0500161 self.adapter_agent = adapter.adapter_agent
162 self.adapter_name = adapter.name
Peter Shafikff803c42017-03-08 11:24:58 -0500163 # registry('main').get_args().external_host_address
164 self.pb_server_ip = '192.168.24.20'
Steve Crooks3c2c7582017-01-10 15:02:26 -0600165 self.pb_server_port = 24497
166 self.pb_server_factory = pb.PBServerFactory(self)
167 # start PB server
Peter Shafikff803c42017-03-08 11:24:58 -0500168 self.listen_port = reactor.listenTCP(self.pb_server_port,
169 self.pb_server_factory)
Steve Crooks3c2c7582017-01-10 15:02:26 -0600170 self.omci_rx_queue = DeferredQueue()
171 log.info('PB-server-started-on-port', port=self.pb_server_port)
172
173 def get_ip(self):
174 return self.pb_server_ip
175
176 def get_port(self):
177 return self.pb_server_port
178
179 def get_host(self):
180 return self.listen_port.getHost()
181
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -0800182 def remote_echo(self, pkt_type, pon, onu, port, crc_ok, msg_size, msg_data):
Steve Crooks3c2c7582017-01-10 15:02:26 -0600183 log.info('received-omci-msg',
184 pkt_type=pkt_type,
185 pon_id=pon,
186 onu_id=onu,
187 port_id=port,
188 crc_ok=crc_ok,
189 msg_size=msg_size,
190 msg_data=hexify(msg_data))
191 self.omci_rx_queue.put((onu, msg_data))
192
Peter Shafik412a7fb2017-02-27 12:39:51 -0500193 def receive_omci_msg(self):
Steve Crooks3c2c7582017-01-10 15:02:26 -0600194 return self.omci_rx_queue.get()
195
Steve Crooks8abe8c22017-03-31 10:48:29 -0500196 def remote_report_stats(self, _object, key, stats_data):
Peter Shafik412a7fb2017-02-27 12:39:51 -0500197 log.info('received-stats-msg',
Steve Crooks8abe8c22017-03-31 10:48:29 -0500198 object=_object,
Peter Shafik412a7fb2017-02-27 12:39:51 -0500199 key=key,
200 stats=stats_data)
201
202 prefix = 'voltha.{}.{}'.format(self.adapter_name, self.device_id)
203
204 try:
205 ts = arrow.utcnow().timestamp
206
207 prefixes = {
208 prefix + '.nni': MetricValuePairs(metrics=stats_data)
209 }
210
211 kpi_event = KpiEvent(
212 type=KpiEventType.slice,
213 ts=ts,
214 prefixes=prefixes
215 )
216
217 self.adapter_agent.submit_kpis(kpi_event)
218
219 except Exception as e:
220 log.exception('failed-to-submit-kpis', e=e)
221
Steve Crooks8abe8c22017-03-31 10:48:29 -0500222 def remote_report_event(self, _object, key, event, event_data=None):
223 def _convert_serial_data(data):
224 b = bytearray()
225 b.extend(data)
226
227 return binascii.hexlify(b)
228
Peter Shafik412a7fb2017-02-27 12:39:51 -0500229 log.info('received-event-msg',
Steve Crooks8abe8c22017-03-31 10:48:29 -0500230 object=_object,
Peter Shafik412a7fb2017-02-27 12:39:51 -0500231 key=key,
232 event_str=event,
233 event_data=event_data)
234
Steve Crooks8abe8c22017-03-31 10:48:29 -0500235 if _object == 'device':
Steve Crooksb8f978b2017-04-11 12:28:43 -0400236 # key: {'device_id': <int>}
237 # event: 'state-changed'
238 # event_data: {'state_change_successful': <False|True>,
239 # 'new_state': <str> ('active-working'|'inactive')}
240 pass
Steve Crooks8abe8c22017-03-31 10:48:29 -0500241 elif _object == 'nni':
Steve Crooksb8f978b2017-04-11 12:28:43 -0400242 # key: {'device_id': <int>, 'nni': <int>}
243 pass
Steve Crooks8abe8c22017-03-31 10:48:29 -0500244 elif _object == 'pon_ni':
Steve Crooksb8f978b2017-04-11 12:28:43 -0400245 # key: {'device_id': <int>, 'pon_ni': <int>}
246 # event: 'state-changed'
247 # event_data: {'state_change_successful': <False|True>,
248 # 'new_state': <str> ('active-working'|'inactive')}
249 #
250 # event: 'onu-discovered'
251 # event_data: {'serial_num_vendor_id': <str>
252 # 'serial_num_vendor_specific': <str>
253 # 'ranging_time': <int>
254 # 'onu_id': <int>
255 # 'us_line_rate': <int> (0=2.5G, 1=10G)
256 # 'ds_pon_id': <int>
257 # 'us_pon_id': <int>
258 # 'tuning_granularity': <int>
259 # 'step_tuning_time': <int>
260 # 'attenuation': <int>
261 # 'power_levelling_caps': <int>}
Steve Crooks8abe8c22017-03-31 10:48:29 -0500262 if 'onu-discovered' == event and event_data is not None:
263 event_data['_device_id'] = key['device_id'] if 'device_id' in key else None
264 event_data['_pon_id'] = key['pon_id'] if 'pon_id' in key else None
265 event_data['_vendor_id'] = _convert_serial_data(event_data['serial_num_vendor_id']) \
266 if 'serial_num_vendor_id' in event_data else None
267 event_data['_vendor_specific'] = _convert_serial_data(event_data['serial_num_vendor_specific']) \
268 if 'serial_num_vendor_specific' in event_data else None
269
270 self.onu_discovered_queue.put(event_data)
271 log.info('onu-discovered-event-added-to-queue', event_data=event_data)
272
273 elif _object == 'onu':
274 # key: {'device_id': <int>, 'pon_ni': <int>, 'onu_id': <int>}
Steve Crooksb8f978b2017-04-11 12:28:43 -0400275 # event: 'activation-completed'
276 # event_data: {'activation_successful': <False|True>,
277 # act_fail_reason': <str>}
278 #
279 # event: 'deactivation-completed'
280 # event_data: {'deactivation_successful': <False|True>}
281 #
282 # event: 'ranging-completed'
283 # event_data: {'ranging_successful': <False|True>,
284 # 'ranging_fail_reason': <str>,
285 # 'eqd': <int>,
286 # 'number_of_ploams': <int>,
287 # 'power_level': <int>}
288 #
289 # event: 'enable-completed'
290 # event_data: {'serial_num-vendor_id': <str>
291 # 'serial_num-vendor_specific: <str>}
292 #
293 # event: 'disable-completed'
294 # event_data: {'serial_num-vendor_id': <str>
295 # 'serial_num-vendor_specific: <str>}
Peter Shafikb2ff5a62017-05-02 15:54:39 -0400296 event_dict = {'event':event, 'event_data':event_data}
297
298 # Get child_device from onu_id
299 child_device = self.adapter_agent.get_child_device(self.device_id, onu_id=key['onu_id'])
300 assert child_device is not None
301
302 # Send the event message to the ONU adapter
303 self.adapter_agent.publish_inter_adapter_message(child_device.id, event_dict)
304
Steve Crooks8abe8c22017-03-31 10:48:29 -0500305 elif _object == 'alloc_id':
306 # key: {'device_id': <int>, 'pon_ni': <int>, 'onu_id': <int>, 'alloc_id': ,<int>}
Steve Crooksb8f978b2017-04-11 12:28:43 -0400307 pass
Steve Crooks8abe8c22017-03-31 10:48:29 -0500308 elif _object == 'gem_port':
309 # key: {'device_id': <int>, 'pon_ni': <int>, 'onu_id': <int>, 'gem_port': ,<int>}
Steve Crooksb8f978b2017-04-11 12:28:43 -0400310 pass
Steve Crooks8abe8c22017-03-31 10:48:29 -0500311 elif _object == 'trx':
Steve Crooksb8f978b2017-04-11 12:28:43 -0400312 # key: {'device_id': <int>, 'pon_ni': <int>}
313 pass
Steve Crooks8abe8c22017-03-31 10:48:29 -0500314 elif _object == 'flow_map':
Steve Crooksb8f978b2017-04-11 12:28:43 -0400315 # key: {'device_id': <int>, 'pon_ni': <int>}
316 pass
317
Steve Crooks8abe8c22017-03-31 10:48:29 -0500318 def remote_report_alarm(self, _object, key, alarm, status, priority,
Peter Shafikff803c42017-03-08 11:24:58 -0500319 alarm_data=None):
Peter Shafik412a7fb2017-02-27 12:39:51 -0500320 log.info('received-alarm-msg',
Steve Crooks8abe8c22017-03-31 10:48:29 -0500321 object=_object,
Peter Shafik412a7fb2017-02-27 12:39:51 -0500322 key=key,
323 alarm=alarm,
324 status=status,
325 priority=priority,
326 alarm_data=alarm_data)
327
Steve Crooks8abe8c22017-03-31 10:48:29 -0500328 id = 'voltha.{}.{}.{}'.format(self.adapter_name, self.device_id, _object)
329 description = '{} Alarm - {} - {}'.format(_object.upper(), alarm.upper(),
Peter Shafikff803c42017-03-08 11:24:58 -0500330 'Raised' if status else 'Cleared')
331
332 if priority == 'low':
333 severity = AlarmEventSeverity.MINOR
334 elif priority == 'medium':
335 severity = AlarmEventSeverity.MAJOR
336 elif priority == 'high':
337 severity = AlarmEventSeverity.CRITICAL
338 else:
339 severity = AlarmEventSeverity.INDETERMINATE
340
341 try:
342 ts = arrow.utcnow().timestamp
343
344 alarm_event = self.adapter_agent.create_alarm(
345 id=id,
346 resource_id=str(key),
347 type=AlarmEventType.EQUIPMENT,
348 category=AlarmEventCategory.PON,
349 severity=severity,
Steve Crooks8abe8c22017-03-31 10:48:29 -0500350 state=AlarmEventState.RAISED if status else AlarmEventState.CLEARED,
Peter Shafikff803c42017-03-08 11:24:58 -0500351 description=description,
352 context=alarm_data,
353 raised_ts = ts)
354
Stephane Barbarieee409292017-04-24 10:30:20 -0400355 self.adapter_agent.submit_alarm(self.device_id, alarm_event)
Peter Shafikff803c42017-03-08 11:24:58 -0500356
357 except Exception as e:
358 log.exception('failed-to-submit-alarm', e=e)
359
Steve Crooksb8f978b2017-04-11 12:28:43 -0400360 # take action based on alarm type, only pon_ni and onu objects report alarms
361 if object == 'pon_ni':
362 # key: {'device_id': <int>, 'pon_ni': <int>}
363 # alarm: 'los'
364 # status: <False|True>
365 pass
366 elif object == 'onu':
Steve Crooks8abe8c22017-03-31 10:48:29 -0500367 # key: {'device_id': <int>, 'pon_ni': <int>, 'onu_id': <int>}
Steve Crooksb8f978b2017-04-11 12:28:43 -0400368 # alarm: <'los'|'lob'|'lopc_miss'|'los_mic_err'|'dow'|'sf'|'sd'|'suf'|'df'|'tiw'|'looc'|'dg'>
369 # status: <False|True>
370 pass
Peter Shafikff803c42017-03-08 11:24:58 -0500371
Zsolt Haraszticc153aa2016-12-14 02:28:59 -0800372@implementer(IAdapterInterface)
373class MapleOltAdapter(object):
Zsolt Haraszticc153aa2016-12-14 02:28:59 -0800374 name = 'maple_olt'
375
376 supported_device_types = [
377 DeviceType(
Steve Crooks3c2c7582017-01-10 15:02:26 -0600378 id=name,
Zsolt Haraszticc153aa2016-12-14 02:28:59 -0800379 adapter=name,
380 accepts_bulk_flow_update=True
381 )
382 ]
383
384 def __init__(self, adapter_agent, config):
385 self.adapter_agent = adapter_agent
386 self.config = config
387 self.descriptor = Adapter(
388 id=self.name,
389 vendor='Voltha project',
Steve Crooks3c2c7582017-01-10 15:02:26 -0600390 version='0.4',
Zsolt Haraszticc153aa2016-12-14 02:28:59 -0800391 config=AdapterConfig(log_level=LogLevel.INFO)
392 )
Steve Crooks3c2c7582017-01-10 15:02:26 -0600393 self.devices_handlers = dict() # device_id -> MapleOltHandler()
394 self.logical_device_id_to_root_device_id = dict()
Zsolt Haraszticc153aa2016-12-14 02:28:59 -0800395
Peter Shafikb2ff5a62017-05-02 15:54:39 -0400396 # register for adapter messages
397 self.adapter_agent.register_for_inter_adapter_messages()
398
Zsolt Haraszticc153aa2016-12-14 02:28:59 -0800399 def start(self):
400 log.debug('starting')
401 log.info('started')
402
403 def stop(self):
404 log.debug('stopping')
405 log.info('stopped')
406
407 def adapter_descriptor(self):
408 return self.descriptor
409
410 def device_types(self):
411 return DeviceTypes(items=self.supported_device_types)
412
413 def health(self):
414 return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
415
416 def change_master_state(self, master):
417 raise NotImplementedError()
418
Peter Shafikba9b3d82017-03-15 18:01:22 -0400419 def update_pm_config(self, device, pm_config):
420 log.info("adapter-update-pm-config", device=device, pm_config=pm_config)
421 handler = self.devices_handlers[device.id]
422 handler.update_pm_metrics(device, pm_config)
Sergio Slobodrianec864c62017-03-09 11:41:43 -0500423
Zsolt Haraszticc153aa2016-12-14 02:28:59 -0800424 def adopt_device(self, device):
Steve Crooks8abe8c22017-03-31 10:48:29 -0500425 log.info("adopt-device", device=device)
Steve Crooks3c2c7582017-01-10 15:02:26 -0600426 self.devices_handlers[device.id] = MapleOltHandler(self, device.id)
427 reactor.callLater(0, self.devices_handlers[device.id].activate, device)
Zsolt Haraszticc153aa2016-12-14 02:28:59 -0800428 return device
429
430 def abandon_device(self, device):
Steve Crooks3c2c7582017-01-10 15:02:26 -0600431 raise NotImplementedError()
432
Khen Nursimulud068d812017-03-06 11:44:18 -0500433 def disable_device(self, device):
434 raise NotImplementedError()
435
436 def reenable_device(self, device):
437 raise NotImplementedError()
438
439 def reboot_device(self, device):
440 raise NotImplementedError()
441
442 def delete_device(self, device):
443 raise NotImplementedError()
444
445 def get_device_details(self, device):
Zsolt Haraszticc153aa2016-12-14 02:28:59 -0800446 raise NotImplementedError()
447
Steve Crooks3c2c7582017-01-10 15:02:26 -0600448 def update_flows_bulk(self, device, flows, groups):
449 log.info('bulk-flow-update', device_id=device.id,
450 flows=flows, groups=groups)
Steve Crooksf248e182017-02-07 10:50:24 -0500451 assert len(groups.items) == 0, "Cannot yet deal with groups"
Steve Crooks3c2c7582017-01-10 15:02:26 -0600452 handler = self.devices_handlers[device.id]
Steve Crooksf248e182017-02-07 10:50:24 -0500453 return handler.update_flow_table(flows.items, device)
Zsolt Haraszticc153aa2016-12-14 02:28:59 -0800454
Steve Crooks3c2c7582017-01-10 15:02:26 -0600455 def update_flows_incrementally(self, device, flow_changes, group_changes):
456 raise NotImplementedError()
457
458 def send_proxied_message(self, proxy_address, msg):
459 log.info('send-proxied-message', proxy_address=proxy_address, msg=msg)
460 handler = self.devices_handlers[proxy_address.device_id]
461 handler.send_proxied_message(proxy_address, msg)
462
463 def receive_proxied_message(self, proxy_address, msg):
464 raise NotImplementedError()
465
466 def receive_packet_out(self, logical_device_id, egress_port_no, msg):
467 def ldi_to_di(ldi):
468 di = self.logical_device_id_to_root_device_id.get(ldi)
469 if di is None:
470 logical_device = self.adapter_agent.get_logical_device(ldi)
471 di = logical_device.root_device_id
472 self.logical_device_id_to_root_device_id[ldi] = di
473 return di
474
475 device_id = ldi_to_di(logical_device_id)
476 handler = self.devices_handlers[device_id]
477 handler.packet_out(egress_port_no, msg)
478
Peter Shafikb2ff5a62017-05-02 15:54:39 -0400479 def receive_inter_adapter_message(self, msg):
480 pass
Steve Crooks3c2c7582017-01-10 15:02:26 -0600481
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500482class MaplePBClientFactory(pb.PBClientFactory, ReconnectingClientFactory):
483 channel = None
484 maxDelay = 60
485 initialDelay = 15
486
487 def clientConnectionMade(self, broker):
488 log.info('pb-client-connection-made')
489 pb.PBClientFactory.clientConnectionMade(self, broker)
490 ReconnectingClientFactory.resetDelay(self)
491
492 def clientConnectionLost(self, connector, reason, reconnecting=0):
493 log.info('pb-client-connection-lost')
Peter Shafikff803c42017-03-08 11:24:58 -0500494 pb.PBClientFactory.clientConnectionLost(self, connector, reason,
495 reconnecting=1)
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500496 ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
497 log.info('pb-client-connection-lost-retrying')
498
499 def clientConnectionFailed(self, connector, reason):
500 log.info('pb-client-connection-failed')
501 pb.PBClientFactory.clientConnectionFailed(self, connector, reason)
Peter Shafikff803c42017-03-08 11:24:58 -0500502 ReconnectingClientFactory.clientConnectionFailed(self, connector,
503 reason)
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500504 log.info('pb-client-connection-failed-retrying')
505
506 def disconnect(self, stopTrying=0):
507 if stopTrying:
508 ReconnectingClientFactory.stopTrying(self)
509 pb.PBClientFactory.disconnect(self)
510
511 def channel_disconnected(self, channel):
512 log.info('pb-channel-disconnected', channel=channel)
513 self.disconnect()
514
515 @inlineCallbacks
516 def getChannel(self):
517 if self.channel is None:
518 try:
519 self.channel = yield self.getRootObject()
520 self.channel.notifyOnDisconnect(self.channel_disconnected)
521 except Exception as e:
522 log.info('pb-client-failed-to-get-channel', exc=str(e))
523 self.channel = None
524 returnValue(self.channel)
525
526
Steve Crooks3c2c7582017-01-10 15:02:26 -0600527class MapleOltHandler(object):
528 def __init__(self, adapter, device_id):
529 self.adapter = adapter
530 self.adapter_agent = adapter.adapter_agent
531 self.device_id = device_id
532 self.log = structlog.get_logger(device_id=device_id)
Steve Crooks3c2c7582017-01-10 15:02:26 -0600533 self.io_port = None
534 self.logical_device_id = None
535 self.interface = registry('main').get_args().interface
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500536 self.pbc_factory = MaplePBClientFactory()
Steve Crooks3c2c7582017-01-10 15:02:26 -0600537 self.pbc_port = 24498
538 self.tx_id = 0
Steve Crooks8abe8c22017-03-31 10:48:29 -0500539 self.onu_discovered_queue = DeferredQueue()
540 self.rx_handler = MapleOltRxHandler(self.device_id, self.adapter, self.onu_discovered_queue)
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500541 self.heartbeat_count = 0
542 self.heartbeat_miss = 0
543 self.heartbeat_interval = 1
544 self.heartbeat_failed_limit = 3
545 self.command_timeout = 5
Peter Shafikba9b3d82017-03-15 18:01:22 -0400546 self.pm_metrics = None
Steve Crooksb8f978b2017-04-11 12:28:43 -0400547 self.onus = {}
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -0800548
Steve Crooks3c2c7582017-01-10 15:02:26 -0600549 def __del__(self):
550 if self.io_port is not None:
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800551 registry('frameio').close_port(self.io_port)
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -0800552
Steve Crooks3c2c7582017-01-10 15:02:26 -0600553 def get_channel(self):
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500554 return self.pbc_factory.getChannel()
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -0800555
Steve Crooks8abe8c22017-03-31 10:48:29 -0500556 def get_proxy_channel_id_from_onu(self, onu_id):
557 return onu_id << 4
558
559 def get_onu_from_channel_id(self, channel_id):
560 return channel_id >> 4
561
562 def get_tunnel_tag_from_onu(self, onu):
563 return 1024 + (onu * 16)
564
565 def get_onu_from_tunnel_tag(self, tunnel_tag):
566 return (tunnel_tag - 1024) / 16
567
Steve Crooksb8f978b2017-04-11 12:28:43 -0400568 def get_new_onu_id(self, vendor, vendor_specific):
569 onu_id = None
570 for i in range(0, 63):
571 if i not in self.onus:
572 onu_id = i
573 break
574
575 if onu_id is not None:
576 self.onus[onu_id] = {'onu_id': onu_id,
577 'vendor': vendor,
578 'vendor_specific': vendor_specific}
579 return onu_id
580
581 def onu_exists(self, onu_id):
582 if onu_id in self.onus:
583 self.log.info('onu-exists',
584 onu_id=onu_id,
585 vendor=self.onus[onu_id]['vendor'],
586 vendor_specific=self.onus[onu_id]['vendor_specific'])
587 return self.onus[onu_id]['vendor'], self.onus[onu_id]['vendor_specific']
588 else:
589 self.log.info('onu-does-not-exist', onu_id=onu_id)
590 return None, None
591
592 def onu_serial_exists(self, sn_vendor, sn_vendor_specific):
593 for key, value in self.onus.iteritems():
594 if sn_vendor in value.itervalues() and sn_vendor_specific in value.itervalues():
595 self.log.info('onu-serial-number-exists',
596 onu_id=value['onu_id'],
597 vendor=sn_vendor,
598 vendor_specific=sn_vendor_specific,
599 onus=self.onus)
600 return value['onu_id']
601
602 self.log.info('onu-serial-number-does-not-exist',
603 vendor=sn_vendor,
604 vendor_specific=sn_vendor_specific,
605 onus=self.onus)
606 return None
607
Steve Crooks3c2c7582017-01-10 15:02:26 -0600608 @inlineCallbacks
609 def send_set_remote(self):
Peter Shafik412a7fb2017-02-27 12:39:51 -0500610 srv_ip = self.rx_handler.get_ip()
611 srv_port = self.rx_handler.get_port()
Steve Crooks3c2c7582017-01-10 15:02:26 -0600612 self.log.info('setting-remote-ip-port', ip=srv_ip, port=srv_port)
613
614 try:
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500615 remote = yield self.get_channel()
Steve Crooks3c2c7582017-01-10 15:02:26 -0600616 data = yield remote.callRemote('set_remote', srv_ip, srv_port)
617 self.log.info('set-remote', data=data, ip=srv_ip, port=srv_port)
618 except Exception as e:
619 self.log.info('set-remote-exception', exc=str(e))
620
621 @inlineCallbacks
Peter Shafikff803c42017-03-08 11:24:58 -0500622 def send_config_classifier(self, olt_no, etype, ip_proto=None,
623 dst_port=None):
Steve Crooks7697a392017-01-16 18:13:33 -0600624 self.log.info('configuring-classifier',
625 olt=olt_no,
626 etype=etype,
627 ip_proto=ip_proto,
Steve Crooks7697a392017-01-16 18:13:33 -0600628 dst_port=dst_port)
629 try:
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500630 remote = yield self.get_channel()
Steve Crooks7697a392017-01-16 18:13:33 -0600631 data = yield remote.callRemote('config_classifier',
632 olt_no,
633 etype,
634 ip_proto,
Steve Crooks7697a392017-01-16 18:13:33 -0600635 dst_port)
636 self.log.info('configured-classifier', data=data)
637 except Exception as e:
638 self.log.info('config-classifier-exception', exc=str(e))
639
640 @inlineCallbacks
Peter Shafikff803c42017-03-08 11:24:58 -0500641 def send_config_acflow(self, olt_no, onu_no, etype, ip_proto=None,
642 dst_port=None):
Steve Crooks7697a392017-01-16 18:13:33 -0600643 self.log.info('configuring-acflow',
644 olt=olt_no,
645 onu=onu_no,
646 etype=etype,
647 ip_proto=ip_proto,
Steve Crooks7697a392017-01-16 18:13:33 -0600648 dst_port=dst_port)
649 try:
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500650 remote = yield self.get_channel()
Steve Crooks7697a392017-01-16 18:13:33 -0600651 data = yield remote.callRemote('config_acflow',
652 olt_no,
653 onu_no,
654 etype,
655 ip_proto,
Steve Crooks7697a392017-01-16 18:13:33 -0600656 dst_port)
657
658 self.log.info('configured-acflow', data=data)
659 except Exception as e:
660 self.log.info('config-acflow-exception', exc=str(e))
661
662 @inlineCallbacks
Steve Crooks3c2c7582017-01-10 15:02:26 -0600663 def send_connect_olt(self, olt_no):
664 self.log.info('connecting-to-olt', olt=olt_no)
665 try:
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500666 remote = yield self.get_channel()
Steve Crooks3c2c7582017-01-10 15:02:26 -0600667 data = yield remote.callRemote('connect_olt', olt_no)
668 self.log.info('connected-to-olt', data=data)
669 except Exception as e:
670 self.log.info('connect-olt-exception', exc=str(e))
671
672 @inlineCallbacks
673 def send_activate_olt(self, olt_no):
674 self.log.info('activating-olt', olt=olt_no)
675 try:
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500676 remote = yield self.get_channel()
Steve Crooks3c2c7582017-01-10 15:02:26 -0600677 data = yield remote.callRemote('activate_olt', olt_no)
678 self.log.info('activated-olt', data=data)
679 except Exception as e:
680 self.log.info('activate-olt-exception', exc=str(e))
681
682 @inlineCallbacks
683 def send_create_onu(self, olt_no, onu_no, serial_no, vendor_no):
684 self.log.info('creating-onu',
685 olt=olt_no,
686 onu=onu_no,
687 serial=serial_no,
688 vendor=vendor_no)
689 try:
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500690 remote = yield self.get_channel()
Steve Crooks3c2c7582017-01-10 15:02:26 -0600691 data = yield remote.callRemote('create_onu',
692 olt_no,
693 onu_no,
694 serial_no,
695 vendor_no)
696 self.log.info('created-onu', data=data)
697 except Exception as e:
698 self.log.info('create-onu-exception', exc=str(e))
699
700 @inlineCallbacks
Steve Crooks7697a392017-01-16 18:13:33 -0600701 def send_configure_alloc_id(self, olt_no, onu_no, alloc_id):
702 self.log.info('configuring-alloc-id',
703 olt=olt_no,
704 onu=onu_no,
705 alloc_id=alloc_id)
706 try:
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500707 remote = yield self.get_channel()
Steve Crooks7697a392017-01-16 18:13:33 -0600708 data = yield remote.callRemote('configure_alloc_id',
709 olt_no,
710 onu_no,
711 alloc_id)
712 self.log.info('configured-alloc-id', data=data)
713 except Exception as e:
714 self.log.info('configure-alloc-id-exception', exc=str(e))
715
716 @inlineCallbacks
717 def send_configure_unicast_gem(self, olt_no, onu_no, uni_gem):
718 self.log.info('configuring-unicast-gem',
719 olt=olt_no,
720 onu=onu_no,
721 unicast_gem_port=uni_gem)
722 try:
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500723 remote = yield self.get_channel()
Steve Crooks7697a392017-01-16 18:13:33 -0600724 data = yield remote.callRemote('configure_unicast_gem',
725 olt_no,
726 onu_no,
727 uni_gem)
728 self.log.info('configured-unicast-gem', data=data)
729 except Exception as e:
730 self.log.info('configure-unicast-gem-exception', exc=str(e))
731
732 @inlineCallbacks
733 def send_configure_multicast_gem(self, olt_no, onu_no, multi_gem):
734 self.log.info('configuring-multicast-gem',
735 olt=olt_no,
736 onu=onu_no,
737 multicast_gem_port=multi_gem)
738 try:
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500739 remote = yield self.get_channel()
Steve Crooks7697a392017-01-16 18:13:33 -0600740 data = yield remote.callRemote('configure_multicast_gem',
741 olt_no,
742 onu_no,
743 multi_gem)
744 self.log.info('configured-multicast-gem', data=data)
745 except Exception as e:
746 self.log.info('configure-multicast-gem-exception', exc=str(e))
747
748 @inlineCallbacks
Steve Crooks3c2c7582017-01-10 15:02:26 -0600749 def send_configure_onu(self, olt_no, onu_no, alloc_id, uni_gem, multi_gem):
750 self.log.info('configuring-onu',
751 olt=olt_no,
752 onu=onu_no,
753 alloc_id=alloc_id,
754 unicast_gem_port=uni_gem,
755 multicast_gem_port=multi_gem)
756 try:
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500757 remote = yield self.get_channel()
Steve Crooks3c2c7582017-01-10 15:02:26 -0600758 data = yield remote.callRemote('configure_onu',
759 olt_no,
760 onu_no,
761 alloc_id,
762 uni_gem,
763 multi_gem)
764 self.log.info('configured-onu', data=data)
765 except Exception as e:
766 self.log.info('configure-onu-exception', exc=str(e))
767
768 @inlineCallbacks
769 def send_activate_onu(self, olt_no, onu_no):
770 self.log.info('activating-onu', olt=olt_no, onu=onu_no)
771 try:
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500772 remote = yield self.get_channel()
Steve Crooks3c2c7582017-01-10 15:02:26 -0600773 data = yield remote.callRemote('activate_onu', olt_no, onu_no)
774 self.log.info('activated-onu', data=data)
775 except Exception as e:
776 self.log.info('activate-onu-exception', exc=str(e))
777
Steve Crooks3c2c7582017-01-10 15:02:26 -0600778
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500779 @inlineCallbacks
780 def heartbeat(self, device_id, state='run'):
781 """Heartbeat OLT hardware
782
Peter Shafikff803c42017-03-08 11:24:58 -0500783 Call PB remote method 'heartbeat' to verify connectivity to OLT
784 hardware. If heartbeat missed self.heartbeat_failed_limit times OLT
785 adapter is set FAILED/UNREACHABLE.
786 No further action from VOLTHA core is expected as result of heartbeat
787 failure. Heartbeat continues following failure and once connectivity is
788 restored adapter state will be set to ACTIVE/REACHABLE
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500789
790 Arguments:
791 device_id: adapter device id
792 state: desired state (stop, start, run)
793 """
794
Peter Shafikff803c42017-03-08 11:24:58 -0500795 self.log.debug('olt-heartbeat', device=device_id, state=state,
796 count=self.heartbeat_count)
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500797
798 def add_timeout(d, duration):
799 return reactor.callLater(duration, d.cancel)
800
801 def cancel_timeout(t):
802 if t.active():
803 t.cancel()
804 self.log.debug('olt-heartbeat-timeout-cancelled')
805
Peter Shafikff803c42017-03-08 11:24:58 -0500806 def heartbeat_alarm(device_id, status, heartbeat_misses=0):
807 try:
808 ts = arrow.utcnow().timestamp
809
810 alarm_data = {'heartbeats_missed':str(heartbeat_misses)}
811
812 alarm_event = self.adapter_agent.create_alarm(
813 id='voltha.{}.{}.olt'.format(self.adapter.name, device_id),
814 resource_id='olt',
815 type=AlarmEventType.EQUIPMENT,
816 category=AlarmEventCategory.PON,
817 severity=AlarmEventSeverity.CRITICAL,
818 state=AlarmEventState.RAISED if status else
819 AlarmEventState.CLEARED,
820 description='OLT Alarm - Heartbeat - {}'.format('Raised'
821 if status
822 else 'Cleared'),
823 context=alarm_data,
824 raised_ts = ts)
825
Stephane Barbarieee409292017-04-24 10:30:20 -0400826 self.adapter_agent.submit_alarm(device_id, alarm_event)
Peter Shafikff803c42017-03-08 11:24:58 -0500827
828 except Exception as e:
829 log.exception('failed-to-submit-alarm', e=e)
830
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500831 if state == 'stop':
Steve Crooks3c2c7582017-01-10 15:02:26 -0600832 return
833
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500834 if state == 'start':
835 self.heartbeat_count = 0
836 self.heartbeat_miss = 0
837
838 try:
839 d = self.get_channel()
840 timeout = add_timeout(d, self.command_timeout)
841 remote = yield d
842 cancel_timeout(timeout)
843
844 d = remote.callRemote('heartbeat', self.heartbeat_count)
845 timeout = add_timeout(d, self.command_timeout)
846 data = yield d
847 cancel_timeout(timeout)
848 except Exception as e:
849 data = -1
Peter Shafikff803c42017-03-08 11:24:58 -0500850 self.log.info('olt-heartbeat-exception', data=data,
851 count=self.heartbeat_miss, exc=str(e))
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500852
853 if data != self.heartbeat_count:
854 # something is not right
855 self.heartbeat_miss += 1
Peter Shafikff803c42017-03-08 11:24:58 -0500856 self.log.info('olt-heartbeat-miss', data=data,
857 count=self.heartbeat_count, miss=self.heartbeat_miss)
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500858 else:
859 if self.heartbeat_miss > 0:
860 self.heartbeat_miss = 0
861 _device = self.adapter_agent.get_device(device_id)
862 _device.connect_status = ConnectStatus.REACHABLE
863 _device.oper_status = OperStatus.ACTIVE
864 _device.reason = ''
865 self.adapter_agent.update_device(_device)
Peter Shafikff803c42017-03-08 11:24:58 -0500866 heartbeat_alarm(device_id, 0)
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500867
868 _device = self.adapter_agent.get_device(device_id)
Peter Shafikff803c42017-03-08 11:24:58 -0500869 if (self.heartbeat_miss >= self.heartbeat_failed_limit) and \
870 (_device.connect_status == ConnectStatus.REACHABLE):
871 self.log.info('olt-heartbeat-failed', data=data,
872 count=self.heartbeat_miss)
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500873 _device = self.adapter_agent.get_device(device_id)
874 _device.connect_status = ConnectStatus.UNREACHABLE
875 _device.oper_status = OperStatus.FAILED
876 _device.reason = 'Lost connectivity to OLT'
877 self.adapter_agent.update_device(_device)
Peter Shafikff803c42017-03-08 11:24:58 -0500878 heartbeat_alarm(device_id, 1, self.heartbeat_miss)
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500879
Peter Shafikff803c42017-03-08 11:24:58 -0500880 self.heartbeat_count += 1
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500881 reactor.callLater(self.heartbeat_interval, self.heartbeat, device_id)
882
883 @inlineCallbacks
Steve Crooks8abe8c22017-03-31 10:48:29 -0500884 def arrive_onu(self):
885 self.log.info('arrive-onu waiting')
886 _data = yield self.onu_discovered_queue.get()
887
888 ok_to_arrive = False
889 olt_id = _data['_device_id']
890 pon_id = _data['_pon_id']
891 onu_id = self.onu_serial_exists(_data['_vendor_id'], _data['_vendor_specific'])
892 self.log.info('arrive-onu-detected', olt_id=olt_id, pon_ni=pon_id, onu_data=_data, onus=self.onus)
893
894 if _data['onu_id'] == 65535:
895 if onu_id is not None:
896 self.log.info('onu-activation-already-in-progress',
897 vendor=_data['_vendor_id'],
898 vendor_specific=_data['_vendor_specific'],
899 onus=self.onus)
900 else:
901 onu_id = self.get_new_onu_id(_data['_vendor_id'],
902 _data['_vendor_specific'])
903 self.log.info('assigned-onu-id',
904 onu_id=onu_id,
905 vendor=_data['_vendor_id'],
906 vendor_specific=_data['_vendor_specific'],
907 onus=self.onus)
908 ok_to_arrive = True
909 else:
910 vendor_id, vendor_specific = self.onu_exists(_data['onu_id'])
911 if vendor_id is not None and vendor_id == _data['_vendor_id'] and \
912 vendor_specific is not None and vendor_specific == _data['_vendor_specific']:
913 onu_id = _data['onu_id']
914 self.log.info('re-discovered-existing-onu',
915 onu_id=onu_id,
916 vendor=_data['_vendor_id'],
917 vendor_specific=_data['_vendor_specific'])
918 ok_to_arrive = True
919 else:
920 self.log.info('onu-id-serial-number-mismatch-detected',
921 onu_id=onu_id,
922 vendor_id=vendor_id,
923 new_vendor_id=_data['_vendor_id'],
924 vendor_specific=vendor_specific,
925 new_vendor_specific=_data['_vendor_specific'])
926
927 if onu_id is not None and ok_to_arrive:
928 self.log.info('arriving-onu', onu_id=onu_id)
929 tunnel_tag = self.get_tunnel_tag_from_onu(onu_id)
930 yield self.send_create_onu(pon_id,
931 onu_id,
932 _data['_vendor_id'],
933 _data['_vendor_specific'])
934 yield self.send_configure_alloc_id(pon_id, onu_id, tunnel_tag)
935 yield self.send_configure_unicast_gem(pon_id, onu_id, tunnel_tag)
936 yield self.send_configure_multicast_gem(pon_id, onu_id, 4000)
937 yield self.send_activate_onu(pon_id, onu_id)
938
939 self.adapter_agent.child_device_detected(
940 parent_device_id=self.device_id,
941 parent_port_no=100,
942 child_device_type='broadcom_onu',
943 proxy_address=Device.ProxyAddress(
944 device_id=self.device_id,
945 channel_id=self.get_proxy_channel_id_from_onu(onu_id), # c-vid
946 onu_id=onu_id,
947 onu_session_id=tunnel_tag # tunnel_tag/gem_port, alloc_id
948 ),
949 vlan=tunnel_tag,
950 serial_number=_data['_vendor_specific']
951 )
952
953 reactor.callLater(1, self.arrive_onu)
954
955 @inlineCallbacks
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500956 def activate(self, device):
957 self.log.info('activating-olt', device=device)
Steve Crooks8abe8c22017-03-31 10:48:29 -0500958
959 while self.onu_discovered_queue.pending:
960 _ = yield self.onu_discovered_queue.get()
961
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500962 if self.logical_device_id is None:
963 if not device.ipv4_address:
964 device.oper_status = OperStatus.FAILED
965 device.reason = 'No ipv4_address field provided'
966 self.adapter_agent.update_device(device)
967 return
968
969 device.root = True
970 device.vendor = 'Broadcom'
971 device.model = 'bcm68620'
972 device.serial_number = device.ipv4_address
973 self.adapter_agent.update_device(device)
974
975 nni_port = Port(
Steve Crooks8abe8c22017-03-31 10:48:29 -0500976 port_no=1,
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500977 label='NNI facing Ethernet port',
978 type=Port.ETHERNET_NNI,
979 admin_state=AdminState.ENABLED,
980 oper_status=OperStatus.ACTIVE
981 )
982 self.adapter_agent.add_port(device.id, nni_port)
983 self.adapter_agent.add_port(device.id, Port(
Steve Crooks8abe8c22017-03-31 10:48:29 -0500984 port_no=100,
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500985 label='PON port',
986 type=Port.PON_OLT,
987 admin_state=AdminState.ENABLED,
988 oper_status=OperStatus.ACTIVE
989 ))
990
991 ld = LogicalDevice(
Peter Shafikff803c42017-03-08 11:24:58 -0500992 # not setting id and datapth_id will let the adapter
993 # agent pick id
Steve Crooks1e1c3e22017-03-06 15:24:49 -0500994 desc=ofp_desc(
995 mfr_desc='cord project',
996 hw_desc='n/a',
997 sw_desc='logical device for Maple-based PON',
998 serial_num=uuid4().hex,
999 dp_desc='n/a'
1000 ),
1001 switch_features=ofp_switch_features(
1002 n_buffers=256, # TODO fake for now
1003 n_tables=2, # TODO ditto
1004 capabilities=( # TODO and ditto
1005 OFPC_FLOW_STATS
1006 | OFPC_TABLE_STATS
1007 | OFPC_PORT_STATS
1008 | OFPC_GROUP_STATS
1009 )
1010 ),
1011 root_device_id=device.id
1012 )
1013 ld_initialized = self.adapter_agent.create_logical_device(ld)
1014 cap = OFPPF_1GB_FD | OFPPF_FIBER
1015 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
1016 id='nni',
1017 ofp_port=ofp_port(
1018 port_no=0, # is 0 OK?
1019 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % 129),
1020 name='nni',
1021 config=0,
1022 state=OFPPS_LIVE,
1023 curr=cap,
1024 advertised=cap,
1025 peer=cap,
1026 curr_speed=OFPPF_1GB_FD,
1027 max_speed=OFPPF_1GB_FD
1028 ),
1029 device_id=device.id,
1030 device_port_no=nni_port.port_no,
1031 root_port=True
1032 ))
1033
1034 device = self.adapter_agent.get_device(device.id)
1035 device.parent_id = ld_initialized.id
1036 device.connect_status = ConnectStatus.UNREACHABLE
1037 device.oper_status = OperStatus.ACTIVATING
1038 self.adapter_agent.update_device(device)
1039 self.logical_device_id = ld_initialized.id
1040
1041 device = self.adapter_agent.get_device(device.id)
Steve Crooks3c2c7582017-01-10 15:02:26 -06001042 self.log.info('initiating-connection-to-olt',
1043 device_id=device.id,
1044 ipv4=device.ipv4_address,
1045 port=self.pbc_port)
Steve Crooks3c2c7582017-01-10 15:02:26 -06001046 try:
Steve Crooks1e1c3e22017-03-06 15:24:49 -05001047 reactor.connectTCP(device.ipv4_address, self.pbc_port, self.pbc_factory)
1048 device.connect_status = ConnectStatus.REACHABLE
1049 device.oper_status = OperStatus.ACTIVE
1050 device.reason = ''
1051 self.adapter_agent.update_device(device)
Steve Crooks3c2c7582017-01-10 15:02:26 -06001052 except Exception as e:
1053 self.log.info('get-channel-exception', exc=str(e))
Steve Crooks1e1c3e22017-03-06 15:24:49 -05001054 device = self.adapter_agent.get_device(device.id)
1055 device.oper_status = OperStatus.FAILED
1056 device.reason = 'Failed to connect to OLT'
1057 self.adapter_agent.update_device(device)
1058 self.pbc_factory.stopTrying()
1059 reactor.callLater(5, self.activate, device)
1060 return
1061
1062 device = self.adapter_agent.get_device(device.id)
1063 self.log.info('connected-to-olt',
1064 device_id=device.id,
1065 ipv4=device.ipv4_address,
1066 port=self.pbc_port)
1067
1068 reactor.callLater(0, self.heartbeat, device.id, state='start')
Steve Crooks3c2c7582017-01-10 15:02:26 -06001069
Steve Crooks7697a392017-01-16 18:13:33 -06001070 yield self.send_set_remote()
Steve Crooks7697a392017-01-16 18:13:33 -06001071 yield self.send_connect_olt(0)
1072 yield self.send_activate_olt(0)
Zsolt Haraszticc153aa2016-12-14 02:28:59 -08001073
Peter Shafikba9b3d82017-03-15 18:01:22 -04001074 # Open the frameio port to receive in-band packet_in messages
Steve Crooks59f1fff2017-01-14 11:16:31 -06001075 self.log.info('registering-frameio')
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -08001076 self.io_port = registry('frameio').open_port(
Steve Crooks59f1fff2017-01-14 11:16:31 -06001077 self.interface, self.rcv_io, is_inband_frame)
Peter Shafikba9b3d82017-03-15 18:01:22 -04001078
1079 # Finally set the initial PM configuration for this device
Steve Crooks8abe8c22017-03-31 10:48:29 -05001080 # TODO: if arrive_onu not working, the following PM stuff was commented out during testing
Peter Shafikba9b3d82017-03-15 18:01:22 -04001081 self.pm_metrics=MapleOltPmMetrics(device)
1082 pm_config = self.pm_metrics.make_proto()
1083 log.info("initial-pm-config", pm_config=pm_config)
1084 self.adapter_agent.update_device_pm_config(pm_config,init=True)
1085
1086 # Apply the PM configuration
1087 self.update_pm_metrics(device, pm_config)
1088
Steve Crooks8abe8c22017-03-31 10:48:29 -05001089 reactor.callLater(1, self.arrive_onu)
1090
Steve Crooks1e1c3e22017-03-06 15:24:49 -05001091 self.log.info('olt-activated', device=device)
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -08001092
Steve Crooks3c2c7582017-01-10 15:02:26 -06001093 def rcv_io(self, port, frame):
Steve Crooksf248e182017-02-07 10:50:24 -05001094 self.log.info('received', iface_name=port.iface_name,
Steve Crooks3c2c7582017-01-10 15:02:26 -06001095 frame_len=len(frame))
1096 pkt = Ether(frame)
1097 if pkt.haslayer(Dot1Q):
1098 outer_shim = pkt.getlayer(Dot1Q)
1099 if isinstance(outer_shim.payload, Dot1Q):
1100 inner_shim = outer_shim.payload
1101 cvid = inner_shim.vlan
1102 logical_port = cvid
1103 popped_frame = (
1104 Ether(src=pkt.src, dst=pkt.dst, type=inner_shim.type) /
1105 inner_shim.payload
1106 )
1107 kw = dict(
1108 logical_device_id=self.logical_device_id,
1109 logical_port_no=logical_port,
1110 )
1111 self.log.info('sending-packet-in', **kw)
1112 self.adapter_agent.send_packet_in(
1113 packet=str(popped_frame), **kw)
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -08001114
Steve Crooksf248e182017-02-07 10:50:24 -05001115 @inlineCallbacks
1116 def update_flow_table(self, flows, device):
1117 self.log.info('bulk-flow-update', device_id=device.id, flows=flows)
1118
1119 def is_downstream(port):
Steve Crooks8abe8c22017-03-31 10:48:29 -05001120 return not is_upstream(port)
Steve Crooksf248e182017-02-07 10:50:24 -05001121
1122 def is_upstream(port):
Steve Crooks8abe8c22017-03-31 10:48:29 -05001123 return port == 100 # Need a better way
1124
Steve Crooksf248e182017-02-07 10:50:24 -05001125
1126 for flow in flows:
1127 _type = None
1128 _ip_proto = None
1129 _port = None
1130 _vlan_vid = None
1131 _udp_dst = None
1132 _udp_src = None
1133 _ipv4_dst = None
1134 _ipv4_src = None
1135 _metadata = None
1136 _output = None
1137 _push_tpid = None
1138 _field = None
1139
1140 try:
1141 _in_port = fd.get_in_port(flow)
1142 assert _in_port is not None
1143
1144 if is_downstream(_in_port):
1145 self.log.info('downstream-flow')
1146 elif is_upstream(_in_port):
1147 self.log.info('upstream-flow')
1148 else:
1149 raise Exception('port should be 1 or 2 by our convention')
1150
1151 _out_port = fd.get_out_port(flow) # may be None
1152 self.log.info('out-port', out_port=_out_port)
1153
1154 for field in fd.get_ofb_fields(flow):
1155
1156 if field.type == fd.ETH_TYPE:
1157 _type = field.eth_type
1158 self.log.info('field-type-eth-type',
1159 eth_type=_type)
1160
1161 elif field.type == fd.IP_PROTO:
1162 _ip_proto = field.ip_proto
1163 self.log.info('field-type-ip-proto',
1164 ip_proto=_ip_proto)
1165
1166 elif field.type == fd.IN_PORT:
1167 _port = field.port
1168 self.log.info('field-type-in-port',
1169 in_port=_port)
1170
1171 elif field.type == fd.VLAN_VID:
1172 _vlan_vid = field.vlan_vid & 0xfff
1173 self.log.info('field-type-vlan-vid',
1174 vlan=_vlan_vid)
1175
1176 elif field.type == fd.VLAN_PCP:
1177 _vlan_pcp = field.vlan_pcp
1178 self.log.info('field-type-vlan-pcp',
1179 pcp=_vlan_pcp)
1180
1181 elif field.type == fd.UDP_DST:
1182 _udp_dst = field.udp_dst
1183 self.log.info('field-type-udp-dst',
1184 udp_dst=_udp_dst)
1185
1186 elif field.type == fd.UDP_SRC:
1187 _udp_src = field.udp_src
1188 self.log.info('field-type-udp-src',
1189 udp_src=_udp_src)
1190
1191 elif field.type == fd.IPV4_DST:
1192 _ipv4_dst = field.ipv4_dst
1193 self.log.info('field-type-ipv4-dst',
1194 ipv4_dst=_ipv4_dst)
1195
1196 elif field.type == fd.IPV4_SRC:
1197 _ipv4_src = field.ipv4_src
1198 self.log.info('field-type-ipv4-src',
1199 ipv4_dst=_ipv4_src)
1200
1201 elif field.type == fd.METADATA:
Steve Crooks8abe8c22017-03-31 10:48:29 -05001202 _metadata = field.table_metadata
Steve Crooksf248e182017-02-07 10:50:24 -05001203 self.log.info('field-type-metadata',
1204 metadata=_metadata)
1205
1206 else:
1207 raise NotImplementedError('field.type={}'.format(
1208 field.type))
1209
1210 for action in fd.get_actions(flow):
1211
1212 if action.type == fd.OUTPUT:
1213 _output = action.output.port
1214 self.log.info('action-type-output',
1215 output=_output, in_port=_in_port)
1216
1217 elif action.type == fd.POP_VLAN:
1218 self.log.info('action-type-pop-vlan',
1219 in_port=_in_port)
1220
1221 elif action.type == fd.PUSH_VLAN:
1222 _push_tpid = action.push.ethertype
1223 log.info('action-type-push-vlan',
1224 push_tpid=_push_tpid, in_port=_in_port)
1225 if action.push.ethertype != 0x8100:
1226 self.log.error('unhandled-tpid',
1227 ethertype=action.push.ethertype)
1228
1229 elif action.type == fd.SET_FIELD:
1230 _field = action.set_field.field.ofb_field
1231 assert (action.set_field.field.oxm_class ==
1232 OFPXMC_OPENFLOW_BASIC)
1233 self.log.info('action-type-set-field',
1234 field=_field, in_port=_in_port)
1235 if _field.type == fd.VLAN_VID:
Kim Kempfaba1e172017-04-10 11:29:42 -07001236 self.log.info('set-field-type-vlan-vid',
Steve Crooksf248e182017-02-07 10:50:24 -05001237 vlan_vid=_field.vlan_vid & 0xfff)
1238 else:
1239 self.log.error('unsupported-action-set-field-type',
1240 field_type=_field.type)
1241 else:
1242 log.error('unsupported-action-type',
1243 action_type=action.type, in_port=_in_port)
1244
Steve Crooks8abe8c22017-03-31 10:48:29 -05001245 if is_upstream(_in_port) and \
1246 (_type == 0x888e or
1247 (_type == 0x800 and (_ip_proto == 2 or _ip_proto == 17))):
1248 yield self.send_config_classifier(0, _type, _ip_proto, _udp_dst)
1249 yield self.send_config_acflow(0, _in_port, _type, _ip_proto, _udp_dst)
1250
1251
Steve Crooksf248e182017-02-07 10:50:24 -05001252
1253 except Exception as e:
1254 log.exception('failed-to-install-flow', e=e, flow=flow)
1255
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -08001256
Steve Crooks8abe8c22017-03-31 10:48:29 -05001257
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -08001258 @inlineCallbacks
Zsolt Haraszticc153aa2016-12-14 02:28:59 -08001259 def send_proxied_message(self, proxy_address, msg):
Steve Crooks3c2c7582017-01-10 15:02:26 -06001260 if isinstance(msg, Packet):
1261 msg = str(msg)
1262
1263 self.log.info('send-proxied-message',
1264 proxy_address=proxy_address.channel_id,
1265 msg=msg)
1266
1267 try:
Steve Crooks1e1c3e22017-03-06 15:24:49 -05001268 remote = yield self.get_channel()
Steve Crooks3c2c7582017-01-10 15:02:26 -06001269 yield remote.callRemote("send_omci",
1270 0,
1271 0,
Steve Crooks8abe8c22017-03-31 10:48:29 -05001272 self.get_onu_from_channel_id(proxy_address.channel_id),
Steve Crooks3c2c7582017-01-10 15:02:26 -06001273 msg)
Peter Shafik412a7fb2017-02-27 12:39:51 -05001274 onu, rmsg = yield self.rx_handler.receive_omci_msg()
Steve Crooks3c2c7582017-01-10 15:02:26 -06001275 self.adapter_agent.receive_proxied_message(proxy_address, rmsg)
1276 except Exception as e:
1277 self.log.info('send-proxied_message-exception', exc=str(e))
1278
1279 def packet_out(self, egress_port, msg):
1280 self.log.info('sending-packet-out',
1281 egress_port=egress_port,
1282 msg=hexify(msg))
1283
1284 pkt = Ether(msg)
1285 out_pkt = (
1286 Ether(src=pkt.src, dst=pkt.dst) /
1287 Dot1Q(vlan=4091) /
1288 Dot1Q(vlan=egress_port, type=pkt.type) /
1289 pkt.payload
Zsolt Haraszticc153aa2016-12-14 02:28:59 -08001290 )
Steve Crooks3c2c7582017-01-10 15:02:26 -06001291 self.io_port.send(str(out_pkt))
Peter Shafik412a7fb2017-02-27 12:39:51 -05001292
1293 @inlineCallbacks
Peter Shafikba9b3d82017-03-15 18:01:22 -04001294 def update_pm_metrics(self, device, pm_config):
1295 self.log.info('update-pm-metrics', device_id=device.id,
1296 pm_config=pm_config)
1297 remote = yield self.get_channel()
1298 self.pm_metrics.update(device, pm_config, remote)