blob: 52fb63b704af5e009a2354ea4143327d2d4d0c4b [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
26from google.protobuf.message import Message
27from grpc._channel import _Rendezvous
khenaidoob9203542018-09-17 22:56:37 -040028from scapy.layers.l2 import Ether, Dot1Q
khenaidoo6fdf0ba2018-11-02 14:38:33 -040029from simplejson import dumps
khenaidoob9203542018-09-17 22:56:37 -040030from twisted.internet import reactor
khenaidoo92e62c52018-10-03 14:02:54 -040031from twisted.internet.defer import inlineCallbacks, returnValue
khenaidoo6fdf0ba2018-11-02 14:38:33 -040032from twisted.internet.task import LoopingCall
khenaidoob9203542018-09-17 22:56:37 -040033
34from adapters.common.frameio.frameio import BpfProgramFilter, hexify
35from adapters.common.utils.asleep import asleep
khenaidoo6fdf0ba2018-11-02 14:38:33 -040036from adapters.common.utils.registry import registry
khenaidoob9203542018-09-17 22:56:37 -040037from adapters.iadapter import OltAdapter
khenaidoo6fdf0ba2018-11-02 14:38:33 -040038from adapters.kafka.kafka_proxy import get_kafka_proxy
khenaidoob9203542018-09-17 22:56:37 -040039from adapters.protos import ponsim_pb2
khenaidoo6fdf0ba2018-11-02 14:38:33 -040040from adapters.protos import third_party
41from adapters.protos.common_pb2 import OperStatus, ConnectStatus
42from adapters.protos.core_adapter_pb2 import SwitchCapability, PortCapability, \
43 InterAdapterMessageType, InterAdapterResponseBody
khenaidoob9203542018-09-17 22:56:37 -040044from adapters.protos.device_pb2 import Port, PmConfig, PmConfigs
45from adapters.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs
khenaidoo6fdf0ba2018-11-02 14:38:33 -040046from adapters.protos.logical_device_pb2 import LogicalPort
khenaidoob9203542018-09-17 22:56:37 -040047from adapters.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
48 OFPPF_1GB_FD, \
49 OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS, \
50 ofp_switch_features, ofp_desc
51from adapters.protos.openflow_13_pb2 import ofp_port
khenaidoo91ecfd62018-11-04 17:13:42 -050052from adapters.protos.ponsim_pb2 import FlowTable, PonSimFrame, PonSimMetricsRequest
khenaidoob9203542018-09-17 22:56:37 -040053
54_ = third_party
55log = structlog.get_logger()
56
57PACKET_IN_VLAN = 4000
58is_inband_frame = BpfProgramFilter('(ether[14:2] & 0xfff) = 0x{:03x}'.format(
59 PACKET_IN_VLAN))
60
khenaidoo6fdf0ba2018-11-02 14:38:33 -040061
khenaidoob9203542018-09-17 22:56:37 -040062def mac_str_to_tuple(mac):
63 return tuple(int(d, 16) for d in mac.split(':'))
64
khenaidoo6fdf0ba2018-11-02 14:38:33 -040065
khenaidoob9203542018-09-17 22:56:37 -040066class AdapterPmMetrics:
67 def __init__(self, device):
68 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'}
75 self.device = device
76 self.id = device.id
77 self.name = 'ponsim_olt'
78 self.default_freq = 150
79 self.grouped = False
80 self.freq_override = False
81 self.pon_metrics_config = dict()
82 self.nni_metrics_config = dict()
83 self.lc = None
84 for m in self.pm_names:
85 self.pon_metrics_config[m] = PmConfig(name=m,
86 type=PmConfig.COUNTER,
87 enabled=True)
88 self.nni_metrics_config[m] = PmConfig(name=m,
89 type=PmConfig.COUNTER,
90 enabled=True)
91
92 def update(self, pm_config):
93 if self.default_freq != pm_config.default_freq:
94 # Update the callback to the new frequency.
95 self.default_freq = pm_config.default_freq
96 self.lc.stop()
97 self.lc.start(interval=self.default_freq / 10)
98 for m in pm_config.metrics:
99 self.pon_metrics_config[m.name].enabled = m.enabled
100 self.nni_metrics_config[m.name].enabled = m.enabled
101
102 def make_proto(self):
103 pm_config = PmConfigs(
104 id=self.id,
105 default_freq=self.default_freq,
106 grouped=False,
107 freq_override=False)
108 for m in sorted(self.pon_metrics_config):
109 pm = self.pon_metrics_config[m] # Either will do they're the same
110 pm_config.metrics.extend([PmConfig(name=pm.name,
111 type=pm.type,
112 enabled=pm.enabled)])
113 return pm_config
114
115 def collect_port_metrics(self, channel):
116 rtrn_port_metrics = dict()
117 stub = ponsim_pb2.PonSimStub(channel)
118 stats = stub.GetStats(Empty())
119 rtrn_port_metrics['pon'] = self.extract_pon_metrics(stats)
120 rtrn_port_metrics['nni'] = self.extract_nni_metrics(stats)
121 return rtrn_port_metrics
122
123 def extract_pon_metrics(self, stats):
124 rtrn_pon_metrics = dict()
125 for m in stats.metrics:
126 if m.port_name == "pon":
127 for p in m.packets:
128 if self.pon_metrics_config[p.name].enabled:
129 rtrn_pon_metrics[p.name] = p.value
130 return rtrn_pon_metrics
131
132 def extract_nni_metrics(self, stats):
133 rtrn_pon_metrics = dict()
134 for m in stats.metrics:
135 if m.port_name == "nni":
136 for p in m.packets:
137 if self.pon_metrics_config[p.name].enabled:
138 rtrn_pon_metrics[p.name] = p.value
139 return rtrn_pon_metrics
140
141 def start_collector(self, callback):
142 log.info("starting-pm-collection", device_name=self.name,
143 device_id=self.device.id)
144 prefix = 'voltha.{}.{}'.format(self.name, self.device.id)
145 self.lc = LoopingCall(callback, self.device.id, prefix)
146 self.lc.start(interval=self.default_freq / 10)
147
148 def stop_collector(self):
149 log.info("stopping-pm-collection", device_name=self.name,
150 device_id=self.device.id)
151 self.lc.stop()
152
153
154class AdapterAlarms:
155 def __init__(self, adapter, device):
156 self.adapter = adapter
157 self.device = device
158 self.lc = None
159
160 def send_alarm(self, context_data, alarm_data):
161 try:
162 current_context = {}
163 for key, value in context_data.__dict__.items():
164 current_context[key] = str(value)
165
166 alarm_event = self.adapter.adapter_agent.create_alarm(
167 resource_id=self.device.id,
168 description="{}.{} - {}".format(self.adapter.name,
169 self.device.id,
170 alarm_data[
171 'description']) if 'description' in alarm_data else None,
172 type=alarm_data['type'] if 'type' in alarm_data else None,
173 category=alarm_data[
174 'category'] if 'category' in alarm_data else None,
175 severity=alarm_data[
176 'severity'] if 'severity' in alarm_data else None,
177 state=alarm_data['state'] if 'state' in alarm_data else None,
178 raised_ts=alarm_data['ts'] if 'ts' in alarm_data else 0,
179 context=current_context
180 )
181
182 self.adapter.adapter_agent.submit_alarm(self.device.id,
183 alarm_event)
184
185 except Exception as e:
186 log.exception('failed-to-send-alarm', e=e)
187
188
189class PonSimOltAdapter(OltAdapter):
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400190 def __init__(self, core_proxy, adapter_proxy, config):
191 super(PonSimOltAdapter, self).__init__(core_proxy=core_proxy,
192 adapter_proxy=adapter_proxy,
khenaidoob9203542018-09-17 22:56:37 -0400193 config=config,
194 device_handler_class=PonSimOltHandler,
195 name='ponsim_olt',
196 vendor='Voltha project',
197 version='0.4',
198 device_type='ponsim_olt',
199 accepts_bulk_flow_update=True,
200 accepts_add_remove_flow_updates=False)
201
202 def update_pm_config(self, device, pm_config):
203 log.info("adapter-update-pm-config", device=device,
204 pm_config=pm_config)
205 handler = self.devices_handlers[device.id]
206 handler.update_pm_config(device, pm_config)
207
208
khenaidoob9203542018-09-17 22:56:37 -0400209class PonSimOltHandler(object):
210 def __init__(self, adapter, device_id):
211 self.adapter = adapter
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400212 self.core_proxy = adapter.core_proxy
213 self.adapter_proxy = adapter.adapter_proxy
khenaidoob9203542018-09-17 22:56:37 -0400214 self.device_id = device_id
215 self.log = structlog.get_logger(device_id=device_id)
216 self.channel = None
217 self.io_port = None
218 self.logical_device_id = None
219 self.nni_port = None
220 self.ofp_port_no = None
221 self.interface = registry('main').get_args().interface
222 self.pm_metrics = None
223 self.alarms = None
224 self.frames = None
225
226 @inlineCallbacks
227 def get_channel(self):
228 if self.channel is None:
229 try:
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400230 device = yield self.core_proxy.get_device(self.device_id)
231 self.log.info('device-info', device=device,
232 host_port=device.host_and_port)
khenaidoob9203542018-09-17 22:56:37 -0400233 self.channel = grpc.insecure_channel(device.host_and_port)
234 except Exception as e:
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400235 log.exception("ponsim-connection-failure", e=e)
khenaidoob9203542018-09-17 22:56:37 -0400236
237 # returnValue(self.channel)
238
239 def close_channel(self):
240 if self.channel is None:
241 self.log.info('grpc-channel-already-closed')
242 return
243 else:
244 if self.frames is not None:
245 self.frames.cancel()
246 self.frames = None
247 self.log.info('cancelled-grpc-frame-stream')
248
249 self.channel.unsubscribe(lambda *args: None)
250 self.channel = None
251
252 self.log.info('grpc-channel-closed')
253
khenaidoo92e62c52018-10-03 14:02:54 -0400254 @inlineCallbacks
khenaidoob9203542018-09-17 22:56:37 -0400255 def _get_nni_port(self):
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400256 ports = yield self.core_proxy.get_ports(self.device_id,
257 Port.ETHERNET_NNI)
khenaidoo92e62c52018-10-03 14:02:54 -0400258 returnValue(ports)
khenaidoob9203542018-09-17 22:56:37 -0400259
260 @inlineCallbacks
261 def activate(self, device):
262 try:
263 self.log.info('activating')
264
265 if not device.host_and_port:
266 device.oper_status = OperStatus.FAILED
267 device.reason = 'No host_and_port field provided'
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400268 self.core_proxy.device_update(device)
khenaidoob9203542018-09-17 22:56:37 -0400269 return
270
271 yield self.get_channel()
272 stub = ponsim_pb2.PonSimStub(self.channel)
273 info = stub.GetDeviceInfo(Empty())
274 log.info('got-info', info=info, device_id=device.id)
275 self.ofp_port_no = info.nni_port
276
277 device.root = True
278 device.vendor = 'ponsim'
279 device.model = 'n/a'
280 device.serial_number = device.host_and_port
khenaidoo92e62c52018-10-03 14:02:54 -0400281 device.mac_address = "AA:BB:CC:DD:EE:FF"
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400282 yield self.core_proxy.device_update(device)
khenaidoob9203542018-09-17 22:56:37 -0400283
284 # Now set the initial PM configuration for this device
285 self.pm_metrics = AdapterPmMetrics(device)
286 pm_config = self.pm_metrics.make_proto()
287 log.info("initial-pm-config", pm_config=pm_config)
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400288 self.core_proxy.device_pm_config_update(pm_config, init=True)
khenaidoob9203542018-09-17 22:56:37 -0400289
290 # Setup alarm handler
291 self.alarms = AdapterAlarms(self.adapter, device)
292
293 nni_port = Port(
294 port_no=info.nni_port,
295 label='NNI facing Ethernet port',
296 type=Port.ETHERNET_NNI,
khenaidoob9203542018-09-17 22:56:37 -0400297 oper_status=OperStatus.ACTIVE
298 )
299 self.nni_port = nni_port
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400300 yield self.core_proxy.port_created(device.id, nni_port)
301 yield self.core_proxy.port_created(device.id, Port(
khenaidoob9203542018-09-17 22:56:37 -0400302 port_no=1,
303 label='PON port',
304 type=Port.PON_OLT,
khenaidoob9203542018-09-17 22:56:37 -0400305 oper_status=OperStatus.ACTIVE
306 ))
khenaidoo92e62c52018-10-03 14:02:54 -0400307
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400308 yield self.core_proxy.device_state_update(device.id,
309 connect_status=ConnectStatus.REACHABLE,
310 oper_status=OperStatus.ACTIVE)
khenaidoob9203542018-09-17 22:56:37 -0400311
312 # register ONUS
313 self.log.info('onu-found', onus=info.onus, len=len(info.onus))
314 for onu in info.onus:
315 vlan_id = onu.uni_port
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400316 yield self.core_proxy.child_device_detected(
khenaidoob9203542018-09-17 22:56:37 -0400317 parent_device_id=device.id,
318 parent_port_no=1,
319 child_device_type='ponsim_onu',
320 channel_id=vlan_id,
321 )
322
323 self.log.info('starting-frame-grpc-stream')
324 reactor.callInThread(self.rcv_grpc)
325 self.log.info('started-frame-grpc-stream')
326
327 # TODO
328 # Start collecting stats from the device after a brief pause
khenaidoo92e62c52018-10-03 14:02:54 -0400329 self.start_kpi_collection(device.id)
khenaidoob9203542018-09-17 22:56:37 -0400330 except Exception as e:
331 log.exception("Exception-activating", e=e)
332
khenaidoob9203542018-09-17 22:56:37 -0400333 def get_ofp_device_info(self, device):
334 return SwitchCapability(
335 desc=ofp_desc(
336 hw_desc='ponsim pon',
337 sw_desc='ponsim pon',
338 serial_num=device.serial_number,
339 dp_desc='n/a'
340 ),
341 switch_features=ofp_switch_features(
342 n_buffers=256, # TODO fake for now
343 n_tables=2, # TODO ditto
344 capabilities=( # TODO and ditto
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400345 OFPC_FLOW_STATS
346 | OFPC_TABLE_STATS
347 | OFPC_PORT_STATS
348 | OFPC_GROUP_STATS
khenaidoob9203542018-09-17 22:56:37 -0400349 )
350 )
351 )
352
353 def get_ofp_port_info(self, device, port_no):
354 # Since the adapter created the device port then it has the reference of the port to
355 # return the capability. TODO: Do a lookup on the NNI port number and return the
356 # appropriate attributes
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400357 self.log.info('get_ofp_port_info', port_no=port_no,
358 info=self.ofp_port_no, device_id=device.id)
khenaidoob9203542018-09-17 22:56:37 -0400359 cap = OFPPF_1GB_FD | OFPPF_FIBER
360 return PortCapability(
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400361 port=LogicalPort(
khenaidoob9203542018-09-17 22:56:37 -0400362 ofp_port=ofp_port(
khenaidoob9203542018-09-17 22:56:37 -0400363 hw_addr=mac_str_to_tuple(
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400364 '00:00:00:00:00:%02x' % port_no),
khenaidoob9203542018-09-17 22:56:37 -0400365 config=0,
366 state=OFPPS_LIVE,
367 curr=cap,
368 advertised=cap,
369 peer=cap,
370 curr_speed=OFPPF_1GB_FD,
371 max_speed=OFPPF_1GB_FD
khenaidoo19d7b632018-10-30 10:49:50 -0400372 ),
373 device_id=device.id,
374 device_port_no=port_no
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400375 )
khenaidoob9203542018-09-17 22:56:37 -0400376 )
377
khenaidoo4d4802d2018-10-04 21:59:49 -0400378 # TODO - change for core 2.0
khenaidoob9203542018-09-17 22:56:37 -0400379 def reconcile(self, device):
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400380 self.log.info('reconciling-OLT-device')
khenaidoob9203542018-09-17 22:56:37 -0400381
382 @inlineCallbacks
383 def rcv_grpc(self):
384 """
385 This call establishes a GRPC stream to receive frames.
386 """
387 yield self.get_channel()
388 stub = ponsim_pb2.PonSimStub(self.channel)
389 # stub = ponsim_pb2.PonSimStub(self.get_channel())
390
391 # Attempt to establish a grpc stream with the remote ponsim service
392 self.frames = stub.ReceiveFrames(Empty())
393
394 self.log.info('start-receiving-grpc-frames')
395
396 try:
397 for frame in self.frames:
398 self.log.info('received-grpc-frame',
399 frame_len=len(frame.payload))
400 self._rcv_frame(frame.payload)
401
402 except _Rendezvous, e:
403 log.warn('grpc-connection-lost', message=e.message)
404
405 self.log.info('stopped-receiving-grpc-frames')
406
khenaidoo19d7b632018-10-30 10:49:50 -0400407 @inlineCallbacks
khenaidoob9203542018-09-17 22:56:37 -0400408 def update_flow_table(self, flows):
khenaidoo19d7b632018-10-30 10:49:50 -0400409 yield self.get_channel()
410 stub = ponsim_pb2.PonSimStub(self.channel)
khenaidoob9203542018-09-17 22:56:37 -0400411
khenaidoo19d7b632018-10-30 10:49:50 -0400412 self.log.info('pushing-olt-flow-table')
khenaidoob9203542018-09-17 22:56:37 -0400413 stub.UpdateFlowTable(FlowTable(
414 port=0,
415 flows=flows
416 ))
417 self.log.info('success')
418
419 def remove_from_flow_table(self, flows):
420 self.log.debug('remove-from-flow-table', flows=flows)
421 # TODO: Update PONSIM code to accept incremental flow changes
422 # Once completed, the accepts_add_remove_flow_updates for this
423 # device type can be set to True
424
425 def add_to_flow_table(self, flows):
426 self.log.debug('add-to-flow-table', flows=flows)
427 # TODO: Update PONSIM code to accept incremental flow changes
428 # Once completed, the accepts_add_remove_flow_updates for this
429 # device type can be set to True
430
431 def update_pm_config(self, device, pm_config):
432 log.info("handler-update-pm-config", device=device,
433 pm_config=pm_config)
434 self.pm_metrics.update(pm_config)
435
436 def send_proxied_message(self, proxy_address, msg):
437 self.log.info('sending-proxied-message')
438 if isinstance(msg, FlowTable):
439 stub = ponsim_pb2.PonSimStub(self.get_channel())
440 self.log.info('pushing-onu-flow-table', port=msg.port)
441 res = stub.UpdateFlowTable(msg)
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400442 self.core_proxy.receive_proxied_message(proxy_address, res)
443
444 @inlineCallbacks
445 def process_inter_adapter_message(self, request):
446 self.log.info('process-inter-adapter-message', msg=request)
447 try:
448 if request.header.type == InterAdapterMessageType.FLOW_REQUEST:
449 f = FlowTable()
450 if request.body:
451 request.body.Unpack(f)
452 stub = ponsim_pb2.PonSimStub(self.channel)
453 self.log.info('pushing-onu-flow-table')
454 res = stub.UpdateFlowTable(f)
455 # Send response back
456 reply = InterAdapterResponseBody()
khenaidoo91ecfd62018-11-04 17:13:42 -0500457 reply.status = True
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400458 self.log.info('sending-response-back', reply=reply)
459 yield self.adapter_proxy.send_inter_adapter_message(
460 msg=reply,
461 type=InterAdapterMessageType.FLOW_RESPONSE,
462 from_adapter=self.adapter.name,
463 to_adapter=request.header.from_topic,
464 to_device_id=request.header.to_device_id,
khenaidoo91ecfd62018-11-04 17:13:42 -0500465 message_id=request.header.id
466 )
467 elif request.header.type == InterAdapterMessageType.METRICS_REQUEST:
468 m = PonSimMetricsRequest()
469 if request.body:
470 request.body.Unpack(m)
471 stub = ponsim_pb2.PonSimStub(self.channel)
472 self.log.info('proxying onu stats request', port=m.port)
473 res = stub.GetStats(m)
474 # Send response back
475 reply = InterAdapterResponseBody()
476 reply.status = True
477 reply.body.Pack(res)
478 self.log.info('sending-response-back', reply=reply)
479 yield self.adapter_proxy.send_inter_adapter_message(
480 msg=reply,
481 type=InterAdapterMessageType.METRICS_RESPONSE,
482 from_adapter=self.adapter.name,
483 to_adapter=request.header.from_topic,
484 to_device_id=request.header.to_device_id,
485 message_id=request.header.id
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400486 )
487 except Exception as e:
488 self.log.exception("error-processing-inter-adapter-message", e=e)
khenaidoob9203542018-09-17 22:56:37 -0400489
490 def packet_out(self, egress_port, msg):
491 self.log.info('sending-packet-out', egress_port=egress_port,
492 msg=hexify(msg))
493 pkt = Ether(msg)
494 out_pkt = pkt
495 if egress_port != self.nni_port.port_no:
496 # don't do the vlan manipulation for the NNI port, vlans are already correct
497 out_pkt = (
498 Ether(src=pkt.src, dst=pkt.dst) /
499 Dot1Q(vlan=egress_port, type=pkt.type) /
500 pkt.payload
501 )
502
503 # TODO need better way of mapping logical ports to PON ports
504 out_port = self.nni_port.port_no if egress_port == self.nni_port.port_no else 1
505
506 # send over grpc stream
507 stub = ponsim_pb2.PonSimStub(self.get_channel())
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400508 frame = PonSimFrame(id=self.device_id, payload=str(out_pkt),
509 out_port=out_port)
khenaidoob9203542018-09-17 22:56:37 -0400510 stub.SendFrame(frame)
511
khenaidoob9203542018-09-17 22:56:37 -0400512 @inlineCallbacks
513 def reboot(self):
514 self.log.info('rebooting', device_id=self.device_id)
515
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400516 yield self.core_proxy.device_state_update(self.device_id,
517 connect_status=ConnectStatus.UNREACHABLE)
khenaidoob9203542018-09-17 22:56:37 -0400518
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400519 # Update the child devices connect state to UNREACHABLE
520 yield self.core_proxy.children_state_update(self.device_id,
521 connect_status=ConnectStatus.UNREACHABLE)
khenaidoob9203542018-09-17 22:56:37 -0400522
523 # Sleep 10 secs, simulating a reboot
524 # TODO: send alert and clear alert after the reboot
525 yield asleep(10)
526
khenaidoo4d4802d2018-10-04 21:59:49 -0400527 # Change the connection status back to REACHABLE. With a
528 # real OLT the connection state must be the actual state
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400529 yield self.core_proxy.device_state_update(self.device_id,
530 connect_status=ConnectStatus.REACHABLE)
khenaidoob9203542018-09-17 22:56:37 -0400531
532 # Update the child devices connect state to REACHABLE
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400533 yield self.core_proxy.children_state_update(self.device_id,
534 connect_status=ConnectStatus.REACHABLE)
khenaidoob9203542018-09-17 22:56:37 -0400535
536 self.log.info('rebooted', device_id=self.device_id)
537
538 def self_test_device(self, device):
539 """
540 This is called to Self a device based on a NBI call.
541 :param device: A Voltha.Device object.
542 :return: Will return result of self test
543 """
544 log.info('self-test-device', device=device.id)
545 raise NotImplementedError()
546
khenaidoo92e62c52018-10-03 14:02:54 -0400547 @inlineCallbacks
khenaidoob9203542018-09-17 22:56:37 -0400548 def disable(self):
549 self.log.info('disabling', device_id=self.device_id)
550
551 self.stop_kpi_collection()
552
khenaidoo92e62c52018-10-03 14:02:54 -0400553 # Update the operational status to UNKNOWN and connection status to UNREACHABLE
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400554 yield self.core_proxy.device_state_update(self.device_id,
555 oper_status=OperStatus.UNKNOWN,
556 connect_status=ConnectStatus.UNREACHABLE)
khenaidoob9203542018-09-17 22:56:37 -0400557
558 self.close_channel()
559 self.log.info('disabled-grpc-channel')
560
khenaidoo91ecfd62018-11-04 17:13:42 -0500561 self.stop_kpi_collection()
562
khenaidoob9203542018-09-17 22:56:37 -0400563 # TODO:
564 # 1) Remove all flows from the device
565 # 2) Remove the device from ponsim
566
khenaidoo92e62c52018-10-03 14:02:54 -0400567 self.log.info('disabled', device_id=self.device_id)
khenaidoob9203542018-09-17 22:56:37 -0400568
khenaidoo92e62c52018-10-03 14:02:54 -0400569 @inlineCallbacks
khenaidoob9203542018-09-17 22:56:37 -0400570 def reenable(self):
571 self.log.info('re-enabling', device_id=self.device_id)
572
khenaidoob9203542018-09-17 22:56:37 -0400573 # Set the ofp_port_no and nni_port in case we bypassed the reconcile
574 # process if the device was in DISABLED state on voltha restart
575 if not self.ofp_port_no and not self.nni_port:
khenaidoo92e62c52018-10-03 14:02:54 -0400576 yield self.get_channel()
577 stub = ponsim_pb2.PonSimStub(self.channel)
khenaidoob9203542018-09-17 22:56:37 -0400578 info = stub.GetDeviceInfo(Empty())
579 log.info('got-info', info=info)
580 self.ofp_port_no = info.nni_port
khenaidoo92e62c52018-10-03 14:02:54 -0400581 ports = yield self._get_nni_port()
582 # For ponsim, we are using only 1 NNI port
583 if ports.items:
584 self.nni_port = ports.items[0]
khenaidoob9203542018-09-17 22:56:37 -0400585
khenaidoo92e62c52018-10-03 14:02:54 -0400586 # Update the state of the NNI port
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400587 yield self.core_proxy.port_state_update(self.device_id,
588 port_type=Port.ETHERNET_NNI,
589 port_no=self.ofp_port_no,
590 oper_status=OperStatus.ACTIVE)
khenaidoob9203542018-09-17 22:56:37 -0400591
khenaidoo92e62c52018-10-03 14:02:54 -0400592 # Update the state of the PON port
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400593 yield self.core_proxy.port_state_update(self.device_id,
594 port_type=Port.PON_OLT,
595 port_no=1,
596 oper_status=OperStatus.ACTIVE)
khenaidoob9203542018-09-17 22:56:37 -0400597
khenaidoo92e62c52018-10-03 14:02:54 -0400598 # Set the operational state of the device to ACTIVE and connect status to REACHABLE
khenaidoo6fdf0ba2018-11-02 14:38:33 -0400599 yield self.core_proxy.device_state_update(self.device_id,
600 connect_status=ConnectStatus.REACHABLE,
601 oper_status=OperStatus.ACTIVE)
khenaidoob9203542018-09-17 22:56:37 -0400602
khenaidoo92e62c52018-10-03 14:02:54 -0400603 # TODO: establish frame grpc-stream
604 # yield reactor.callInThread(self.rcv_grpc)
khenaidoob9203542018-09-17 22:56:37 -0400605
khenaidoo92e62c52018-10-03 14:02:54 -0400606 self.start_kpi_collection(self.device_id)
khenaidoob9203542018-09-17 22:56:37 -0400607
khenaidoo92e62c52018-10-03 14:02:54 -0400608 self.log.info('re-enabled', device_id=self.device_id)
khenaidoob9203542018-09-17 22:56:37 -0400609
610 def delete(self):
611 self.log.info('deleting', device_id=self.device_id)
612
khenaidoob9203542018-09-17 22:56:37 -0400613 self.close_channel()
614 self.log.info('disabled-grpc-channel')
615
616 # TODO:
617 # 1) Remove all flows from the device
618 # 2) Remove the device from ponsim
619
620 self.log.info('deleted', device_id=self.device_id)
621
622 def start_kpi_collection(self, device_id):
623
khenaidoo4d4802d2018-10-04 21:59:49 -0400624 kafka_cluster_proxy = get_kafka_proxy()
625
khenaidoob9203542018-09-17 22:56:37 -0400626 def _collect(device_id, prefix):
627
628 try:
629 # Step 1: gather metrics from device
630 port_metrics = \
khenaidoo92e62c52018-10-03 14:02:54 -0400631 self.pm_metrics.collect_port_metrics(self.channel)
khenaidoob9203542018-09-17 22:56:37 -0400632
633 # Step 2: prepare the KpiEvent for submission
634 # we can time-stamp them here (or could use time derived from OLT
635 ts = arrow.utcnow().timestamp
636 kpi_event = KpiEvent(
637 type=KpiEventType.slice,
638 ts=ts,
639 prefixes={
640 # OLT NNI port
641 prefix + '.nni': MetricValuePairs(
642 metrics=port_metrics['nni']),
643 # OLT PON port
644 prefix + '.pon': MetricValuePairs(
645 metrics=port_metrics['pon'])
646 }
647 )
648
khenaidoo91ecfd62018-11-04 17:13:42 -0500649 # Step 3: submit directly to the kafka bus
khenaidoo4d4802d2018-10-04 21:59:49 -0400650 if kafka_cluster_proxy:
651 if isinstance(kpi_event, Message):
652 kpi_event = dumps(MessageToDict(kpi_event, True, True))
653 kafka_cluster_proxy.send_message("voltha.kpis", kpi_event)
khenaidoob9203542018-09-17 22:56:37 -0400654
655 except Exception as e:
656 log.exception('failed-to-submit-kpis', e=e)
657
658 self.pm_metrics.start_collector(_collect)
659
660 def stop_kpi_collection(self):
661 self.pm_metrics.stop_collector()