blob: e6f42abd6caea401b3060fdc714aec59a2d0d973 [file] [log] [blame]
Zsolt Haraszti656ecc62016-12-28 15:08:23 -08001#
Zsolt Haraszti3eb27a52017-01-03 21:56:48 -08002# Copyright 2017 the original author or authors.
Zsolt Haraszti656ecc62016-12-28 15:08:23 -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
17"""
18Fully simulated OLT/ONU adapter.
19"""
20from uuid import uuid4
21
Sergio Slobodrian98eff412017-03-15 14:46:30 -040022import arrow
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080023import grpc
Stephane Barbarie4475a252017-03-31 13:49:20 -040024import json
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080025import structlog
26from scapy.layers.l2 import Ether, Dot1Q
Stephane Barbarie4475a252017-03-31 13:49:20 -040027from scapy.layers.inet import Raw
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080028from twisted.internet import reactor
Khen Nursimulud068d812017-03-06 11:44:18 -050029from twisted.internet.defer import inlineCallbacks
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080030
31from common.frameio.frameio import BpfProgramFilter, hexify
Khen Nursimulud068d812017-03-06 11:44:18 -050032from common.utils.asleep import asleep
Sergio Slobodrian98eff412017-03-15 14:46:30 -040033from twisted.internet.task import LoopingCall
Shad Ansari14bcd992017-06-13 14:27:20 -070034from voltha.adapters.iadapter import IAdapter
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080035from voltha.core.logical_device_agent import mac_str_to_tuple
36from voltha.protos import third_party
37from voltha.protos import ponsim_pb2
Shad Ansari14bcd992017-06-13 14:27:20 -070038from voltha.protos.common_pb2 import OperStatus, ConnectStatus, AdminState
39from voltha.protos.device_pb2 import Port, Device, PmConfig, PmConfigs
Sergio Slobodrian98eff412017-03-15 14:46:30 -040040from voltha.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080041from google.protobuf.empty_pb2 import Empty
42
43from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice
Stephane Barbarie5253c652017-03-22 16:29:46 -040044from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
45 OFPPF_1GB_FD, \
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080046 OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS, \
47 ofp_switch_features, ofp_desc
48from voltha.protos.openflow_13_pb2 import ofp_port
49from voltha.protos.ponsim_pb2 import FlowTable
50from voltha.registry import registry
51
52_ = third_party
53log = structlog.get_logger()
54
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080055PACKET_IN_VLAN = 4000
56is_inband_frame = BpfProgramFilter('(ether[14:2] & 0xfff) = 0x{:03x}'.format(
57 PACKET_IN_VLAN))
58
Stephane Barbarie5253c652017-03-22 16:29:46 -040059
Sergio Slobodrian98eff412017-03-15 14:46:30 -040060class AdapterPmMetrics:
Stephane Barbarie5253c652017-03-22 16:29:46 -040061 def __init__(self, device):
Sergio Slobodrianec6e3912017-04-02 11:46:55 -040062 self.pm_names = {'tx_64_pkts', 'tx_65_127_pkts', 'tx_128_255_pkts',
63 'tx_256_511_pkts', 'tx_512_1023_pkts',
64 'tx_1024_1518_pkts', 'tx_1519_9k_pkts',
65 'rx_64_pkts', 'rx_65_127_pkts',
66 'rx_128_255_pkts', 'rx_256_511_pkts',
67 'rx_512_1023_pkts', 'rx_1024_1518_pkts',
68 'rx_1519_9k_pkts'}
Sergio Slobodrian98eff412017-03-15 14:46:30 -040069 self.device = device
70 self.id = device.id
71 self.name = 'ponsim_olt'
Stephane Barbarie5253c652017-03-22 16:29:46 -040072 # self.id = "abc"
Sergio Slobodrian98eff412017-03-15 14:46:30 -040073 self.default_freq = 150
74 self.grouped = False
75 self.freq_override = False
76 self.pon_metrics_config = dict()
77 self.nni_metrics_config = dict()
78 self.lc = None
79 for m in self.pm_names:
80 self.pon_metrics_config[m] = PmConfig(name=m,
81 type=PmConfig.COUNTER,
82 enabled=True)
83 self.nni_metrics_config[m] = PmConfig(name=m,
84 type=PmConfig.COUNTER,
85 enabled=True)
86
87 def update(self, pm_config):
88 if self.default_freq != pm_config.default_freq:
89 # Update the callback to the new frequency.
90 self.default_freq = pm_config.default_freq
91 self.lc.stop()
Stephane Barbarie5253c652017-03-22 16:29:46 -040092 self.lc.start(interval=self.default_freq / 10)
Sergio Slobodrian98eff412017-03-15 14:46:30 -040093 for m in pm_config.metrics:
94 self.pon_metrics_config[m.name].enabled = m.enabled
95 self.nni_metrics_config[m.name].enabled = m.enabled
96
97 def make_proto(self):
98 pm_config = PmConfigs(
99 id=self.id,
100 default_freq=self.default_freq,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400101 grouped=False,
102 freq_override=False)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400103 for m in sorted(self.pon_metrics_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400104 pm = self.pon_metrics_config[m] # Either will do they're the same
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400105 pm_config.metrics.extend([PmConfig(name=pm.name,
106 type=pm.type,
107 enabled=pm.enabled)])
108 return pm_config
109
110 def collect_port_metrics(self, channel):
111 rtrn_port_metrics = dict()
112 stub = ponsim_pb2.PonSimStub(channel)
113 stats = stub.GetStats(Empty())
114 rtrn_port_metrics['pon'] = self.extract_pon_metrics(stats)
115 rtrn_port_metrics['nni'] = self.extract_nni_metrics(stats)
116 return rtrn_port_metrics
117
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400118 def extract_pon_metrics(self, stats):
119 rtrn_pon_metrics = dict()
120 for m in stats.metrics:
121 if m.port_name == "pon":
122 for p in m.packets:
123 if self.pon_metrics_config[p.name].enabled:
124 rtrn_pon_metrics[p.name] = p.value
125 return rtrn_pon_metrics
126
127 def extract_nni_metrics(self, stats):
128 rtrn_pon_metrics = dict()
129 for m in stats.metrics:
130 if m.port_name == "nni":
131 for p in m.packets:
132 if self.pon_metrics_config[p.name].enabled:
133 rtrn_pon_metrics[p.name] = p.value
134 return rtrn_pon_metrics
135
136 def start_collector(self, callback):
137 log.info("starting-pm-collection", device_name=self.name,
138 device_id=self.device.id)
139 prefix = 'voltha.{}.{}'.format(self.name, self.device.id)
140 self.lc = LoopingCall(callback, self.device.id, prefix)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400141 self.lc.start(interval=self.default_freq / 10)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400142
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800143
Stephane Barbarie4475a252017-03-31 13:49:20 -0400144class AdapterAlarms:
145 def __init__(self, adapter, device):
146 self.adapter = adapter
147 self.device = device
148 self.lc = None
149
150 def send_alarm(self, context_data, alarm_data):
151 try:
152 current_context = {}
153 for key, value in context_data.__dict__.items():
154 current_context[key] = str(value)
155
156 alarm_event = self.adapter.adapter_agent.create_alarm(
157 resource_id=self.device.id,
khenaidoo032d3302017-06-09 14:50:04 -0400158 description="{}.{} - {}".format(self.adapter.name,
159 self.device.id,
160 alarm_data[
161 'description']) if 'description' in alarm_data else None,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400162 type=alarm_data['type'] if 'type' in alarm_data else None,
khenaidoo032d3302017-06-09 14:50:04 -0400163 category=alarm_data[
164 'category'] if 'category' in alarm_data else None,
165 severity=alarm_data[
166 'severity'] if 'severity' in alarm_data else None,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400167 state=alarm_data['state'] if 'state' in alarm_data else None,
168 raised_ts=alarm_data['ts'] if 'ts' in alarm_data else 0,
169 context=current_context
170 )
171
khenaidoo032d3302017-06-09 14:50:04 -0400172 self.adapter.adapter_agent.submit_alarm(self.device.id,
173 alarm_event)
Stephane Barbarie4475a252017-03-31 13:49:20 -0400174
175 except Exception as e:
176 log.exception('failed-to-send-alarm', e=e)
177
Shad Ansari14bcd992017-06-13 14:27:20 -0700178class PonSimOltAdapter(IAdapter):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800179 def __init__(self, adapter_agent, config):
Shad Ansari14bcd992017-06-13 14:27:20 -0700180 super(PonSimOltAdapter, self).__init__(adapter_agent=adapter_agent,
181 config=config,
182 name='ponsim_olt',
183 vendor='Voltha project',
184 version='0.4')
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800185 self.devices_handlers = dict() # device_id -> PonSimOltHandler()
186 self.logical_device_id_to_root_device_id = dict()
187
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400188 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400189 log.info("adapter-update-pm-config", device=device,
190 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400191 handler = self.devices_handlers[device.id]
192 handler.update_pm_config(device, pm_config)
Sergio Slobodrianec864c62017-03-09 11:41:43 -0500193
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800194 def adopt_device(self, device):
195 self.devices_handlers[device.id] = PonSimOltHandler(self, device.id)
196 reactor.callLater(0, self.devices_handlers[device.id].activate, device)
197 return device
198
khenaidoo032d3302017-06-09 14:50:04 -0400199 def reconcile_device(self, device):
200 try:
201 self.devices_handlers[device.id] = PonSimOltHandler(self,
202 device.id)
203 # Work only required for devices that are in ENABLED state
204 if device.admin_state == AdminState.ENABLED:
205 reactor.callLater(0,
206 self.devices_handlers[device.id].reconcile,
207 device)
208 else:
209 # Invoke the children reconciliation which would setup the
210 # basic children data structures
211 self.adapter_agent.reconcile_child_devices(device.id)
212 return device
213 except Exception, e:
214 log.exception('Exception', e=e)
215
Khen Nursimulud068d812017-03-06 11:44:18 -0500216 def disable_device(self, device):
217 log.info('disable-device', device_id=device.id)
218 reactor.callLater(0, self.devices_handlers[device.id].disable)
219 return device
220
221 def reenable_device(self, device):
222 log.info('reenable-device', device_id=device.id)
223 reactor.callLater(0, self.devices_handlers[device.id].reenable)
224 return device
225
226 def reboot_device(self, device):
227 log.info('reboot-device', device_id=device.id)
228 reactor.callLater(0, self.devices_handlers[device.id].reboot)
229 return device
230
231 def delete_device(self, device):
232 log.info('delete-device', device_id=device.id)
khenaidoo032d3302017-06-09 14:50:04 -0400233 # TODO: Update the logical device mapping
Khen Nursimulud068d812017-03-06 11:44:18 -0500234 reactor.callLater(0, self.devices_handlers[device.id].delete)
235 return device
236
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800237 def update_flows_bulk(self, device, flows, groups):
238 log.info('bulk-flow-update', device_id=device.id,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400239 flows=flows, groups=groups)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800240 assert len(groups.items) == 0
241 handler = self.devices_handlers[device.id]
242 return handler.update_flow_table(flows.items)
243
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800244 def send_proxied_message(self, proxy_address, msg):
245 log.info('send-proxied-message', proxy_address=proxy_address, msg=msg)
246 handler = self.devices_handlers[proxy_address.device_id]
247 handler.send_proxied_message(proxy_address, msg)
248
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800249 def receive_packet_out(self, logical_device_id, egress_port_no, msg):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800250 def ldi_to_di(ldi):
251 di = self.logical_device_id_to_root_device_id.get(ldi)
252 if di is None:
253 logical_device = self.adapter_agent.get_logical_device(ldi)
254 di = logical_device.root_device_id
255 self.logical_device_id_to_root_device_id[ldi] = di
256 return di
257
258 device_id = ldi_to_di(logical_device_id)
259 handler = self.devices_handlers[device_id]
260 handler.packet_out(egress_port_no, msg)
261
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800262class PonSimOltHandler(object):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800263 def __init__(self, adapter, device_id):
264 self.adapter = adapter
265 self.adapter_agent = adapter.adapter_agent
266 self.device_id = device_id
267 self.log = structlog.get_logger(device_id=device_id)
268 self.channel = None
269 self.io_port = None
270 self.logical_device_id = None
Khen Nursimulud068d812017-03-06 11:44:18 -0500271 self.nni_port = None
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400272 self.ofp_port_no = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800273 self.interface = registry('main').get_args().interface
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400274 self.pm_metrics = None
Stephane Barbarie4475a252017-03-31 13:49:20 -0400275 self.alarms = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800276
277 def __del__(self):
278 if self.io_port is not None:
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800279 registry('frameio').close_port(self.io_port)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800280
281 def get_channel(self):
282 if self.channel is None:
283 device = self.adapter_agent.get_device(self.device_id)
284 self.channel = grpc.insecure_channel(device.host_and_port)
285 return self.channel
286
khenaidoo032d3302017-06-09 14:50:04 -0400287 def _get_nni_port(self):
288 ports = self.adapter_agent.get_ports(self.device_id, Port.ETHERNET_NNI)
289 if ports:
290 # For now, we use on one NNI port
291 return ports[0]
292
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800293 def activate(self, device):
294 self.log.info('activating')
295
296 if not device.host_and_port:
297 device.oper_status = OperStatus.FAILED
298 device.reason = 'No host_and_port field provided'
299 self.adapter_agent.update_device(device)
300 return
301
302 stub = ponsim_pb2.PonSimStub(self.get_channel())
303 info = stub.GetDeviceInfo(Empty())
304 log.info('got-info', info=info)
305
306 device.root = True
307 device.vendor = 'ponsim'
Stephane Barbarie5253c652017-03-22 16:29:46 -0400308 device.model = 'n/a'
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800309 device.serial_number = device.host_and_port
310 device.connect_status = ConnectStatus.REACHABLE
311 self.adapter_agent.update_device(device)
312
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400313 # Now set the initial PM configuration for this device
Stephane Barbarie5253c652017-03-22 16:29:46 -0400314 self.pm_metrics = AdapterPmMetrics(device)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400315 pm_config = self.pm_metrics.make_proto()
316 log.info("initial-pm-config", pm_config=pm_config)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400317 self.adapter_agent.update_device_pm_config(pm_config, init=True)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400318
Stephane Barbarie4475a252017-03-31 13:49:20 -0400319 # Setup alarm handler
320 self.alarms = AdapterAlarms(self.adapter, device)
321
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800322 nni_port = Port(
323 port_no=2,
324 label='NNI facing Ethernet port',
325 type=Port.ETHERNET_NNI,
326 admin_state=AdminState.ENABLED,
327 oper_status=OperStatus.ACTIVE
328 )
Khen Nursimulud068d812017-03-06 11:44:18 -0500329 self.nni_port = nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800330 self.adapter_agent.add_port(device.id, nni_port)
331 self.adapter_agent.add_port(device.id, Port(
332 port_no=1,
333 label='PON port',
334 type=Port.PON_OLT,
335 admin_state=AdminState.ENABLED,
336 oper_status=OperStatus.ACTIVE
337 ))
338
339 ld = LogicalDevice(
340 # not setting id and datapth_id will let the adapter agent pick id
341 desc=ofp_desc(
342 mfr_desc='cord porject',
343 hw_desc='simualted pon',
344 sw_desc='simualted pon',
345 serial_num=uuid4().hex,
346 dp_desc='n/a'
347 ),
348 switch_features=ofp_switch_features(
349 n_buffers=256, # TODO fake for now
350 n_tables=2, # TODO ditto
351 capabilities=( # TODO and ditto
352 OFPC_FLOW_STATS
353 | OFPC_TABLE_STATS
354 | OFPC_PORT_STATS
355 | OFPC_GROUP_STATS
356 )
357 ),
358 root_device_id=device.id
359 )
360 ld_initialized = self.adapter_agent.create_logical_device(ld)
361 cap = OFPPF_1GB_FD | OFPPF_FIBER
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400362 self.ofp_port_no = info.nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800363 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
364 id='nni',
365 ofp_port=ofp_port(
366 port_no=info.nni_port,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400367 hw_addr=mac_str_to_tuple(
368 '00:00:00:00:00:%02x' % info.nni_port),
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800369 name='nni',
370 config=0,
371 state=OFPPS_LIVE,
372 curr=cap,
373 advertised=cap,
374 peer=cap,
375 curr_speed=OFPPF_1GB_FD,
376 max_speed=OFPPF_1GB_FD
377 ),
378 device_id=device.id,
379 device_port_no=nni_port.port_no,
380 root_port=True
381 ))
382
383 device = self.adapter_agent.get_device(device.id)
384 device.parent_id = ld_initialized.id
385 device.oper_status = OperStatus.ACTIVE
386 self.adapter_agent.update_device(device)
387 self.logical_device_id = ld_initialized.id
388
389 # register ONUS per uni port
390 for port_no in info.uni_ports:
391 vlan_id = port_no
392 self.adapter_agent.child_device_detected(
393 parent_device_id=device.id,
394 parent_port_no=1,
395 child_device_type='ponsim_onu',
396 proxy_address=Device.ProxyAddress(
397 device_id=device.id,
398 channel_id=vlan_id
399 ),
400 vlan=vlan_id
401 )
402
403 # finally, open the frameio port to receive in-band packet_in messages
404 self.log.info('registering-frameio')
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800405 self.io_port = registry('frameio').open_port(
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800406 self.interface, self.rcv_io, is_inband_frame)
407 self.log.info('registered-frameio')
408
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400409 # Start collecting stats from the device after a brief pause
410 self.start_kpi_collection(device.id)
411
khenaidoo032d3302017-06-09 14:50:04 -0400412 def reconcile(self, device):
413 self.log.info('reconciling-OLT-device-starts')
414
415 if not device.host_and_port:
416 device.oper_status = OperStatus.FAILED
417 device.reason = 'No host_and_port field provided'
418 self.adapter_agent.update_device(device)
419 return
420
421 try:
422 stub = ponsim_pb2.PonSimStub(self.get_channel())
423 info = stub.GetDeviceInfo(Empty())
424 log.info('got-info', info=info)
425 # TODO: Verify we are connected to the same device we are
426 # reconciling - not much data in ponsim to differentiate at the
427 # time
428 device.oper_status = OperStatus.ACTIVE
429 self.adapter_agent.update_device(device)
430 self.ofp_port_no = info.nni_port
431 self.nni_port = self._get_nni_port()
432 except Exception, e:
433 log.exception('device-unreachable', e=e)
434 device.connect_status = ConnectStatus.UNREACHABLE
435 device.oper_status = OperStatus.UNKNOWN
436 self.adapter_agent.update_device(device)
437 return
438
439 # Now set the initial PM configuration for this device
440 self.pm_metrics = AdapterPmMetrics(device)
441 pm_config = self.pm_metrics.make_proto()
442 log.info("initial-pm-config", pm_config=pm_config)
443 self.adapter_agent.update_device_pm_config(pm_config, init=True)
444
445 # Setup alarm handler
446 self.alarms = AdapterAlarms(self.adapter, device)
447
448 # TODO: Is there anything required to verify nni and PON ports
449
450 # Set the logical device id
451 device = self.adapter_agent.get_device(device.id)
452 if device.parent_id:
453 self.logical_device_id = device.parent_id
454 self.adapter_agent.reconcile_logical_device(device.parent_id)
455 else:
456 self.log.info('no-logical-device-set')
457
458 # Reconcile child devices
459 self.adapter_agent.reconcile_child_devices(device.id)
460
461 # finally, open the frameio port to receive in-band packet_in messages
462 self.io_port = registry('frameio').open_port(
463 self.interface, self.rcv_io, is_inband_frame)
464
465 # Start collecting stats from the device after a brief pause
466 self.start_kpi_collection(device.id)
467
468 self.log.info('reconciling-OLT-device-ends')
469
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800470 def rcv_io(self, port, frame):
khenaidoo032d3302017-06-09 14:50:04 -0400471 self.log.info('received', iface_name=port.iface_name,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400472 frame_len=len(frame))
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800473 pkt = Ether(frame)
474 if pkt.haslayer(Dot1Q):
475 outer_shim = pkt.getlayer(Dot1Q)
476 if isinstance(outer_shim.payload, Dot1Q):
477 inner_shim = outer_shim.payload
478 cvid = inner_shim.vlan
479 logical_port = cvid
480 popped_frame = (
481 Ether(src=pkt.src, dst=pkt.dst, type=inner_shim.type) /
482 inner_shim.payload
483 )
484 kw = dict(
485 logical_device_id=self.logical_device_id,
486 logical_port_no=logical_port,
487 )
488 self.log.info('sending-packet-in', **kw)
489 self.adapter_agent.send_packet_in(
490 packet=str(popped_frame), **kw)
Stephane Barbarie4475a252017-03-31 13:49:20 -0400491 elif pkt.haslayer(Raw):
492 raw_data = json.loads(pkt.getlayer(Raw).load)
493 self.alarms.send_alarm(self, raw_data)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800494
495 def update_flow_table(self, flows):
496 stub = ponsim_pb2.PonSimStub(self.get_channel())
497 self.log.info('pushing-olt-flow-table')
498 stub.UpdateFlowTable(FlowTable(
499 port=0,
500 flows=flows
501 ))
502 self.log.info('success')
503
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400504 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400505 log.info("handler-update-pm-config", device=device,
506 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400507 self.pm_metrics.update(pm_config)
508
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800509 def send_proxied_message(self, proxy_address, msg):
510 self.log.info('sending-proxied-message')
511 if isinstance(msg, FlowTable):
512 stub = ponsim_pb2.PonSimStub(self.get_channel())
513 self.log.info('pushing-onu-flow-table', port=msg.port)
514 res = stub.UpdateFlowTable(msg)
515 self.adapter_agent.receive_proxied_message(proxy_address, res)
516
517 def packet_out(self, egress_port, msg):
518 self.log.info('sending-packet-out', egress_port=egress_port,
519 msg=hexify(msg))
520 pkt = Ether(msg)
521 out_pkt = (
522 Ether(src=pkt.src, dst=pkt.dst) /
523 Dot1Q(vlan=4000) /
524 Dot1Q(vlan=egress_port, type=pkt.type) /
525 pkt.payload
526 )
527 self.io_port.send(str(out_pkt))
Khen Nursimulud068d812017-03-06 11:44:18 -0500528
529 @inlineCallbacks
530 def reboot(self):
531 self.log.info('rebooting', device_id=self.device_id)
532
533 # Update the operational status to ACTIVATING and connect status to
534 # UNREACHABLE
535 device = self.adapter_agent.get_device(self.device_id)
536 previous_oper_status = device.oper_status
537 previous_conn_status = device.connect_status
538 device.oper_status = OperStatus.ACTIVATING
539 device.connect_status = ConnectStatus.UNREACHABLE
540 self.adapter_agent.update_device(device)
541
khenaidoo71d0a6c2017-03-22 21:46:04 -0400542 # Update the child devices connect state to UNREACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400543 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400544 connect_status=ConnectStatus.UNREACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400545
Khen Nursimulud068d812017-03-06 11:44:18 -0500546 # Sleep 10 secs, simulating a reboot
Stephane Barbarie5253c652017-03-22 16:29:46 -0400547 # TODO: send alert and clear alert after the reboot
Khen Nursimulud068d812017-03-06 11:44:18 -0500548 yield asleep(10)
549
550 # Change the operational status back to its previous state. With a
551 # real OLT the operational state should be the state the device is
552 # after a reboot.
553 # Get the latest device reference
554 device = self.adapter_agent.get_device(self.device_id)
555 device.oper_status = previous_oper_status
556 device.connect_status = previous_conn_status
557 self.adapter_agent.update_device(device)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400558
559 # Update the child devices connect state to REACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400560 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400561 connect_status=ConnectStatus.REACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400562
Khen Nursimulud068d812017-03-06 11:44:18 -0500563 self.log.info('rebooted', device_id=self.device_id)
564
565 def disable(self):
566 self.log.info('disabling', device_id=self.device_id)
567
568 # Get the latest device reference
569 device = self.adapter_agent.get_device(self.device_id)
570
571 # Update the operational status to UNKNOWN
572 device.oper_status = OperStatus.UNKNOWN
573 device.connect_status = ConnectStatus.UNREACHABLE
574 self.adapter_agent.update_device(device)
575
576 # Remove the logical device
577 logical_device = self.adapter_agent.get_logical_device(
Stephane Barbarie5253c652017-03-22 16:29:46 -0400578 self.logical_device_id)
Khen Nursimulud068d812017-03-06 11:44:18 -0500579 self.adapter_agent.delete_logical_device(logical_device)
580
581 # Disable all child devices first
khenaidoo2d7af132017-03-23 15:45:51 -0400582 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400583 admin_state=AdminState.DISABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500584
Khen Nursimulud068d812017-03-06 11:44:18 -0500585 # Remove the peer references from this device
586 self.adapter_agent.delete_all_peer_references(self.device_id)
587
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400588 # Set all ports to disabled
589 self.adapter_agent.disable_all_ports(self.device_id)
590
Khen Nursimulud068d812017-03-06 11:44:18 -0500591 # close the frameio port
592 registry('frameio').close_port(self.io_port)
593
khenaidoo032d3302017-06-09 14:50:04 -0400594 # Update the logice device mapping
595 if self.logical_device_id in \
596 self.adapter.logical_device_id_to_root_device_id:
597 del self.adapter.logical_device_id_to_root_device_id[
598 self.logical_device_id]
599
Khen Nursimulud068d812017-03-06 11:44:18 -0500600 # TODO:
601 # 1) Remove all flows from the device
602 # 2) Remove the device from ponsim
603
604 self.log.info('disabled', device_id=device.id)
605
Khen Nursimulud068d812017-03-06 11:44:18 -0500606 def reenable(self):
607 self.log.info('re-enabling', device_id=self.device_id)
608
609 # Get the latest device reference
610 device = self.adapter_agent.get_device(self.device_id)
611
khenaidoo032d3302017-06-09 14:50:04 -0400612 # Set the ofp_port_no and nni_port in case we bypassed the reconcile
613 # process if the device was in DISABLED state on voltha restart
614 if not self.ofp_port_no and not self.nni_port:
615 stub = ponsim_pb2.PonSimStub(self.get_channel())
616 info = stub.GetDeviceInfo(Empty())
617 log.info('got-info', info=info)
618 self.ofp_port_no = info.nni_port
619 self.nni_port = self._get_nni_port()
620
Khen Nursimulud068d812017-03-06 11:44:18 -0500621 # Update the connect status to REACHABLE
622 device.connect_status = ConnectStatus.REACHABLE
623 self.adapter_agent.update_device(device)
624
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400625 # Set all ports to enabled
626 self.adapter_agent.enable_all_ports(self.device_id)
627
Khen Nursimulud068d812017-03-06 11:44:18 -0500628 ld = LogicalDevice(
629 # not setting id and datapth_id will let the adapter agent pick id
630 desc=ofp_desc(
631 mfr_desc='cord porject',
632 hw_desc='simulated pon',
633 sw_desc='simulated pon',
634 serial_num=uuid4().hex,
635 dp_desc='n/a'
636 ),
637 switch_features=ofp_switch_features(
638 n_buffers=256, # TODO fake for now
639 n_tables=2, # TODO ditto
640 capabilities=( # TODO and ditto
641 OFPC_FLOW_STATS
642 | OFPC_TABLE_STATS
643 | OFPC_PORT_STATS
644 | OFPC_GROUP_STATS
645 )
646 ),
647 root_device_id=device.id
648 )
649 ld_initialized = self.adapter_agent.create_logical_device(ld)
650 cap = OFPPF_1GB_FD | OFPPF_FIBER
651 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
652 id='nni',
653 ofp_port=ofp_port(
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400654 port_no=self.ofp_port_no,
Khen Nursimulud068d812017-03-06 11:44:18 -0500655 hw_addr=mac_str_to_tuple(
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400656 '00:00:00:00:00:%02x' % self.ofp_port_no),
Khen Nursimulud068d812017-03-06 11:44:18 -0500657 name='nni',
658 config=0,
659 state=OFPPS_LIVE,
660 curr=cap,
661 advertised=cap,
662 peer=cap,
663 curr_speed=OFPPF_1GB_FD,
664 max_speed=OFPPF_1GB_FD
665 ),
666 device_id=device.id,
667 device_port_no=self.nni_port.port_no,
668 root_port=True
669 ))
670
671 device = self.adapter_agent.get_device(device.id)
672 device.parent_id = ld_initialized.id
673 device.oper_status = OperStatus.ACTIVE
674 self.adapter_agent.update_device(device)
675 self.logical_device_id = ld_initialized.id
676
677 # Reenable all child devices
khenaidoo2d7af132017-03-23 15:45:51 -0400678 self.adapter_agent.update_child_devices_state(device.id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400679 admin_state=AdminState.ENABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500680
Khen Nursimulud068d812017-03-06 11:44:18 -0500681 # finally, open the frameio port to receive in-band packet_in messages
Khen Nursimulud068d812017-03-06 11:44:18 -0500682 self.io_port = registry('frameio').open_port(
683 self.interface, self.rcv_io, is_inband_frame)
Khen Nursimulud068d812017-03-06 11:44:18 -0500684
685 self.log.info('re-enabled', device_id=device.id)
686
Khen Nursimulud068d812017-03-06 11:44:18 -0500687 def delete(self):
688 self.log.info('deleting', device_id=self.device_id)
689
690 # Remove all child devices
691 self.adapter_agent.delete_all_child_devices(self.device_id)
692
693 # TODO:
694 # 1) Remove all flows from the device
695 # 2) Remove the device from ponsim
696
697 self.log.info('deleted', device_id=self.device_id)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400698
699 def start_kpi_collection(self, device_id):
700
701 def _collect(device_id, prefix):
702
703 try:
704 # Step 1: gather metrics from device
705 port_metrics = \
Stephane Barbarie5253c652017-03-22 16:29:46 -0400706 self.pm_metrics.collect_port_metrics(self.get_channel())
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400707
708 # Step 2: prepare the KpiEvent for submission
709 # we can time-stamp them here (or could use time derived from OLT
710 ts = arrow.utcnow().timestamp
711 kpi_event = KpiEvent(
712 type=KpiEventType.slice,
713 ts=ts,
714 prefixes={
715 # OLT NNI port
716 prefix + '.nni': MetricValuePairs(
717 metrics=port_metrics['nni']),
718 # OLT PON port
719 prefix + '.pon': MetricValuePairs(
720 metrics=port_metrics['pon'])
721 }
722 )
723
724 # Step 3: submit
725 self.adapter_agent.submit_kpis(kpi_event)
726
727 except Exception as e:
728 log.exception('failed-to-submit-kpis', e=e)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400729
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400730 self.pm_metrics.start_collector(_collect)