blob: 994ab149768270b216f01226adb4f0784a70f94b [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,
Shad Ansari96f817b2017-06-18 23:17:44 -0700182 device_handler_class=PonSimOltHandler,
Shad Ansari14bcd992017-06-13 14:27:20 -0700183 name='ponsim_olt',
184 vendor='Voltha project',
185 version='0.4')
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800186 self.logical_device_id_to_root_device_id = dict()
187
Shad Ansari96f817b2017-06-18 23:17:44 -0700188
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400189 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400190 log.info("adapter-update-pm-config", device=device,
191 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400192 handler = self.devices_handlers[device.id]
193 handler.update_pm_config(device, pm_config)
Sergio Slobodrianec864c62017-03-09 11:41:43 -0500194
khenaidoo032d3302017-06-09 14:50:04 -0400195 def reconcile_device(self, device):
196 try:
197 self.devices_handlers[device.id] = PonSimOltHandler(self,
198 device.id)
199 # Work only required for devices that are in ENABLED state
200 if device.admin_state == AdminState.ENABLED:
201 reactor.callLater(0,
202 self.devices_handlers[device.id].reconcile,
203 device)
204 else:
205 # Invoke the children reconciliation which would setup the
206 # basic children data structures
207 self.adapter_agent.reconcile_child_devices(device.id)
208 return device
209 except Exception, e:
210 log.exception('Exception', e=e)
211
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800212 def receive_packet_out(self, logical_device_id, egress_port_no, msg):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800213 def ldi_to_di(ldi):
214 di = self.logical_device_id_to_root_device_id.get(ldi)
215 if di is None:
216 logical_device = self.adapter_agent.get_logical_device(ldi)
217 di = logical_device.root_device_id
218 self.logical_device_id_to_root_device_id[ldi] = di
219 return di
220
221 device_id = ldi_to_di(logical_device_id)
222 handler = self.devices_handlers[device_id]
223 handler.packet_out(egress_port_no, msg)
224
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800225class PonSimOltHandler(object):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800226 def __init__(self, adapter, device_id):
227 self.adapter = adapter
228 self.adapter_agent = adapter.adapter_agent
229 self.device_id = device_id
230 self.log = structlog.get_logger(device_id=device_id)
231 self.channel = None
232 self.io_port = None
233 self.logical_device_id = None
Khen Nursimulud068d812017-03-06 11:44:18 -0500234 self.nni_port = None
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400235 self.ofp_port_no = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800236 self.interface = registry('main').get_args().interface
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400237 self.pm_metrics = None
Stephane Barbarie4475a252017-03-31 13:49:20 -0400238 self.alarms = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800239
240 def __del__(self):
241 if self.io_port is not None:
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800242 registry('frameio').close_port(self.io_port)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800243
244 def get_channel(self):
245 if self.channel is None:
246 device = self.adapter_agent.get_device(self.device_id)
247 self.channel = grpc.insecure_channel(device.host_and_port)
248 return self.channel
249
khenaidoo032d3302017-06-09 14:50:04 -0400250 def _get_nni_port(self):
251 ports = self.adapter_agent.get_ports(self.device_id, Port.ETHERNET_NNI)
252 if ports:
253 # For now, we use on one NNI port
254 return ports[0]
255
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800256 def activate(self, device):
257 self.log.info('activating')
258
259 if not device.host_and_port:
260 device.oper_status = OperStatus.FAILED
261 device.reason = 'No host_and_port field provided'
262 self.adapter_agent.update_device(device)
263 return
264
265 stub = ponsim_pb2.PonSimStub(self.get_channel())
266 info = stub.GetDeviceInfo(Empty())
267 log.info('got-info', info=info)
268
269 device.root = True
270 device.vendor = 'ponsim'
Stephane Barbarie5253c652017-03-22 16:29:46 -0400271 device.model = 'n/a'
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800272 device.serial_number = device.host_and_port
273 device.connect_status = ConnectStatus.REACHABLE
274 self.adapter_agent.update_device(device)
275
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400276 # Now set the initial PM configuration for this device
Stephane Barbarie5253c652017-03-22 16:29:46 -0400277 self.pm_metrics = AdapterPmMetrics(device)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400278 pm_config = self.pm_metrics.make_proto()
279 log.info("initial-pm-config", pm_config=pm_config)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400280 self.adapter_agent.update_device_pm_config(pm_config, init=True)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400281
Stephane Barbarie4475a252017-03-31 13:49:20 -0400282 # Setup alarm handler
283 self.alarms = AdapterAlarms(self.adapter, device)
284
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800285 nni_port = Port(
286 port_no=2,
287 label='NNI facing Ethernet port',
288 type=Port.ETHERNET_NNI,
289 admin_state=AdminState.ENABLED,
290 oper_status=OperStatus.ACTIVE
291 )
Khen Nursimulud068d812017-03-06 11:44:18 -0500292 self.nni_port = nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800293 self.adapter_agent.add_port(device.id, nni_port)
294 self.adapter_agent.add_port(device.id, Port(
295 port_no=1,
296 label='PON port',
297 type=Port.PON_OLT,
298 admin_state=AdminState.ENABLED,
299 oper_status=OperStatus.ACTIVE
300 ))
301
302 ld = LogicalDevice(
303 # not setting id and datapth_id will let the adapter agent pick id
304 desc=ofp_desc(
305 mfr_desc='cord porject',
306 hw_desc='simualted pon',
307 sw_desc='simualted pon',
308 serial_num=uuid4().hex,
309 dp_desc='n/a'
310 ),
311 switch_features=ofp_switch_features(
312 n_buffers=256, # TODO fake for now
313 n_tables=2, # TODO ditto
314 capabilities=( # TODO and ditto
315 OFPC_FLOW_STATS
316 | OFPC_TABLE_STATS
317 | OFPC_PORT_STATS
318 | OFPC_GROUP_STATS
319 )
320 ),
321 root_device_id=device.id
322 )
323 ld_initialized = self.adapter_agent.create_logical_device(ld)
324 cap = OFPPF_1GB_FD | OFPPF_FIBER
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400325 self.ofp_port_no = info.nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800326 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
327 id='nni',
328 ofp_port=ofp_port(
329 port_no=info.nni_port,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400330 hw_addr=mac_str_to_tuple(
331 '00:00:00:00:00:%02x' % info.nni_port),
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800332 name='nni',
333 config=0,
334 state=OFPPS_LIVE,
335 curr=cap,
336 advertised=cap,
337 peer=cap,
338 curr_speed=OFPPF_1GB_FD,
339 max_speed=OFPPF_1GB_FD
340 ),
341 device_id=device.id,
342 device_port_no=nni_port.port_no,
343 root_port=True
344 ))
345
346 device = self.adapter_agent.get_device(device.id)
347 device.parent_id = ld_initialized.id
348 device.oper_status = OperStatus.ACTIVE
349 self.adapter_agent.update_device(device)
350 self.logical_device_id = ld_initialized.id
351
352 # register ONUS per uni port
353 for port_no in info.uni_ports:
354 vlan_id = port_no
355 self.adapter_agent.child_device_detected(
356 parent_device_id=device.id,
357 parent_port_no=1,
358 child_device_type='ponsim_onu',
359 proxy_address=Device.ProxyAddress(
360 device_id=device.id,
361 channel_id=vlan_id
362 ),
363 vlan=vlan_id
364 )
365
366 # finally, open the frameio port to receive in-band packet_in messages
367 self.log.info('registering-frameio')
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800368 self.io_port = registry('frameio').open_port(
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800369 self.interface, self.rcv_io, is_inband_frame)
370 self.log.info('registered-frameio')
371
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400372 # Start collecting stats from the device after a brief pause
373 self.start_kpi_collection(device.id)
374
khenaidoo032d3302017-06-09 14:50:04 -0400375 def reconcile(self, device):
376 self.log.info('reconciling-OLT-device-starts')
377
378 if not device.host_and_port:
379 device.oper_status = OperStatus.FAILED
380 device.reason = 'No host_and_port field provided'
381 self.adapter_agent.update_device(device)
382 return
383
384 try:
385 stub = ponsim_pb2.PonSimStub(self.get_channel())
386 info = stub.GetDeviceInfo(Empty())
387 log.info('got-info', info=info)
388 # TODO: Verify we are connected to the same device we are
389 # reconciling - not much data in ponsim to differentiate at the
390 # time
391 device.oper_status = OperStatus.ACTIVE
392 self.adapter_agent.update_device(device)
393 self.ofp_port_no = info.nni_port
394 self.nni_port = self._get_nni_port()
395 except Exception, e:
396 log.exception('device-unreachable', e=e)
397 device.connect_status = ConnectStatus.UNREACHABLE
398 device.oper_status = OperStatus.UNKNOWN
399 self.adapter_agent.update_device(device)
400 return
401
402 # Now set the initial PM configuration for this device
403 self.pm_metrics = AdapterPmMetrics(device)
404 pm_config = self.pm_metrics.make_proto()
405 log.info("initial-pm-config", pm_config=pm_config)
406 self.adapter_agent.update_device_pm_config(pm_config, init=True)
407
408 # Setup alarm handler
409 self.alarms = AdapterAlarms(self.adapter, device)
410
411 # TODO: Is there anything required to verify nni and PON ports
412
413 # Set the logical device id
414 device = self.adapter_agent.get_device(device.id)
415 if device.parent_id:
416 self.logical_device_id = device.parent_id
417 self.adapter_agent.reconcile_logical_device(device.parent_id)
418 else:
419 self.log.info('no-logical-device-set')
420
421 # Reconcile child devices
422 self.adapter_agent.reconcile_child_devices(device.id)
423
424 # finally, open the frameio port to receive in-band packet_in messages
425 self.io_port = registry('frameio').open_port(
426 self.interface, self.rcv_io, is_inband_frame)
427
428 # Start collecting stats from the device after a brief pause
429 self.start_kpi_collection(device.id)
430
431 self.log.info('reconciling-OLT-device-ends')
432
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800433 def rcv_io(self, port, frame):
khenaidoo032d3302017-06-09 14:50:04 -0400434 self.log.info('received', iface_name=port.iface_name,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400435 frame_len=len(frame))
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800436 pkt = Ether(frame)
437 if pkt.haslayer(Dot1Q):
438 outer_shim = pkt.getlayer(Dot1Q)
439 if isinstance(outer_shim.payload, Dot1Q):
440 inner_shim = outer_shim.payload
441 cvid = inner_shim.vlan
442 logical_port = cvid
443 popped_frame = (
444 Ether(src=pkt.src, dst=pkt.dst, type=inner_shim.type) /
445 inner_shim.payload
446 )
447 kw = dict(
448 logical_device_id=self.logical_device_id,
449 logical_port_no=logical_port,
450 )
451 self.log.info('sending-packet-in', **kw)
452 self.adapter_agent.send_packet_in(
453 packet=str(popped_frame), **kw)
Stephane Barbarie4475a252017-03-31 13:49:20 -0400454 elif pkt.haslayer(Raw):
455 raw_data = json.loads(pkt.getlayer(Raw).load)
456 self.alarms.send_alarm(self, raw_data)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800457
458 def update_flow_table(self, flows):
459 stub = ponsim_pb2.PonSimStub(self.get_channel())
460 self.log.info('pushing-olt-flow-table')
461 stub.UpdateFlowTable(FlowTable(
462 port=0,
463 flows=flows
464 ))
465 self.log.info('success')
466
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400467 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400468 log.info("handler-update-pm-config", device=device,
469 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400470 self.pm_metrics.update(pm_config)
471
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800472 def send_proxied_message(self, proxy_address, msg):
473 self.log.info('sending-proxied-message')
474 if isinstance(msg, FlowTable):
475 stub = ponsim_pb2.PonSimStub(self.get_channel())
476 self.log.info('pushing-onu-flow-table', port=msg.port)
477 res = stub.UpdateFlowTable(msg)
478 self.adapter_agent.receive_proxied_message(proxy_address, res)
479
480 def packet_out(self, egress_port, msg):
481 self.log.info('sending-packet-out', egress_port=egress_port,
482 msg=hexify(msg))
483 pkt = Ether(msg)
484 out_pkt = (
485 Ether(src=pkt.src, dst=pkt.dst) /
486 Dot1Q(vlan=4000) /
487 Dot1Q(vlan=egress_port, type=pkt.type) /
488 pkt.payload
489 )
490 self.io_port.send(str(out_pkt))
Khen Nursimulud068d812017-03-06 11:44:18 -0500491
492 @inlineCallbacks
493 def reboot(self):
494 self.log.info('rebooting', device_id=self.device_id)
495
496 # Update the operational status to ACTIVATING and connect status to
497 # UNREACHABLE
498 device = self.adapter_agent.get_device(self.device_id)
499 previous_oper_status = device.oper_status
500 previous_conn_status = device.connect_status
501 device.oper_status = OperStatus.ACTIVATING
502 device.connect_status = ConnectStatus.UNREACHABLE
503 self.adapter_agent.update_device(device)
504
khenaidoo71d0a6c2017-03-22 21:46:04 -0400505 # Update the child devices connect state to UNREACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400506 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400507 connect_status=ConnectStatus.UNREACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400508
Khen Nursimulud068d812017-03-06 11:44:18 -0500509 # Sleep 10 secs, simulating a reboot
Stephane Barbarie5253c652017-03-22 16:29:46 -0400510 # TODO: send alert and clear alert after the reboot
Khen Nursimulud068d812017-03-06 11:44:18 -0500511 yield asleep(10)
512
513 # Change the operational status back to its previous state. With a
514 # real OLT the operational state should be the state the device is
515 # after a reboot.
516 # Get the latest device reference
517 device = self.adapter_agent.get_device(self.device_id)
518 device.oper_status = previous_oper_status
519 device.connect_status = previous_conn_status
520 self.adapter_agent.update_device(device)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400521
522 # Update the child devices connect state to REACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400523 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400524 connect_status=ConnectStatus.REACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400525
Khen Nursimulud068d812017-03-06 11:44:18 -0500526 self.log.info('rebooted', device_id=self.device_id)
527
528 def disable(self):
529 self.log.info('disabling', device_id=self.device_id)
530
531 # Get the latest device reference
532 device = self.adapter_agent.get_device(self.device_id)
533
534 # Update the operational status to UNKNOWN
535 device.oper_status = OperStatus.UNKNOWN
536 device.connect_status = ConnectStatus.UNREACHABLE
537 self.adapter_agent.update_device(device)
538
539 # Remove the logical device
540 logical_device = self.adapter_agent.get_logical_device(
Stephane Barbarie5253c652017-03-22 16:29:46 -0400541 self.logical_device_id)
Khen Nursimulud068d812017-03-06 11:44:18 -0500542 self.adapter_agent.delete_logical_device(logical_device)
543
544 # Disable all child devices first
khenaidoo2d7af132017-03-23 15:45:51 -0400545 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400546 admin_state=AdminState.DISABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500547
Khen Nursimulud068d812017-03-06 11:44:18 -0500548 # Remove the peer references from this device
549 self.adapter_agent.delete_all_peer_references(self.device_id)
550
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400551 # Set all ports to disabled
552 self.adapter_agent.disable_all_ports(self.device_id)
553
Khen Nursimulud068d812017-03-06 11:44:18 -0500554 # close the frameio port
555 registry('frameio').close_port(self.io_port)
556
khenaidoo032d3302017-06-09 14:50:04 -0400557 # Update the logice device mapping
558 if self.logical_device_id in \
559 self.adapter.logical_device_id_to_root_device_id:
560 del self.adapter.logical_device_id_to_root_device_id[
561 self.logical_device_id]
562
Khen Nursimulud068d812017-03-06 11:44:18 -0500563 # TODO:
564 # 1) Remove all flows from the device
565 # 2) Remove the device from ponsim
566
567 self.log.info('disabled', device_id=device.id)
568
Khen Nursimulud068d812017-03-06 11:44:18 -0500569 def reenable(self):
570 self.log.info('re-enabling', device_id=self.device_id)
571
572 # Get the latest device reference
573 device = self.adapter_agent.get_device(self.device_id)
574
khenaidoo032d3302017-06-09 14:50:04 -0400575 # Set the ofp_port_no and nni_port in case we bypassed the reconcile
576 # process if the device was in DISABLED state on voltha restart
577 if not self.ofp_port_no and not self.nni_port:
578 stub = ponsim_pb2.PonSimStub(self.get_channel())
579 info = stub.GetDeviceInfo(Empty())
580 log.info('got-info', info=info)
581 self.ofp_port_no = info.nni_port
582 self.nni_port = self._get_nni_port()
583
Khen Nursimulud068d812017-03-06 11:44:18 -0500584 # Update the connect status to REACHABLE
585 device.connect_status = ConnectStatus.REACHABLE
586 self.adapter_agent.update_device(device)
587
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400588 # Set all ports to enabled
589 self.adapter_agent.enable_all_ports(self.device_id)
590
Khen Nursimulud068d812017-03-06 11:44:18 -0500591 ld = LogicalDevice(
592 # not setting id and datapth_id will let the adapter agent pick id
593 desc=ofp_desc(
594 mfr_desc='cord porject',
595 hw_desc='simulated pon',
596 sw_desc='simulated pon',
597 serial_num=uuid4().hex,
598 dp_desc='n/a'
599 ),
600 switch_features=ofp_switch_features(
601 n_buffers=256, # TODO fake for now
602 n_tables=2, # TODO ditto
603 capabilities=( # TODO and ditto
604 OFPC_FLOW_STATS
605 | OFPC_TABLE_STATS
606 | OFPC_PORT_STATS
607 | OFPC_GROUP_STATS
608 )
609 ),
610 root_device_id=device.id
611 )
612 ld_initialized = self.adapter_agent.create_logical_device(ld)
613 cap = OFPPF_1GB_FD | OFPPF_FIBER
614 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
615 id='nni',
616 ofp_port=ofp_port(
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400617 port_no=self.ofp_port_no,
Khen Nursimulud068d812017-03-06 11:44:18 -0500618 hw_addr=mac_str_to_tuple(
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400619 '00:00:00:00:00:%02x' % self.ofp_port_no),
Khen Nursimulud068d812017-03-06 11:44:18 -0500620 name='nni',
621 config=0,
622 state=OFPPS_LIVE,
623 curr=cap,
624 advertised=cap,
625 peer=cap,
626 curr_speed=OFPPF_1GB_FD,
627 max_speed=OFPPF_1GB_FD
628 ),
629 device_id=device.id,
630 device_port_no=self.nni_port.port_no,
631 root_port=True
632 ))
633
634 device = self.adapter_agent.get_device(device.id)
635 device.parent_id = ld_initialized.id
636 device.oper_status = OperStatus.ACTIVE
637 self.adapter_agent.update_device(device)
638 self.logical_device_id = ld_initialized.id
639
640 # Reenable all child devices
khenaidoo2d7af132017-03-23 15:45:51 -0400641 self.adapter_agent.update_child_devices_state(device.id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400642 admin_state=AdminState.ENABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500643
Khen Nursimulud068d812017-03-06 11:44:18 -0500644 # finally, open the frameio port to receive in-band packet_in messages
Khen Nursimulud068d812017-03-06 11:44:18 -0500645 self.io_port = registry('frameio').open_port(
646 self.interface, self.rcv_io, is_inband_frame)
Khen Nursimulud068d812017-03-06 11:44:18 -0500647
648 self.log.info('re-enabled', device_id=device.id)
649
Khen Nursimulud068d812017-03-06 11:44:18 -0500650 def delete(self):
651 self.log.info('deleting', device_id=self.device_id)
652
653 # Remove all child devices
654 self.adapter_agent.delete_all_child_devices(self.device_id)
655
656 # TODO:
657 # 1) Remove all flows from the device
658 # 2) Remove the device from ponsim
659
660 self.log.info('deleted', device_id=self.device_id)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400661
662 def start_kpi_collection(self, device_id):
663
664 def _collect(device_id, prefix):
665
666 try:
667 # Step 1: gather metrics from device
668 port_metrics = \
Stephane Barbarie5253c652017-03-22 16:29:46 -0400669 self.pm_metrics.collect_port_metrics(self.get_channel())
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400670
671 # Step 2: prepare the KpiEvent for submission
672 # we can time-stamp them here (or could use time derived from OLT
673 ts = arrow.utcnow().timestamp
674 kpi_event = KpiEvent(
675 type=KpiEventType.slice,
676 ts=ts,
677 prefixes={
678 # OLT NNI port
679 prefix + '.nni': MetricValuePairs(
680 metrics=port_metrics['nni']),
681 # OLT PON port
682 prefix + '.pon': MetricValuePairs(
683 metrics=port_metrics['pon'])
684 }
685 )
686
687 # Step 3: submit
688 self.adapter_agent.submit_kpis(kpi_event)
689
690 except Exception as e:
691 log.exception('failed-to-submit-kpis', e=e)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400692
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400693 self.pm_metrics.start_collector(_collect)