blob: 58cba69eef72b19bc36bf49e39c9bbba1b9eb1d9 [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 -080030from zope.interface import implementer
31
32from common.frameio.frameio import BpfProgramFilter, hexify
Khen Nursimulud068d812017-03-06 11:44:18 -050033from common.utils.asleep import asleep
Sergio Slobodrian98eff412017-03-15 14:46:30 -040034from twisted.internet.task import LoopingCall
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080035from voltha.adapters.interface import IAdapterInterface
36from voltha.core.logical_device_agent import mac_str_to_tuple
37from voltha.protos import third_party
38from voltha.protos import ponsim_pb2
39from voltha.protos.adapter_pb2 import Adapter
40from voltha.protos.adapter_pb2 import AdapterConfig
41from voltha.protos.common_pb2 import LogLevel, OperStatus, ConnectStatus, \
42 AdminState
Sergio Slobodrian98eff412017-03-15 14:46:30 -040043from voltha.protos.device_pb2 import DeviceType, DeviceTypes, Port, Device, \
Stephane Barbarie5253c652017-03-22 16:29:46 -040044 PmConfig, PmConfigs
Sergio Slobodrian98eff412017-03-15 14:46:30 -040045from voltha.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080046from voltha.protos.health_pb2 import HealthStatus
47from google.protobuf.empty_pb2 import Empty
48
49from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice
Stephane Barbarie5253c652017-03-22 16:29:46 -040050from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
51 OFPPF_1GB_FD, \
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080052 OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS, \
53 ofp_switch_features, ofp_desc
54from voltha.protos.openflow_13_pb2 import ofp_port
55from voltha.protos.ponsim_pb2 import FlowTable
56from voltha.registry import registry
57
58_ = third_party
59log = structlog.get_logger()
60
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080061PACKET_IN_VLAN = 4000
62is_inband_frame = BpfProgramFilter('(ether[14:2] & 0xfff) = 0x{:03x}'.format(
63 PACKET_IN_VLAN))
64
Stephane Barbarie5253c652017-03-22 16:29:46 -040065
Sergio Slobodrian98eff412017-03-15 14:46:30 -040066class AdapterPmMetrics:
Stephane Barbarie5253c652017-03-22 16:29:46 -040067 def __init__(self, device):
Sergio Slobodrianec6e3912017-04-02 11:46:55 -040068 self.pm_names = {'tx_64_pkts', 'tx_65_127_pkts', 'tx_128_255_pkts',
69 'tx_256_511_pkts', 'tx_512_1023_pkts',
70 'tx_1024_1518_pkts', 'tx_1519_9k_pkts',
71 'rx_64_pkts', 'rx_65_127_pkts',
72 'rx_128_255_pkts', 'rx_256_511_pkts',
73 'rx_512_1023_pkts', 'rx_1024_1518_pkts',
74 'rx_1519_9k_pkts'}
Sergio Slobodrian98eff412017-03-15 14:46:30 -040075 self.device = device
76 self.id = device.id
77 self.name = 'ponsim_olt'
Stephane Barbarie5253c652017-03-22 16:29:46 -040078 # self.id = "abc"
Sergio Slobodrian98eff412017-03-15 14:46:30 -040079 self.default_freq = 150
80 self.grouped = False
81 self.freq_override = False
82 self.pon_metrics_config = dict()
83 self.nni_metrics_config = dict()
84 self.lc = None
85 for m in self.pm_names:
86 self.pon_metrics_config[m] = PmConfig(name=m,
87 type=PmConfig.COUNTER,
88 enabled=True)
89 self.nni_metrics_config[m] = PmConfig(name=m,
90 type=PmConfig.COUNTER,
91 enabled=True)
92
93 def update(self, pm_config):
94 if self.default_freq != pm_config.default_freq:
95 # Update the callback to the new frequency.
96 self.default_freq = pm_config.default_freq
97 self.lc.stop()
Stephane Barbarie5253c652017-03-22 16:29:46 -040098 self.lc.start(interval=self.default_freq / 10)
Sergio Slobodrian98eff412017-03-15 14:46:30 -040099 for m in pm_config.metrics:
100 self.pon_metrics_config[m.name].enabled = m.enabled
101 self.nni_metrics_config[m.name].enabled = m.enabled
102
103 def make_proto(self):
104 pm_config = PmConfigs(
105 id=self.id,
106 default_freq=self.default_freq,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400107 grouped=False,
108 freq_override=False)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400109 for m in sorted(self.pon_metrics_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400110 pm = self.pon_metrics_config[m] # Either will do they're the same
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400111 pm_config.metrics.extend([PmConfig(name=pm.name,
112 type=pm.type,
113 enabled=pm.enabled)])
114 return pm_config
115
116 def collect_port_metrics(self, channel):
117 rtrn_port_metrics = dict()
118 stub = ponsim_pb2.PonSimStub(channel)
119 stats = stub.GetStats(Empty())
120 rtrn_port_metrics['pon'] = self.extract_pon_metrics(stats)
121 rtrn_port_metrics['nni'] = self.extract_nni_metrics(stats)
122 return rtrn_port_metrics
123
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400124 def extract_pon_metrics(self, stats):
125 rtrn_pon_metrics = dict()
126 for m in stats.metrics:
127 if m.port_name == "pon":
128 for p in m.packets:
129 if self.pon_metrics_config[p.name].enabled:
130 rtrn_pon_metrics[p.name] = p.value
131 return rtrn_pon_metrics
132
133 def extract_nni_metrics(self, stats):
134 rtrn_pon_metrics = dict()
135 for m in stats.metrics:
136 if m.port_name == "nni":
137 for p in m.packets:
138 if self.pon_metrics_config[p.name].enabled:
139 rtrn_pon_metrics[p.name] = p.value
140 return rtrn_pon_metrics
141
142 def start_collector(self, callback):
143 log.info("starting-pm-collection", device_name=self.name,
144 device_id=self.device.id)
145 prefix = 'voltha.{}.{}'.format(self.name, self.device.id)
146 self.lc = LoopingCall(callback, self.device.id, prefix)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400147 self.lc.start(interval=self.default_freq / 10)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400148
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800149
Stephane Barbarie4475a252017-03-31 13:49:20 -0400150class AdapterAlarms:
151 def __init__(self, adapter, device):
152 self.adapter = adapter
153 self.device = device
154 self.lc = None
155
156 def send_alarm(self, context_data, alarm_data):
157 try:
158 current_context = {}
159 for key, value in context_data.__dict__.items():
160 current_context[key] = str(value)
161
162 alarm_event = self.adapter.adapter_agent.create_alarm(
163 resource_id=self.device.id,
164 description="{}.{} - {}".format(self.adapter.name, self.device.id,
165 alarm_data['description']) if 'description' in alarm_data else None,
166 type=alarm_data['type'] if 'type' in alarm_data else None,
167 category=alarm_data['category'] if 'category' in alarm_data else None,
168 severity=alarm_data['severity'] if 'severity' in alarm_data else None,
169 state=alarm_data['state'] if 'state' in alarm_data else None,
170 raised_ts=alarm_data['ts'] if 'ts' in alarm_data else 0,
171 context=current_context
172 )
173
Stephane Barbarieee409292017-04-24 10:30:20 -0400174 self.adapter.adapter_agent.submit_alarm(self.device.id, alarm_event)
Stephane Barbarie4475a252017-03-31 13:49:20 -0400175
176 except Exception as e:
177 log.exception('failed-to-send-alarm', e=e)
178
179
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800180@implementer(IAdapterInterface)
181class PonSimOltAdapter(object):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800182 name = 'ponsim_olt'
183
184 supported_device_types = [
185 DeviceType(
186 id=name,
187 adapter=name,
188 accepts_bulk_flow_update=True
189 )
190 ]
191
192 def __init__(self, adapter_agent, config):
193 self.adapter_agent = adapter_agent
194 self.config = config
195 self.descriptor = Adapter(
196 id=self.name,
197 vendor='Voltha project',
198 version='0.4',
199 config=AdapterConfig(log_level=LogLevel.INFO)
200 )
201 self.devices_handlers = dict() # device_id -> PonSimOltHandler()
202 self.logical_device_id_to_root_device_id = dict()
203
204 def start(self):
205 log.debug('starting')
206 log.info('started')
207
208 def stop(self):
Khen Nursimulud068d812017-03-06 11:44:18 -0500209 """
210 This method is called when this device instance is no longer
211 required, which means there is a request to remove this device.
212 :return:
213 """
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800214 log.debug('stopping')
215 log.info('stopped')
216
217 def adapter_descriptor(self):
218 return self.descriptor
219
220 def device_types(self):
221 return DeviceTypes(items=self.supported_device_types)
222
223 def health(self):
224 return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
225
226 def change_master_state(self, master):
227 raise NotImplementedError()
228
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400229 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400230 log.info("adapter-update-pm-config", device=device,
231 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400232 handler = self.devices_handlers[device.id]
233 handler.update_pm_config(device, pm_config)
Sergio Slobodrianec864c62017-03-09 11:41:43 -0500234
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800235 def adopt_device(self, device):
236 self.devices_handlers[device.id] = PonSimOltHandler(self, device.id)
237 reactor.callLater(0, self.devices_handlers[device.id].activate, device)
238 return device
239
240 def abandon_device(self, device):
241 raise NotImplementedError()
242
Khen Nursimulud068d812017-03-06 11:44:18 -0500243 def disable_device(self, device):
244 log.info('disable-device', device_id=device.id)
245 reactor.callLater(0, self.devices_handlers[device.id].disable)
246 return device
247
248 def reenable_device(self, device):
249 log.info('reenable-device', device_id=device.id)
250 reactor.callLater(0, self.devices_handlers[device.id].reenable)
251 return device
252
253 def reboot_device(self, device):
254 log.info('reboot-device', device_id=device.id)
255 reactor.callLater(0, self.devices_handlers[device.id].reboot)
256 return device
257
258 def delete_device(self, device):
259 log.info('delete-device', device_id=device.id)
260 reactor.callLater(0, self.devices_handlers[device.id].delete)
261 return device
262
263 def get_device_details(self, device):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800264 raise NotImplementedError()
265
266 def update_flows_bulk(self, device, flows, groups):
267 log.info('bulk-flow-update', device_id=device.id,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400268 flows=flows, groups=groups)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800269 assert len(groups.items) == 0
270 handler = self.devices_handlers[device.id]
271 return handler.update_flow_table(flows.items)
272
273 def update_flows_incrementally(self, device, flow_changes, group_changes):
274 raise NotImplementedError()
275
276 def send_proxied_message(self, proxy_address, msg):
277 log.info('send-proxied-message', proxy_address=proxy_address, msg=msg)
278 handler = self.devices_handlers[proxy_address.device_id]
279 handler.send_proxied_message(proxy_address, msg)
280
281 def receive_proxied_message(self, proxy_address, msg):
282 raise NotImplementedError()
283
284 def receive_packet_out(self, logical_device_id, egress_port_no, msg):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800285 def ldi_to_di(ldi):
286 di = self.logical_device_id_to_root_device_id.get(ldi)
287 if di is None:
288 logical_device = self.adapter_agent.get_logical_device(ldi)
289 di = logical_device.root_device_id
290 self.logical_device_id_to_root_device_id[ldi] = di
291 return di
292
293 device_id = ldi_to_di(logical_device_id)
294 handler = self.devices_handlers[device_id]
295 handler.packet_out(egress_port_no, msg)
296
Peter Shafikb2ff5a62017-05-02 15:54:39 -0400297 def receive_inter_adapter_message(self, msg):
298 raise NotImplementedError()
299
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800300
301class PonSimOltHandler(object):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800302 def __init__(self, adapter, device_id):
303 self.adapter = adapter
304 self.adapter_agent = adapter.adapter_agent
305 self.device_id = device_id
306 self.log = structlog.get_logger(device_id=device_id)
307 self.channel = None
308 self.io_port = None
309 self.logical_device_id = None
Khen Nursimulud068d812017-03-06 11:44:18 -0500310 self.nni_port = None
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400311 self.ofp_port_no = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800312 self.interface = registry('main').get_args().interface
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400313 self.pm_metrics = None
Stephane Barbarie4475a252017-03-31 13:49:20 -0400314 self.alarms = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800315
316 def __del__(self):
317 if self.io_port is not None:
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800318 registry('frameio').close_port(self.io_port)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800319
320 def get_channel(self):
321 if self.channel is None:
322 device = self.adapter_agent.get_device(self.device_id)
323 self.channel = grpc.insecure_channel(device.host_and_port)
324 return self.channel
325
326 def activate(self, device):
327 self.log.info('activating')
328
329 if not device.host_and_port:
330 device.oper_status = OperStatus.FAILED
331 device.reason = 'No host_and_port field provided'
332 self.adapter_agent.update_device(device)
333 return
334
335 stub = ponsim_pb2.PonSimStub(self.get_channel())
336 info = stub.GetDeviceInfo(Empty())
337 log.info('got-info', info=info)
338
339 device.root = True
340 device.vendor = 'ponsim'
Stephane Barbarie5253c652017-03-22 16:29:46 -0400341 device.model = 'n/a'
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800342 device.serial_number = device.host_and_port
343 device.connect_status = ConnectStatus.REACHABLE
344 self.adapter_agent.update_device(device)
345
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400346 # Now set the initial PM configuration for this device
Stephane Barbarie5253c652017-03-22 16:29:46 -0400347 self.pm_metrics = AdapterPmMetrics(device)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400348 pm_config = self.pm_metrics.make_proto()
349 log.info("initial-pm-config", pm_config=pm_config)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400350 self.adapter_agent.update_device_pm_config(pm_config, init=True)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400351
Stephane Barbarie4475a252017-03-31 13:49:20 -0400352 # Setup alarm handler
353 self.alarms = AdapterAlarms(self.adapter, device)
354
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800355 nni_port = Port(
356 port_no=2,
357 label='NNI facing Ethernet port',
358 type=Port.ETHERNET_NNI,
359 admin_state=AdminState.ENABLED,
360 oper_status=OperStatus.ACTIVE
361 )
Khen Nursimulud068d812017-03-06 11:44:18 -0500362 self.nni_port = nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800363 self.adapter_agent.add_port(device.id, nni_port)
364 self.adapter_agent.add_port(device.id, Port(
365 port_no=1,
366 label='PON port',
367 type=Port.PON_OLT,
368 admin_state=AdminState.ENABLED,
369 oper_status=OperStatus.ACTIVE
370 ))
371
372 ld = LogicalDevice(
373 # not setting id and datapth_id will let the adapter agent pick id
374 desc=ofp_desc(
375 mfr_desc='cord porject',
376 hw_desc='simualted pon',
377 sw_desc='simualted pon',
378 serial_num=uuid4().hex,
379 dp_desc='n/a'
380 ),
381 switch_features=ofp_switch_features(
382 n_buffers=256, # TODO fake for now
383 n_tables=2, # TODO ditto
384 capabilities=( # TODO and ditto
385 OFPC_FLOW_STATS
386 | OFPC_TABLE_STATS
387 | OFPC_PORT_STATS
388 | OFPC_GROUP_STATS
389 )
390 ),
391 root_device_id=device.id
392 )
393 ld_initialized = self.adapter_agent.create_logical_device(ld)
394 cap = OFPPF_1GB_FD | OFPPF_FIBER
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400395 self.ofp_port_no = info.nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800396 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
397 id='nni',
398 ofp_port=ofp_port(
399 port_no=info.nni_port,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400400 hw_addr=mac_str_to_tuple(
401 '00:00:00:00:00:%02x' % info.nni_port),
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800402 name='nni',
403 config=0,
404 state=OFPPS_LIVE,
405 curr=cap,
406 advertised=cap,
407 peer=cap,
408 curr_speed=OFPPF_1GB_FD,
409 max_speed=OFPPF_1GB_FD
410 ),
411 device_id=device.id,
412 device_port_no=nni_port.port_no,
413 root_port=True
414 ))
415
416 device = self.adapter_agent.get_device(device.id)
417 device.parent_id = ld_initialized.id
418 device.oper_status = OperStatus.ACTIVE
419 self.adapter_agent.update_device(device)
420 self.logical_device_id = ld_initialized.id
421
422 # register ONUS per uni port
423 for port_no in info.uni_ports:
424 vlan_id = port_no
425 self.adapter_agent.child_device_detected(
426 parent_device_id=device.id,
427 parent_port_no=1,
428 child_device_type='ponsim_onu',
429 proxy_address=Device.ProxyAddress(
430 device_id=device.id,
431 channel_id=vlan_id
432 ),
433 vlan=vlan_id
434 )
435
436 # finally, open the frameio port to receive in-band packet_in messages
437 self.log.info('registering-frameio')
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800438 self.io_port = registry('frameio').open_port(
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800439 self.interface, self.rcv_io, is_inband_frame)
440 self.log.info('registered-frameio')
441
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400442 # Start collecting stats from the device after a brief pause
443 self.start_kpi_collection(device.id)
444
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800445 def rcv_io(self, port, frame):
446 self.log.info('reveived', iface_name=port.iface_name,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400447 frame_len=len(frame))
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800448 pkt = Ether(frame)
449 if pkt.haslayer(Dot1Q):
450 outer_shim = pkt.getlayer(Dot1Q)
451 if isinstance(outer_shim.payload, Dot1Q):
452 inner_shim = outer_shim.payload
453 cvid = inner_shim.vlan
454 logical_port = cvid
455 popped_frame = (
456 Ether(src=pkt.src, dst=pkt.dst, type=inner_shim.type) /
457 inner_shim.payload
458 )
459 kw = dict(
460 logical_device_id=self.logical_device_id,
461 logical_port_no=logical_port,
462 )
463 self.log.info('sending-packet-in', **kw)
464 self.adapter_agent.send_packet_in(
465 packet=str(popped_frame), **kw)
Stephane Barbarie4475a252017-03-31 13:49:20 -0400466 elif pkt.haslayer(Raw):
467 raw_data = json.loads(pkt.getlayer(Raw).load)
468 self.alarms.send_alarm(self, raw_data)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800469
470 def update_flow_table(self, flows):
471 stub = ponsim_pb2.PonSimStub(self.get_channel())
472 self.log.info('pushing-olt-flow-table')
473 stub.UpdateFlowTable(FlowTable(
474 port=0,
475 flows=flows
476 ))
477 self.log.info('success')
478
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400479 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400480 log.info("handler-update-pm-config", device=device,
481 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400482 self.pm_metrics.update(pm_config)
483
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800484 def send_proxied_message(self, proxy_address, msg):
485 self.log.info('sending-proxied-message')
486 if isinstance(msg, FlowTable):
487 stub = ponsim_pb2.PonSimStub(self.get_channel())
488 self.log.info('pushing-onu-flow-table', port=msg.port)
489 res = stub.UpdateFlowTable(msg)
490 self.adapter_agent.receive_proxied_message(proxy_address, res)
491
492 def packet_out(self, egress_port, msg):
493 self.log.info('sending-packet-out', egress_port=egress_port,
494 msg=hexify(msg))
495 pkt = Ether(msg)
496 out_pkt = (
497 Ether(src=pkt.src, dst=pkt.dst) /
498 Dot1Q(vlan=4000) /
499 Dot1Q(vlan=egress_port, type=pkt.type) /
500 pkt.payload
501 )
502 self.io_port.send(str(out_pkt))
Khen Nursimulud068d812017-03-06 11:44:18 -0500503
504 @inlineCallbacks
505 def reboot(self):
506 self.log.info('rebooting', device_id=self.device_id)
507
508 # Update the operational status to ACTIVATING and connect status to
509 # UNREACHABLE
510 device = self.adapter_agent.get_device(self.device_id)
511 previous_oper_status = device.oper_status
512 previous_conn_status = device.connect_status
513 device.oper_status = OperStatus.ACTIVATING
514 device.connect_status = ConnectStatus.UNREACHABLE
515 self.adapter_agent.update_device(device)
516
khenaidoo71d0a6c2017-03-22 21:46:04 -0400517 # Update the child devices connect state to UNREACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400518 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400519 connect_status=ConnectStatus.UNREACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400520
Khen Nursimulud068d812017-03-06 11:44:18 -0500521 # Sleep 10 secs, simulating a reboot
Stephane Barbarie5253c652017-03-22 16:29:46 -0400522 # TODO: send alert and clear alert after the reboot
Khen Nursimulud068d812017-03-06 11:44:18 -0500523 yield asleep(10)
524
525 # Change the operational status back to its previous state. With a
526 # real OLT the operational state should be the state the device is
527 # after a reboot.
528 # Get the latest device reference
529 device = self.adapter_agent.get_device(self.device_id)
530 device.oper_status = previous_oper_status
531 device.connect_status = previous_conn_status
532 self.adapter_agent.update_device(device)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400533
534 # Update the child devices connect state to REACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400535 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400536 connect_status=ConnectStatus.REACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400537
Khen Nursimulud068d812017-03-06 11:44:18 -0500538 self.log.info('rebooted', device_id=self.device_id)
539
540 def disable(self):
541 self.log.info('disabling', device_id=self.device_id)
542
543 # Get the latest device reference
544 device = self.adapter_agent.get_device(self.device_id)
545
546 # Update the operational status to UNKNOWN
547 device.oper_status = OperStatus.UNKNOWN
548 device.connect_status = ConnectStatus.UNREACHABLE
549 self.adapter_agent.update_device(device)
550
551 # Remove the logical device
552 logical_device = self.adapter_agent.get_logical_device(
Stephane Barbarie5253c652017-03-22 16:29:46 -0400553 self.logical_device_id)
Khen Nursimulud068d812017-03-06 11:44:18 -0500554 self.adapter_agent.delete_logical_device(logical_device)
555
556 # Disable all child devices first
khenaidoo2d7af132017-03-23 15:45:51 -0400557 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400558 admin_state=AdminState.DISABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500559
Khen Nursimulud068d812017-03-06 11:44:18 -0500560 # Remove the peer references from this device
561 self.adapter_agent.delete_all_peer_references(self.device_id)
562
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400563 # Set all ports to disabled
564 self.adapter_agent.disable_all_ports(self.device_id)
565
Khen Nursimulud068d812017-03-06 11:44:18 -0500566 # close the frameio port
567 registry('frameio').close_port(self.io_port)
568
569 # TODO:
570 # 1) Remove all flows from the device
571 # 2) Remove the device from ponsim
572
573 self.log.info('disabled', device_id=device.id)
574
Khen Nursimulud068d812017-03-06 11:44:18 -0500575 def reenable(self):
576 self.log.info('re-enabling', device_id=self.device_id)
577
578 # Get the latest device reference
579 device = self.adapter_agent.get_device(self.device_id)
580
581 # Update the connect status to REACHABLE
582 device.connect_status = ConnectStatus.REACHABLE
583 self.adapter_agent.update_device(device)
584
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400585 # Set all ports to enabled
586 self.adapter_agent.enable_all_ports(self.device_id)
587
Khen Nursimulud068d812017-03-06 11:44:18 -0500588 ld = LogicalDevice(
589 # not setting id and datapth_id will let the adapter agent pick id
590 desc=ofp_desc(
591 mfr_desc='cord porject',
592 hw_desc='simulated pon',
593 sw_desc='simulated pon',
594 serial_num=uuid4().hex,
595 dp_desc='n/a'
596 ),
597 switch_features=ofp_switch_features(
598 n_buffers=256, # TODO fake for now
599 n_tables=2, # TODO ditto
600 capabilities=( # TODO and ditto
601 OFPC_FLOW_STATS
602 | OFPC_TABLE_STATS
603 | OFPC_PORT_STATS
604 | OFPC_GROUP_STATS
605 )
606 ),
607 root_device_id=device.id
608 )
609 ld_initialized = self.adapter_agent.create_logical_device(ld)
610 cap = OFPPF_1GB_FD | OFPPF_FIBER
611 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
612 id='nni',
613 ofp_port=ofp_port(
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400614 port_no=self.ofp_port_no,
Khen Nursimulud068d812017-03-06 11:44:18 -0500615 hw_addr=mac_str_to_tuple(
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400616 '00:00:00:00:00:%02x' % self.ofp_port_no),
Khen Nursimulud068d812017-03-06 11:44:18 -0500617 name='nni',
618 config=0,
619 state=OFPPS_LIVE,
620 curr=cap,
621 advertised=cap,
622 peer=cap,
623 curr_speed=OFPPF_1GB_FD,
624 max_speed=OFPPF_1GB_FD
625 ),
626 device_id=device.id,
627 device_port_no=self.nni_port.port_no,
628 root_port=True
629 ))
630
631 device = self.adapter_agent.get_device(device.id)
632 device.parent_id = ld_initialized.id
633 device.oper_status = OperStatus.ACTIVE
634 self.adapter_agent.update_device(device)
635 self.logical_device_id = ld_initialized.id
636
637 # Reenable all child devices
khenaidoo2d7af132017-03-23 15:45:51 -0400638 self.adapter_agent.update_child_devices_state(device.id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400639 admin_state=AdminState.ENABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500640
Khen Nursimulud068d812017-03-06 11:44:18 -0500641 # finally, open the frameio port to receive in-band packet_in messages
642 self.log.info('registering-frameio')
643 self.io_port = registry('frameio').open_port(
644 self.interface, self.rcv_io, is_inband_frame)
645 self.log.info('registered-frameio')
646
647 self.log.info('re-enabled', device_id=device.id)
648
Khen Nursimulud068d812017-03-06 11:44:18 -0500649 def delete(self):
650 self.log.info('deleting', device_id=self.device_id)
651
652 # Remove all child devices
653 self.adapter_agent.delete_all_child_devices(self.device_id)
654
655 # TODO:
656 # 1) Remove all flows from the device
657 # 2) Remove the device from ponsim
658
659 self.log.info('deleted', device_id=self.device_id)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400660
661 def start_kpi_collection(self, device_id):
662
663 def _collect(device_id, prefix):
664
665 try:
666 # Step 1: gather metrics from device
667 port_metrics = \
Stephane Barbarie5253c652017-03-22 16:29:46 -0400668 self.pm_metrics.collect_port_metrics(self.get_channel())
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400669
670 # Step 2: prepare the KpiEvent for submission
671 # we can time-stamp them here (or could use time derived from OLT
672 ts = arrow.utcnow().timestamp
673 kpi_event = KpiEvent(
674 type=KpiEventType.slice,
675 ts=ts,
676 prefixes={
677 # OLT NNI port
678 prefix + '.nni': MetricValuePairs(
679 metrics=port_metrics['nni']),
680 # OLT PON port
681 prefix + '.pon': MetricValuePairs(
682 metrics=port_metrics['pon'])
683 }
684 )
685
686 # Step 3: submit
687 self.adapter_agent.submit_kpis(kpi_event)
688
689 except Exception as e:
690 log.exception('failed-to-submit-kpis', e=e)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400691
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400692 self.pm_metrics.start_collector(_collect)