blob: 138e248bbde5a63291abd3414ca2b986bf04b2dc [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 Barbarie4db8ca22017-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 Shafik9107f2e2017-05-02 15:54:39 -0400297 def receive_inter_adapter_message(self, msg):
298 raise NotImplementedError()
299
Stephane Barbarie980a0912017-05-11 11:27:06 -0400300 def suppress_alarm(self, filter):
301 raise NotImplementedError()
302
303 def unsuppress_alarm(self, filter):
304 raise NotImplementedError()
305
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800306
307class PonSimOltHandler(object):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800308 def __init__(self, adapter, device_id):
309 self.adapter = adapter
310 self.adapter_agent = adapter.adapter_agent
311 self.device_id = device_id
312 self.log = structlog.get_logger(device_id=device_id)
313 self.channel = None
314 self.io_port = None
315 self.logical_device_id = None
Khen Nursimulud068d812017-03-06 11:44:18 -0500316 self.nni_port = None
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400317 self.ofp_port_no = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800318 self.interface = registry('main').get_args().interface
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400319 self.pm_metrics = None
Stephane Barbarie4475a252017-03-31 13:49:20 -0400320 self.alarms = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800321
322 def __del__(self):
323 if self.io_port is not None:
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800324 registry('frameio').close_port(self.io_port)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800325
326 def get_channel(self):
327 if self.channel is None:
328 device = self.adapter_agent.get_device(self.device_id)
329 self.channel = grpc.insecure_channel(device.host_and_port)
330 return self.channel
331
332 def activate(self, device):
333 self.log.info('activating')
334
335 if not device.host_and_port:
336 device.oper_status = OperStatus.FAILED
337 device.reason = 'No host_and_port field provided'
338 self.adapter_agent.update_device(device)
339 return
340
341 stub = ponsim_pb2.PonSimStub(self.get_channel())
342 info = stub.GetDeviceInfo(Empty())
343 log.info('got-info', info=info)
344
345 device.root = True
346 device.vendor = 'ponsim'
Stephane Barbarie5253c652017-03-22 16:29:46 -0400347 device.model = 'n/a'
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800348 device.serial_number = device.host_and_port
349 device.connect_status = ConnectStatus.REACHABLE
350 self.adapter_agent.update_device(device)
351
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400352 # Now set the initial PM configuration for this device
Stephane Barbarie5253c652017-03-22 16:29:46 -0400353 self.pm_metrics = AdapterPmMetrics(device)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400354 pm_config = self.pm_metrics.make_proto()
355 log.info("initial-pm-config", pm_config=pm_config)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400356 self.adapter_agent.update_device_pm_config(pm_config, init=True)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400357
Stephane Barbarie4475a252017-03-31 13:49:20 -0400358 # Setup alarm handler
359 self.alarms = AdapterAlarms(self.adapter, device)
360
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800361 nni_port = Port(
362 port_no=2,
363 label='NNI facing Ethernet port',
364 type=Port.ETHERNET_NNI,
365 admin_state=AdminState.ENABLED,
366 oper_status=OperStatus.ACTIVE
367 )
Khen Nursimulud068d812017-03-06 11:44:18 -0500368 self.nni_port = nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800369 self.adapter_agent.add_port(device.id, nni_port)
370 self.adapter_agent.add_port(device.id, Port(
371 port_no=1,
372 label='PON port',
373 type=Port.PON_OLT,
374 admin_state=AdminState.ENABLED,
375 oper_status=OperStatus.ACTIVE
376 ))
377
378 ld = LogicalDevice(
379 # not setting id and datapth_id will let the adapter agent pick id
380 desc=ofp_desc(
381 mfr_desc='cord porject',
382 hw_desc='simualted pon',
383 sw_desc='simualted pon',
384 serial_num=uuid4().hex,
385 dp_desc='n/a'
386 ),
387 switch_features=ofp_switch_features(
388 n_buffers=256, # TODO fake for now
389 n_tables=2, # TODO ditto
390 capabilities=( # TODO and ditto
391 OFPC_FLOW_STATS
392 | OFPC_TABLE_STATS
393 | OFPC_PORT_STATS
394 | OFPC_GROUP_STATS
395 )
396 ),
397 root_device_id=device.id
398 )
399 ld_initialized = self.adapter_agent.create_logical_device(ld)
400 cap = OFPPF_1GB_FD | OFPPF_FIBER
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400401 self.ofp_port_no = info.nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800402 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
403 id='nni',
404 ofp_port=ofp_port(
405 port_no=info.nni_port,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400406 hw_addr=mac_str_to_tuple(
407 '00:00:00:00:00:%02x' % info.nni_port),
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800408 name='nni',
409 config=0,
410 state=OFPPS_LIVE,
411 curr=cap,
412 advertised=cap,
413 peer=cap,
414 curr_speed=OFPPF_1GB_FD,
415 max_speed=OFPPF_1GB_FD
416 ),
417 device_id=device.id,
418 device_port_no=nni_port.port_no,
419 root_port=True
420 ))
421
422 device = self.adapter_agent.get_device(device.id)
423 device.parent_id = ld_initialized.id
424 device.oper_status = OperStatus.ACTIVE
425 self.adapter_agent.update_device(device)
426 self.logical_device_id = ld_initialized.id
427
428 # register ONUS per uni port
429 for port_no in info.uni_ports:
430 vlan_id = port_no
431 self.adapter_agent.child_device_detected(
432 parent_device_id=device.id,
433 parent_port_no=1,
434 child_device_type='ponsim_onu',
435 proxy_address=Device.ProxyAddress(
436 device_id=device.id,
437 channel_id=vlan_id
438 ),
439 vlan=vlan_id
440 )
441
442 # finally, open the frameio port to receive in-band packet_in messages
443 self.log.info('registering-frameio')
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800444 self.io_port = registry('frameio').open_port(
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800445 self.interface, self.rcv_io, is_inband_frame)
446 self.log.info('registered-frameio')
447
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400448 # Start collecting stats from the device after a brief pause
449 self.start_kpi_collection(device.id)
450
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800451 def rcv_io(self, port, frame):
452 self.log.info('reveived', iface_name=port.iface_name,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400453 frame_len=len(frame))
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800454 pkt = Ether(frame)
455 if pkt.haslayer(Dot1Q):
456 outer_shim = pkt.getlayer(Dot1Q)
457 if isinstance(outer_shim.payload, Dot1Q):
458 inner_shim = outer_shim.payload
459 cvid = inner_shim.vlan
460 logical_port = cvid
461 popped_frame = (
462 Ether(src=pkt.src, dst=pkt.dst, type=inner_shim.type) /
463 inner_shim.payload
464 )
465 kw = dict(
466 logical_device_id=self.logical_device_id,
467 logical_port_no=logical_port,
468 )
469 self.log.info('sending-packet-in', **kw)
470 self.adapter_agent.send_packet_in(
471 packet=str(popped_frame), **kw)
Stephane Barbarie4475a252017-03-31 13:49:20 -0400472 elif pkt.haslayer(Raw):
473 raw_data = json.loads(pkt.getlayer(Raw).load)
474 self.alarms.send_alarm(self, raw_data)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800475
476 def update_flow_table(self, flows):
477 stub = ponsim_pb2.PonSimStub(self.get_channel())
478 self.log.info('pushing-olt-flow-table')
479 stub.UpdateFlowTable(FlowTable(
480 port=0,
481 flows=flows
482 ))
483 self.log.info('success')
484
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400485 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400486 log.info("handler-update-pm-config", device=device,
487 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400488 self.pm_metrics.update(pm_config)
489
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800490 def send_proxied_message(self, proxy_address, msg):
491 self.log.info('sending-proxied-message')
492 if isinstance(msg, FlowTable):
493 stub = ponsim_pb2.PonSimStub(self.get_channel())
494 self.log.info('pushing-onu-flow-table', port=msg.port)
495 res = stub.UpdateFlowTable(msg)
496 self.adapter_agent.receive_proxied_message(proxy_address, res)
497
498 def packet_out(self, egress_port, msg):
499 self.log.info('sending-packet-out', egress_port=egress_port,
500 msg=hexify(msg))
501 pkt = Ether(msg)
502 out_pkt = (
503 Ether(src=pkt.src, dst=pkt.dst) /
504 Dot1Q(vlan=4000) /
505 Dot1Q(vlan=egress_port, type=pkt.type) /
506 pkt.payload
507 )
508 self.io_port.send(str(out_pkt))
Khen Nursimulud068d812017-03-06 11:44:18 -0500509
510 @inlineCallbacks
511 def reboot(self):
512 self.log.info('rebooting', device_id=self.device_id)
513
514 # Update the operational status to ACTIVATING and connect status to
515 # UNREACHABLE
516 device = self.adapter_agent.get_device(self.device_id)
517 previous_oper_status = device.oper_status
518 previous_conn_status = device.connect_status
519 device.oper_status = OperStatus.ACTIVATING
520 device.connect_status = ConnectStatus.UNREACHABLE
521 self.adapter_agent.update_device(device)
522
khenaidoo71d0a6c2017-03-22 21:46:04 -0400523 # Update the child devices connect state to UNREACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400524 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400525 connect_status=ConnectStatus.UNREACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400526
Khen Nursimulud068d812017-03-06 11:44:18 -0500527 # Sleep 10 secs, simulating a reboot
Stephane Barbarie5253c652017-03-22 16:29:46 -0400528 # TODO: send alert and clear alert after the reboot
Khen Nursimulud068d812017-03-06 11:44:18 -0500529 yield asleep(10)
530
531 # Change the operational status back to its previous state. With a
532 # real OLT the operational state should be the state the device is
533 # after a reboot.
534 # Get the latest device reference
535 device = self.adapter_agent.get_device(self.device_id)
536 device.oper_status = previous_oper_status
537 device.connect_status = previous_conn_status
538 self.adapter_agent.update_device(device)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400539
540 # Update the child devices connect state to REACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400541 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400542 connect_status=ConnectStatus.REACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400543
Khen Nursimulud068d812017-03-06 11:44:18 -0500544 self.log.info('rebooted', device_id=self.device_id)
545
546 def disable(self):
547 self.log.info('disabling', device_id=self.device_id)
548
549 # Get the latest device reference
550 device = self.adapter_agent.get_device(self.device_id)
551
552 # Update the operational status to UNKNOWN
553 device.oper_status = OperStatus.UNKNOWN
554 device.connect_status = ConnectStatus.UNREACHABLE
555 self.adapter_agent.update_device(device)
556
557 # Remove the logical device
558 logical_device = self.adapter_agent.get_logical_device(
Stephane Barbarie5253c652017-03-22 16:29:46 -0400559 self.logical_device_id)
Khen Nursimulud068d812017-03-06 11:44:18 -0500560 self.adapter_agent.delete_logical_device(logical_device)
561
562 # Disable all child devices first
khenaidoo2d7af132017-03-23 15:45:51 -0400563 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400564 admin_state=AdminState.DISABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500565
Khen Nursimulud068d812017-03-06 11:44:18 -0500566 # Remove the peer references from this device
567 self.adapter_agent.delete_all_peer_references(self.device_id)
568
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400569 # Set all ports to disabled
570 self.adapter_agent.disable_all_ports(self.device_id)
571
Khen Nursimulud068d812017-03-06 11:44:18 -0500572 # close the frameio port
573 registry('frameio').close_port(self.io_port)
574
575 # TODO:
576 # 1) Remove all flows from the device
577 # 2) Remove the device from ponsim
578
579 self.log.info('disabled', device_id=device.id)
580
Khen Nursimulud068d812017-03-06 11:44:18 -0500581 def reenable(self):
582 self.log.info('re-enabling', device_id=self.device_id)
583
584 # Get the latest device reference
585 device = self.adapter_agent.get_device(self.device_id)
586
587 # Update the connect status to REACHABLE
588 device.connect_status = ConnectStatus.REACHABLE
589 self.adapter_agent.update_device(device)
590
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400591 # Set all ports to enabled
592 self.adapter_agent.enable_all_ports(self.device_id)
593
Khen Nursimulud068d812017-03-06 11:44:18 -0500594 ld = LogicalDevice(
595 # not setting id and datapth_id will let the adapter agent pick id
596 desc=ofp_desc(
597 mfr_desc='cord porject',
598 hw_desc='simulated pon',
599 sw_desc='simulated pon',
600 serial_num=uuid4().hex,
601 dp_desc='n/a'
602 ),
603 switch_features=ofp_switch_features(
604 n_buffers=256, # TODO fake for now
605 n_tables=2, # TODO ditto
606 capabilities=( # TODO and ditto
607 OFPC_FLOW_STATS
608 | OFPC_TABLE_STATS
609 | OFPC_PORT_STATS
610 | OFPC_GROUP_STATS
611 )
612 ),
613 root_device_id=device.id
614 )
615 ld_initialized = self.adapter_agent.create_logical_device(ld)
616 cap = OFPPF_1GB_FD | OFPPF_FIBER
617 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
618 id='nni',
619 ofp_port=ofp_port(
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400620 port_no=self.ofp_port_no,
Khen Nursimulud068d812017-03-06 11:44:18 -0500621 hw_addr=mac_str_to_tuple(
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400622 '00:00:00:00:00:%02x' % self.ofp_port_no),
Khen Nursimulud068d812017-03-06 11:44:18 -0500623 name='nni',
624 config=0,
625 state=OFPPS_LIVE,
626 curr=cap,
627 advertised=cap,
628 peer=cap,
629 curr_speed=OFPPF_1GB_FD,
630 max_speed=OFPPF_1GB_FD
631 ),
632 device_id=device.id,
633 device_port_no=self.nni_port.port_no,
634 root_port=True
635 ))
636
637 device = self.adapter_agent.get_device(device.id)
638 device.parent_id = ld_initialized.id
639 device.oper_status = OperStatus.ACTIVE
640 self.adapter_agent.update_device(device)
641 self.logical_device_id = ld_initialized.id
642
643 # Reenable all child devices
khenaidoo2d7af132017-03-23 15:45:51 -0400644 self.adapter_agent.update_child_devices_state(device.id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400645 admin_state=AdminState.ENABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500646
Khen Nursimulud068d812017-03-06 11:44:18 -0500647 # finally, open the frameio port to receive in-band packet_in messages
648 self.log.info('registering-frameio')
649 self.io_port = registry('frameio').open_port(
650 self.interface, self.rcv_io, is_inband_frame)
651 self.log.info('registered-frameio')
652
653 self.log.info('re-enabled', device_id=device.id)
654
Khen Nursimulud068d812017-03-06 11:44:18 -0500655 def delete(self):
656 self.log.info('deleting', device_id=self.device_id)
657
658 # Remove all child devices
659 self.adapter_agent.delete_all_child_devices(self.device_id)
660
661 # TODO:
662 # 1) Remove all flows from the device
663 # 2) Remove the device from ponsim
664
665 self.log.info('deleted', device_id=self.device_id)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400666
667 def start_kpi_collection(self, device_id):
668
669 def _collect(device_id, prefix):
670
671 try:
672 # Step 1: gather metrics from device
673 port_metrics = \
Stephane Barbarie5253c652017-03-22 16:29:46 -0400674 self.pm_metrics.collect_port_metrics(self.get_channel())
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400675
676 # Step 2: prepare the KpiEvent for submission
677 # we can time-stamp them here (or could use time derived from OLT
678 ts = arrow.utcnow().timestamp
679 kpi_event = KpiEvent(
680 type=KpiEventType.slice,
681 ts=ts,
682 prefixes={
683 # OLT NNI port
684 prefix + '.nni': MetricValuePairs(
685 metrics=port_metrics['nni']),
686 # OLT PON port
687 prefix + '.pon': MetricValuePairs(
688 metrics=port_metrics['pon'])
689 }
690 )
691
692 # Step 3: submit
693 self.adapter_agent.submit_kpis(kpi_event)
694
695 except Exception as e:
696 log.exception('failed-to-submit-kpis', e=e)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400697
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400698 self.pm_metrics.start_collector(_collect)