blob: 9f4ba89935dc542be3fa63c18a78d27d7b7fe643 [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"""
Nikolay Titov176f1db2017-08-10 12:38:43 -040020import sys
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080021from uuid import uuid4
22
Sergio Slobodrian98eff412017-03-15 14:46:30 -040023import arrow
Jeff55a2f132018-08-05 21:10:41 -070024import voltha.core.flow_decomposer as fd
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080025import grpc
Stephane Barbarie4475a252017-03-31 13:49:20 -040026import json
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080027import structlog
28from scapy.layers.l2 import Ether, Dot1Q
Stephane Barbarie4475a252017-03-31 13:49:20 -040029from scapy.layers.inet import Raw
Stephane Barbarie35595062018-02-08 08:34:39 -050030from twisted.internet import reactor
Khen Nursimulud068d812017-03-06 11:44:18 -050031from twisted.internet.defer import inlineCallbacks
Stephane Barbarie35595062018-02-08 08:34:39 -050032from grpc._channel import _Rendezvous
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080033
34from common.frameio.frameio import BpfProgramFilter, hexify
Khen Nursimulud068d812017-03-06 11:44:18 -050035from common.utils.asleep import asleep
Sergio Slobodrian98eff412017-03-15 14:46:30 -040036from twisted.internet.task import LoopingCall
Shad Ansarid1aa9e72017-06-23 21:34:25 -070037from voltha.adapters.iadapter import OltAdapter
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080038from voltha.core.logical_device_agent import mac_str_to_tuple
39from voltha.protos import third_party
Jeff55a2f132018-08-05 21:10:41 -070040from voltha.protos import openflow_13_pb2 as ofp
Scott Bakerd865fa22018-11-07 11:45:28 -080041from voltha.protos import ponsim_pb2, ponsim_pb2_grpc
Shad Ansari14bcd992017-06-13 14:27:20 -070042from voltha.protos.common_pb2 import OperStatus, ConnectStatus, AdminState
Scott Baker50873cc2018-09-21 18:14:09 -070043from voltha.protos.common_pb2 import OperationResp
Shad Ansari14bcd992017-06-13 14:27:20 -070044from voltha.protos.device_pb2 import Port, Device, 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 google.protobuf.empty_pb2 import Empty
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080047from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice
Stephane Barbarie5253c652017-03-22 16:29:46 -040048from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
49 OFPPF_1GB_FD, \
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080050 OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS, \
51 ofp_switch_features, ofp_desc
52from voltha.protos.openflow_13_pb2 import ofp_port
Scott Bakerb5be94d2018-10-09 16:13:32 -070053from voltha.protos.ponsim_pb2 import FlowTable, PonSimFrame, PonSimMetricsRequest
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080054from voltha.registry import registry
55
Nikolay Titov89004ec2017-06-19 18:22:42 -040056from voltha.protos.bbf_fiber_base_pb2 import \
khenaidoof3593a82018-06-01 16:41:31 -040057 ChannelgroupConfig, ChannelpartitionConfig, ChannelpairConfig, \
Nikolay Titov176f1db2017-08-10 12:38:43 -040058 ChannelterminationConfig, OntaniConfig, VOntaniConfig, VEnetConfig
59from voltha.protos.bbf_fiber_traffic_descriptor_profile_body_pb2 import \
60 TrafficDescriptorProfileData
61from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
62from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
63from voltha.protos.bbf_fiber_multicast_gemport_body_pb2 import \
64 MulticastGemportsConfigData
Stephane Barbarie35595062018-02-08 08:34:39 -050065
Nikolay Titov176f1db2017-08-10 12:38:43 -040066from voltha.protos.bbf_fiber_multicast_distribution_set_body_pb2 import \
67 MulticastDistributionSetData
Nikolay Titov89004ec2017-06-19 18:22:42 -040068
Nikolay Titov176f1db2017-08-10 12:38:43 -040069from voltha.protos.ponsim_pb2 import InterfaceConfig, TcontInterfaceConfig
Nikolay Titov89004ec2017-06-19 18:22:42 -040070
Scott Baker50873cc2018-09-21 18:14:09 -070071from voltha.extensions.alarms.adapter_alarms import AdapterAlarms as VolthaAdapterAlarms
72from voltha.extensions.alarms.simulator.simulate_alarms import AdapterAlarmSimulator
73
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080074_ = third_party
75log = structlog.get_logger()
76
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080077PACKET_IN_VLAN = 4000
78is_inband_frame = BpfProgramFilter('(ether[14:2] & 0xfff) = 0x{:03x}'.format(
79 PACKET_IN_VLAN))
80
Stephane Barbarie5253c652017-03-22 16:29:46 -040081
Sergio Slobodrian98eff412017-03-15 14:46:30 -040082class AdapterPmMetrics:
Stephane Barbarie5253c652017-03-22 16:29:46 -040083 def __init__(self, device):
Sergio Slobodrianec6e3912017-04-02 11:46:55 -040084 self.pm_names = {'tx_64_pkts', 'tx_65_127_pkts', 'tx_128_255_pkts',
85 'tx_256_511_pkts', 'tx_512_1023_pkts',
86 'tx_1024_1518_pkts', 'tx_1519_9k_pkts',
87 'rx_64_pkts', 'rx_65_127_pkts',
88 'rx_128_255_pkts', 'rx_256_511_pkts',
89 'rx_512_1023_pkts', 'rx_1024_1518_pkts',
90 'rx_1519_9k_pkts'}
Sergio Slobodrian98eff412017-03-15 14:46:30 -040091 self.device = device
92 self.id = device.id
93 self.name = 'ponsim_olt'
Stephane Barbarie5253c652017-03-22 16:29:46 -040094 # self.id = "abc"
Sergio Slobodrian98eff412017-03-15 14:46:30 -040095 self.default_freq = 150
96 self.grouped = False
97 self.freq_override = False
98 self.pon_metrics_config = dict()
99 self.nni_metrics_config = dict()
100 self.lc = None
101 for m in self.pm_names:
102 self.pon_metrics_config[m] = PmConfig(name=m,
103 type=PmConfig.COUNTER,
104 enabled=True)
105 self.nni_metrics_config[m] = PmConfig(name=m,
106 type=PmConfig.COUNTER,
107 enabled=True)
108
109 def update(self, pm_config):
110 if self.default_freq != pm_config.default_freq:
111 # Update the callback to the new frequency.
112 self.default_freq = pm_config.default_freq
113 self.lc.stop()
Stephane Barbarie5253c652017-03-22 16:29:46 -0400114 self.lc.start(interval=self.default_freq / 10)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400115 for m in pm_config.metrics:
116 self.pon_metrics_config[m.name].enabled = m.enabled
117 self.nni_metrics_config[m.name].enabled = m.enabled
118
119 def make_proto(self):
120 pm_config = PmConfigs(
121 id=self.id,
122 default_freq=self.default_freq,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400123 grouped=False,
124 freq_override=False)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400125 for m in sorted(self.pon_metrics_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400126 pm = self.pon_metrics_config[m] # Either will do they're the same
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400127 pm_config.metrics.extend([PmConfig(name=pm.name,
128 type=pm.type,
129 enabled=pm.enabled)])
130 return pm_config
131
132 def collect_port_metrics(self, channel):
133 rtrn_port_metrics = dict()
Scott Bakerd865fa22018-11-07 11:45:28 -0800134 stub = ponsim_pb2_grpc.PonSimStub(channel)
Scott Bakerb5be94d2018-10-09 16:13:32 -0700135 stats = stub.GetStats(ponsim_pb2.PonSimMetricsRequest(port=0))
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400136 rtrn_port_metrics['pon'] = self.extract_pon_metrics(stats)
137 rtrn_port_metrics['nni'] = self.extract_nni_metrics(stats)
138 return rtrn_port_metrics
139
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400140 def extract_pon_metrics(self, stats):
141 rtrn_pon_metrics = dict()
142 for m in stats.metrics:
143 if m.port_name == "pon":
144 for p in m.packets:
145 if self.pon_metrics_config[p.name].enabled:
146 rtrn_pon_metrics[p.name] = p.value
147 return rtrn_pon_metrics
148
149 def extract_nni_metrics(self, stats):
150 rtrn_pon_metrics = dict()
151 for m in stats.metrics:
152 if m.port_name == "nni":
153 for p in m.packets:
154 if self.pon_metrics_config[p.name].enabled:
155 rtrn_pon_metrics[p.name] = p.value
156 return rtrn_pon_metrics
157
158 def start_collector(self, callback):
159 log.info("starting-pm-collection", device_name=self.name,
160 device_id=self.device.id)
161 prefix = 'voltha.{}.{}'.format(self.name, self.device.id)
162 self.lc = LoopingCall(callback, self.device.id, prefix)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400163 self.lc.start(interval=self.default_freq / 10)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400164
Stephane Barbarie35595062018-02-08 08:34:39 -0500165 def stop_collector(self):
166 log.info("stopping-pm-collection", device_name=self.name,
167 device_id=self.device.id)
168 self.lc.stop()
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800169
khenaidoof3593a82018-06-01 16:41:31 -0400170
Stephane Barbarie4475a252017-03-31 13:49:20 -0400171class AdapterAlarms:
172 def __init__(self, adapter, device):
173 self.adapter = adapter
174 self.device = device
175 self.lc = None
176
177 def send_alarm(self, context_data, alarm_data):
178 try:
179 current_context = {}
180 for key, value in context_data.__dict__.items():
181 current_context[key] = str(value)
182
183 alarm_event = self.adapter.adapter_agent.create_alarm(
184 resource_id=self.device.id,
khenaidoo032d3302017-06-09 14:50:04 -0400185 description="{}.{} - {}".format(self.adapter.name,
186 self.device.id,
187 alarm_data[
188 'description']) if 'description' in alarm_data else None,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400189 type=alarm_data['type'] if 'type' in alarm_data else None,
khenaidoo032d3302017-06-09 14:50:04 -0400190 category=alarm_data[
191 'category'] if 'category' in alarm_data else None,
192 severity=alarm_data[
193 'severity'] if 'severity' in alarm_data else None,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400194 state=alarm_data['state'] if 'state' in alarm_data else None,
195 raised_ts=alarm_data['ts'] if 'ts' in alarm_data else 0,
196 context=current_context
197 )
198
khenaidoo032d3302017-06-09 14:50:04 -0400199 self.adapter.adapter_agent.submit_alarm(self.device.id,
200 alarm_event)
Stephane Barbarie4475a252017-03-31 13:49:20 -0400201
202 except Exception as e:
203 log.exception('failed-to-send-alarm', e=e)
204
khenaidoof3593a82018-06-01 16:41:31 -0400205
Shad Ansarid1aa9e72017-06-23 21:34:25 -0700206class PonSimOltAdapter(OltAdapter):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800207 def __init__(self, adapter_agent, config):
Shad Ansari14bcd992017-06-13 14:27:20 -0700208 super(PonSimOltAdapter, self).__init__(adapter_agent=adapter_agent,
209 config=config,
Shad Ansari96f817b2017-06-18 23:17:44 -0700210 device_handler_class=PonSimOltHandler,
Shad Ansari14bcd992017-06-13 14:27:20 -0700211 name='ponsim_olt',
212 vendor='Voltha project',
Nikolay Titov89004ec2017-06-19 18:22:42 -0400213 version='0.4',
khenaidoof3593a82018-06-01 16:41:31 -0400214 device_type='ponsim_olt',
215 accepts_bulk_flow_update=True,
216 accepts_add_remove_flow_updates=False)
Shad Ansari96f817b2017-06-18 23:17:44 -0700217
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400218 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400219 log.info("adapter-update-pm-config", device=device,
220 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400221 handler = self.devices_handlers[device.id]
222 handler.update_pm_config(device, pm_config)
Sergio Slobodrianec864c62017-03-09 11:41:43 -0500223
Nikolay Titov89004ec2017-06-19 18:22:42 -0400224 def create_interface(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400225 if super(PonSimOltAdapter, self)._get_handler(device):
226 log.info('create-interface', device_id=device.id)
227 self.devices_handlers[device.id].create_interface(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400228
229 def update_interface(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400230 if super(PonSimOltAdapter, self)._get_handler(device):
231 log.info('update-interface', device_id=device.id)
232 self.devices_handlers[device.id].update_interface(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400233
234 def remove_interface(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400235 if super(PonSimOltAdapter, self)._get_handler(device):
236 log.info('remove-interface', device_id=device.id)
237 self.devices_handlers[device.id].remove_interface(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400238
Nikolay Titov176f1db2017-08-10 12:38:43 -0400239 def create_tcont(self, device, tcont_data, traffic_descriptor_data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400240 if super(PonSimOltAdapter, self)._get_handler(device):
241 log.info('create-tcont', device_id=device.id)
242 self.devices_handlers[device.id].create_tcont(
243 tcont_data, traffic_descriptor_data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400244
245 def update_tcont(self, device, tcont_data, traffic_descriptor_data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400246 if super(PonSimOltAdapter, self)._get_handler(device):
247 log.info('update-tcont', device_id=device.id)
248 self.devices_handlers[device.id].update_tcont(
249 tcont_data, traffic_descriptor_data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400250
251 def remove_tcont(self, device, tcont_data, traffic_descriptor_data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400252 if super(PonSimOltAdapter, self)._get_handler(device):
253 log.info('remove-tcont', device_id=device.id)
254 self.devices_handlers[device.id].remove_tcont(
255 tcont_data, traffic_descriptor_data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400256
257 def create_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400258 if super(PonSimOltAdapter, self)._get_handler(device):
259 log.info('create-gemport', device_id=device.id)
260 self.devices_handlers[device.id].create_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400261
262 def update_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400263 if super(PonSimOltAdapter, self)._get_handler(device):
264 log.info('update-gemport', device_id=device.id)
265 self.devices_handlers[device.id].update_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400266
267 def remove_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400268 if super(PonSimOltAdapter, self)._get_handler(device):
269 log.info('remove-gemport', device_id=device.id)
270 self.devices_handlers[device.id].remove_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400271
272 def create_multicast_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400273 if super(PonSimOltAdapter, self)._get_handler(device):
274 log.info('create-multicast-gemport', device_id=device.id)
275 self.devices_handlers[device.id].create_multicast_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400276
277 def update_multicast_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400278 if super(PonSimOltAdapter, self)._get_handler(device):
279 log.info('update-multicast-gemport', device_id=device.id)
280 self.devices_handlers[device.id].update_multicast_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400281
282 def remove_multicast_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400283 if super(PonSimOltAdapter, self)._get_handler(device):
284 log.info('remove-multicast-gemport', device_id=device.id)
285 self.devices_handlers[device.id].remove_multicast_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400286
287 def create_multicast_distribution_set(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400288 if super(PonSimOltAdapter, self)._get_handler(device):
289 log.info('create-multicast-distribution-set', device_id=device.id)
290 self.devices_handlers[device.id].create_multicast_distribution_set(
291 data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400292
293 def update_multicast_distribution_set(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400294 if super(PonSimOltAdapter, self)._get_handler(device):
295 log.info('update-multicast-distribution-set', device_id=device.id)
296 self.devices_handlers[device.id].update_multicast_distribution_set(
297 data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400298
299 def remove_multicast_distribution_set(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400300 if super(PonSimOltAdapter, self)._get_handler(device):
301 log.info('remove-multicast-distribution-set', device_id=device.id)
302 self.devices_handlers[device.id].remove_multicast_distribution_set(
303 data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400304
Scott Baker50873cc2018-09-21 18:14:09 -0700305 def simulate_alarm(self, device, alarm):
306 handler = self.devices_handlers[device.id]
307 handler.simulate_alarm(alarm)
308 return OperationResp(code=OperationResp.OPERATION_SUCCESS)
khenaidoof3593a82018-06-01 16:41:31 -0400309
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800310class PonSimOltHandler(object):
Nikolay Titov176f1db2017-08-10 12:38:43 -0400311 xpon_ponsim_olt_itfs = {
312 'create_interface': {
313 'method_name': 'CreateInterface',
314 'log': 'create-interface'},
315 'update_interface': {
316 'method_name': 'UpdateInterface',
317 'log': 'update-interface'},
318 'remove_interface': {
319 'method_name': 'RemoveInterface',
320 'log': 'remove-interface'},
321 'create_tcont': {
322 'method_name': 'CreateTcont',
323 'log': 'create-tconts-config-data'},
324 'update_tcont': {
325 'method_name': 'UpdateTcont',
326 'log': 'update-tconts-config-data'},
327 'remove_tcont': {
328 'method_name': 'RemoveTcont',
329 'log': 'remove-tconts-config-data'},
330 'create_gemport': {
331 'method_name': 'CreateGemport',
332 'log': 'create-gemports-config-data'},
333 'update_gemport': {
334 'method_name': 'UpdateGemport',
335 'log': 'update-gemports-config-data'},
336 'remove_gemport': {
337 'method_name': 'RemoveGemport',
338 'log': 'remove-gemports-config-data'},
339 'create_multicast_gemport': {
340 'method_name': 'CreateMulticastGemport',
341 'log': 'create-multicast-gemports-config-data'},
342 'update_multicast_gemport': {
343 'method_name': 'UpdateMulticastGemport',
344 'log': 'update-multicast-gemports-config-data'},
345 'remove_multicast_gemport': {
346 'method_name': 'RemoveMulticastGemport',
347 'log': 'remove-multicast-gemports-config-data'},
348 'create_multicast_distribution_set': {
349 'method_name': 'CreateMulticastDistributionSet',
350 'log': 'create-multicast-distribution-set-data'},
351 'update_multicast_distribution_set': {
352 'method_name': 'UpdateMulticastDistributionSet',
353 'log': 'update-multicast-distribution-set-data'},
354 'remove_multicast_distribution_set': {
355 'method_name': 'RemoveMulticastDistributionSet',
356 'log': 'remove-multicast-distribution-set-data'},
khenaidoof3593a82018-06-01 16:41:31 -0400357 }
Nikolay Titov176f1db2017-08-10 12:38:43 -0400358
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800359 def __init__(self, adapter, device_id):
360 self.adapter = adapter
361 self.adapter_agent = adapter.adapter_agent
362 self.device_id = device_id
363 self.log = structlog.get_logger(device_id=device_id)
364 self.channel = None
365 self.io_port = None
366 self.logical_device_id = None
Khen Nursimulud068d812017-03-06 11:44:18 -0500367 self.nni_port = None
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400368 self.ofp_port_no = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800369 self.interface = registry('main').get_args().interface
Stephane Barbarie35595062018-02-08 08:34:39 -0500370 self.ponsim_comm = registry('main').get_args().ponsim_comm
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400371 self.pm_metrics = None
Stephane Barbarie4475a252017-03-31 13:49:20 -0400372 self.alarms = None
Stephane Barbarie35595062018-02-08 08:34:39 -0500373 self.frames = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800374
375 def __del__(self):
376 if self.io_port is not None:
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800377 registry('frameio').close_port(self.io_port)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800378
379 def get_channel(self):
380 if self.channel is None:
381 device = self.adapter_agent.get_device(self.device_id)
schowdhury9e247752017-07-14 06:56:20 -0700382
Jeff70e8b2d2018-07-24 13:37:29 -0700383 self.channel = grpc.insecure_channel(device.host_and_port)
schowdhury9e247752017-07-14 06:56:20 -0700384
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800385 return self.channel
386
Stephane Barbarie35595062018-02-08 08:34:39 -0500387 def close_channel(self):
388 if self.channel is None:
389 self.log.info('grpc-channel-already-closed')
390 return
391 else:
392 if self.frames is not None:
393 self.frames.cancel()
394 self.frames = None
395 self.log.info('cancelled-grpc-frame-stream')
396
397 self.channel.unsubscribe(lambda *args: None)
398 self.channel = None
399
400 self.log.info('grpc-channel-closed')
401
khenaidoo032d3302017-06-09 14:50:04 -0400402 def _get_nni_port(self):
403 ports = self.adapter_agent.get_ports(self.device_id, Port.ETHERNET_NNI)
404 if ports:
405 # For now, we use on one NNI port
406 return ports[0]
407
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800408 def activate(self, device):
409 self.log.info('activating')
410
411 if not device.host_and_port:
412 device.oper_status = OperStatus.FAILED
413 device.reason = 'No host_and_port field provided'
414 self.adapter_agent.update_device(device)
415 return
416
Scott Bakerd865fa22018-11-07 11:45:28 -0800417 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800418 info = stub.GetDeviceInfo(Empty())
419 log.info('got-info', info=info)
420
421 device.root = True
422 device.vendor = 'ponsim'
Stephane Barbarie5253c652017-03-22 16:29:46 -0400423 device.model = 'n/a'
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800424 device.serial_number = device.host_and_port
425 device.connect_status = ConnectStatus.REACHABLE
426 self.adapter_agent.update_device(device)
427
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400428 # Now set the initial PM configuration for this device
Stephane Barbarie5253c652017-03-22 16:29:46 -0400429 self.pm_metrics = AdapterPmMetrics(device)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400430 pm_config = self.pm_metrics.make_proto()
431 log.info("initial-pm-config", pm_config=pm_config)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400432 self.adapter_agent.update_device_pm_config(pm_config, init=True)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400433
Stephane Barbarie4475a252017-03-31 13:49:20 -0400434 # Setup alarm handler
435 self.alarms = AdapterAlarms(self.adapter, device)
436
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800437 nni_port = Port(
Jonathan Hart32fe8812018-08-21 17:10:12 -0700438 port_no=info.nni_port,
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800439 label='NNI facing Ethernet port',
440 type=Port.ETHERNET_NNI,
441 admin_state=AdminState.ENABLED,
442 oper_status=OperStatus.ACTIVE
443 )
Khen Nursimulud068d812017-03-06 11:44:18 -0500444 self.nni_port = nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800445 self.adapter_agent.add_port(device.id, nni_port)
446 self.adapter_agent.add_port(device.id, Port(
447 port_no=1,
448 label='PON port',
449 type=Port.PON_OLT,
450 admin_state=AdminState.ENABLED,
451 oper_status=OperStatus.ACTIVE
452 ))
453
454 ld = LogicalDevice(
khenaidoo507d9222017-10-10 16:23:49 -0400455 # not setting id and datapath_id. Adapter agent will pick the id
456 # and will pick the datapath_id is it is not provided
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800457 desc=ofp_desc(
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800458 hw_desc='simualted pon',
459 sw_desc='simualted pon',
khenaidoof3593a82018-06-01 16:41:31 -0400460 # serial_num=uuid4().hex,
Jonathan Hart32fe8812018-08-21 17:10:12 -0700461 serial_num=device.serial_number,
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800462 dp_desc='n/a'
463 ),
464 switch_features=ofp_switch_features(
465 n_buffers=256, # TODO fake for now
466 n_tables=2, # TODO ditto
467 capabilities=( # TODO and ditto
khenaidoof3593a82018-06-01 16:41:31 -0400468 OFPC_FLOW_STATS
469 | OFPC_TABLE_STATS
470 | OFPC_PORT_STATS
471 | OFPC_GROUP_STATS
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800472 )
473 ),
474 root_device_id=device.id
475 )
khenaidoo507d9222017-10-10 16:23:49 -0400476 mac_address = "AA:BB:CC:DD:EE:FF"
477 ld_initialized = self.adapter_agent.create_logical_device(ld,
478 dpid=mac_address)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800479 cap = OFPPF_1GB_FD | OFPPF_FIBER
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400480 self.ofp_port_no = info.nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800481 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
482 id='nni',
483 ofp_port=ofp_port(
484 port_no=info.nni_port,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400485 hw_addr=mac_str_to_tuple(
486 '00:00:00:00:00:%02x' % info.nni_port),
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800487 name='nni',
488 config=0,
489 state=OFPPS_LIVE,
490 curr=cap,
491 advertised=cap,
492 peer=cap,
493 curr_speed=OFPPF_1GB_FD,
494 max_speed=OFPPF_1GB_FD
495 ),
496 device_id=device.id,
497 device_port_no=nni_port.port_no,
498 root_port=True
499 ))
500
501 device = self.adapter_agent.get_device(device.id)
502 device.parent_id = ld_initialized.id
503 device.oper_status = OperStatus.ACTIVE
504 self.adapter_agent.update_device(device)
505 self.logical_device_id = ld_initialized.id
506
Andy Bavierea82b462018-07-27 16:48:13 -0700507 # register ONUS
508 for onu in info.onus:
509 vlan_id = onu.uni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800510 self.adapter_agent.child_device_detected(
511 parent_device_id=device.id,
512 parent_port_no=1,
Niren R Chidrawarefcebcd2017-07-19 20:03:39 -0400513 child_device_type='ponsim_onu',
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800514 proxy_address=Device.ProxyAddress(
515 device_id=device.id,
516 channel_id=vlan_id
517 ),
Nikolay Titov89004ec2017-06-19 18:22:42 -0400518 admin_state=AdminState.ENABLED,
Andy Bavierea82b462018-07-27 16:48:13 -0700519 vlan=vlan_id,
520 serial_number=onu.serial_number
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800521 )
522
Stephane Barbarie35595062018-02-08 08:34:39 -0500523 if self.ponsim_comm == 'grpc':
524 self.log.info('starting-frame-grpc-stream')
525 reactor.callInThread(self.rcv_grpc)
526 self.log.info('started-frame-grpc-stream')
527 else:
528 # finally, open the frameio port to receive in-band packet_in messages
529 self.log.info('registering-frameio')
530 self.io_port = registry('frameio').open_port(
531 self.interface, self.rcv_io, is_inband_frame)
532 self.log.info('registered-frameio')
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800533
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400534 # Start collecting stats from the device after a brief pause
535 self.start_kpi_collection(device.id)
536
khenaidoo032d3302017-06-09 14:50:04 -0400537 def reconcile(self, device):
538 self.log.info('reconciling-OLT-device-starts')
539
540 if not device.host_and_port:
541 device.oper_status = OperStatus.FAILED
542 device.reason = 'No host_and_port field provided'
543 self.adapter_agent.update_device(device)
544 return
545
546 try:
Scott Bakerd865fa22018-11-07 11:45:28 -0800547 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
khenaidoo032d3302017-06-09 14:50:04 -0400548 info = stub.GetDeviceInfo(Empty())
549 log.info('got-info', info=info)
550 # TODO: Verify we are connected to the same device we are
551 # reconciling - not much data in ponsim to differentiate at the
552 # time
553 device.oper_status = OperStatus.ACTIVE
554 self.adapter_agent.update_device(device)
555 self.ofp_port_no = info.nni_port
556 self.nni_port = self._get_nni_port()
557 except Exception, e:
558 log.exception('device-unreachable', e=e)
559 device.connect_status = ConnectStatus.UNREACHABLE
560 device.oper_status = OperStatus.UNKNOWN
561 self.adapter_agent.update_device(device)
562 return
563
564 # Now set the initial PM configuration for this device
565 self.pm_metrics = AdapterPmMetrics(device)
566 pm_config = self.pm_metrics.make_proto()
567 log.info("initial-pm-config", pm_config=pm_config)
568 self.adapter_agent.update_device_pm_config(pm_config, init=True)
569
570 # Setup alarm handler
571 self.alarms = AdapterAlarms(self.adapter, device)
572
573 # TODO: Is there anything required to verify nni and PON ports
574
575 # Set the logical device id
576 device = self.adapter_agent.get_device(device.id)
577 if device.parent_id:
578 self.logical_device_id = device.parent_id
579 self.adapter_agent.reconcile_logical_device(device.parent_id)
580 else:
581 self.log.info('no-logical-device-set')
582
583 # Reconcile child devices
584 self.adapter_agent.reconcile_child_devices(device.id)
585
Stephane Barbarie35595062018-02-08 08:34:39 -0500586 if self.ponsim_comm == 'grpc':
587 reactor.callInThread(self.rcv_grpc)
588 else:
589 # finally, open the frameio port to receive in-band packet_in messages
590 self.io_port = registry('frameio').open_port(
591 self.interface, self.rcv_io, is_inband_frame)
khenaidoo032d3302017-06-09 14:50:04 -0400592
593 # Start collecting stats from the device after a brief pause
594 self.start_kpi_collection(device.id)
595
596 self.log.info('reconciling-OLT-device-ends')
597
Stephane Barbarie35595062018-02-08 08:34:39 -0500598 def _rcv_frame(self, frame):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800599 pkt = Ether(frame)
Stephane Barbarie35595062018-02-08 08:34:39 -0500600
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800601 if pkt.haslayer(Dot1Q):
602 outer_shim = pkt.getlayer(Dot1Q)
Stephane Barbarie35595062018-02-08 08:34:39 -0500603
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800604 if isinstance(outer_shim.payload, Dot1Q):
605 inner_shim = outer_shim.payload
606 cvid = inner_shim.vlan
607 logical_port = cvid
608 popped_frame = (
khenaidoof3593a82018-06-01 16:41:31 -0400609 Ether(src=pkt.src, dst=pkt.dst, type=inner_shim.type) /
610 inner_shim.payload
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800611 )
612 kw = dict(
613 logical_device_id=self.logical_device_id,
614 logical_port_no=logical_port,
615 )
616 self.log.info('sending-packet-in', **kw)
617 self.adapter_agent.send_packet_in(
618 packet=str(popped_frame), **kw)
Stephane Barbarie4475a252017-03-31 13:49:20 -0400619 elif pkt.haslayer(Raw):
620 raw_data = json.loads(pkt.getlayer(Raw).load)
621 self.alarms.send_alarm(self, raw_data)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800622
Stephane Barbarie35595062018-02-08 08:34:39 -0500623 @inlineCallbacks
624 def rcv_grpc(self):
625 """
626 This call establishes a GRPC stream to receive frames.
627 """
Scott Bakerd865fa22018-11-07 11:45:28 -0800628 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Stephane Barbarie35595062018-02-08 08:34:39 -0500629
630 # Attempt to establish a grpc stream with the remote ponsim service
631 self.frames = stub.ReceiveFrames(Empty())
632
633 self.log.info('start-receiving-grpc-frames')
634
635 try:
636 for frame in self.frames:
khenaidoof3593a82018-06-01 16:41:31 -0400637 self.log.info('received-grpc-frame',
638 frame_len=len(frame.payload))
Stephane Barbarie35595062018-02-08 08:34:39 -0500639 self._rcv_frame(frame.payload)
640
641 except _Rendezvous, e:
khenaidoof3593a82018-06-01 16:41:31 -0400642 log.warn('grpc-connection-lost', message=e.message)
Stephane Barbarie35595062018-02-08 08:34:39 -0500643
644 self.log.info('stopped-receiving-grpc-frames')
645
646 def rcv_io(self, port, frame):
647 self.log.info('received-io-frame', iface_name=port.iface_name,
648 frame_len=len(frame))
649 self._rcv_frame(frame)
650
Jeff55a2f132018-08-05 21:10:41 -0700651 # VOLTHA's flow decomposition removes the information about which flows
652 # are trap flows where traffic should be forwarded to the controller.
653 # We'll go through the flows and change the output port of flows that we
654 # know to be trap flows to the OF CONTROLLER port.
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800655 def update_flow_table(self, flows):
Scott Bakerd865fa22018-11-07 11:45:28 -0800656 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800657 self.log.info('pushing-olt-flow-table')
Jeff55a2f132018-08-05 21:10:41 -0700658 for flow in flows:
659 classifier_info = {}
660 for field in fd.get_ofb_fields(flow):
661 if field.type == fd.ETH_TYPE:
662 classifier_info['eth_type'] = field.eth_type
663 self.log.debug('field-type-eth-type',
664 eth_type=classifier_info['eth_type'])
665 elif field.type == fd.IP_PROTO:
666 classifier_info['ip_proto'] = field.ip_proto
667 self.log.debug('field-type-ip-proto',
668 ip_proto=classifier_info['ip_proto'])
669 if ('ip_proto' in classifier_info and (
670 classifier_info['ip_proto'] == 17 or
671 classifier_info['ip_proto'] == 2)) or (
672 'eth_type' in classifier_info and
673 classifier_info['eth_type'] == 0x888e):
674 for action in fd.get_actions(flow):
675 if action.type == ofp.OFPAT_OUTPUT:
676 action.output.port = ofp.OFPP_CONTROLLER
677 self.log.info('out_port', out_port=fd.get_out_port(flow))
678
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800679 stub.UpdateFlowTable(FlowTable(
680 port=0,
681 flows=flows
682 ))
683 self.log.info('success')
684
khenaidoof3593a82018-06-01 16:41:31 -0400685 def remove_from_flow_table(self, flows):
686 self.log.debug('remove-from-flow-table', flows=flows)
687 # TODO: Update PONSIM code to accept incremental flow changes
688 # Once completed, the accepts_add_remove_flow_updates for this
689 # device type can be set to True
690
691 def add_to_flow_table(self, flows):
692 self.log.debug('add-to-flow-table', flows=flows)
693 # TODO: Update PONSIM code to accept incremental flow changes
694 # Once completed, the accepts_add_remove_flow_updates for this
695 # device type can be set to True
696
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400697 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400698 log.info("handler-update-pm-config", device=device,
699 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400700 self.pm_metrics.update(pm_config)
701
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800702 def send_proxied_message(self, proxy_address, msg):
703 self.log.info('sending-proxied-message')
704 if isinstance(msg, FlowTable):
Scott Bakerd865fa22018-11-07 11:45:28 -0800705 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800706 self.log.info('pushing-onu-flow-table', port=msg.port)
707 res = stub.UpdateFlowTable(msg)
708 self.adapter_agent.receive_proxied_message(proxy_address, res)
Scott Bakerb5be94d2018-10-09 16:13:32 -0700709 elif isinstance(msg, PonSimMetricsRequest):
Scott Bakerd865fa22018-11-07 11:45:28 -0800710 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Scott Bakerb5be94d2018-10-09 16:13:32 -0700711 self.log.info('proxying onu stats request', port=msg.port)
712 res = stub.GetStats(msg)
713 self.adapter_agent.receive_proxied_message(proxy_address, res)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800714
715 def packet_out(self, egress_port, msg):
716 self.log.info('sending-packet-out', egress_port=egress_port,
717 msg=hexify(msg))
718 pkt = Ether(msg)
Jonathan Hart32fe8812018-08-21 17:10:12 -0700719 out_pkt = pkt
720 if egress_port != self.nni_port.port_no:
721 # don't do the vlan manipulation for the NNI port, vlans are already correct
722 out_pkt = (
723 Ether(src=pkt.src, dst=pkt.dst) /
724 Dot1Q(vlan=egress_port, type=pkt.type) /
725 pkt.payload
726 )
727
728 # TODO need better way of mapping logical ports to PON ports
729 out_port = self.nni_port.port_no if egress_port == self.nni_port.port_no else 1
Stephane Barbarie35595062018-02-08 08:34:39 -0500730
731 if self.ponsim_comm == 'grpc':
732 # send over grpc stream
Scott Bakerd865fa22018-11-07 11:45:28 -0800733 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Jonathan Hart32fe8812018-08-21 17:10:12 -0700734 frame = PonSimFrame(id=self.device_id, payload=str(out_pkt), out_port=out_port)
Stephane Barbarie35595062018-02-08 08:34:39 -0500735 stub.SendFrame(frame)
736 else:
737 # send over frameio
738 self.io_port.send(str(out_pkt))
739
Khen Nursimulud068d812017-03-06 11:44:18 -0500740 @inlineCallbacks
741 def reboot(self):
742 self.log.info('rebooting', device_id=self.device_id)
743
744 # Update the operational status to ACTIVATING and connect status to
745 # UNREACHABLE
746 device = self.adapter_agent.get_device(self.device_id)
747 previous_oper_status = device.oper_status
748 previous_conn_status = device.connect_status
749 device.oper_status = OperStatus.ACTIVATING
750 device.connect_status = ConnectStatus.UNREACHABLE
751 self.adapter_agent.update_device(device)
752
khenaidoo71d0a6c2017-03-22 21:46:04 -0400753 # Update the child devices connect state to UNREACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400754 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400755 connect_status=ConnectStatus.UNREACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400756
Khen Nursimulud068d812017-03-06 11:44:18 -0500757 # Sleep 10 secs, simulating a reboot
Stephane Barbarie5253c652017-03-22 16:29:46 -0400758 # TODO: send alert and clear alert after the reboot
Khen Nursimulud068d812017-03-06 11:44:18 -0500759 yield asleep(10)
760
761 # Change the operational status back to its previous state. With a
762 # real OLT the operational state should be the state the device is
763 # after a reboot.
764 # Get the latest device reference
765 device = self.adapter_agent.get_device(self.device_id)
766 device.oper_status = previous_oper_status
767 device.connect_status = previous_conn_status
768 self.adapter_agent.update_device(device)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400769
770 # Update the child devices connect state to REACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400771 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400772 connect_status=ConnectStatus.REACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400773
Khen Nursimulud068d812017-03-06 11:44:18 -0500774 self.log.info('rebooted', device_id=self.device_id)
775
sathishg5ae86222017-06-28 15:16:29 +0530776 def self_test_device(self, device):
777 """
778 This is called to Self a device based on a NBI call.
779 :param device: A Voltha.Device object.
780 :return: Will return result of self test
781 """
782 log.info('self-test-device', device=device.id)
783 raise NotImplementedError()
784
Khen Nursimulud068d812017-03-06 11:44:18 -0500785 def disable(self):
786 self.log.info('disabling', device_id=self.device_id)
787
Stephane Barbarie35595062018-02-08 08:34:39 -0500788 self.stop_kpi_collection()
789
Khen Nursimulud068d812017-03-06 11:44:18 -0500790 # Get the latest device reference
791 device = self.adapter_agent.get_device(self.device_id)
792
793 # Update the operational status to UNKNOWN
794 device.oper_status = OperStatus.UNKNOWN
795 device.connect_status = ConnectStatus.UNREACHABLE
796 self.adapter_agent.update_device(device)
797
798 # Remove the logical device
799 logical_device = self.adapter_agent.get_logical_device(
Stephane Barbarie5253c652017-03-22 16:29:46 -0400800 self.logical_device_id)
Khen Nursimulud068d812017-03-06 11:44:18 -0500801 self.adapter_agent.delete_logical_device(logical_device)
802
803 # Disable all child devices first
khenaidoo2d7af132017-03-23 15:45:51 -0400804 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400805 admin_state=AdminState.DISABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500806
Khen Nursimulud068d812017-03-06 11:44:18 -0500807 # Remove the peer references from this device
808 self.adapter_agent.delete_all_peer_references(self.device_id)
809
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400810 # Set all ports to disabled
811 self.adapter_agent.disable_all_ports(self.device_id)
812
Stephane Barbarie35595062018-02-08 08:34:39 -0500813 self.close_channel()
814 self.log.info('disabled-grpc-channel')
815
816 if self.ponsim_comm == 'frameio':
817 # close the frameio port
818 registry('frameio').close_port(self.io_port)
819 self.log.info('disabled-frameio-port')
Khen Nursimulud068d812017-03-06 11:44:18 -0500820
khenaidoo032d3302017-06-09 14:50:04 -0400821 # Update the logice device mapping
822 if self.logical_device_id in \
823 self.adapter.logical_device_id_to_root_device_id:
824 del self.adapter.logical_device_id_to_root_device_id[
825 self.logical_device_id]
826
Khen Nursimulud068d812017-03-06 11:44:18 -0500827 # TODO:
828 # 1) Remove all flows from the device
829 # 2) Remove the device from ponsim
830
831 self.log.info('disabled', device_id=device.id)
832
Khen Nursimulud068d812017-03-06 11:44:18 -0500833 def reenable(self):
834 self.log.info('re-enabling', device_id=self.device_id)
835
836 # Get the latest device reference
837 device = self.adapter_agent.get_device(self.device_id)
838
khenaidoo032d3302017-06-09 14:50:04 -0400839 # Set the ofp_port_no and nni_port in case we bypassed the reconcile
840 # process if the device was in DISABLED state on voltha restart
841 if not self.ofp_port_no and not self.nni_port:
Scott Bakerd865fa22018-11-07 11:45:28 -0800842 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
khenaidoo032d3302017-06-09 14:50:04 -0400843 info = stub.GetDeviceInfo(Empty())
844 log.info('got-info', info=info)
845 self.ofp_port_no = info.nni_port
846 self.nni_port = self._get_nni_port()
847
Khen Nursimulud068d812017-03-06 11:44:18 -0500848 # Update the connect status to REACHABLE
849 device.connect_status = ConnectStatus.REACHABLE
850 self.adapter_agent.update_device(device)
851
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400852 # Set all ports to enabled
853 self.adapter_agent.enable_all_ports(self.device_id)
854
Khen Nursimulud068d812017-03-06 11:44:18 -0500855 ld = LogicalDevice(
856 # not setting id and datapth_id will let the adapter agent pick id
857 desc=ofp_desc(
Khen Nursimulud068d812017-03-06 11:44:18 -0500858 hw_desc='simulated pon',
859 sw_desc='simulated pon',
860 serial_num=uuid4().hex,
861 dp_desc='n/a'
862 ),
863 switch_features=ofp_switch_features(
864 n_buffers=256, # TODO fake for now
865 n_tables=2, # TODO ditto
866 capabilities=( # TODO and ditto
khenaidoof3593a82018-06-01 16:41:31 -0400867 OFPC_FLOW_STATS
868 | OFPC_TABLE_STATS
869 | OFPC_PORT_STATS
870 | OFPC_GROUP_STATS
Khen Nursimulud068d812017-03-06 11:44:18 -0500871 )
872 ),
873 root_device_id=device.id
874 )
khenaidoo507d9222017-10-10 16:23:49 -0400875 mac_address = "AA:BB:CC:DD:EE:FF"
876 ld_initialized = self.adapter_agent.create_logical_device(ld,
877 dpid=mac_address)
Khen Nursimulud068d812017-03-06 11:44:18 -0500878 cap = OFPPF_1GB_FD | OFPPF_FIBER
879 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
880 id='nni',
881 ofp_port=ofp_port(
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400882 port_no=self.ofp_port_no,
Khen Nursimulud068d812017-03-06 11:44:18 -0500883 hw_addr=mac_str_to_tuple(
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400884 '00:00:00:00:00:%02x' % self.ofp_port_no),
Khen Nursimulud068d812017-03-06 11:44:18 -0500885 name='nni',
886 config=0,
887 state=OFPPS_LIVE,
888 curr=cap,
889 advertised=cap,
890 peer=cap,
891 curr_speed=OFPPF_1GB_FD,
892 max_speed=OFPPF_1GB_FD
893 ),
894 device_id=device.id,
895 device_port_no=self.nni_port.port_no,
896 root_port=True
897 ))
898
899 device = self.adapter_agent.get_device(device.id)
900 device.parent_id = ld_initialized.id
901 device.oper_status = OperStatus.ACTIVE
902 self.adapter_agent.update_device(device)
903 self.logical_device_id = ld_initialized.id
904
905 # Reenable all child devices
khenaidoo2d7af132017-03-23 15:45:51 -0400906 self.adapter_agent.update_child_devices_state(device.id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400907 admin_state=AdminState.ENABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500908
Stephane Barbarie35595062018-02-08 08:34:39 -0500909 if self.ponsim_comm == 'grpc':
910 # establish frame grpc-stream
911 reactor.callInThread(self.rcv_grpc)
912 else:
913 # finally, open the frameio port to receive in-band packet_in messages
914 self.io_port = registry('frameio').open_port(
915 self.interface, self.rcv_io, is_inband_frame)
916
917 self.start_kpi_collection(device.id)
Khen Nursimulud068d812017-03-06 11:44:18 -0500918
919 self.log.info('re-enabled', device_id=device.id)
920
Khen Nursimulud068d812017-03-06 11:44:18 -0500921 def delete(self):
922 self.log.info('deleting', device_id=self.device_id)
923
924 # Remove all child devices
925 self.adapter_agent.delete_all_child_devices(self.device_id)
926
Stephane Barbarie35595062018-02-08 08:34:39 -0500927 self.close_channel()
928 self.log.info('disabled-grpc-channel')
929
930 if self.ponsim_comm == 'frameio':
931 # close the frameio port
932 registry('frameio').close_port(self.io_port)
933 self.log.info('disabled-frameio-port')
934
Khen Nursimulud068d812017-03-06 11:44:18 -0500935 # TODO:
936 # 1) Remove all flows from the device
937 # 2) Remove the device from ponsim
938
939 self.log.info('deleted', device_id=self.device_id)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400940
941 def start_kpi_collection(self, device_id):
942
943 def _collect(device_id, prefix):
944
945 try:
946 # Step 1: gather metrics from device
947 port_metrics = \
Stephane Barbarie5253c652017-03-22 16:29:46 -0400948 self.pm_metrics.collect_port_metrics(self.get_channel())
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400949
950 # Step 2: prepare the KpiEvent for submission
951 # we can time-stamp them here (or could use time derived from OLT
952 ts = arrow.utcnow().timestamp
953 kpi_event = KpiEvent(
954 type=KpiEventType.slice,
955 ts=ts,
956 prefixes={
957 # OLT NNI port
958 prefix + '.nni': MetricValuePairs(
959 metrics=port_metrics['nni']),
960 # OLT PON port
961 prefix + '.pon': MetricValuePairs(
962 metrics=port_metrics['pon'])
963 }
964 )
965
966 # Step 3: submit
967 self.adapter_agent.submit_kpis(kpi_event)
968
969 except Exception as e:
970 log.exception('failed-to-submit-kpis', e=e)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400971
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400972 self.pm_metrics.start_collector(_collect)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400973
Stephane Barbarie35595062018-02-08 08:34:39 -0500974 def stop_kpi_collection(self):
975 self.pm_metrics.stop_collector()
976
Nikolay Titov89004ec2017-06-19 18:22:42 -0400977 def get_interface_config(self, data):
978 interfaceConfig = InterfaceConfig()
979 if isinstance(data, ChannelgroupConfig):
980 interfaceConfig.channel_group_config.CopyFrom(data)
981 elif isinstance(data, ChannelpartitionConfig):
982 interfaceConfig.channel_partition_config.CopyFrom(data)
983 elif isinstance(data, ChannelpairConfig):
984 interfaceConfig.channel_pair_config.CopyFrom(data)
985 elif isinstance(data, ChannelterminationConfig):
986 interfaceConfig.channel_termination_config.CopyFrom(data)
987 elif isinstance(data, OntaniConfig):
988 interfaceConfig.ont_ani_config.CopyFrom(data)
989 elif isinstance(data, VOntaniConfig):
990 interfaceConfig.vont_ani_config.CopyFrom(data)
991 elif isinstance(data, VEnetConfig):
992 interfaceConfig.venet_config.CopyFrom(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400993 elif isinstance(data, TrafficDescriptorProfileData):
994 interfaceConfig.traffic_descriptor_profile_config_data.CopyFrom(
995 data)
996 elif isinstance(data, TcontsConfigData):
997 interfaceConfig.tconts_config_data.CopyFrom(data)
998 elif isinstance(data, GemportsConfigData):
999 interfaceConfig.gemports_config_data.CopyFrom(data)
1000 elif isinstance(data, MulticastGemportsConfigData):
1001 interfaceConfig.multicast_gemports_config_data.CopyFrom(data)
1002 elif isinstance(data, MulticastDistributionSetData):
1003 interfaceConfig.multicast_distribution_set_data.CopyFrom(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -04001004 else:
1005 return None
1006 return interfaceConfig
1007
Nikolay Titov176f1db2017-08-10 12:38:43 -04001008 def xpon_ponsim_olt_interface(self, method_name, data, data2=None):
Nikolay Titov89004ec2017-06-19 18:22:42 -04001009 interfaceConfig = self.get_interface_config(data)
1010 if interfaceConfig is not None:
Nikolay Titov176f1db2017-08-10 12:38:43 -04001011 self.log.info(
1012 'forwarding-{}-request-to-olt-for-interface-type'
khenaidoof3593a82018-06-01 16:41:31 -04001013 .format(self.xpon_ponsim_olt_itfs[method_name]['log']),
Nikolay Titov176f1db2017-08-10 12:38:43 -04001014 interface_type=type(data))
Scott Bakerd865fa22018-11-07 11:45:28 -08001015 stub = ponsim_pb2_grpc.XPonSimStub(self.get_channel())
Nikolay Titov176f1db2017-08-10 12:38:43 -04001016 _method = getattr(
1017 stub, self.xpon_ponsim_olt_itfs[method_name]['method_name'])
1018 if isinstance(data, TcontsConfigData):
1019 tcont_config = TcontInterfaceConfig()
1020 tcont_config.tconts_config_data.CopyFrom(data)
1021 tcont_config.traffic_descriptor_profile_config_data.CopyFrom(
1022 data2)
1023 _method(tcont_config)
1024 else:
1025 _method(interfaceConfig)
Nikolay Titov89004ec2017-06-19 18:22:42 -04001026 self.log.info('success')
1027
Nikolay Titov176f1db2017-08-10 12:38:43 -04001028 def create_interface(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001029 _method_name = sys._getframe().f_code.co_name
1030 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001031
Nikolay Titov89004ec2017-06-19 18:22:42 -04001032 def update_interface(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001033 _method_name = sys._getframe().f_code.co_name
1034 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov89004ec2017-06-19 18:22:42 -04001035
1036 def remove_interface(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001037 _method_name = sys._getframe().f_code.co_name
1038 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001039
1040 def create_tcont(self, tcont_data, traffic_descriptor_data):
khenaidoof3593a82018-06-01 16:41:31 -04001041 _method_name = sys._getframe().f_code.co_name
1042 self.xpon_ponsim_olt_interface(_method_name, tcont_data,
1043 traffic_descriptor_data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001044
1045 def update_tcont(self, tcont_data, traffic_descriptor_data):
khenaidoof3593a82018-06-01 16:41:31 -04001046 _method_name = sys._getframe().f_code.co_name
1047 self.xpon_ponsim_olt_interface(_method_name, tcont_data,
1048 traffic_descriptor_data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001049
1050 def remove_tcont(self, tcont_data, traffic_descriptor_data):
khenaidoof3593a82018-06-01 16:41:31 -04001051 _method_name = sys._getframe().f_code.co_name
1052 self.xpon_ponsim_olt_interface(_method_name, tcont_data,
1053 traffic_descriptor_data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001054
1055 def create_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001056 _method_name = sys._getframe().f_code.co_name
1057 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001058
1059 def update_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001060 _method_name = sys._getframe().f_code.co_name
1061 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001062
1063 def remove_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001064 _method_name = sys._getframe().f_code.co_name
1065 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001066
1067 def create_multicast_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001068 _method_name = sys._getframe().f_code.co_name
1069 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001070
1071 def update_multicast_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001072 _method_name = sys._getframe().f_code.co_name
1073 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001074
1075 def remove_multicast_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001076 _method_name = sys._getframe().f_code.co_name
1077 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001078
1079 def create_multicast_distribution_set(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001080 _method_name = sys._getframe().f_code.co_name
1081 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001082
1083 def update_multicast_distribution_set(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001084 _method_name = sys._getframe().f_code.co_name
1085 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001086
1087 def remove_multicast_distribution_set(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001088 _method_name = sys._getframe().f_code.co_name
1089 self.xpon_ponsim_olt_interface(_method_name, data);
Scott Baker50873cc2018-09-21 18:14:09 -07001090
1091 def simulate_alarm(self, alarm):
1092 # Ponsim_olt implements its own AdapterAlarms class, rather than using the Voltha alarm extension. Until that
1093 # has been reconciled, temporarily instantiate the Voltha alarm extension's AdapterAlarms here, for the
1094 # purpose of sending simulated alarms.
1095 alarms = VolthaAdapterAlarms(self.adapter_agent, self.device_id, self.logical_device_id)
1096 simulator = AdapterAlarmSimulator(alarms)
1097 simulator.simulate_alarm(alarm)