blob: 42a36c1472c8fa64c3ad3a441725f96fab865552 [file] [log] [blame]
khenaidoob9203542018-09-17 22:56:37 -04001#
2# Copyright 2017 the original author or authors.
3#
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 adapter.
19"""
khenaidoob9203542018-09-17 22:56:37 -040020
21import arrow
khenaidoob9203542018-09-17 22:56:37 -040022import grpc
23import structlog
khenaidoo6fdf0ba2018-11-02 14:38:33 -040024from google.protobuf.empty_pb2 import Empty
25from google.protobuf.json_format import MessageToDict
khenaidoofdbad6e2018-11-06 22:26:38 -050026from scapy.layers.inet import Raw
27import json
khenaidoo6fdf0ba2018-11-02 14:38:33 -040028from google.protobuf.message import Message
29from grpc._channel import _Rendezvous
khenaidoob9203542018-09-17 22:56:37 -040030from scapy.layers.l2 import Ether, Dot1Q
khenaidoo6fdf0ba2018-11-02 14:38:33 -040031from simplejson import dumps
khenaidoob9203542018-09-17 22:56:37 -040032from twisted.internet import reactor
khenaidoo92e62c52018-10-03 14:02:54 -040033from twisted.internet.defer import inlineCallbacks, returnValue
khenaidoo6fdf0ba2018-11-02 14:38:33 -040034from twisted.internet.task import LoopingCall
khenaidoob9203542018-09-17 22:56:37 -040035
khenaidoofdbad6e2018-11-06 22:26:38 -050036from python.adapters.common.frameio.frameio import BpfProgramFilter, hexify
37from python.common.utils.asleep import asleep
38from python.common.utils.registry import registry
39from python.adapters.iadapter import OltAdapter
40from python.adapters.kafka.kafka_proxy import get_kafka_proxy
41from python.protos import ponsim_pb2
42from python.protos import third_party
43from python.protos.common_pb2 import OperStatus, ConnectStatus
khenaidoo79232702018-12-04 11:00:41 -050044from python.protos.inter_container_pb2 import SwitchCapability, PortCapability, \
khenaidoo6fdf0ba2018-11-02 14:38:33 -040045 InterAdapterMessageType, InterAdapterResponseBody
khenaidoofdbad6e2018-11-06 22:26:38 -050046from python.protos.device_pb2 import Port, PmConfig, PmConfigs
47from python.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs
48from python.protos.logical_device_pb2 import LogicalPort
49from python.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
khenaidoob9203542018-09-17 22:56:37 -040050 OFPPF_1GB_FD, \
51 OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS, \
52 ofp_switch_features, ofp_desc
khenaidoofdbad6e2018-11-06 22:26:38 -050053from python.protos.openflow_13_pb2 import ofp_port
54from python.protos.ponsim_pb2 import FlowTable, PonSimFrame, PonSimMetricsRequest
khenaidoob9203542018-09-17 22:56:37 -040055
56_ = third_party
57log = structlog.get_logger()
58
59PACKET_IN_VLAN = 4000
60is_inband_frame = BpfProgramFilter('(ether[14:2] & 0xfff) = 0x{:03x}'.format(
61 PACKET_IN_VLAN))
62
khenaidoo6fdf0ba2018-11-02 14:38:33 -040063
khenaidoob9203542018-09-17 22:56:37 -040064def mac_str_to_tuple(mac):
65 return tuple(int(d, 16) for d in mac.split(':'))
66
khenaidoo6fdf0ba2018-11-02 14:38:33 -040067
khenaidoob9203542018-09-17 22:56:37 -040068class AdapterPmMetrics:
69 def __init__(self, device):
70 self.pm_names = {'tx_64_pkts', 'tx_65_127_pkts', 'tx_128_255_pkts',
71 'tx_256_511_pkts', 'tx_512_1023_pkts',
72 'tx_1024_1518_pkts', 'tx_1519_9k_pkts',
73 'rx_64_pkts', 'rx_65_127_pkts',
74 'rx_128_255_pkts', 'rx_256_511_pkts',
75 'rx_512_1023_pkts', 'rx_1024_1518_pkts',
76 'rx_1519_9k_pkts'}
77 self.device = device
78 self.id = device.id
79 self.name = 'ponsim_olt'
80 self.default_freq = 150
81 self.grouped = False
82 self.freq_override = False
83 self.pon_metrics_config = dict()
84 self.nni_metrics_config = dict()
85 self.lc = None
86 for m in self.pm_names:
87 self.pon_metrics_config[m] = PmConfig(name=m,
88 type=PmConfig.COUNTER,
89 enabled=True)
90 self.nni_metrics_config[m] = PmConfig(name=m,
91 type=PmConfig.COUNTER,
92 enabled=True)
93
94 def update(self, pm_config):
95 if self.default_freq != pm_config.default_freq:
96 # Update the callback to the new frequency.
97 self.default_freq = pm_config.default_freq
98 self.lc.stop()
99 self.lc.start(interval=self.default_freq / 10)
100 for m in pm_config.metrics:
101 self.pon_metrics_config[m.name].enabled = m.enabled
102 self.nni_metrics_config[m.name].enabled = m.enabled
103
104 def make_proto(self):
105 pm_config = PmConfigs(
106 id=self.id,
107 default_freq=self.default_freq,
108 grouped=False,
109 freq_override=False)
110 for m in sorted(self.pon_metrics_config):
111 pm = self.pon_metrics_config[m] # Either will do they're the same
112 pm_config.metrics.extend([PmConfig(name=pm.name,
113 type=pm.type,
114 enabled=pm.enabled)])
115 return pm_config
116
117 def collect_port_metrics(self, channel):
118 rtrn_port_metrics = dict()
119 stub = ponsim_pb2.PonSimStub(channel)
120 stats = stub.GetStats(Empty())
121 rtrn_port_metrics['pon'] = self.extract_pon_metrics(stats)
122 rtrn_port_metrics['nni'] = self.extract_nni_metrics(stats)
123 return rtrn_port_metrics
124
125 def extract_pon_metrics(self, stats):
126 rtrn_pon_metrics = dict()
127 for m in stats.metrics:
128 if m.port_name == "pon":
129 for p in m.packets:
130 if self.pon_metrics_config[p.name].enabled:
131 rtrn_pon_metrics[p.name] = p.value
132 return rtrn_pon_metrics
133
134 def extract_nni_metrics(self, stats):
135 rtrn_pon_metrics = dict()
136 for m in stats.metrics:
137 if m.port_name == "nni":
138 for p in m.packets:
139 if self.pon_metrics_config[p.name].enabled:
140 rtrn_pon_metrics[p.name] = p.value
141 return rtrn_pon_metrics
142
143 def start_collector(self, callback):
144 log.info("starting-pm-collection", device_name=self.name,
145 device_id=self.device.id)
146 prefix = 'voltha.{}.{}'.format(self.name, self.device.id)
147 self.lc = LoopingCall(callback, self.device.id, prefix)
148 self.lc.start(interval=self.default_freq / 10)
149
150 def stop_collector(self):
151 log.info("stopping-pm-collection", device_name=self.name,
152 device_id=self.device.id)
153 self.lc.stop()
154
155
156class AdapterAlarms:
157 def __init__(self, adapter, device):
158 self.adapter = adapter
159 self.device = device
160 self.lc = None
161
khenaidoofdbad6e2018-11-06 22:26:38 -0500162 # TODO: Implement code to send to kafka cluster directly instead of
163 # going through the voltha core.
khenaidoob9203542018-09-17 22:56:37 -0400164 def send_alarm(self, context_data, alarm_data):
khenaidoofdbad6e2018-11-06 22:26:38 -0500165 log.debug("send-alarm-not-implemented")
166 return
khenaidoob9203542018-09-17 22:56:37 -0400167
168
169class PonSimOltAdapter(OltAdapter):
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400170 def __init__(self, core_proxy, adapter_proxy, config):
171 super(PonSimOltAdapter, self).__init__(core_proxy=core_proxy,
172 adapter_proxy=adapter_proxy,
khenaidoob9203542018-09-17 22:56:37 -0400173 config=config,
174 device_handler_class=PonSimOltHandler,
175 name='ponsim_olt',
176 vendor='Voltha project',
177 version='0.4',
178 device_type='ponsim_olt',
179 accepts_bulk_flow_update=True,
180 accepts_add_remove_flow_updates=False)
181
182 def update_pm_config(self, device, pm_config):
183 log.info("adapter-update-pm-config", device=device,
184 pm_config=pm_config)
185 handler = self.devices_handlers[device.id]
186 handler.update_pm_config(device, pm_config)
187
188
khenaidoob9203542018-09-17 22:56:37 -0400189class PonSimOltHandler(object):
190 def __init__(self, adapter, device_id):
191 self.adapter = adapter
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400192 self.core_proxy = adapter.core_proxy
193 self.adapter_proxy = adapter.adapter_proxy
khenaidoob9203542018-09-17 22:56:37 -0400194 self.device_id = device_id
195 self.log = structlog.get_logger(device_id=device_id)
196 self.channel = None
197 self.io_port = None
198 self.logical_device_id = None
199 self.nni_port = None
200 self.ofp_port_no = None
201 self.interface = registry('main').get_args().interface
202 self.pm_metrics = None
203 self.alarms = None
204 self.frames = None
205
206 @inlineCallbacks
207 def get_channel(self):
208 if self.channel is None:
209 try:
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400210 device = yield self.core_proxy.get_device(self.device_id)
211 self.log.info('device-info', device=device,
212 host_port=device.host_and_port)
khenaidoob9203542018-09-17 22:56:37 -0400213 self.channel = grpc.insecure_channel(device.host_and_port)
214 except Exception as e:
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400215 log.exception("ponsim-connection-failure", e=e)
khenaidoob9203542018-09-17 22:56:37 -0400216
217 # returnValue(self.channel)
218
219 def close_channel(self):
220 if self.channel is None:
221 self.log.info('grpc-channel-already-closed')
222 return
223 else:
224 if self.frames is not None:
225 self.frames.cancel()
226 self.frames = None
227 self.log.info('cancelled-grpc-frame-stream')
228
229 self.channel.unsubscribe(lambda *args: None)
230 self.channel = None
231
232 self.log.info('grpc-channel-closed')
233
khenaidoo92e62c52018-10-03 14:02:54 -0400234 @inlineCallbacks
khenaidoob9203542018-09-17 22:56:37 -0400235 def _get_nni_port(self):
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400236 ports = yield self.core_proxy.get_ports(self.device_id,
237 Port.ETHERNET_NNI)
khenaidoo92e62c52018-10-03 14:02:54 -0400238 returnValue(ports)
khenaidoob9203542018-09-17 22:56:37 -0400239
240 @inlineCallbacks
241 def activate(self, device):
242 try:
243 self.log.info('activating')
244
245 if not device.host_and_port:
246 device.oper_status = OperStatus.FAILED
247 device.reason = 'No host_and_port field provided'
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400248 self.core_proxy.device_update(device)
khenaidoob9203542018-09-17 22:56:37 -0400249 return
250
251 yield self.get_channel()
252 stub = ponsim_pb2.PonSimStub(self.channel)
253 info = stub.GetDeviceInfo(Empty())
254 log.info('got-info', info=info, device_id=device.id)
255 self.ofp_port_no = info.nni_port
256
257 device.root = True
258 device.vendor = 'ponsim'
259 device.model = 'n/a'
260 device.serial_number = device.host_and_port
khenaidoo92e62c52018-10-03 14:02:54 -0400261 device.mac_address = "AA:BB:CC:DD:EE:FF"
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400262 yield self.core_proxy.device_update(device)
khenaidoob9203542018-09-17 22:56:37 -0400263
264 # Now set the initial PM configuration for this device
265 self.pm_metrics = AdapterPmMetrics(device)
266 pm_config = self.pm_metrics.make_proto()
267 log.info("initial-pm-config", pm_config=pm_config)
khenaidoodf5a9752019-02-14 14:25:19 -0500268 yield self.core_proxy.device_pm_config_update(pm_config, init=True)
khenaidoob9203542018-09-17 22:56:37 -0400269
270 # Setup alarm handler
271 self.alarms = AdapterAlarms(self.adapter, device)
272
273 nni_port = Port(
274 port_no=info.nni_port,
khenaidoo54544ae2019-03-18 13:22:39 -0400275 label='nni-'+ str(info.nni_port),
khenaidoob9203542018-09-17 22:56:37 -0400276 type=Port.ETHERNET_NNI,
khenaidoob9203542018-09-17 22:56:37 -0400277 oper_status=OperStatus.ACTIVE
278 )
279 self.nni_port = nni_port
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400280 yield self.core_proxy.port_created(device.id, nni_port)
281 yield self.core_proxy.port_created(device.id, Port(
khenaidoob9203542018-09-17 22:56:37 -0400282 port_no=1,
khenaidoo54544ae2019-03-18 13:22:39 -0400283 label='pon-1',
khenaidoob9203542018-09-17 22:56:37 -0400284 type=Port.PON_OLT,
khenaidoob9203542018-09-17 22:56:37 -0400285 oper_status=OperStatus.ACTIVE
286 ))
khenaidoo92e62c52018-10-03 14:02:54 -0400287
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400288 yield self.core_proxy.device_state_update(device.id,
289 connect_status=ConnectStatus.REACHABLE,
290 oper_status=OperStatus.ACTIVE)
khenaidoob9203542018-09-17 22:56:37 -0400291
292 # register ONUS
293 self.log.info('onu-found', onus=info.onus, len=len(info.onus))
294 for onu in info.onus:
295 vlan_id = onu.uni_port
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400296 yield self.core_proxy.child_device_detected(
khenaidoob9203542018-09-17 22:56:37 -0400297 parent_device_id=device.id,
298 parent_port_no=1,
299 child_device_type='ponsim_onu',
300 channel_id=vlan_id,
khenaidoo1ce37ad2019-03-24 22:07:24 -0400301 serial_number=onu.serial_number,
khenaidoob9203542018-09-17 22:56:37 -0400302 )
303
304 self.log.info('starting-frame-grpc-stream')
305 reactor.callInThread(self.rcv_grpc)
306 self.log.info('started-frame-grpc-stream')
307
khenaidoob9203542018-09-17 22:56:37 -0400308 # Start collecting stats from the device after a brief pause
khenaidoo92e62c52018-10-03 14:02:54 -0400309 self.start_kpi_collection(device.id)
khenaidoob9203542018-09-17 22:56:37 -0400310 except Exception as e:
311 log.exception("Exception-activating", e=e)
312
khenaidoob9203542018-09-17 22:56:37 -0400313 def get_ofp_device_info(self, device):
314 return SwitchCapability(
315 desc=ofp_desc(
316 hw_desc='ponsim pon',
317 sw_desc='ponsim pon',
318 serial_num=device.serial_number,
khenaidoo54e0ddf2019-02-27 16:21:33 -0500319 mfr_desc="VOLTHA Project",
khenaidoob9203542018-09-17 22:56:37 -0400320 dp_desc='n/a'
321 ),
322 switch_features=ofp_switch_features(
323 n_buffers=256, # TODO fake for now
324 n_tables=2, # TODO ditto
325 capabilities=( # TODO and ditto
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400326 OFPC_FLOW_STATS
327 | OFPC_TABLE_STATS
328 | OFPC_PORT_STATS
329 | OFPC_GROUP_STATS
khenaidoob9203542018-09-17 22:56:37 -0400330 )
331 )
332 )
333
334 def get_ofp_port_info(self, device, port_no):
335 # Since the adapter created the device port then it has the reference of the port to
336 # return the capability. TODO: Do a lookup on the NNI port number and return the
337 # appropriate attributes
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400338 self.log.info('get_ofp_port_info', port_no=port_no,
339 info=self.ofp_port_no, device_id=device.id)
khenaidoob9203542018-09-17 22:56:37 -0400340 cap = OFPPF_1GB_FD | OFPPF_FIBER
341 return PortCapability(
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400342 port=LogicalPort(
khenaidoob9203542018-09-17 22:56:37 -0400343 ofp_port=ofp_port(
khenaidoob9203542018-09-17 22:56:37 -0400344 hw_addr=mac_str_to_tuple(
khenaidoobcf205b2019-01-25 22:21:14 -0500345 'AA:BB:CC:DD:EE:%02x' % port_no),
khenaidoob9203542018-09-17 22:56:37 -0400346 config=0,
347 state=OFPPS_LIVE,
348 curr=cap,
349 advertised=cap,
350 peer=cap,
351 curr_speed=OFPPF_1GB_FD,
352 max_speed=OFPPF_1GB_FD
khenaidoo19d7b632018-10-30 10:49:50 -0400353 ),
354 device_id=device.id,
355 device_port_no=port_no
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400356 )
khenaidoob9203542018-09-17 22:56:37 -0400357 )
358
khenaidoo4d4802d2018-10-04 21:59:49 -0400359 # TODO - change for core 2.0
khenaidoob9203542018-09-17 22:56:37 -0400360 def reconcile(self, device):
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400361 self.log.info('reconciling-OLT-device')
khenaidoob9203542018-09-17 22:56:37 -0400362
khenaidoo90847922018-12-03 14:47:51 -0500363 @inlineCallbacks
khenaidoofdbad6e2018-11-06 22:26:38 -0500364 def _rcv_frame(self, frame):
365 pkt = Ether(frame)
366
367 if pkt.haslayer(Dot1Q):
368 outer_shim = pkt.getlayer(Dot1Q)
369
370 if isinstance(outer_shim.payload, Dot1Q):
371 inner_shim = outer_shim.payload
372 cvid = inner_shim.vlan
373 popped_frame = (
374 Ether(src=pkt.src, dst=pkt.dst, type=inner_shim.type) /
375 inner_shim.payload
376 )
377 self.log.info('sending-packet-in',device_id=self.device_id, port=cvid)
khenaidoo90847922018-12-03 14:47:51 -0500378 yield self.core_proxy.send_packet_in(device_id=self.device_id,
khenaidoofdbad6e2018-11-06 22:26:38 -0500379 port=cvid,
380 packet=str(popped_frame))
381 elif pkt.haslayer(Raw):
382 raw_data = json.loads(pkt.getlayer(Raw).load)
383 self.alarms.send_alarm(self, raw_data)
384
khenaidoob9203542018-09-17 22:56:37 -0400385 @inlineCallbacks
386 def rcv_grpc(self):
387 """
388 This call establishes a GRPC stream to receive frames.
389 """
390 yield self.get_channel()
391 stub = ponsim_pb2.PonSimStub(self.channel)
392 # stub = ponsim_pb2.PonSimStub(self.get_channel())
393
394 # Attempt to establish a grpc stream with the remote ponsim service
395 self.frames = stub.ReceiveFrames(Empty())
396
397 self.log.info('start-receiving-grpc-frames')
398
399 try:
400 for frame in self.frames:
401 self.log.info('received-grpc-frame',
402 frame_len=len(frame.payload))
khenaidoo303a26f2019-02-28 11:53:32 -0500403 self._rcv_frame(frame.payload)
khenaidoob9203542018-09-17 22:56:37 -0400404
405 except _Rendezvous, e:
406 log.warn('grpc-connection-lost', message=e.message)
407
408 self.log.info('stopped-receiving-grpc-frames')
409
khenaidoo19d7b632018-10-30 10:49:50 -0400410 @inlineCallbacks
khenaidoob9203542018-09-17 22:56:37 -0400411 def update_flow_table(self, flows):
khenaidoo19d7b632018-10-30 10:49:50 -0400412 yield self.get_channel()
413 stub = ponsim_pb2.PonSimStub(self.channel)
khenaidoob9203542018-09-17 22:56:37 -0400414
khenaidoo19d7b632018-10-30 10:49:50 -0400415 self.log.info('pushing-olt-flow-table')
khenaidoob9203542018-09-17 22:56:37 -0400416 stub.UpdateFlowTable(FlowTable(
417 port=0,
418 flows=flows
419 ))
420 self.log.info('success')
421
422 def remove_from_flow_table(self, flows):
423 self.log.debug('remove-from-flow-table', flows=flows)
424 # TODO: Update PONSIM code to accept incremental flow changes
425 # Once completed, the accepts_add_remove_flow_updates for this
426 # device type can be set to True
427
428 def add_to_flow_table(self, flows):
429 self.log.debug('add-to-flow-table', flows=flows)
430 # TODO: Update PONSIM code to accept incremental flow changes
431 # Once completed, the accepts_add_remove_flow_updates for this
432 # device type can be set to True
433
434 def update_pm_config(self, device, pm_config):
435 log.info("handler-update-pm-config", device=device,
436 pm_config=pm_config)
437 self.pm_metrics.update(pm_config)
438
439 def send_proxied_message(self, proxy_address, msg):
440 self.log.info('sending-proxied-message')
441 if isinstance(msg, FlowTable):
442 stub = ponsim_pb2.PonSimStub(self.get_channel())
443 self.log.info('pushing-onu-flow-table', port=msg.port)
444 res = stub.UpdateFlowTable(msg)
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400445 self.core_proxy.receive_proxied_message(proxy_address, res)
446
447 @inlineCallbacks
448 def process_inter_adapter_message(self, request):
449 self.log.info('process-inter-adapter-message', msg=request)
450 try:
451 if request.header.type == InterAdapterMessageType.FLOW_REQUEST:
452 f = FlowTable()
453 if request.body:
454 request.body.Unpack(f)
455 stub = ponsim_pb2.PonSimStub(self.channel)
456 self.log.info('pushing-onu-flow-table')
457 res = stub.UpdateFlowTable(f)
458 # Send response back
459 reply = InterAdapterResponseBody()
khenaidoo91ecfd62018-11-04 17:13:42 -0500460 reply.status = True
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400461 self.log.info('sending-response-back', reply=reply)
462 yield self.adapter_proxy.send_inter_adapter_message(
463 msg=reply,
464 type=InterAdapterMessageType.FLOW_RESPONSE,
465 from_adapter=self.adapter.name,
466 to_adapter=request.header.from_topic,
467 to_device_id=request.header.to_device_id,
khenaidoo91ecfd62018-11-04 17:13:42 -0500468 message_id=request.header.id
469 )
470 elif request.header.type == InterAdapterMessageType.METRICS_REQUEST:
471 m = PonSimMetricsRequest()
472 if request.body:
473 request.body.Unpack(m)
474 stub = ponsim_pb2.PonSimStub(self.channel)
475 self.log.info('proxying onu stats request', port=m.port)
476 res = stub.GetStats(m)
477 # Send response back
478 reply = InterAdapterResponseBody()
479 reply.status = True
480 reply.body.Pack(res)
481 self.log.info('sending-response-back', reply=reply)
482 yield self.adapter_proxy.send_inter_adapter_message(
483 msg=reply,
484 type=InterAdapterMessageType.METRICS_RESPONSE,
485 from_adapter=self.adapter.name,
486 to_adapter=request.header.from_topic,
487 to_device_id=request.header.to_device_id,
488 message_id=request.header.id
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400489 )
490 except Exception as e:
491 self.log.exception("error-processing-inter-adapter-message", e=e)
khenaidoob9203542018-09-17 22:56:37 -0400492
493 def packet_out(self, egress_port, msg):
494 self.log.info('sending-packet-out', egress_port=egress_port,
495 msg=hexify(msg))
khenaidoobcf205b2019-01-25 22:21:14 -0500496 try:
497 pkt = Ether(msg)
498 out_pkt = pkt
499 if egress_port != self.nni_port.port_no:
500 # don't do the vlan manipulation for the NNI port, vlans are already correct
501 out_pkt = (
502 Ether(src=pkt.src, dst=pkt.dst) /
503 Dot1Q(vlan=egress_port, type=pkt.type) /
504 pkt.payload
505 )
khenaidoob9203542018-09-17 22:56:37 -0400506
khenaidoobcf205b2019-01-25 22:21:14 -0500507 # TODO need better way of mapping logical ports to PON ports
508 out_port = self.nni_port.port_no if egress_port == self.nni_port.port_no else 1
khenaidoob9203542018-09-17 22:56:37 -0400509
khenaidoobcf205b2019-01-25 22:21:14 -0500510 # send over grpc stream
511 stub = ponsim_pb2.PonSimStub(self.channel)
512 frame = PonSimFrame(id=self.device_id, payload=str(out_pkt),
513 out_port=out_port)
514 stub.SendFrame(frame)
515 except Exception as e:
516 self.log.exception("error-processing-packet-out", e=e)
517
khenaidoob9203542018-09-17 22:56:37 -0400518
khenaidoob9203542018-09-17 22:56:37 -0400519 @inlineCallbacks
520 def reboot(self):
521 self.log.info('rebooting', device_id=self.device_id)
522
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400523 yield self.core_proxy.device_state_update(self.device_id,
524 connect_status=ConnectStatus.UNREACHABLE)
khenaidoob9203542018-09-17 22:56:37 -0400525
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400526 # Update the child devices connect state to UNREACHABLE
527 yield self.core_proxy.children_state_update(self.device_id,
528 connect_status=ConnectStatus.UNREACHABLE)
khenaidoob9203542018-09-17 22:56:37 -0400529
530 # Sleep 10 secs, simulating a reboot
531 # TODO: send alert and clear alert after the reboot
532 yield asleep(10)
533
khenaidoo4d4802d2018-10-04 21:59:49 -0400534 # Change the connection status back to REACHABLE. With a
535 # real OLT the connection state must be the actual state
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400536 yield self.core_proxy.device_state_update(self.device_id,
537 connect_status=ConnectStatus.REACHABLE)
khenaidoob9203542018-09-17 22:56:37 -0400538
539 # Update the child devices connect state to REACHABLE
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400540 yield self.core_proxy.children_state_update(self.device_id,
541 connect_status=ConnectStatus.REACHABLE)
khenaidoob9203542018-09-17 22:56:37 -0400542
543 self.log.info('rebooted', device_id=self.device_id)
544
545 def self_test_device(self, device):
546 """
547 This is called to Self a device based on a NBI call.
548 :param device: A Voltha.Device object.
549 :return: Will return result of self test
550 """
551 log.info('self-test-device', device=device.id)
552 raise NotImplementedError()
553
khenaidoo92e62c52018-10-03 14:02:54 -0400554 @inlineCallbacks
khenaidoob9203542018-09-17 22:56:37 -0400555 def disable(self):
556 self.log.info('disabling', device_id=self.device_id)
557
558 self.stop_kpi_collection()
559
khenaidoo92e62c52018-10-03 14:02:54 -0400560 # Update the operational status to UNKNOWN and connection status to UNREACHABLE
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400561 yield self.core_proxy.device_state_update(self.device_id,
562 oper_status=OperStatus.UNKNOWN,
563 connect_status=ConnectStatus.UNREACHABLE)
khenaidoob9203542018-09-17 22:56:37 -0400564
565 self.close_channel()
566 self.log.info('disabled-grpc-channel')
567
khenaidoo91ecfd62018-11-04 17:13:42 -0500568 self.stop_kpi_collection()
569
khenaidoob9203542018-09-17 22:56:37 -0400570 # TODO:
571 # 1) Remove all flows from the device
572 # 2) Remove the device from ponsim
573
khenaidoo92e62c52018-10-03 14:02:54 -0400574 self.log.info('disabled', device_id=self.device_id)
khenaidoob9203542018-09-17 22:56:37 -0400575
khenaidoo92e62c52018-10-03 14:02:54 -0400576 @inlineCallbacks
khenaidoob9203542018-09-17 22:56:37 -0400577 def reenable(self):
578 self.log.info('re-enabling', device_id=self.device_id)
579
khenaidoob9203542018-09-17 22:56:37 -0400580 # Set the ofp_port_no and nni_port in case we bypassed the reconcile
581 # process if the device was in DISABLED state on voltha restart
582 if not self.ofp_port_no and not self.nni_port:
khenaidoo92e62c52018-10-03 14:02:54 -0400583 yield self.get_channel()
584 stub = ponsim_pb2.PonSimStub(self.channel)
khenaidoob9203542018-09-17 22:56:37 -0400585 info = stub.GetDeviceInfo(Empty())
586 log.info('got-info', info=info)
587 self.ofp_port_no = info.nni_port
khenaidoo92e62c52018-10-03 14:02:54 -0400588 ports = yield self._get_nni_port()
589 # For ponsim, we are using only 1 NNI port
590 if ports.items:
591 self.nni_port = ports.items[0]
khenaidoob9203542018-09-17 22:56:37 -0400592
khenaidoo92e62c52018-10-03 14:02:54 -0400593 # Update the state of the NNI port
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400594 yield self.core_proxy.port_state_update(self.device_id,
595 port_type=Port.ETHERNET_NNI,
596 port_no=self.ofp_port_no,
597 oper_status=OperStatus.ACTIVE)
khenaidoob9203542018-09-17 22:56:37 -0400598
khenaidoo92e62c52018-10-03 14:02:54 -0400599 # Update the state of the PON port
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400600 yield self.core_proxy.port_state_update(self.device_id,
601 port_type=Port.PON_OLT,
602 port_no=1,
603 oper_status=OperStatus.ACTIVE)
khenaidoob9203542018-09-17 22:56:37 -0400604
khenaidoo92e62c52018-10-03 14:02:54 -0400605 # Set the operational state of the device to ACTIVE and connect status to REACHABLE
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400606 yield self.core_proxy.device_state_update(self.device_id,
607 connect_status=ConnectStatus.REACHABLE,
608 oper_status=OperStatus.ACTIVE)
khenaidoob9203542018-09-17 22:56:37 -0400609
khenaidoo92e62c52018-10-03 14:02:54 -0400610 # TODO: establish frame grpc-stream
611 # yield reactor.callInThread(self.rcv_grpc)
khenaidoob9203542018-09-17 22:56:37 -0400612
khenaidoo92e62c52018-10-03 14:02:54 -0400613 self.start_kpi_collection(self.device_id)
khenaidoob9203542018-09-17 22:56:37 -0400614
khenaidoo92e62c52018-10-03 14:02:54 -0400615 self.log.info('re-enabled', device_id=self.device_id)
khenaidoob9203542018-09-17 22:56:37 -0400616
617 def delete(self):
618 self.log.info('deleting', device_id=self.device_id)
619
khenaidoob9203542018-09-17 22:56:37 -0400620 self.close_channel()
621 self.log.info('disabled-grpc-channel')
622
623 # TODO:
624 # 1) Remove all flows from the device
625 # 2) Remove the device from ponsim
626
627 self.log.info('deleted', device_id=self.device_id)
628
629 def start_kpi_collection(self, device_id):
630
khenaidoo4d4802d2018-10-04 21:59:49 -0400631 kafka_cluster_proxy = get_kafka_proxy()
632
khenaidoob9203542018-09-17 22:56:37 -0400633 def _collect(device_id, prefix):
634
635 try:
636 # Step 1: gather metrics from device
637 port_metrics = \
khenaidoo92e62c52018-10-03 14:02:54 -0400638 self.pm_metrics.collect_port_metrics(self.channel)
khenaidoob9203542018-09-17 22:56:37 -0400639
640 # Step 2: prepare the KpiEvent for submission
641 # we can time-stamp them here (or could use time derived from OLT
642 ts = arrow.utcnow().timestamp
643 kpi_event = KpiEvent(
644 type=KpiEventType.slice,
645 ts=ts,
646 prefixes={
647 # OLT NNI port
648 prefix + '.nni': MetricValuePairs(
649 metrics=port_metrics['nni']),
650 # OLT PON port
651 prefix + '.pon': MetricValuePairs(
652 metrics=port_metrics['pon'])
653 }
654 )
655
khenaidoo91ecfd62018-11-04 17:13:42 -0500656 # Step 3: submit directly to the kafka bus
khenaidoo4d4802d2018-10-04 21:59:49 -0400657 if kafka_cluster_proxy:
658 if isinstance(kpi_event, Message):
659 kpi_event = dumps(MessageToDict(kpi_event, True, True))
660 kafka_cluster_proxy.send_message("voltha.kpis", kpi_event)
khenaidoob9203542018-09-17 22:56:37 -0400661
662 except Exception as e:
663 log.exception('failed-to-submit-kpis', e=e)
664
665 self.pm_metrics.start_collector(_collect)
666
667 def stop_kpi_collection(self):
668 self.pm_metrics.stop_collector()