blob: 15c97fd258a5967b2bd30e21f26d614f4c4da2b1 [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
Andy Bavier97566512018-11-14 16:17:41 -070027import copy
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080028import structlog
Andy Bavierf365ea32019-05-30 16:40:50 -070029import hashlib
Andy Bavier904efcf2019-04-01 16:50:34 -070030from scapy.layers.l2 import Ether, Dot1Q, Dot1AD
Gamze Abaka53cc0a22019-01-31 12:06:11 +000031from scapy.layers.inet import IP, Raw
Stephane Barbarie35595062018-02-08 08:34:39 -050032from twisted.internet import reactor
Khen Nursimulud068d812017-03-06 11:44:18 -050033from twisted.internet.defer import inlineCallbacks
Stephane Barbarie35595062018-02-08 08:34:39 -050034from grpc._channel import _Rendezvous
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080035
36from common.frameio.frameio import BpfProgramFilter, hexify
Khen Nursimulud068d812017-03-06 11:44:18 -050037from common.utils.asleep import asleep
Sergio Slobodrian98eff412017-03-15 14:46:30 -040038from twisted.internet.task import LoopingCall
Shad Ansarid1aa9e72017-06-23 21:34:25 -070039from voltha.adapters.iadapter import OltAdapter
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080040from voltha.core.logical_device_agent import mac_str_to_tuple
41from voltha.protos import third_party
Jeff55a2f132018-08-05 21:10:41 -070042from voltha.protos import openflow_13_pb2 as ofp
Scott Bakerd865fa22018-11-07 11:45:28 -080043from voltha.protos import ponsim_pb2, ponsim_pb2_grpc
Shad Ansari14bcd992017-06-13 14:27:20 -070044from voltha.protos.common_pb2 import OperStatus, ConnectStatus, AdminState
Scott Baker50873cc2018-09-21 18:14:09 -070045from voltha.protos.common_pb2 import OperationResp
Shad Ansari14bcd992017-06-13 14:27:20 -070046from voltha.protos.device_pb2 import Port, Device, PmConfig, PmConfigs
Sergio Slobodrian98eff412017-03-15 14:46:30 -040047from voltha.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080048from google.protobuf.empty_pb2 import Empty
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080049from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice
Stephane Barbarie5253c652017-03-22 16:29:46 -040050from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
51 OFPPF_1GB_FD, \
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080052 OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS, \
53 ofp_switch_features, ofp_desc
54from voltha.protos.openflow_13_pb2 import ofp_port
Scott Bakerb5be94d2018-10-09 16:13:32 -070055from voltha.protos.ponsim_pb2 import FlowTable, PonSimFrame, PonSimMetricsRequest
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080056from voltha.registry import registry
57
Nikolay Titov89004ec2017-06-19 18:22:42 -040058from voltha.protos.bbf_fiber_base_pb2 import \
khenaidoof3593a82018-06-01 16:41:31 -040059 ChannelgroupConfig, ChannelpartitionConfig, ChannelpairConfig, \
Nikolay Titov176f1db2017-08-10 12:38:43 -040060 ChannelterminationConfig, OntaniConfig, VOntaniConfig, VEnetConfig
61from voltha.protos.bbf_fiber_traffic_descriptor_profile_body_pb2 import \
62 TrafficDescriptorProfileData
63from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
64from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
65from voltha.protos.bbf_fiber_multicast_gemport_body_pb2 import \
66 MulticastGemportsConfigData
Stephane Barbarie35595062018-02-08 08:34:39 -050067
Nikolay Titov176f1db2017-08-10 12:38:43 -040068from voltha.protos.bbf_fiber_multicast_distribution_set_body_pb2 import \
69 MulticastDistributionSetData
Nikolay Titov89004ec2017-06-19 18:22:42 -040070
Nikolay Titov176f1db2017-08-10 12:38:43 -040071from voltha.protos.ponsim_pb2 import InterfaceConfig, TcontInterfaceConfig
Nikolay Titov89004ec2017-06-19 18:22:42 -040072
Scott Baker50873cc2018-09-21 18:14:09 -070073from voltha.extensions.alarms.adapter_alarms import AdapterAlarms as VolthaAdapterAlarms
74from voltha.extensions.alarms.simulator.simulate_alarms import AdapterAlarmSimulator
75
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080076_ = third_party
77log = structlog.get_logger()
78
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080079PACKET_IN_VLAN = 4000
80is_inband_frame = BpfProgramFilter('(ether[14:2] & 0xfff) = 0x{:03x}'.format(
81 PACKET_IN_VLAN))
82
Andy Bavier97566512018-11-14 16:17:41 -070083EAP_ETH_TYPE = 0x888e
84
85# Classifier
86ETH_TYPE = 'eth_type'
87TPID = 'tpid'
88IP_PROTO = 'ip_proto'
89IN_PORT = 'in_port'
90VLAN_VID = 'vlan_vid'
91VLAN_PCP = 'vlan_pcp'
92UDP_DST = 'udp_dst'
93UDP_SRC = 'udp_src'
94IPV4_DST = 'ipv4_dst'
95IPV4_SRC = 'ipv4_src'
96METADATA = 'metadata'
97OUTPUT = 'output'
Stephane Barbarie5253c652017-03-22 16:29:46 -040098
Sergio Slobodrian98eff412017-03-15 14:46:30 -040099class AdapterPmMetrics:
Stephane Barbarie5253c652017-03-22 16:29:46 -0400100 def __init__(self, device):
Sergio Slobodrianec6e3912017-04-02 11:46:55 -0400101 self.pm_names = {'tx_64_pkts', 'tx_65_127_pkts', 'tx_128_255_pkts',
102 'tx_256_511_pkts', 'tx_512_1023_pkts',
103 'tx_1024_1518_pkts', 'tx_1519_9k_pkts',
104 'rx_64_pkts', 'rx_65_127_pkts',
105 'rx_128_255_pkts', 'rx_256_511_pkts',
106 'rx_512_1023_pkts', 'rx_1024_1518_pkts',
107 'rx_1519_9k_pkts'}
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400108 self.device = device
109 self.id = device.id
110 self.name = 'ponsim_olt'
Stephane Barbarie5253c652017-03-22 16:29:46 -0400111 # self.id = "abc"
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400112 self.default_freq = 150
113 self.grouped = False
114 self.freq_override = False
115 self.pon_metrics_config = dict()
116 self.nni_metrics_config = dict()
117 self.lc = None
118 for m in self.pm_names:
119 self.pon_metrics_config[m] = PmConfig(name=m,
120 type=PmConfig.COUNTER,
121 enabled=True)
122 self.nni_metrics_config[m] = PmConfig(name=m,
123 type=PmConfig.COUNTER,
124 enabled=True)
125
126 def update(self, pm_config):
127 if self.default_freq != pm_config.default_freq:
128 # Update the callback to the new frequency.
129 self.default_freq = pm_config.default_freq
130 self.lc.stop()
Stephane Barbarie5253c652017-03-22 16:29:46 -0400131 self.lc.start(interval=self.default_freq / 10)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400132 for m in pm_config.metrics:
133 self.pon_metrics_config[m.name].enabled = m.enabled
134 self.nni_metrics_config[m.name].enabled = m.enabled
135
136 def make_proto(self):
137 pm_config = PmConfigs(
138 id=self.id,
139 default_freq=self.default_freq,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400140 grouped=False,
141 freq_override=False)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400142 for m in sorted(self.pon_metrics_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400143 pm = self.pon_metrics_config[m] # Either will do they're the same
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400144 pm_config.metrics.extend([PmConfig(name=pm.name,
145 type=pm.type,
146 enabled=pm.enabled)])
147 return pm_config
148
149 def collect_port_metrics(self, channel):
150 rtrn_port_metrics = dict()
Scott Bakerd865fa22018-11-07 11:45:28 -0800151 stub = ponsim_pb2_grpc.PonSimStub(channel)
Scott Bakerb5be94d2018-10-09 16:13:32 -0700152 stats = stub.GetStats(ponsim_pb2.PonSimMetricsRequest(port=0))
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400153 rtrn_port_metrics['pon'] = self.extract_pon_metrics(stats)
154 rtrn_port_metrics['nni'] = self.extract_nni_metrics(stats)
155 return rtrn_port_metrics
156
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400157 def extract_pon_metrics(self, stats):
158 rtrn_pon_metrics = dict()
159 for m in stats.metrics:
160 if m.port_name == "pon":
161 for p in m.packets:
162 if self.pon_metrics_config[p.name].enabled:
163 rtrn_pon_metrics[p.name] = p.value
164 return rtrn_pon_metrics
165
166 def extract_nni_metrics(self, stats):
167 rtrn_pon_metrics = dict()
168 for m in stats.metrics:
169 if m.port_name == "nni":
170 for p in m.packets:
171 if self.pon_metrics_config[p.name].enabled:
172 rtrn_pon_metrics[p.name] = p.value
173 return rtrn_pon_metrics
174
175 def start_collector(self, callback):
176 log.info("starting-pm-collection", device_name=self.name,
177 device_id=self.device.id)
178 prefix = 'voltha.{}.{}'.format(self.name, self.device.id)
179 self.lc = LoopingCall(callback, self.device.id, prefix)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400180 self.lc.start(interval=self.default_freq / 10)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400181
Stephane Barbarie35595062018-02-08 08:34:39 -0500182 def stop_collector(self):
183 log.info("stopping-pm-collection", device_name=self.name,
184 device_id=self.device.id)
185 self.lc.stop()
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800186
khenaidoof3593a82018-06-01 16:41:31 -0400187
Stephane Barbarie4475a252017-03-31 13:49:20 -0400188class AdapterAlarms:
189 def __init__(self, adapter, device):
190 self.adapter = adapter
191 self.device = device
192 self.lc = None
193
194 def send_alarm(self, context_data, alarm_data):
195 try:
196 current_context = {}
197 for key, value in context_data.__dict__.items():
198 current_context[key] = str(value)
199
200 alarm_event = self.adapter.adapter_agent.create_alarm(
201 resource_id=self.device.id,
khenaidoo032d3302017-06-09 14:50:04 -0400202 description="{}.{} - {}".format(self.adapter.name,
203 self.device.id,
204 alarm_data[
205 'description']) if 'description' in alarm_data else None,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400206 type=alarm_data['type'] if 'type' in alarm_data else None,
khenaidoo032d3302017-06-09 14:50:04 -0400207 category=alarm_data[
208 'category'] if 'category' in alarm_data else None,
209 severity=alarm_data[
210 'severity'] if 'severity' in alarm_data else None,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400211 state=alarm_data['state'] if 'state' in alarm_data else None,
212 raised_ts=alarm_data['ts'] if 'ts' in alarm_data else 0,
213 context=current_context
214 )
215
khenaidoo032d3302017-06-09 14:50:04 -0400216 self.adapter.adapter_agent.submit_alarm(self.device.id,
217 alarm_event)
Stephane Barbarie4475a252017-03-31 13:49:20 -0400218
219 except Exception as e:
220 log.exception('failed-to-send-alarm', e=e)
221
khenaidoof3593a82018-06-01 16:41:31 -0400222
Shad Ansarid1aa9e72017-06-23 21:34:25 -0700223class PonSimOltAdapter(OltAdapter):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800224 def __init__(self, adapter_agent, config):
Shad Ansari14bcd992017-06-13 14:27:20 -0700225 super(PonSimOltAdapter, self).__init__(adapter_agent=adapter_agent,
226 config=config,
Shad Ansari96f817b2017-06-18 23:17:44 -0700227 device_handler_class=PonSimOltHandler,
Shad Ansari14bcd992017-06-13 14:27:20 -0700228 name='ponsim_olt',
229 vendor='Voltha project',
Nikolay Titov89004ec2017-06-19 18:22:42 -0400230 version='0.4',
khenaidoof3593a82018-06-01 16:41:31 -0400231 device_type='ponsim_olt',
232 accepts_bulk_flow_update=True,
233 accepts_add_remove_flow_updates=False)
Shad Ansari96f817b2017-06-18 23:17:44 -0700234
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400235 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400236 log.info("adapter-update-pm-config", device=device,
237 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400238 handler = self.devices_handlers[device.id]
239 handler.update_pm_config(device, pm_config)
Sergio Slobodrianec864c62017-03-09 11:41:43 -0500240
Nikolay Titov89004ec2017-06-19 18:22:42 -0400241 def create_interface(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400242 if super(PonSimOltAdapter, self)._get_handler(device):
243 log.info('create-interface', device_id=device.id)
244 self.devices_handlers[device.id].create_interface(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400245
246 def update_interface(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400247 if super(PonSimOltAdapter, self)._get_handler(device):
248 log.info('update-interface', device_id=device.id)
249 self.devices_handlers[device.id].update_interface(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400250
251 def remove_interface(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400252 if super(PonSimOltAdapter, self)._get_handler(device):
253 log.info('remove-interface', device_id=device.id)
254 self.devices_handlers[device.id].remove_interface(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400255
Nikolay Titov176f1db2017-08-10 12:38:43 -0400256 def create_tcont(self, device, tcont_data, traffic_descriptor_data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400257 if super(PonSimOltAdapter, self)._get_handler(device):
258 log.info('create-tcont', device_id=device.id)
259 self.devices_handlers[device.id].create_tcont(
260 tcont_data, traffic_descriptor_data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400261
262 def update_tcont(self, device, tcont_data, traffic_descriptor_data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400263 if super(PonSimOltAdapter, self)._get_handler(device):
264 log.info('update-tcont', device_id=device.id)
265 self.devices_handlers[device.id].update_tcont(
266 tcont_data, traffic_descriptor_data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400267
268 def remove_tcont(self, device, tcont_data, traffic_descriptor_data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400269 if super(PonSimOltAdapter, self)._get_handler(device):
270 log.info('remove-tcont', device_id=device.id)
271 self.devices_handlers[device.id].remove_tcont(
272 tcont_data, traffic_descriptor_data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400273
274 def create_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400275 if super(PonSimOltAdapter, self)._get_handler(device):
276 log.info('create-gemport', device_id=device.id)
277 self.devices_handlers[device.id].create_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400278
279 def update_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400280 if super(PonSimOltAdapter, self)._get_handler(device):
281 log.info('update-gemport', device_id=device.id)
282 self.devices_handlers[device.id].update_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400283
284 def remove_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400285 if super(PonSimOltAdapter, self)._get_handler(device):
286 log.info('remove-gemport', device_id=device.id)
287 self.devices_handlers[device.id].remove_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400288
289 def create_multicast_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400290 if super(PonSimOltAdapter, self)._get_handler(device):
291 log.info('create-multicast-gemport', device_id=device.id)
292 self.devices_handlers[device.id].create_multicast_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400293
294 def update_multicast_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400295 if super(PonSimOltAdapter, self)._get_handler(device):
296 log.info('update-multicast-gemport', device_id=device.id)
297 self.devices_handlers[device.id].update_multicast_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400298
299 def remove_multicast_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400300 if super(PonSimOltAdapter, self)._get_handler(device):
301 log.info('remove-multicast-gemport', device_id=device.id)
302 self.devices_handlers[device.id].remove_multicast_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400303
304 def create_multicast_distribution_set(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400305 if super(PonSimOltAdapter, self)._get_handler(device):
306 log.info('create-multicast-distribution-set', device_id=device.id)
307 self.devices_handlers[device.id].create_multicast_distribution_set(
308 data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400309
310 def update_multicast_distribution_set(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400311 if super(PonSimOltAdapter, self)._get_handler(device):
312 log.info('update-multicast-distribution-set', device_id=device.id)
313 self.devices_handlers[device.id].update_multicast_distribution_set(
314 data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400315
316 def remove_multicast_distribution_set(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400317 if super(PonSimOltAdapter, self)._get_handler(device):
318 log.info('remove-multicast-distribution-set', device_id=device.id)
319 self.devices_handlers[device.id].remove_multicast_distribution_set(
320 data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400321
Scott Baker50873cc2018-09-21 18:14:09 -0700322 def simulate_alarm(self, device, alarm):
323 handler = self.devices_handlers[device.id]
324 handler.simulate_alarm(alarm)
325 return OperationResp(code=OperationResp.OPERATION_SUCCESS)
khenaidoof3593a82018-06-01 16:41:31 -0400326
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800327class PonSimOltHandler(object):
Nikolay Titov176f1db2017-08-10 12:38:43 -0400328 xpon_ponsim_olt_itfs = {
329 'create_interface': {
330 'method_name': 'CreateInterface',
331 'log': 'create-interface'},
332 'update_interface': {
333 'method_name': 'UpdateInterface',
334 'log': 'update-interface'},
335 'remove_interface': {
336 'method_name': 'RemoveInterface',
337 'log': 'remove-interface'},
338 'create_tcont': {
339 'method_name': 'CreateTcont',
340 'log': 'create-tconts-config-data'},
341 'update_tcont': {
342 'method_name': 'UpdateTcont',
343 'log': 'update-tconts-config-data'},
344 'remove_tcont': {
345 'method_name': 'RemoveTcont',
346 'log': 'remove-tconts-config-data'},
347 'create_gemport': {
348 'method_name': 'CreateGemport',
349 'log': 'create-gemports-config-data'},
350 'update_gemport': {
351 'method_name': 'UpdateGemport',
352 'log': 'update-gemports-config-data'},
353 'remove_gemport': {
354 'method_name': 'RemoveGemport',
355 'log': 'remove-gemports-config-data'},
356 'create_multicast_gemport': {
357 'method_name': 'CreateMulticastGemport',
358 'log': 'create-multicast-gemports-config-data'},
359 'update_multicast_gemport': {
360 'method_name': 'UpdateMulticastGemport',
361 'log': 'update-multicast-gemports-config-data'},
362 'remove_multicast_gemport': {
363 'method_name': 'RemoveMulticastGemport',
364 'log': 'remove-multicast-gemports-config-data'},
365 'create_multicast_distribution_set': {
366 'method_name': 'CreateMulticastDistributionSet',
367 'log': 'create-multicast-distribution-set-data'},
368 'update_multicast_distribution_set': {
369 'method_name': 'UpdateMulticastDistributionSet',
370 'log': 'update-multicast-distribution-set-data'},
371 'remove_multicast_distribution_set': {
372 'method_name': 'RemoveMulticastDistributionSet',
373 'log': 'remove-multicast-distribution-set-data'},
khenaidoof3593a82018-06-01 16:41:31 -0400374 }
Nikolay Titov176f1db2017-08-10 12:38:43 -0400375
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800376 def __init__(self, adapter, device_id):
377 self.adapter = adapter
378 self.adapter_agent = adapter.adapter_agent
379 self.device_id = device_id
380 self.log = structlog.get_logger(device_id=device_id)
381 self.channel = None
382 self.io_port = None
383 self.logical_device_id = None
Khen Nursimulud068d812017-03-06 11:44:18 -0500384 self.nni_port = None
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400385 self.ofp_port_no = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800386 self.interface = registry('main').get_args().interface
Stephane Barbarie35595062018-02-08 08:34:39 -0500387 self.ponsim_comm = registry('main').get_args().ponsim_comm
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400388 self.pm_metrics = None
Stephane Barbarie4475a252017-03-31 13:49:20 -0400389 self.alarms = None
Stephane Barbarie35595062018-02-08 08:34:39 -0500390 self.frames = None
Andy Bavier97566512018-11-14 16:17:41 -0700391 self.ctag_map = {}
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800392
393 def __del__(self):
394 if self.io_port is not None:
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800395 registry('frameio').close_port(self.io_port)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800396
397 def get_channel(self):
398 if self.channel is None:
399 device = self.adapter_agent.get_device(self.device_id)
schowdhury9e247752017-07-14 06:56:20 -0700400
Jeff70e8b2d2018-07-24 13:37:29 -0700401 self.channel = grpc.insecure_channel(device.host_and_port)
schowdhury9e247752017-07-14 06:56:20 -0700402
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800403 return self.channel
404
Stephane Barbarie35595062018-02-08 08:34:39 -0500405 def close_channel(self):
406 if self.channel is None:
407 self.log.info('grpc-channel-already-closed')
408 return
409 else:
410 if self.frames is not None:
411 self.frames.cancel()
412 self.frames = None
413 self.log.info('cancelled-grpc-frame-stream')
414
415 self.channel.unsubscribe(lambda *args: None)
416 self.channel = None
417
418 self.log.info('grpc-channel-closed')
419
khenaidoo032d3302017-06-09 14:50:04 -0400420 def _get_nni_port(self):
421 ports = self.adapter_agent.get_ports(self.device_id, Port.ETHERNET_NNI)
422 if ports:
423 # For now, we use on one NNI port
424 return ports[0]
425
Andy Bavierf365ea32019-05-30 16:40:50 -0700426 # Generate a MAC address based on OLT serial_number (i.e., host_and_port)
427 # An example of calculating the same value in the shell:
428 # $ echo -ne olt0.voltha.svc:50060 | md5sum | cut -c -12
429 def get_mac_address(self, device):
430 hexdig = hashlib.md5(device.serial_number).hexdigest()
431 mac_address = "%s:%s:%s:%s:%s:%s" % (hexdig[0:2], hexdig[2:4], hexdig[4:6], hexdig[6:8], hexdig[8:10], hexdig[10:12])
432 log.info("generated-mac-address", mac_address=mac_address, serial_number=device.serial_number)
433 return mac_address
434
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800435 def activate(self, device):
436 self.log.info('activating')
437
438 if not device.host_and_port:
439 device.oper_status = OperStatus.FAILED
440 device.reason = 'No host_and_port field provided'
441 self.adapter_agent.update_device(device)
442 return
443
Scott Bakerd865fa22018-11-07 11:45:28 -0800444 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800445 info = stub.GetDeviceInfo(Empty())
446 log.info('got-info', info=info)
447
448 device.root = True
449 device.vendor = 'ponsim'
Stephane Barbarie5253c652017-03-22 16:29:46 -0400450 device.model = 'n/a'
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800451 device.serial_number = device.host_and_port
452 device.connect_status = ConnectStatus.REACHABLE
453 self.adapter_agent.update_device(device)
454
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400455 # Now set the initial PM configuration for this device
Stephane Barbarie5253c652017-03-22 16:29:46 -0400456 self.pm_metrics = AdapterPmMetrics(device)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400457 pm_config = self.pm_metrics.make_proto()
458 log.info("initial-pm-config", pm_config=pm_config)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400459 self.adapter_agent.update_device_pm_config(pm_config, init=True)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400460
Stephane Barbarie4475a252017-03-31 13:49:20 -0400461 # Setup alarm handler
462 self.alarms = AdapterAlarms(self.adapter, device)
463
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800464 nni_port = Port(
Jonathan Hart32fe8812018-08-21 17:10:12 -0700465 port_no=info.nni_port,
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800466 label='NNI facing Ethernet port',
467 type=Port.ETHERNET_NNI,
468 admin_state=AdminState.ENABLED,
469 oper_status=OperStatus.ACTIVE
470 )
Khen Nursimulud068d812017-03-06 11:44:18 -0500471 self.nni_port = nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800472 self.adapter_agent.add_port(device.id, nni_port)
473 self.adapter_agent.add_port(device.id, Port(
474 port_no=1,
475 label='PON port',
476 type=Port.PON_OLT,
477 admin_state=AdminState.ENABLED,
478 oper_status=OperStatus.ACTIVE
479 ))
480
481 ld = LogicalDevice(
khenaidoo507d9222017-10-10 16:23:49 -0400482 # not setting id and datapath_id. Adapter agent will pick the id
483 # and will pick the datapath_id is it is not provided
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800484 desc=ofp_desc(
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800485 hw_desc='simualted pon',
486 sw_desc='simualted pon',
khenaidoof3593a82018-06-01 16:41:31 -0400487 # serial_num=uuid4().hex,
Jonathan Hart32fe8812018-08-21 17:10:12 -0700488 serial_num=device.serial_number,
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800489 dp_desc='n/a'
490 ),
491 switch_features=ofp_switch_features(
492 n_buffers=256, # TODO fake for now
493 n_tables=2, # TODO ditto
494 capabilities=( # TODO and ditto
khenaidoof3593a82018-06-01 16:41:31 -0400495 OFPC_FLOW_STATS
496 | OFPC_TABLE_STATS
497 | OFPC_PORT_STATS
498 | OFPC_GROUP_STATS
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800499 )
500 ),
501 root_device_id=device.id
502 )
Andy Bavierf365ea32019-05-30 16:40:50 -0700503
504 mac_address = self.get_mac_address(device)
khenaidoo507d9222017-10-10 16:23:49 -0400505 ld_initialized = self.adapter_agent.create_logical_device(ld,
506 dpid=mac_address)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800507 cap = OFPPF_1GB_FD | OFPPF_FIBER
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400508 self.ofp_port_no = info.nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800509 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
510 id='nni',
511 ofp_port=ofp_port(
512 port_no=info.nni_port,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400513 hw_addr=mac_str_to_tuple(
514 '00:00:00:00:00:%02x' % info.nni_port),
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800515 name='nni',
516 config=0,
517 state=OFPPS_LIVE,
518 curr=cap,
519 advertised=cap,
520 peer=cap,
521 curr_speed=OFPPF_1GB_FD,
522 max_speed=OFPPF_1GB_FD
523 ),
524 device_id=device.id,
525 device_port_no=nni_port.port_no,
526 root_port=True
527 ))
528
529 device = self.adapter_agent.get_device(device.id)
530 device.parent_id = ld_initialized.id
531 device.oper_status = OperStatus.ACTIVE
532 self.adapter_agent.update_device(device)
533 self.logical_device_id = ld_initialized.id
534
Andy Bavierea82b462018-07-27 16:48:13 -0700535 # register ONUS
536 for onu in info.onus:
537 vlan_id = onu.uni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800538 self.adapter_agent.child_device_detected(
539 parent_device_id=device.id,
540 parent_port_no=1,
Niren R Chidrawarefcebcd2017-07-19 20:03:39 -0400541 child_device_type='ponsim_onu',
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800542 proxy_address=Device.ProxyAddress(
543 device_id=device.id,
544 channel_id=vlan_id
545 ),
Nikolay Titov89004ec2017-06-19 18:22:42 -0400546 admin_state=AdminState.ENABLED,
Andy Bavierea82b462018-07-27 16:48:13 -0700547 vlan=vlan_id,
548 serial_number=onu.serial_number
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800549 )
550
Stephane Barbarie35595062018-02-08 08:34:39 -0500551 if self.ponsim_comm == 'grpc':
552 self.log.info('starting-frame-grpc-stream')
553 reactor.callInThread(self.rcv_grpc)
554 self.log.info('started-frame-grpc-stream')
555 else:
556 # finally, open the frameio port to receive in-band packet_in messages
557 self.log.info('registering-frameio')
558 self.io_port = registry('frameio').open_port(
559 self.interface, self.rcv_io, is_inband_frame)
560 self.log.info('registered-frameio')
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800561
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400562 # Start collecting stats from the device after a brief pause
563 self.start_kpi_collection(device.id)
564
khenaidoo032d3302017-06-09 14:50:04 -0400565 def reconcile(self, device):
566 self.log.info('reconciling-OLT-device-starts')
567
568 if not device.host_and_port:
569 device.oper_status = OperStatus.FAILED
570 device.reason = 'No host_and_port field provided'
571 self.adapter_agent.update_device(device)
572 return
573
574 try:
Scott Bakerd865fa22018-11-07 11:45:28 -0800575 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
khenaidoo032d3302017-06-09 14:50:04 -0400576 info = stub.GetDeviceInfo(Empty())
577 log.info('got-info', info=info)
578 # TODO: Verify we are connected to the same device we are
579 # reconciling - not much data in ponsim to differentiate at the
580 # time
581 device.oper_status = OperStatus.ACTIVE
582 self.adapter_agent.update_device(device)
583 self.ofp_port_no = info.nni_port
584 self.nni_port = self._get_nni_port()
585 except Exception, e:
586 log.exception('device-unreachable', e=e)
587 device.connect_status = ConnectStatus.UNREACHABLE
588 device.oper_status = OperStatus.UNKNOWN
589 self.adapter_agent.update_device(device)
590 return
591
592 # Now set the initial PM configuration for this device
593 self.pm_metrics = AdapterPmMetrics(device)
594 pm_config = self.pm_metrics.make_proto()
595 log.info("initial-pm-config", pm_config=pm_config)
596 self.adapter_agent.update_device_pm_config(pm_config, init=True)
597
598 # Setup alarm handler
599 self.alarms = AdapterAlarms(self.adapter, device)
600
601 # TODO: Is there anything required to verify nni and PON ports
602
603 # Set the logical device id
604 device = self.adapter_agent.get_device(device.id)
605 if device.parent_id:
606 self.logical_device_id = device.parent_id
607 self.adapter_agent.reconcile_logical_device(device.parent_id)
608 else:
609 self.log.info('no-logical-device-set')
610
611 # Reconcile child devices
612 self.adapter_agent.reconcile_child_devices(device.id)
613
Stephane Barbarie35595062018-02-08 08:34:39 -0500614 if self.ponsim_comm == 'grpc':
615 reactor.callInThread(self.rcv_grpc)
616 else:
617 # finally, open the frameio port to receive in-band packet_in messages
618 self.io_port = registry('frameio').open_port(
619 self.interface, self.rcv_io, is_inband_frame)
khenaidoo032d3302017-06-09 14:50:04 -0400620
621 # Start collecting stats from the device after a brief pause
622 self.start_kpi_collection(device.id)
623
624 self.log.info('reconciling-OLT-device-ends')
625
Stephane Barbarie35595062018-02-08 08:34:39 -0500626 def _rcv_frame(self, frame):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800627 pkt = Ether(frame)
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000628 self.log.info('received packet', pkt=pkt)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800629 if pkt.haslayer(Dot1Q):
Andy Bavier904efcf2019-04-01 16:50:34 -0700630 if pkt.haslayer(Dot1AD):
631 outer_shim = pkt.getlayer(Dot1AD)
632 else:
633 outer_shim = pkt.getlayer(Dot1Q)
Stephane Barbarie35595062018-02-08 08:34:39 -0500634
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000635 if pkt.haslayer(IP) or outer_shim.type == EAP_ETH_TYPE:
Andy Bavier904efcf2019-04-01 16:50:34 -0700636 # We don't have any context about the packet at this point.
637 # Assume that only downstream traffic is double-tagged.
638 if isinstance(outer_shim.payload, Dot1Q):
639 logical_port = int(self.nni_port.port_no)
640 else:
641 cvid = outer_shim.vlan
642 logical_port = self.get_subscriber_uni_port(cvid)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800643 popped_frame = (
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000644 Ether(src=pkt.src, dst=pkt.dst, type=outer_shim.type) /
645 outer_shim.payload
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800646 )
647 kw = dict(
648 logical_device_id=self.logical_device_id,
649 logical_port_no=logical_port,
650 )
651 self.log.info('sending-packet-in', **kw)
652 self.adapter_agent.send_packet_in(
653 packet=str(popped_frame), **kw)
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000654
Stephane Barbarie4475a252017-03-31 13:49:20 -0400655 elif pkt.haslayer(Raw):
656 raw_data = json.loads(pkt.getlayer(Raw).load)
657 self.alarms.send_alarm(self, raw_data)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800658
Stephane Barbarie35595062018-02-08 08:34:39 -0500659 @inlineCallbacks
660 def rcv_grpc(self):
661 """
662 This call establishes a GRPC stream to receive frames.
663 """
Scott Bakerd865fa22018-11-07 11:45:28 -0800664 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Stephane Barbarie35595062018-02-08 08:34:39 -0500665
666 # Attempt to establish a grpc stream with the remote ponsim service
667 self.frames = stub.ReceiveFrames(Empty())
668
669 self.log.info('start-receiving-grpc-frames')
670
671 try:
672 for frame in self.frames:
khenaidoof3593a82018-06-01 16:41:31 -0400673 self.log.info('received-grpc-frame',
674 frame_len=len(frame.payload))
Stephane Barbarie35595062018-02-08 08:34:39 -0500675 self._rcv_frame(frame.payload)
676
677 except _Rendezvous, e:
khenaidoof3593a82018-06-01 16:41:31 -0400678 log.warn('grpc-connection-lost', message=e.message)
Stephane Barbarie35595062018-02-08 08:34:39 -0500679
680 self.log.info('stopped-receiving-grpc-frames')
681
682 def rcv_io(self, port, frame):
683 self.log.info('received-io-frame', iface_name=port.iface_name,
684 frame_len=len(frame))
685 self._rcv_frame(frame)
686
Andy Bavier97566512018-11-14 16:17:41 -0700687 def to_controller(self, flow):
688 for action in fd.get_actions(flow):
689 if action.type == ofp.OFPAT_OUTPUT:
690 action.output.port = ofp.OFPP_CONTROLLER
691 self.log.info('sending flow to controller')
692
Andy Bavier97566512018-11-14 16:17:41 -0700693 # Lookup UNI port for a particular subscriber ctag
694 def get_subscriber_uni_port(self, ctag):
Andy Bavier86b1f482019-06-12 13:29:43 -0700695 self.log.debug('get_subscriber_uni_port', ctag=ctag)
Andy Bavier97566512018-11-14 16:17:41 -0700696 c = int(ctag)
697 if c in self.ctag_map:
698 return self.ctag_map[c]
Andy Bavier86b1f482019-06-12 13:29:43 -0700699 elif self.is_uni_port(c):
700 return c
701 self.log.debug('get_subscriber_uni_port: no mapping found', ctag=ctag, ctag_map=self.ctag_map)
702 return None
Andy Bavier97566512018-11-14 16:17:41 -0700703
704 def update_ctag_map(self, ctag, uni_port):
Andy Bavier86b1f482019-06-12 13:29:43 -0700705 if ctag is None:
706 for (c, u) in self.ctag_map.iteritems():
707 if u == int(uni_port):
708 self.log.debug('deleting ctag mapping', ctag=c, uni_port=u)
709 del self.ctag_map[c]
710 return
711 else:
712 c = int(ctag)
713 u = int(uni_port)
714 if not self.is_uni_port(u):
715 self.log.warning('unknown UNI port', uni_port=u)
716 if c in self.ctag_map:
717 if self.ctag_map[c] == u:
718 return
719 else:
720 self.log.warning('changing UNI port for ctag',
721 ctag=c, old=self.ctag_map[c], new=u)
Andy Bavier97566512018-11-14 16:17:41 -0700722
Andy Bavier86b1f482019-06-12 13:29:43 -0700723 self.ctag_map[c] = u
724 self.log.debug('added mapping', ctag=c, uni_port=u)
Andy Bavier97566512018-11-14 16:17:41 -0700725
726 def is_uni_port(self, vlan_id):
Andy Bavier86b1f482019-06-12 13:29:43 -0700727 for onu in self.adapter_agent.get_child_devices(self.device_id):
728 if onu.vlan == vlan_id:
729 return True
730 return False
Andy Bavier97566512018-11-14 16:17:41 -0700731
Andy Bavier86b1f482019-06-12 13:29:43 -0700732 def get_classifier_info(self, flow):
733 classifier_info = {}
734 for field in fd.get_ofb_fields(flow):
735 if field.type == fd.ETH_TYPE:
736 classifier_info[ETH_TYPE] = field.eth_type
737 elif field.type == fd.IP_PROTO:
738 classifier_info[IP_PROTO] = field.ip_proto
739 elif field.type == fd.IN_PORT:
740 classifier_info[IN_PORT] = field.port
741 elif field.type == fd.VLAN_VID:
742 classifier_info[VLAN_VID] = field.vlan_vid & 0xfff
743 elif field.type == fd.VLAN_PCP:
744 classifier_info[VLAN_PCP] = field.vlan_pcp
745 elif field.type == fd.UDP_DST:
746 classifier_info[UDP_DST] = field.udp_dst
747 elif field.type == fd.UDP_SRC:
748 classifier_info[UDP_SRC] = field.udp_src
749 elif field.type == fd.IPV4_DST:
750 classifier_info[IPV4_DST] = field.ipv4_dst
751 elif field.type == fd.IPV4_SRC:
752 classifier_info[IPV4_SRC] = field.ipv4_src
753 elif field.type == fd.METADATA:
754 classifier_info[METADATA] = field.table_metadata
755 else:
756 self.log.debug('field-type-unhandled field.type={}'.format(
757 field.type))
758
759 return classifier_info
Andy Bavier97566512018-11-14 16:17:41 -0700760
Jeff55a2f132018-08-05 21:10:41 -0700761 # VOLTHA's flow decomposition removes the information about which flows
762 # are trap flows where traffic should be forwarded to the controller.
763 # We'll go through the flows and change the output port of flows that we
764 # know to be trap flows to the OF CONTROLLER port.
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800765 def update_flow_table(self, flows):
Scott Bakerd865fa22018-11-07 11:45:28 -0800766 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800767 self.log.info('pushing-olt-flow-table')
Andy Bavier97566512018-11-14 16:17:41 -0700768
Andy Bavier97566512018-11-14 16:17:41 -0700769 eapol_flows = {}
Andy Bavier4df0eb42019-04-23 15:54:17 -0700770 eapol_flow_without_vlan = False
Andy Bavier97566512018-11-14 16:17:41 -0700771
Jeff55a2f132018-08-05 21:10:41 -0700772 for flow in flows:
Andy Bavier86b1f482019-06-12 13:29:43 -0700773 classifier_info = self.get_classifier_info(flow)
Andy Bavier97566512018-11-14 16:17:41 -0700774
775 self.log.debug('classifier_info', classifier_info=classifier_info)
776
777 if IP_PROTO in classifier_info:
778 if classifier_info[IP_PROTO] == 17:
779 if UDP_SRC in classifier_info:
780 if classifier_info[UDP_SRC] == 68:
781 self.log.info('dhcp upstream flow add')
Andy Bavier97566512018-11-14 16:17:41 -0700782 elif classifier_info[UDP_SRC] == 67:
783 self.log.info('dhcp downstream flow add')
784 self.to_controller(flow)
785 elif classifier_info[IP_PROTO] == 2:
786 self.log.info('igmp flow add')
787 self.to_controller(flow)
788 else:
789 self.log.warn("Invalid-Classifier-to-handle",
790 classifier_info=classifier_info)
791 elif ETH_TYPE in classifier_info:
792 if classifier_info[ETH_TYPE] == EAP_ETH_TYPE:
793 self.log.info('eapol flow add')
794 self.to_controller(flow)
795 if VLAN_VID in classifier_info:
796 eapol_flows[classifier_info[VLAN_VID]] = flow
Andy Bavier4df0eb42019-04-23 15:54:17 -0700797 else:
798 eapol_flow_without_vlan = True
Andy Bavier97566512018-11-14 16:17:41 -0700799
Andy Bavier4df0eb42019-04-23 15:54:17 -0700800 # The OLT app is now adding EAPOL flows with VLAN_VID=4091 but Ponsim can't
801 # properly handle this because it uses VLAN_VID to encode the UNI port ID.
802 # Add an EAPOL trap flow with no VLAN_VID match if we see the 4091 match.
803 if 4091 in eapol_flows and not eapol_flow_without_vlan:
804 new_eapol_flow = [
805 fd.mk_flow_stat(
806 priority=10000,
807 match_fields=[fd.in_port(1), fd.eth_type(EAP_ETH_TYPE)],
808 actions=[fd.output(ofp.OFPP_CONTROLLER)]
809 )
810 ]
811 flows.extend(new_eapol_flow)
812 self.log.info('add eapol flow with no VLAN_VID match')
813
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800814 stub.UpdateFlowTable(FlowTable(
815 port=0,
816 flows=flows
817 ))
818 self.log.info('success')
819
khenaidoof3593a82018-06-01 16:41:31 -0400820 def remove_from_flow_table(self, flows):
821 self.log.debug('remove-from-flow-table', flows=flows)
822 # TODO: Update PONSIM code to accept incremental flow changes
823 # Once completed, the accepts_add_remove_flow_updates for this
824 # device type can be set to True
825
826 def add_to_flow_table(self, flows):
827 self.log.debug('add-to-flow-table', flows=flows)
828 # TODO: Update PONSIM code to accept incremental flow changes
829 # Once completed, the accepts_add_remove_flow_updates for this
830 # device type can be set to True
831
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400832 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400833 log.info("handler-update-pm-config", device=device,
834 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400835 self.pm_metrics.update(pm_config)
836
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800837 def send_proxied_message(self, proxy_address, msg):
Zack Williams18357ed2018-11-14 10:41:08 -0700838 self.log.debug('sending-proxied-message')
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800839 if isinstance(msg, FlowTable):
Scott Bakerd865fa22018-11-07 11:45:28 -0800840 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800841 self.log.info('pushing-onu-flow-table', port=msg.port)
Andy Bavier86b1f482019-06-12 13:29:43 -0700842
843 # Extract ctag -> uni_port mapping from ONU flows.
844 # Below we assume that a downstream flow whose VLAN_VID is not
845 # equal to the logcal port is stripping the ctag.
846 # If we find such a flow we add the mapping to the ctag_map.
847 # Note that this wouldn't be necessary if we actually knew the logical
848 # port that an upstream packet arrived on.
849 logical_port_id = "uni-{}".format(msg.port)
850 logical_port = self.adapter_agent.get_logical_port(self.logical_device_id, logical_port_id)
851 if logical_port:
852 uni_port_id = logical_port.device_port_no
853 ctag = None
854
855 for flow in msg.flows:
856 classifier_info = self.get_classifier_info(flow)
857 self.log.debug('classifier_info', classifier_info=classifier_info)
858
859 if VLAN_VID in classifier_info and IN_PORT in classifier_info:
860 if classifier_info[IN_PORT] != uni_port_id and classifier_info[VLAN_VID] != msg.port:
861 if ctag is not None:
862 self.log.error('more than one ctag inferred', ctag1=ctag, ctag2=classifier_info[VLAN_VID])
863 ctag = classifier_info[VLAN_VID]
864
865 self.update_ctag_map(ctag, msg.port)
866 else:
867 self.log.error('no logical port found', id=logical_port_id)
868
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800869 res = stub.UpdateFlowTable(msg)
870 self.adapter_agent.receive_proxied_message(proxy_address, res)
Scott Bakerb5be94d2018-10-09 16:13:32 -0700871 elif isinstance(msg, PonSimMetricsRequest):
Scott Bakerd865fa22018-11-07 11:45:28 -0800872 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Zack Williams18357ed2018-11-14 10:41:08 -0700873 self.log.debug('proxying onu stats request', port=msg.port)
Scott Bakerb5be94d2018-10-09 16:13:32 -0700874 res = stub.GetStats(msg)
875 self.adapter_agent.receive_proxied_message(proxy_address, res)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800876
877 def packet_out(self, egress_port, msg):
Zack Williams18357ed2018-11-14 10:41:08 -0700878 self.log.debug('sending-packet-out', egress_port=egress_port,
879 msg_hex=hexify(msg))
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800880 pkt = Ether(msg)
Jonathan Hart32fe8812018-08-21 17:10:12 -0700881 out_pkt = pkt
Andy Bavier97566512018-11-14 16:17:41 -0700882 self.log.debug("packet_out: incoming: %s" % pkt.summary())
Jonathan Hart32fe8812018-08-21 17:10:12 -0700883 if egress_port != self.nni_port.port_no:
884 # don't do the vlan manipulation for the NNI port, vlans are already correct
Andy Bavier97566512018-11-14 16:17:41 -0700885 if pkt.haslayer(Dot1Q):
Andy Bavier904efcf2019-04-01 16:50:34 -0700886 if pkt.haslayer(Dot1AD):
887 outer_shim = pkt.getlayer(Dot1AD)
888 else:
889 outer_shim = pkt.getlayer(Dot1Q)
890 if isinstance(outer_shim.payload, Dot1Q):
891 # If double tag, remove the outer tag
892 out_pkt = (
893 Ether(src=pkt.src, dst=pkt.dst,
894 type=outer_shim.type) /
895 outer_shim.payload
896 )
897 else:
898 out_pkt = pkt
Andy Bavier97566512018-11-14 16:17:41 -0700899 else:
900 # Add egress port as VLAN tag
901 out_pkt = (
Jonathan Hart32fe8812018-08-21 17:10:12 -0700902 Ether(src=pkt.src, dst=pkt.dst) /
903 Dot1Q(vlan=egress_port, type=pkt.type) /
904 pkt.payload
Andy Bavier97566512018-11-14 16:17:41 -0700905 )
906 self.log.debug("packet_out: outgoing: %s" % out_pkt.summary())
Jonathan Hart32fe8812018-08-21 17:10:12 -0700907
908 # TODO need better way of mapping logical ports to PON ports
909 out_port = self.nni_port.port_no if egress_port == self.nni_port.port_no else 1
Stephane Barbarie35595062018-02-08 08:34:39 -0500910
911 if self.ponsim_comm == 'grpc':
912 # send over grpc stream
Scott Bakerd865fa22018-11-07 11:45:28 -0800913 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Jonathan Hart32fe8812018-08-21 17:10:12 -0700914 frame = PonSimFrame(id=self.device_id, payload=str(out_pkt), out_port=out_port)
Stephane Barbarie35595062018-02-08 08:34:39 -0500915 stub.SendFrame(frame)
916 else:
917 # send over frameio
918 self.io_port.send(str(out_pkt))
919
Khen Nursimulud068d812017-03-06 11:44:18 -0500920 @inlineCallbacks
921 def reboot(self):
922 self.log.info('rebooting', device_id=self.device_id)
923
924 # Update the operational status to ACTIVATING and connect status to
925 # UNREACHABLE
926 device = self.adapter_agent.get_device(self.device_id)
927 previous_oper_status = device.oper_status
928 previous_conn_status = device.connect_status
929 device.oper_status = OperStatus.ACTIVATING
930 device.connect_status = ConnectStatus.UNREACHABLE
931 self.adapter_agent.update_device(device)
932
khenaidoo71d0a6c2017-03-22 21:46:04 -0400933 # Update the child devices connect state to UNREACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400934 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400935 connect_status=ConnectStatus.UNREACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400936
Khen Nursimulud068d812017-03-06 11:44:18 -0500937 # Sleep 10 secs, simulating a reboot
Stephane Barbarie5253c652017-03-22 16:29:46 -0400938 # TODO: send alert and clear alert after the reboot
Khen Nursimulud068d812017-03-06 11:44:18 -0500939 yield asleep(10)
940
941 # Change the operational status back to its previous state. With a
942 # real OLT the operational state should be the state the device is
943 # after a reboot.
944 # Get the latest device reference
945 device = self.adapter_agent.get_device(self.device_id)
946 device.oper_status = previous_oper_status
947 device.connect_status = previous_conn_status
948 self.adapter_agent.update_device(device)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400949
950 # Update the child devices connect state to REACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400951 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400952 connect_status=ConnectStatus.REACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400953
Khen Nursimulud068d812017-03-06 11:44:18 -0500954 self.log.info('rebooted', device_id=self.device_id)
955
sathishg5ae86222017-06-28 15:16:29 +0530956 def self_test_device(self, device):
957 """
958 This is called to Self a device based on a NBI call.
959 :param device: A Voltha.Device object.
960 :return: Will return result of self test
961 """
962 log.info('self-test-device', device=device.id)
963 raise NotImplementedError()
964
Khen Nursimulud068d812017-03-06 11:44:18 -0500965 def disable(self):
966 self.log.info('disabling', device_id=self.device_id)
967
Stephane Barbarie35595062018-02-08 08:34:39 -0500968 self.stop_kpi_collection()
969
Khen Nursimulud068d812017-03-06 11:44:18 -0500970 # Get the latest device reference
971 device = self.adapter_agent.get_device(self.device_id)
972
973 # Update the operational status to UNKNOWN
974 device.oper_status = OperStatus.UNKNOWN
975 device.connect_status = ConnectStatus.UNREACHABLE
976 self.adapter_agent.update_device(device)
977
Khen Nursimulud068d812017-03-06 11:44:18 -0500978 # Disable all child devices first
khenaidoo2d7af132017-03-23 15:45:51 -0400979 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400980 admin_state=AdminState.DISABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500981
Khen Nursimulud068d812017-03-06 11:44:18 -0500982 # Remove the peer references from this device
983 self.adapter_agent.delete_all_peer_references(self.device_id)
984
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400985 # Set all ports to disabled
986 self.adapter_agent.disable_all_ports(self.device_id)
987
Stephane Barbarie35595062018-02-08 08:34:39 -0500988 self.close_channel()
989 self.log.info('disabled-grpc-channel')
990
991 if self.ponsim_comm == 'frameio':
992 # close the frameio port
993 registry('frameio').close_port(self.io_port)
994 self.log.info('disabled-frameio-port')
Khen Nursimulud068d812017-03-06 11:44:18 -0500995
Khen Nursimulud068d812017-03-06 11:44:18 -0500996 self.log.info('disabled', device_id=device.id)
997
Khen Nursimulud068d812017-03-06 11:44:18 -0500998 def reenable(self):
999 self.log.info('re-enabling', device_id=self.device_id)
1000
1001 # Get the latest device reference
1002 device = self.adapter_agent.get_device(self.device_id)
1003
khenaidoo032d3302017-06-09 14:50:04 -04001004 # Set the ofp_port_no and nni_port in case we bypassed the reconcile
1005 # process if the device was in DISABLED state on voltha restart
1006 if not self.ofp_port_no and not self.nni_port:
Scott Bakerd865fa22018-11-07 11:45:28 -08001007 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
khenaidoo032d3302017-06-09 14:50:04 -04001008 info = stub.GetDeviceInfo(Empty())
1009 log.info('got-info', info=info)
1010 self.ofp_port_no = info.nni_port
1011 self.nni_port = self._get_nni_port()
1012
Khen Nursimulud068d812017-03-06 11:44:18 -05001013 # Update the connect status to REACHABLE
1014 device.connect_status = ConnectStatus.REACHABLE
1015 self.adapter_agent.update_device(device)
1016
Khen Nursimuluc60afa12017-03-13 14:33:50 -04001017 # Set all ports to enabled
1018 self.adapter_agent.enable_all_ports(self.device_id)
1019
Khen Nursimulud068d812017-03-06 11:44:18 -05001020 device = self.adapter_agent.get_device(device.id)
Khen Nursimulud068d812017-03-06 11:44:18 -05001021 device.oper_status = OperStatus.ACTIVE
1022 self.adapter_agent.update_device(device)
Khen Nursimulud068d812017-03-06 11:44:18 -05001023
1024 # Reenable all child devices
khenaidoo2d7af132017-03-23 15:45:51 -04001025 self.adapter_agent.update_child_devices_state(device.id,
Stephane Barbarie4475a252017-03-31 13:49:20 -04001026 admin_state=AdminState.ENABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -05001027
Stephane Barbarie35595062018-02-08 08:34:39 -05001028 if self.ponsim_comm == 'grpc':
1029 # establish frame grpc-stream
1030 reactor.callInThread(self.rcv_grpc)
1031 else:
1032 # finally, open the frameio port to receive in-band packet_in messages
1033 self.io_port = registry('frameio').open_port(
1034 self.interface, self.rcv_io, is_inband_frame)
1035
1036 self.start_kpi_collection(device.id)
Khen Nursimulud068d812017-03-06 11:44:18 -05001037
1038 self.log.info('re-enabled', device_id=device.id)
1039
Khen Nursimulud068d812017-03-06 11:44:18 -05001040 def delete(self):
1041 self.log.info('deleting', device_id=self.device_id)
1042
1043 # Remove all child devices
1044 self.adapter_agent.delete_all_child_devices(self.device_id)
1045
Stephane Barbarie35595062018-02-08 08:34:39 -05001046 self.close_channel()
1047 self.log.info('disabled-grpc-channel')
1048
1049 if self.ponsim_comm == 'frameio':
1050 # close the frameio port
1051 registry('frameio').close_port(self.io_port)
1052 self.log.info('disabled-frameio-port')
1053
Khen Nursimulud068d812017-03-06 11:44:18 -05001054 # TODO:
1055 # 1) Remove all flows from the device
1056 # 2) Remove the device from ponsim
1057
1058 self.log.info('deleted', device_id=self.device_id)
Sergio Slobodrian98eff412017-03-15 14:46:30 -04001059
1060 def start_kpi_collection(self, device_id):
1061
1062 def _collect(device_id, prefix):
1063
1064 try:
1065 # Step 1: gather metrics from device
1066 port_metrics = \
Stephane Barbarie5253c652017-03-22 16:29:46 -04001067 self.pm_metrics.collect_port_metrics(self.get_channel())
Sergio Slobodrian98eff412017-03-15 14:46:30 -04001068
1069 # Step 2: prepare the KpiEvent for submission
1070 # we can time-stamp them here (or could use time derived from OLT
1071 ts = arrow.utcnow().timestamp
1072 kpi_event = KpiEvent(
1073 type=KpiEventType.slice,
1074 ts=ts,
1075 prefixes={
1076 # OLT NNI port
1077 prefix + '.nni': MetricValuePairs(
1078 metrics=port_metrics['nni']),
1079 # OLT PON port
1080 prefix + '.pon': MetricValuePairs(
1081 metrics=port_metrics['pon'])
1082 }
1083 )
1084
1085 # Step 3: submit
1086 self.adapter_agent.submit_kpis(kpi_event)
1087
1088 except Exception as e:
1089 log.exception('failed-to-submit-kpis', e=e)
Stephane Barbarie5253c652017-03-22 16:29:46 -04001090
Sergio Slobodrian98eff412017-03-15 14:46:30 -04001091 self.pm_metrics.start_collector(_collect)
Nikolay Titov89004ec2017-06-19 18:22:42 -04001092
Stephane Barbarie35595062018-02-08 08:34:39 -05001093 def stop_kpi_collection(self):
1094 self.pm_metrics.stop_collector()
1095
Nikolay Titov89004ec2017-06-19 18:22:42 -04001096 def get_interface_config(self, data):
1097 interfaceConfig = InterfaceConfig()
1098 if isinstance(data, ChannelgroupConfig):
1099 interfaceConfig.channel_group_config.CopyFrom(data)
1100 elif isinstance(data, ChannelpartitionConfig):
1101 interfaceConfig.channel_partition_config.CopyFrom(data)
1102 elif isinstance(data, ChannelpairConfig):
1103 interfaceConfig.channel_pair_config.CopyFrom(data)
1104 elif isinstance(data, ChannelterminationConfig):
1105 interfaceConfig.channel_termination_config.CopyFrom(data)
1106 elif isinstance(data, OntaniConfig):
1107 interfaceConfig.ont_ani_config.CopyFrom(data)
1108 elif isinstance(data, VOntaniConfig):
1109 interfaceConfig.vont_ani_config.CopyFrom(data)
1110 elif isinstance(data, VEnetConfig):
1111 interfaceConfig.venet_config.CopyFrom(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -04001112 elif isinstance(data, TrafficDescriptorProfileData):
1113 interfaceConfig.traffic_descriptor_profile_config_data.CopyFrom(
1114 data)
1115 elif isinstance(data, TcontsConfigData):
1116 interfaceConfig.tconts_config_data.CopyFrom(data)
1117 elif isinstance(data, GemportsConfigData):
1118 interfaceConfig.gemports_config_data.CopyFrom(data)
1119 elif isinstance(data, MulticastGemportsConfigData):
1120 interfaceConfig.multicast_gemports_config_data.CopyFrom(data)
1121 elif isinstance(data, MulticastDistributionSetData):
1122 interfaceConfig.multicast_distribution_set_data.CopyFrom(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -04001123 else:
1124 return None
1125 return interfaceConfig
1126
Nikolay Titov176f1db2017-08-10 12:38:43 -04001127 def xpon_ponsim_olt_interface(self, method_name, data, data2=None):
Nikolay Titov89004ec2017-06-19 18:22:42 -04001128 interfaceConfig = self.get_interface_config(data)
1129 if interfaceConfig is not None:
Nikolay Titov176f1db2017-08-10 12:38:43 -04001130 self.log.info(
1131 'forwarding-{}-request-to-olt-for-interface-type'
khenaidoof3593a82018-06-01 16:41:31 -04001132 .format(self.xpon_ponsim_olt_itfs[method_name]['log']),
Nikolay Titov176f1db2017-08-10 12:38:43 -04001133 interface_type=type(data))
Scott Bakerd865fa22018-11-07 11:45:28 -08001134 stub = ponsim_pb2_grpc.XPonSimStub(self.get_channel())
Nikolay Titov176f1db2017-08-10 12:38:43 -04001135 _method = getattr(
1136 stub, self.xpon_ponsim_olt_itfs[method_name]['method_name'])
1137 if isinstance(data, TcontsConfigData):
1138 tcont_config = TcontInterfaceConfig()
1139 tcont_config.tconts_config_data.CopyFrom(data)
1140 tcont_config.traffic_descriptor_profile_config_data.CopyFrom(
1141 data2)
1142 _method(tcont_config)
1143 else:
1144 _method(interfaceConfig)
Nikolay Titov89004ec2017-06-19 18:22:42 -04001145 self.log.info('success')
1146
Nikolay Titov176f1db2017-08-10 12:38:43 -04001147 def create_interface(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001148 _method_name = sys._getframe().f_code.co_name
1149 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001150
Nikolay Titov89004ec2017-06-19 18:22:42 -04001151 def update_interface(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001152 _method_name = sys._getframe().f_code.co_name
1153 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov89004ec2017-06-19 18:22:42 -04001154
1155 def remove_interface(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001156 _method_name = sys._getframe().f_code.co_name
1157 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001158
1159 def create_tcont(self, tcont_data, traffic_descriptor_data):
khenaidoof3593a82018-06-01 16:41:31 -04001160 _method_name = sys._getframe().f_code.co_name
1161 self.xpon_ponsim_olt_interface(_method_name, tcont_data,
1162 traffic_descriptor_data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001163
1164 def update_tcont(self, tcont_data, traffic_descriptor_data):
khenaidoof3593a82018-06-01 16:41:31 -04001165 _method_name = sys._getframe().f_code.co_name
1166 self.xpon_ponsim_olt_interface(_method_name, tcont_data,
1167 traffic_descriptor_data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001168
1169 def remove_tcont(self, tcont_data, traffic_descriptor_data):
khenaidoof3593a82018-06-01 16:41:31 -04001170 _method_name = sys._getframe().f_code.co_name
1171 self.xpon_ponsim_olt_interface(_method_name, tcont_data,
1172 traffic_descriptor_data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001173
1174 def create_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001175 _method_name = sys._getframe().f_code.co_name
1176 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001177
1178 def update_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001179 _method_name = sys._getframe().f_code.co_name
1180 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001181
1182 def remove_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001183 _method_name = sys._getframe().f_code.co_name
1184 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001185
1186 def create_multicast_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001187 _method_name = sys._getframe().f_code.co_name
1188 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001189
1190 def update_multicast_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001191 _method_name = sys._getframe().f_code.co_name
1192 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001193
1194 def remove_multicast_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001195 _method_name = sys._getframe().f_code.co_name
1196 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001197
1198 def create_multicast_distribution_set(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001199 _method_name = sys._getframe().f_code.co_name
1200 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001201
1202 def update_multicast_distribution_set(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001203 _method_name = sys._getframe().f_code.co_name
1204 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001205
1206 def remove_multicast_distribution_set(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001207 _method_name = sys._getframe().f_code.co_name
1208 self.xpon_ponsim_olt_interface(_method_name, data);
Scott Baker50873cc2018-09-21 18:14:09 -07001209
1210 def simulate_alarm(self, alarm):
1211 # Ponsim_olt implements its own AdapterAlarms class, rather than using the Voltha alarm extension. Until that
1212 # has been reconciled, temporarily instantiate the Voltha alarm extension's AdapterAlarms here, for the
1213 # purpose of sending simulated alarms.
1214 alarms = VolthaAdapterAlarms(self.adapter_agent, self.device_id, self.logical_device_id)
1215 simulator = AdapterAlarmSimulator(alarms)
1216 simulator.simulate_alarm(alarm)