blob: 2e78aba1b8f797f247153890163fa07012f6d4ae [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
29from scapy.layers.l2 import Ether, Dot1Q
Gamze Abaka53cc0a22019-01-31 12:06:11 +000030from scapy.layers.inet import IP, Raw
Stephane Barbarie35595062018-02-08 08:34:39 -050031from twisted.internet import reactor
Khen Nursimulud068d812017-03-06 11:44:18 -050032from twisted.internet.defer import inlineCallbacks
Stephane Barbarie35595062018-02-08 08:34:39 -050033from grpc._channel import _Rendezvous
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080034
35from common.frameio.frameio import BpfProgramFilter, hexify
Khen Nursimulud068d812017-03-06 11:44:18 -050036from common.utils.asleep import asleep
Sergio Slobodrian98eff412017-03-15 14:46:30 -040037from twisted.internet.task import LoopingCall
Shad Ansarid1aa9e72017-06-23 21:34:25 -070038from voltha.adapters.iadapter import OltAdapter
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080039from voltha.core.logical_device_agent import mac_str_to_tuple
40from voltha.protos import third_party
Jeff55a2f132018-08-05 21:10:41 -070041from voltha.protos import openflow_13_pb2 as ofp
Scott Bakerd865fa22018-11-07 11:45:28 -080042from voltha.protos import ponsim_pb2, ponsim_pb2_grpc
Shad Ansari14bcd992017-06-13 14:27:20 -070043from voltha.protos.common_pb2 import OperStatus, ConnectStatus, AdminState
Scott Baker50873cc2018-09-21 18:14:09 -070044from voltha.protos.common_pb2 import OperationResp
Shad Ansari14bcd992017-06-13 14:27:20 -070045from voltha.protos.device_pb2 import Port, Device, PmConfig, PmConfigs
Sergio Slobodrian98eff412017-03-15 14:46:30 -040046from voltha.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080047from google.protobuf.empty_pb2 import Empty
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080048from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice
Stephane Barbarie5253c652017-03-22 16:29:46 -040049from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
50 OFPPF_1GB_FD, \
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080051 OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS, \
52 ofp_switch_features, ofp_desc
53from voltha.protos.openflow_13_pb2 import ofp_port
Scott Bakerb5be94d2018-10-09 16:13:32 -070054from voltha.protos.ponsim_pb2 import FlowTable, PonSimFrame, PonSimMetricsRequest
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080055from voltha.registry import registry
56
Nikolay Titov89004ec2017-06-19 18:22:42 -040057from voltha.protos.bbf_fiber_base_pb2 import \
khenaidoof3593a82018-06-01 16:41:31 -040058 ChannelgroupConfig, ChannelpartitionConfig, ChannelpairConfig, \
Nikolay Titov176f1db2017-08-10 12:38:43 -040059 ChannelterminationConfig, OntaniConfig, VOntaniConfig, VEnetConfig
60from voltha.protos.bbf_fiber_traffic_descriptor_profile_body_pb2 import \
61 TrafficDescriptorProfileData
62from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
63from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
64from voltha.protos.bbf_fiber_multicast_gemport_body_pb2 import \
65 MulticastGemportsConfigData
Stephane Barbarie35595062018-02-08 08:34:39 -050066
Nikolay Titov176f1db2017-08-10 12:38:43 -040067from voltha.protos.bbf_fiber_multicast_distribution_set_body_pb2 import \
68 MulticastDistributionSetData
Nikolay Titov89004ec2017-06-19 18:22:42 -040069
Nikolay Titov176f1db2017-08-10 12:38:43 -040070from voltha.protos.ponsim_pb2 import InterfaceConfig, TcontInterfaceConfig
Nikolay Titov89004ec2017-06-19 18:22:42 -040071
Scott Baker50873cc2018-09-21 18:14:09 -070072from voltha.extensions.alarms.adapter_alarms import AdapterAlarms as VolthaAdapterAlarms
73from voltha.extensions.alarms.simulator.simulate_alarms import AdapterAlarmSimulator
74
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080075_ = third_party
76log = structlog.get_logger()
77
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080078PACKET_IN_VLAN = 4000
79is_inband_frame = BpfProgramFilter('(ether[14:2] & 0xfff) = 0x{:03x}'.format(
80 PACKET_IN_VLAN))
81
Andy Bavier97566512018-11-14 16:17:41 -070082EAP_ETH_TYPE = 0x888e
83
84# Classifier
85ETH_TYPE = 'eth_type'
86TPID = 'tpid'
87IP_PROTO = 'ip_proto'
88IN_PORT = 'in_port'
89VLAN_VID = 'vlan_vid'
90VLAN_PCP = 'vlan_pcp'
91UDP_DST = 'udp_dst'
92UDP_SRC = 'udp_src'
93IPV4_DST = 'ipv4_dst'
94IPV4_SRC = 'ipv4_src'
95METADATA = 'metadata'
96OUTPUT = 'output'
Stephane Barbarie5253c652017-03-22 16:29:46 -040097
Sergio Slobodrian98eff412017-03-15 14:46:30 -040098class AdapterPmMetrics:
Stephane Barbarie5253c652017-03-22 16:29:46 -040099 def __init__(self, device):
Sergio Slobodrianec6e3912017-04-02 11:46:55 -0400100 self.pm_names = {'tx_64_pkts', 'tx_65_127_pkts', 'tx_128_255_pkts',
101 'tx_256_511_pkts', 'tx_512_1023_pkts',
102 'tx_1024_1518_pkts', 'tx_1519_9k_pkts',
103 'rx_64_pkts', 'rx_65_127_pkts',
104 'rx_128_255_pkts', 'rx_256_511_pkts',
105 'rx_512_1023_pkts', 'rx_1024_1518_pkts',
106 'rx_1519_9k_pkts'}
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400107 self.device = device
108 self.id = device.id
109 self.name = 'ponsim_olt'
Stephane Barbarie5253c652017-03-22 16:29:46 -0400110 # self.id = "abc"
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400111 self.default_freq = 150
112 self.grouped = False
113 self.freq_override = False
114 self.pon_metrics_config = dict()
115 self.nni_metrics_config = dict()
116 self.lc = None
117 for m in self.pm_names:
118 self.pon_metrics_config[m] = PmConfig(name=m,
119 type=PmConfig.COUNTER,
120 enabled=True)
121 self.nni_metrics_config[m] = PmConfig(name=m,
122 type=PmConfig.COUNTER,
123 enabled=True)
124
125 def update(self, pm_config):
126 if self.default_freq != pm_config.default_freq:
127 # Update the callback to the new frequency.
128 self.default_freq = pm_config.default_freq
129 self.lc.stop()
Stephane Barbarie5253c652017-03-22 16:29:46 -0400130 self.lc.start(interval=self.default_freq / 10)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400131 for m in pm_config.metrics:
132 self.pon_metrics_config[m.name].enabled = m.enabled
133 self.nni_metrics_config[m.name].enabled = m.enabled
134
135 def make_proto(self):
136 pm_config = PmConfigs(
137 id=self.id,
138 default_freq=self.default_freq,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400139 grouped=False,
140 freq_override=False)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400141 for m in sorted(self.pon_metrics_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400142 pm = self.pon_metrics_config[m] # Either will do they're the same
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400143 pm_config.metrics.extend([PmConfig(name=pm.name,
144 type=pm.type,
145 enabled=pm.enabled)])
146 return pm_config
147
148 def collect_port_metrics(self, channel):
149 rtrn_port_metrics = dict()
Scott Bakerd865fa22018-11-07 11:45:28 -0800150 stub = ponsim_pb2_grpc.PonSimStub(channel)
Scott Bakerb5be94d2018-10-09 16:13:32 -0700151 stats = stub.GetStats(ponsim_pb2.PonSimMetricsRequest(port=0))
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400152 rtrn_port_metrics['pon'] = self.extract_pon_metrics(stats)
153 rtrn_port_metrics['nni'] = self.extract_nni_metrics(stats)
154 return rtrn_port_metrics
155
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400156 def extract_pon_metrics(self, stats):
157 rtrn_pon_metrics = dict()
158 for m in stats.metrics:
159 if m.port_name == "pon":
160 for p in m.packets:
161 if self.pon_metrics_config[p.name].enabled:
162 rtrn_pon_metrics[p.name] = p.value
163 return rtrn_pon_metrics
164
165 def extract_nni_metrics(self, stats):
166 rtrn_pon_metrics = dict()
167 for m in stats.metrics:
168 if m.port_name == "nni":
169 for p in m.packets:
170 if self.pon_metrics_config[p.name].enabled:
171 rtrn_pon_metrics[p.name] = p.value
172 return rtrn_pon_metrics
173
174 def start_collector(self, callback):
175 log.info("starting-pm-collection", device_name=self.name,
176 device_id=self.device.id)
177 prefix = 'voltha.{}.{}'.format(self.name, self.device.id)
178 self.lc = LoopingCall(callback, self.device.id, prefix)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400179 self.lc.start(interval=self.default_freq / 10)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400180
Stephane Barbarie35595062018-02-08 08:34:39 -0500181 def stop_collector(self):
182 log.info("stopping-pm-collection", device_name=self.name,
183 device_id=self.device.id)
184 self.lc.stop()
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800185
khenaidoof3593a82018-06-01 16:41:31 -0400186
Stephane Barbarie4475a252017-03-31 13:49:20 -0400187class AdapterAlarms:
188 def __init__(self, adapter, device):
189 self.adapter = adapter
190 self.device = device
191 self.lc = None
192
193 def send_alarm(self, context_data, alarm_data):
194 try:
195 current_context = {}
196 for key, value in context_data.__dict__.items():
197 current_context[key] = str(value)
198
199 alarm_event = self.adapter.adapter_agent.create_alarm(
200 resource_id=self.device.id,
khenaidoo032d3302017-06-09 14:50:04 -0400201 description="{}.{} - {}".format(self.adapter.name,
202 self.device.id,
203 alarm_data[
204 'description']) if 'description' in alarm_data else None,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400205 type=alarm_data['type'] if 'type' in alarm_data else None,
khenaidoo032d3302017-06-09 14:50:04 -0400206 category=alarm_data[
207 'category'] if 'category' in alarm_data else None,
208 severity=alarm_data[
209 'severity'] if 'severity' in alarm_data else None,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400210 state=alarm_data['state'] if 'state' in alarm_data else None,
211 raised_ts=alarm_data['ts'] if 'ts' in alarm_data else 0,
212 context=current_context
213 )
214
khenaidoo032d3302017-06-09 14:50:04 -0400215 self.adapter.adapter_agent.submit_alarm(self.device.id,
216 alarm_event)
Stephane Barbarie4475a252017-03-31 13:49:20 -0400217
218 except Exception as e:
219 log.exception('failed-to-send-alarm', e=e)
220
khenaidoof3593a82018-06-01 16:41:31 -0400221
Shad Ansarid1aa9e72017-06-23 21:34:25 -0700222class PonSimOltAdapter(OltAdapter):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800223 def __init__(self, adapter_agent, config):
Shad Ansari14bcd992017-06-13 14:27:20 -0700224 super(PonSimOltAdapter, self).__init__(adapter_agent=adapter_agent,
225 config=config,
Shad Ansari96f817b2017-06-18 23:17:44 -0700226 device_handler_class=PonSimOltHandler,
Shad Ansari14bcd992017-06-13 14:27:20 -0700227 name='ponsim_olt',
228 vendor='Voltha project',
Nikolay Titov89004ec2017-06-19 18:22:42 -0400229 version='0.4',
khenaidoof3593a82018-06-01 16:41:31 -0400230 device_type='ponsim_olt',
231 accepts_bulk_flow_update=True,
232 accepts_add_remove_flow_updates=False)
Shad Ansari96f817b2017-06-18 23:17:44 -0700233
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400234 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400235 log.info("adapter-update-pm-config", device=device,
236 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400237 handler = self.devices_handlers[device.id]
238 handler.update_pm_config(device, pm_config)
Sergio Slobodrianec864c62017-03-09 11:41:43 -0500239
Nikolay Titov89004ec2017-06-19 18:22:42 -0400240 def create_interface(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400241 if super(PonSimOltAdapter, self)._get_handler(device):
242 log.info('create-interface', device_id=device.id)
243 self.devices_handlers[device.id].create_interface(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400244
245 def update_interface(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400246 if super(PonSimOltAdapter, self)._get_handler(device):
247 log.info('update-interface', device_id=device.id)
248 self.devices_handlers[device.id].update_interface(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400249
250 def remove_interface(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400251 if super(PonSimOltAdapter, self)._get_handler(device):
252 log.info('remove-interface', device_id=device.id)
253 self.devices_handlers[device.id].remove_interface(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400254
Nikolay Titov176f1db2017-08-10 12:38:43 -0400255 def create_tcont(self, device, tcont_data, traffic_descriptor_data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400256 if super(PonSimOltAdapter, self)._get_handler(device):
257 log.info('create-tcont', device_id=device.id)
258 self.devices_handlers[device.id].create_tcont(
259 tcont_data, traffic_descriptor_data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400260
261 def update_tcont(self, device, tcont_data, traffic_descriptor_data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400262 if super(PonSimOltAdapter, self)._get_handler(device):
263 log.info('update-tcont', device_id=device.id)
264 self.devices_handlers[device.id].update_tcont(
265 tcont_data, traffic_descriptor_data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400266
267 def remove_tcont(self, device, tcont_data, traffic_descriptor_data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400268 if super(PonSimOltAdapter, self)._get_handler(device):
269 log.info('remove-tcont', device_id=device.id)
270 self.devices_handlers[device.id].remove_tcont(
271 tcont_data, traffic_descriptor_data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400272
273 def create_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400274 if super(PonSimOltAdapter, self)._get_handler(device):
275 log.info('create-gemport', device_id=device.id)
276 self.devices_handlers[device.id].create_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400277
278 def update_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400279 if super(PonSimOltAdapter, self)._get_handler(device):
280 log.info('update-gemport', device_id=device.id)
281 self.devices_handlers[device.id].update_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400282
283 def remove_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400284 if super(PonSimOltAdapter, self)._get_handler(device):
285 log.info('remove-gemport', device_id=device.id)
286 self.devices_handlers[device.id].remove_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400287
288 def create_multicast_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400289 if super(PonSimOltAdapter, self)._get_handler(device):
290 log.info('create-multicast-gemport', device_id=device.id)
291 self.devices_handlers[device.id].create_multicast_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400292
293 def update_multicast_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400294 if super(PonSimOltAdapter, self)._get_handler(device):
295 log.info('update-multicast-gemport', device_id=device.id)
296 self.devices_handlers[device.id].update_multicast_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400297
298 def remove_multicast_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400299 if super(PonSimOltAdapter, self)._get_handler(device):
300 log.info('remove-multicast-gemport', device_id=device.id)
301 self.devices_handlers[device.id].remove_multicast_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400302
303 def create_multicast_distribution_set(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400304 if super(PonSimOltAdapter, self)._get_handler(device):
305 log.info('create-multicast-distribution-set', device_id=device.id)
306 self.devices_handlers[device.id].create_multicast_distribution_set(
307 data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400308
309 def update_multicast_distribution_set(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400310 if super(PonSimOltAdapter, self)._get_handler(device):
311 log.info('update-multicast-distribution-set', device_id=device.id)
312 self.devices_handlers[device.id].update_multicast_distribution_set(
313 data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400314
315 def remove_multicast_distribution_set(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400316 if super(PonSimOltAdapter, self)._get_handler(device):
317 log.info('remove-multicast-distribution-set', device_id=device.id)
318 self.devices_handlers[device.id].remove_multicast_distribution_set(
319 data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400320
Scott Baker50873cc2018-09-21 18:14:09 -0700321 def simulate_alarm(self, device, alarm):
322 handler = self.devices_handlers[device.id]
323 handler.simulate_alarm(alarm)
324 return OperationResp(code=OperationResp.OPERATION_SUCCESS)
khenaidoof3593a82018-06-01 16:41:31 -0400325
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800326class PonSimOltHandler(object):
Nikolay Titov176f1db2017-08-10 12:38:43 -0400327 xpon_ponsim_olt_itfs = {
328 'create_interface': {
329 'method_name': 'CreateInterface',
330 'log': 'create-interface'},
331 'update_interface': {
332 'method_name': 'UpdateInterface',
333 'log': 'update-interface'},
334 'remove_interface': {
335 'method_name': 'RemoveInterface',
336 'log': 'remove-interface'},
337 'create_tcont': {
338 'method_name': 'CreateTcont',
339 'log': 'create-tconts-config-data'},
340 'update_tcont': {
341 'method_name': 'UpdateTcont',
342 'log': 'update-tconts-config-data'},
343 'remove_tcont': {
344 'method_name': 'RemoveTcont',
345 'log': 'remove-tconts-config-data'},
346 'create_gemport': {
347 'method_name': 'CreateGemport',
348 'log': 'create-gemports-config-data'},
349 'update_gemport': {
350 'method_name': 'UpdateGemport',
351 'log': 'update-gemports-config-data'},
352 'remove_gemport': {
353 'method_name': 'RemoveGemport',
354 'log': 'remove-gemports-config-data'},
355 'create_multicast_gemport': {
356 'method_name': 'CreateMulticastGemport',
357 'log': 'create-multicast-gemports-config-data'},
358 'update_multicast_gemport': {
359 'method_name': 'UpdateMulticastGemport',
360 'log': 'update-multicast-gemports-config-data'},
361 'remove_multicast_gemport': {
362 'method_name': 'RemoveMulticastGemport',
363 'log': 'remove-multicast-gemports-config-data'},
364 'create_multicast_distribution_set': {
365 'method_name': 'CreateMulticastDistributionSet',
366 'log': 'create-multicast-distribution-set-data'},
367 'update_multicast_distribution_set': {
368 'method_name': 'UpdateMulticastDistributionSet',
369 'log': 'update-multicast-distribution-set-data'},
370 'remove_multicast_distribution_set': {
371 'method_name': 'RemoveMulticastDistributionSet',
372 'log': 'remove-multicast-distribution-set-data'},
khenaidoof3593a82018-06-01 16:41:31 -0400373 }
Nikolay Titov176f1db2017-08-10 12:38:43 -0400374
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800375 def __init__(self, adapter, device_id):
376 self.adapter = adapter
377 self.adapter_agent = adapter.adapter_agent
378 self.device_id = device_id
379 self.log = structlog.get_logger(device_id=device_id)
380 self.channel = None
381 self.io_port = None
382 self.logical_device_id = None
Khen Nursimulud068d812017-03-06 11:44:18 -0500383 self.nni_port = None
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400384 self.ofp_port_no = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800385 self.interface = registry('main').get_args().interface
Stephane Barbarie35595062018-02-08 08:34:39 -0500386 self.ponsim_comm = registry('main').get_args().ponsim_comm
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400387 self.pm_metrics = None
Stephane Barbarie4475a252017-03-31 13:49:20 -0400388 self.alarms = None
Stephane Barbarie35595062018-02-08 08:34:39 -0500389 self.frames = None
Andy Bavier97566512018-11-14 16:17:41 -0700390 self.uni_ports = []
391 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
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800426 def activate(self, device):
427 self.log.info('activating')
428
429 if not device.host_and_port:
430 device.oper_status = OperStatus.FAILED
431 device.reason = 'No host_and_port field provided'
432 self.adapter_agent.update_device(device)
433 return
434
Scott Bakerd865fa22018-11-07 11:45:28 -0800435 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800436 info = stub.GetDeviceInfo(Empty())
437 log.info('got-info', info=info)
438
439 device.root = True
440 device.vendor = 'ponsim'
Stephane Barbarie5253c652017-03-22 16:29:46 -0400441 device.model = 'n/a'
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800442 device.serial_number = device.host_and_port
443 device.connect_status = ConnectStatus.REACHABLE
444 self.adapter_agent.update_device(device)
445
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400446 # Now set the initial PM configuration for this device
Stephane Barbarie5253c652017-03-22 16:29:46 -0400447 self.pm_metrics = AdapterPmMetrics(device)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400448 pm_config = self.pm_metrics.make_proto()
449 log.info("initial-pm-config", pm_config=pm_config)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400450 self.adapter_agent.update_device_pm_config(pm_config, init=True)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400451
Stephane Barbarie4475a252017-03-31 13:49:20 -0400452 # Setup alarm handler
453 self.alarms = AdapterAlarms(self.adapter, device)
454
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800455 nni_port = Port(
Jonathan Hart32fe8812018-08-21 17:10:12 -0700456 port_no=info.nni_port,
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800457 label='NNI facing Ethernet port',
458 type=Port.ETHERNET_NNI,
459 admin_state=AdminState.ENABLED,
460 oper_status=OperStatus.ACTIVE
461 )
Khen Nursimulud068d812017-03-06 11:44:18 -0500462 self.nni_port = nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800463 self.adapter_agent.add_port(device.id, nni_port)
464 self.adapter_agent.add_port(device.id, Port(
465 port_no=1,
466 label='PON port',
467 type=Port.PON_OLT,
468 admin_state=AdminState.ENABLED,
469 oper_status=OperStatus.ACTIVE
470 ))
471
472 ld = LogicalDevice(
khenaidoo507d9222017-10-10 16:23:49 -0400473 # not setting id and datapath_id. Adapter agent will pick the id
474 # and will pick the datapath_id is it is not provided
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800475 desc=ofp_desc(
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800476 hw_desc='simualted pon',
477 sw_desc='simualted pon',
khenaidoof3593a82018-06-01 16:41:31 -0400478 # serial_num=uuid4().hex,
Jonathan Hart32fe8812018-08-21 17:10:12 -0700479 serial_num=device.serial_number,
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800480 dp_desc='n/a'
481 ),
482 switch_features=ofp_switch_features(
483 n_buffers=256, # TODO fake for now
484 n_tables=2, # TODO ditto
485 capabilities=( # TODO and ditto
khenaidoof3593a82018-06-01 16:41:31 -0400486 OFPC_FLOW_STATS
487 | OFPC_TABLE_STATS
488 | OFPC_PORT_STATS
489 | OFPC_GROUP_STATS
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800490 )
491 ),
492 root_device_id=device.id
493 )
khenaidoo507d9222017-10-10 16:23:49 -0400494 mac_address = "AA:BB:CC:DD:EE:FF"
495 ld_initialized = self.adapter_agent.create_logical_device(ld,
496 dpid=mac_address)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800497 cap = OFPPF_1GB_FD | OFPPF_FIBER
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400498 self.ofp_port_no = info.nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800499 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
500 id='nni',
501 ofp_port=ofp_port(
502 port_no=info.nni_port,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400503 hw_addr=mac_str_to_tuple(
504 '00:00:00:00:00:%02x' % info.nni_port),
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800505 name='nni',
506 config=0,
507 state=OFPPS_LIVE,
508 curr=cap,
509 advertised=cap,
510 peer=cap,
511 curr_speed=OFPPF_1GB_FD,
512 max_speed=OFPPF_1GB_FD
513 ),
514 device_id=device.id,
515 device_port_no=nni_port.port_no,
516 root_port=True
517 ))
518
519 device = self.adapter_agent.get_device(device.id)
520 device.parent_id = ld_initialized.id
521 device.oper_status = OperStatus.ACTIVE
522 self.adapter_agent.update_device(device)
523 self.logical_device_id = ld_initialized.id
524
Andy Bavierea82b462018-07-27 16:48:13 -0700525 # register ONUS
526 for onu in info.onus:
527 vlan_id = onu.uni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800528 self.adapter_agent.child_device_detected(
529 parent_device_id=device.id,
530 parent_port_no=1,
Niren R Chidrawarefcebcd2017-07-19 20:03:39 -0400531 child_device_type='ponsim_onu',
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800532 proxy_address=Device.ProxyAddress(
533 device_id=device.id,
534 channel_id=vlan_id
535 ),
Nikolay Titov89004ec2017-06-19 18:22:42 -0400536 admin_state=AdminState.ENABLED,
Andy Bavierea82b462018-07-27 16:48:13 -0700537 vlan=vlan_id,
538 serial_number=onu.serial_number
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800539 )
Andy Bavier97566512018-11-14 16:17:41 -0700540 self.uni_ports.append(int(onu.uni_port))
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800541
Stephane Barbarie35595062018-02-08 08:34:39 -0500542 if self.ponsim_comm == 'grpc':
543 self.log.info('starting-frame-grpc-stream')
544 reactor.callInThread(self.rcv_grpc)
545 self.log.info('started-frame-grpc-stream')
546 else:
547 # finally, open the frameio port to receive in-band packet_in messages
548 self.log.info('registering-frameio')
549 self.io_port = registry('frameio').open_port(
550 self.interface, self.rcv_io, is_inband_frame)
551 self.log.info('registered-frameio')
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800552
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400553 # Start collecting stats from the device after a brief pause
554 self.start_kpi_collection(device.id)
555
khenaidoo032d3302017-06-09 14:50:04 -0400556 def reconcile(self, device):
557 self.log.info('reconciling-OLT-device-starts')
558
559 if not device.host_and_port:
560 device.oper_status = OperStatus.FAILED
561 device.reason = 'No host_and_port field provided'
562 self.adapter_agent.update_device(device)
563 return
564
565 try:
Scott Bakerd865fa22018-11-07 11:45:28 -0800566 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
khenaidoo032d3302017-06-09 14:50:04 -0400567 info = stub.GetDeviceInfo(Empty())
568 log.info('got-info', info=info)
569 # TODO: Verify we are connected to the same device we are
570 # reconciling - not much data in ponsim to differentiate at the
571 # time
572 device.oper_status = OperStatus.ACTIVE
573 self.adapter_agent.update_device(device)
574 self.ofp_port_no = info.nni_port
575 self.nni_port = self._get_nni_port()
576 except Exception, e:
577 log.exception('device-unreachable', e=e)
578 device.connect_status = ConnectStatus.UNREACHABLE
579 device.oper_status = OperStatus.UNKNOWN
580 self.adapter_agent.update_device(device)
581 return
582
583 # Now set the initial PM configuration for this device
584 self.pm_metrics = AdapterPmMetrics(device)
585 pm_config = self.pm_metrics.make_proto()
586 log.info("initial-pm-config", pm_config=pm_config)
587 self.adapter_agent.update_device_pm_config(pm_config, init=True)
588
589 # Setup alarm handler
590 self.alarms = AdapterAlarms(self.adapter, device)
591
592 # TODO: Is there anything required to verify nni and PON ports
593
594 # Set the logical device id
595 device = self.adapter_agent.get_device(device.id)
596 if device.parent_id:
597 self.logical_device_id = device.parent_id
598 self.adapter_agent.reconcile_logical_device(device.parent_id)
599 else:
600 self.log.info('no-logical-device-set')
601
602 # Reconcile child devices
603 self.adapter_agent.reconcile_child_devices(device.id)
604
Stephane Barbarie35595062018-02-08 08:34:39 -0500605 if self.ponsim_comm == 'grpc':
606 reactor.callInThread(self.rcv_grpc)
607 else:
608 # finally, open the frameio port to receive in-band packet_in messages
609 self.io_port = registry('frameio').open_port(
610 self.interface, self.rcv_io, is_inband_frame)
khenaidoo032d3302017-06-09 14:50:04 -0400611
612 # Start collecting stats from the device after a brief pause
613 self.start_kpi_collection(device.id)
614
615 self.log.info('reconciling-OLT-device-ends')
616
Stephane Barbarie35595062018-02-08 08:34:39 -0500617 def _rcv_frame(self, frame):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800618 pkt = Ether(frame)
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000619 self.log.info('received packet', pkt=pkt)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800620 if pkt.haslayer(Dot1Q):
621 outer_shim = pkt.getlayer(Dot1Q)
Stephane Barbarie35595062018-02-08 08:34:39 -0500622
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000623 if pkt.haslayer(IP) or outer_shim.type == EAP_ETH_TYPE:
624 cvid = outer_shim.vlan
Andy Bavier97566512018-11-14 16:17:41 -0700625 logical_port = self.get_subscriber_uni_port(cvid)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800626 popped_frame = (
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000627 Ether(src=pkt.src, dst=pkt.dst, type=outer_shim.type) /
628 outer_shim.payload
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800629 )
630 kw = dict(
631 logical_device_id=self.logical_device_id,
632 logical_port_no=logical_port,
633 )
634 self.log.info('sending-packet-in', **kw)
635 self.adapter_agent.send_packet_in(
636 packet=str(popped_frame), **kw)
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000637
Stephane Barbarie4475a252017-03-31 13:49:20 -0400638 elif pkt.haslayer(Raw):
639 raw_data = json.loads(pkt.getlayer(Raw).load)
640 self.alarms.send_alarm(self, raw_data)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800641
Stephane Barbarie35595062018-02-08 08:34:39 -0500642 @inlineCallbacks
643 def rcv_grpc(self):
644 """
645 This call establishes a GRPC stream to receive frames.
646 """
Scott Bakerd865fa22018-11-07 11:45:28 -0800647 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Stephane Barbarie35595062018-02-08 08:34:39 -0500648
649 # Attempt to establish a grpc stream with the remote ponsim service
650 self.frames = stub.ReceiveFrames(Empty())
651
652 self.log.info('start-receiving-grpc-frames')
653
654 try:
655 for frame in self.frames:
khenaidoof3593a82018-06-01 16:41:31 -0400656 self.log.info('received-grpc-frame',
657 frame_len=len(frame.payload))
Stephane Barbarie35595062018-02-08 08:34:39 -0500658 self._rcv_frame(frame.payload)
659
660 except _Rendezvous, e:
khenaidoof3593a82018-06-01 16:41:31 -0400661 log.warn('grpc-connection-lost', message=e.message)
Stephane Barbarie35595062018-02-08 08:34:39 -0500662
663 self.log.info('stopped-receiving-grpc-frames')
664
665 def rcv_io(self, port, frame):
666 self.log.info('received-io-frame', iface_name=port.iface_name,
667 frame_len=len(frame))
668 self._rcv_frame(frame)
669
Andy Bavier97566512018-11-14 16:17:41 -0700670 def to_controller(self, flow):
671 for action in fd.get_actions(flow):
672 if action.type == ofp.OFPAT_OUTPUT:
673 action.output.port = ofp.OFPP_CONTROLLER
674 self.log.info('sending flow to controller')
675
676 # Lookup subscriber ctag for a particular PON port
677 def get_subscriber_ctag(self, flows, port):
678 self.log.debug('looking from subscriber flow for port', port=port)
679
680 for flow in flows:
681 in_port = fd.get_in_port(flow)
682 out_port = fd.get_out_port(flow)
683 if in_port == port and out_port == self.nni_port.port_no:
684 fields = fd.get_ofb_fields(flow)
685 self.log.debug('subscriber flow found', fields=fields)
686 for field in fields:
687 if field.type == fd.VLAN_VID:
688 self.log.debug('subscriber ctag found',
689 vlan_id=field.vlan_vid)
690 return field.vlan_vid & 0x0fff
691 self.log.debug('No subscriber flow found', port=port)
692 return None
693
694 # Lookup UNI port for a particular subscriber ctag
695 def get_subscriber_uni_port(self, ctag):
696 self.log.debug('get_subscriber_uni_port', ctag=ctag, ctag_map=self.ctag_map)
697 c = int(ctag)
698 if c in self.ctag_map:
699 return self.ctag_map[c]
Gamze Abaka53cc0a22019-01-31 12:06:11 +0000700 # return None
701 # HACK: temporarily pass atest
702 return int(128)
Andy Bavier97566512018-11-14 16:17:41 -0700703
704 def clear_ctag_map(self):
705 self.ctag_map = {}
706
707 def update_ctag_map(self, ctag, uni_port):
708 c = int(ctag)
709 u = int(uni_port)
710 if not self.is_uni_port(u):
711 self.log.warning('update_ctag_map: unknown UNI port', uni_port=u)
712 if c in self.ctag_map and self.ctag_map[c] != u:
713 self.log.warning('update_ctag_map: changing UNI port for ctag',
714 ctag=c, old=self.ctag_map[c], new=u)
715 self.ctag_map[c] = u
716
717 # Create a new flow that's a copy of the old flow but change the vlan_vid
718 # Used to create per-subscriber DHCP and EAPOL flows
719 def create_secondary_flow(self, flow, vlan_id):
720 secondary_flow = copy.deepcopy(flow)
721 for field in fd.get_ofb_fields(secondary_flow):
722 if field.type == fd.VLAN_VID:
723 field.vlan_vid = vlan_id | 0x1000
724 return secondary_flow
725
726 def is_uni_port(self, vlan_id):
727 return int(vlan_id) in self.uni_ports
728
729 def create_secondary_flows(self, trapflows, allflows, type):
730 secondary_flows = []
731 for vlan_vid, flow in trapflows.iteritems():
732 if self.is_uni_port(vlan_vid):
733 self.update_ctag_map(vlan_vid, vlan_vid)
734 ctag = self.get_subscriber_ctag(allflows, fd.get_in_port(flow))
735 if ctag is not None:
736 self.update_ctag_map(ctag, vlan_vid)
737 if ctag not in trapflows:
738 self.log.info('add secondary %s flow' % type, ctag=ctag)
739 secondary_flows.append(self.create_secondary_flow(flow, ctag))
740 return secondary_flows
741
Jeff55a2f132018-08-05 21:10:41 -0700742 # VOLTHA's flow decomposition removes the information about which flows
743 # are trap flows where traffic should be forwarded to the controller.
744 # We'll go through the flows and change the output port of flows that we
745 # know to be trap flows to the OF CONTROLLER port.
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800746 def update_flow_table(self, flows):
Scott Bakerd865fa22018-11-07 11:45:28 -0800747 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800748 self.log.info('pushing-olt-flow-table')
Andy Bavier97566512018-11-14 16:17:41 -0700749
750 self.clear_ctag_map()
751 dhcp_upstream_flows = {}
752 eapol_flows = {}
753 secondary_flows = []
754
Jeff55a2f132018-08-05 21:10:41 -0700755 for flow in flows:
756 classifier_info = {}
757 for field in fd.get_ofb_fields(flow):
758 if field.type == fd.ETH_TYPE:
Andy Bavier97566512018-11-14 16:17:41 -0700759 classifier_info[ETH_TYPE] = field.eth_type
Jeff55a2f132018-08-05 21:10:41 -0700760 elif field.type == fd.IP_PROTO:
Andy Bavier97566512018-11-14 16:17:41 -0700761 classifier_info[IP_PROTO] = field.ip_proto
762 elif field.type == fd.IN_PORT:
763 classifier_info[IN_PORT] = field.port
764 elif field.type == fd.VLAN_VID:
765 classifier_info[VLAN_VID] = field.vlan_vid & 0xfff
766 elif field.type == fd.VLAN_PCP:
767 classifier_info[VLAN_PCP] = field.vlan_pcp
768 elif field.type == fd.UDP_DST:
769 classifier_info[UDP_DST] = field.udp_dst
770 elif field.type == fd.UDP_SRC:
771 classifier_info[UDP_SRC] = field.udp_src
772 elif field.type == fd.IPV4_DST:
773 classifier_info[IPV4_DST] = field.ipv4_dst
774 elif field.type == fd.IPV4_SRC:
775 classifier_info[IPV4_SRC] = field.ipv4_src
776 elif field.type == fd.METADATA:
777 classifier_info[METADATA] = field.table_metadata
778 else:
779 self.log.debug('field-type-unhandled field.type={}'.format(
780 field.type))
781
782 self.log.debug('classifier_info', classifier_info=classifier_info)
783
784 if IP_PROTO in classifier_info:
785 if classifier_info[IP_PROTO] == 17:
786 if UDP_SRC in classifier_info:
787 if classifier_info[UDP_SRC] == 68:
788 self.log.info('dhcp upstream flow add')
789 if VLAN_VID in classifier_info:
790 dhcp_upstream_flows[classifier_info[VLAN_VID]] = flow
791 elif classifier_info[UDP_SRC] == 67:
792 self.log.info('dhcp downstream flow add')
793 self.to_controller(flow)
794 elif classifier_info[IP_PROTO] == 2:
795 self.log.info('igmp flow add')
796 self.to_controller(flow)
797 else:
798 self.log.warn("Invalid-Classifier-to-handle",
799 classifier_info=classifier_info)
800 elif ETH_TYPE in classifier_info:
801 if classifier_info[ETH_TYPE] == EAP_ETH_TYPE:
802 self.log.info('eapol flow add')
803 self.to_controller(flow)
804 if VLAN_VID in classifier_info:
805 eapol_flows[classifier_info[VLAN_VID]] = flow
806
Jeff55a2f132018-08-05 21:10:41 -0700807 self.log.info('out_port', out_port=fd.get_out_port(flow))
808
Andy Bavier97566512018-11-14 16:17:41 -0700809 flows.extend(self.create_secondary_flows(dhcp_upstream_flows, flows, "DHCP"))
810 flows.extend(self.create_secondary_flows(eapol_flows, flows, "EAPOL"))
811
812 self.log.debug('ctag_map', ctag_map=self.ctag_map)
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)
842 res = stub.UpdateFlowTable(msg)
843 self.adapter_agent.receive_proxied_message(proxy_address, res)
Scott Bakerb5be94d2018-10-09 16:13:32 -0700844 elif isinstance(msg, PonSimMetricsRequest):
Scott Bakerd865fa22018-11-07 11:45:28 -0800845 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Zack Williams18357ed2018-11-14 10:41:08 -0700846 self.log.debug('proxying onu stats request', port=msg.port)
Scott Bakerb5be94d2018-10-09 16:13:32 -0700847 res = stub.GetStats(msg)
848 self.adapter_agent.receive_proxied_message(proxy_address, res)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800849
850 def packet_out(self, egress_port, msg):
Zack Williams18357ed2018-11-14 10:41:08 -0700851 self.log.debug('sending-packet-out', egress_port=egress_port,
852 msg_hex=hexify(msg))
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800853 pkt = Ether(msg)
Jonathan Hart32fe8812018-08-21 17:10:12 -0700854 out_pkt = pkt
Andy Bavier97566512018-11-14 16:17:41 -0700855 self.log.debug("packet_out: incoming: %s" % pkt.summary())
Jonathan Hart32fe8812018-08-21 17:10:12 -0700856 if egress_port != self.nni_port.port_no:
857 # don't do the vlan manipulation for the NNI port, vlans are already correct
Andy Bavier97566512018-11-14 16:17:41 -0700858 if pkt.haslayer(Dot1Q):
859 # For QinQ-tagged packets from ONOS:
860 # - Outer header is 802.1AD
861 # - Inner header is 802.1Q
862 # - Send inner header and payload
863 payload = pkt.getlayer(Dot1Q)
864 out_pkt = (
865 Ether(src=pkt.src, dst=pkt.dst) /
866 payload
867 )
868 else:
869 # Add egress port as VLAN tag
870 out_pkt = (
Jonathan Hart32fe8812018-08-21 17:10:12 -0700871 Ether(src=pkt.src, dst=pkt.dst) /
872 Dot1Q(vlan=egress_port, type=pkt.type) /
873 pkt.payload
Andy Bavier97566512018-11-14 16:17:41 -0700874 )
875 self.log.debug("packet_out: outgoing: %s" % out_pkt.summary())
Jonathan Hart32fe8812018-08-21 17:10:12 -0700876
877 # TODO need better way of mapping logical ports to PON ports
878 out_port = self.nni_port.port_no if egress_port == self.nni_port.port_no else 1
Stephane Barbarie35595062018-02-08 08:34:39 -0500879
880 if self.ponsim_comm == 'grpc':
881 # send over grpc stream
Scott Bakerd865fa22018-11-07 11:45:28 -0800882 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
Jonathan Hart32fe8812018-08-21 17:10:12 -0700883 frame = PonSimFrame(id=self.device_id, payload=str(out_pkt), out_port=out_port)
Stephane Barbarie35595062018-02-08 08:34:39 -0500884 stub.SendFrame(frame)
885 else:
886 # send over frameio
887 self.io_port.send(str(out_pkt))
888
Khen Nursimulud068d812017-03-06 11:44:18 -0500889 @inlineCallbacks
890 def reboot(self):
891 self.log.info('rebooting', device_id=self.device_id)
892
893 # Update the operational status to ACTIVATING and connect status to
894 # UNREACHABLE
895 device = self.adapter_agent.get_device(self.device_id)
896 previous_oper_status = device.oper_status
897 previous_conn_status = device.connect_status
898 device.oper_status = OperStatus.ACTIVATING
899 device.connect_status = ConnectStatus.UNREACHABLE
900 self.adapter_agent.update_device(device)
901
khenaidoo71d0a6c2017-03-22 21:46:04 -0400902 # Update the child devices connect state to UNREACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400903 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400904 connect_status=ConnectStatus.UNREACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400905
Khen Nursimulud068d812017-03-06 11:44:18 -0500906 # Sleep 10 secs, simulating a reboot
Stephane Barbarie5253c652017-03-22 16:29:46 -0400907 # TODO: send alert and clear alert after the reboot
Khen Nursimulud068d812017-03-06 11:44:18 -0500908 yield asleep(10)
909
910 # Change the operational status back to its previous state. With a
911 # real OLT the operational state should be the state the device is
912 # after a reboot.
913 # Get the latest device reference
914 device = self.adapter_agent.get_device(self.device_id)
915 device.oper_status = previous_oper_status
916 device.connect_status = previous_conn_status
917 self.adapter_agent.update_device(device)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400918
919 # Update the child devices connect state to REACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400920 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400921 connect_status=ConnectStatus.REACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400922
Khen Nursimulud068d812017-03-06 11:44:18 -0500923 self.log.info('rebooted', device_id=self.device_id)
924
sathishg5ae86222017-06-28 15:16:29 +0530925 def self_test_device(self, device):
926 """
927 This is called to Self a device based on a NBI call.
928 :param device: A Voltha.Device object.
929 :return: Will return result of self test
930 """
931 log.info('self-test-device', device=device.id)
932 raise NotImplementedError()
933
Khen Nursimulud068d812017-03-06 11:44:18 -0500934 def disable(self):
935 self.log.info('disabling', device_id=self.device_id)
936
Stephane Barbarie35595062018-02-08 08:34:39 -0500937 self.stop_kpi_collection()
938
Khen Nursimulud068d812017-03-06 11:44:18 -0500939 # Get the latest device reference
940 device = self.adapter_agent.get_device(self.device_id)
941
942 # Update the operational status to UNKNOWN
943 device.oper_status = OperStatus.UNKNOWN
944 device.connect_status = ConnectStatus.UNREACHABLE
945 self.adapter_agent.update_device(device)
946
947 # Remove the logical device
948 logical_device = self.adapter_agent.get_logical_device(
Stephane Barbarie5253c652017-03-22 16:29:46 -0400949 self.logical_device_id)
Khen Nursimulud068d812017-03-06 11:44:18 -0500950 self.adapter_agent.delete_logical_device(logical_device)
951
952 # Disable all child devices first
khenaidoo2d7af132017-03-23 15:45:51 -0400953 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400954 admin_state=AdminState.DISABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500955
Khen Nursimulud068d812017-03-06 11:44:18 -0500956 # Remove the peer references from this device
957 self.adapter_agent.delete_all_peer_references(self.device_id)
958
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400959 # Set all ports to disabled
960 self.adapter_agent.disable_all_ports(self.device_id)
961
Stephane Barbarie35595062018-02-08 08:34:39 -0500962 self.close_channel()
963 self.log.info('disabled-grpc-channel')
964
965 if self.ponsim_comm == 'frameio':
966 # close the frameio port
967 registry('frameio').close_port(self.io_port)
968 self.log.info('disabled-frameio-port')
Khen Nursimulud068d812017-03-06 11:44:18 -0500969
khenaidoo032d3302017-06-09 14:50:04 -0400970 # Update the logice device mapping
971 if self.logical_device_id in \
972 self.adapter.logical_device_id_to_root_device_id:
973 del self.adapter.logical_device_id_to_root_device_id[
974 self.logical_device_id]
975
Khen Nursimulud068d812017-03-06 11:44:18 -0500976 # TODO:
977 # 1) Remove all flows from the device
978 # 2) Remove the device from ponsim
979
980 self.log.info('disabled', device_id=device.id)
981
Khen Nursimulud068d812017-03-06 11:44:18 -0500982 def reenable(self):
983 self.log.info('re-enabling', device_id=self.device_id)
984
985 # Get the latest device reference
986 device = self.adapter_agent.get_device(self.device_id)
987
khenaidoo032d3302017-06-09 14:50:04 -0400988 # Set the ofp_port_no and nni_port in case we bypassed the reconcile
989 # process if the device was in DISABLED state on voltha restart
990 if not self.ofp_port_no and not self.nni_port:
Scott Bakerd865fa22018-11-07 11:45:28 -0800991 stub = ponsim_pb2_grpc.PonSimStub(self.get_channel())
khenaidoo032d3302017-06-09 14:50:04 -0400992 info = stub.GetDeviceInfo(Empty())
993 log.info('got-info', info=info)
994 self.ofp_port_no = info.nni_port
995 self.nni_port = self._get_nni_port()
996
Khen Nursimulud068d812017-03-06 11:44:18 -0500997 # Update the connect status to REACHABLE
998 device.connect_status = ConnectStatus.REACHABLE
999 self.adapter_agent.update_device(device)
1000
Khen Nursimuluc60afa12017-03-13 14:33:50 -04001001 # Set all ports to enabled
1002 self.adapter_agent.enable_all_ports(self.device_id)
1003
Khen Nursimulud068d812017-03-06 11:44:18 -05001004 ld = LogicalDevice(
1005 # not setting id and datapth_id will let the adapter agent pick id
1006 desc=ofp_desc(
Khen Nursimulud068d812017-03-06 11:44:18 -05001007 hw_desc='simulated pon',
1008 sw_desc='simulated pon',
1009 serial_num=uuid4().hex,
1010 dp_desc='n/a'
1011 ),
1012 switch_features=ofp_switch_features(
1013 n_buffers=256, # TODO fake for now
1014 n_tables=2, # TODO ditto
1015 capabilities=( # TODO and ditto
khenaidoof3593a82018-06-01 16:41:31 -04001016 OFPC_FLOW_STATS
1017 | OFPC_TABLE_STATS
1018 | OFPC_PORT_STATS
1019 | OFPC_GROUP_STATS
Khen Nursimulud068d812017-03-06 11:44:18 -05001020 )
1021 ),
1022 root_device_id=device.id
1023 )
khenaidoo507d9222017-10-10 16:23:49 -04001024 mac_address = "AA:BB:CC:DD:EE:FF"
1025 ld_initialized = self.adapter_agent.create_logical_device(ld,
1026 dpid=mac_address)
Khen Nursimulud068d812017-03-06 11:44:18 -05001027 cap = OFPPF_1GB_FD | OFPPF_FIBER
1028 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
1029 id='nni',
1030 ofp_port=ofp_port(
Khen Nursimuluc60afa12017-03-13 14:33:50 -04001031 port_no=self.ofp_port_no,
Khen Nursimulud068d812017-03-06 11:44:18 -05001032 hw_addr=mac_str_to_tuple(
Khen Nursimuluc60afa12017-03-13 14:33:50 -04001033 '00:00:00:00:00:%02x' % self.ofp_port_no),
Khen Nursimulud068d812017-03-06 11:44:18 -05001034 name='nni',
1035 config=0,
1036 state=OFPPS_LIVE,
1037 curr=cap,
1038 advertised=cap,
1039 peer=cap,
1040 curr_speed=OFPPF_1GB_FD,
1041 max_speed=OFPPF_1GB_FD
1042 ),
1043 device_id=device.id,
1044 device_port_no=self.nni_port.port_no,
1045 root_port=True
1046 ))
1047
1048 device = self.adapter_agent.get_device(device.id)
1049 device.parent_id = ld_initialized.id
1050 device.oper_status = OperStatus.ACTIVE
1051 self.adapter_agent.update_device(device)
1052 self.logical_device_id = ld_initialized.id
1053
1054 # Reenable all child devices
khenaidoo2d7af132017-03-23 15:45:51 -04001055 self.adapter_agent.update_child_devices_state(device.id,
Stephane Barbarie4475a252017-03-31 13:49:20 -04001056 admin_state=AdminState.ENABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -05001057
Stephane Barbarie35595062018-02-08 08:34:39 -05001058 if self.ponsim_comm == 'grpc':
1059 # establish frame grpc-stream
1060 reactor.callInThread(self.rcv_grpc)
1061 else:
1062 # finally, open the frameio port to receive in-band packet_in messages
1063 self.io_port = registry('frameio').open_port(
1064 self.interface, self.rcv_io, is_inband_frame)
1065
1066 self.start_kpi_collection(device.id)
Khen Nursimulud068d812017-03-06 11:44:18 -05001067
1068 self.log.info('re-enabled', device_id=device.id)
1069
Khen Nursimulud068d812017-03-06 11:44:18 -05001070 def delete(self):
1071 self.log.info('deleting', device_id=self.device_id)
1072
1073 # Remove all child devices
1074 self.adapter_agent.delete_all_child_devices(self.device_id)
1075
Stephane Barbarie35595062018-02-08 08:34:39 -05001076 self.close_channel()
1077 self.log.info('disabled-grpc-channel')
1078
1079 if self.ponsim_comm == 'frameio':
1080 # close the frameio port
1081 registry('frameio').close_port(self.io_port)
1082 self.log.info('disabled-frameio-port')
1083
Khen Nursimulud068d812017-03-06 11:44:18 -05001084 # TODO:
1085 # 1) Remove all flows from the device
1086 # 2) Remove the device from ponsim
1087
1088 self.log.info('deleted', device_id=self.device_id)
Sergio Slobodrian98eff412017-03-15 14:46:30 -04001089
1090 def start_kpi_collection(self, device_id):
1091
1092 def _collect(device_id, prefix):
1093
1094 try:
1095 # Step 1: gather metrics from device
1096 port_metrics = \
Stephane Barbarie5253c652017-03-22 16:29:46 -04001097 self.pm_metrics.collect_port_metrics(self.get_channel())
Sergio Slobodrian98eff412017-03-15 14:46:30 -04001098
1099 # Step 2: prepare the KpiEvent for submission
1100 # we can time-stamp them here (or could use time derived from OLT
1101 ts = arrow.utcnow().timestamp
1102 kpi_event = KpiEvent(
1103 type=KpiEventType.slice,
1104 ts=ts,
1105 prefixes={
1106 # OLT NNI port
1107 prefix + '.nni': MetricValuePairs(
1108 metrics=port_metrics['nni']),
1109 # OLT PON port
1110 prefix + '.pon': MetricValuePairs(
1111 metrics=port_metrics['pon'])
1112 }
1113 )
1114
1115 # Step 3: submit
1116 self.adapter_agent.submit_kpis(kpi_event)
1117
1118 except Exception as e:
1119 log.exception('failed-to-submit-kpis', e=e)
Stephane Barbarie5253c652017-03-22 16:29:46 -04001120
Sergio Slobodrian98eff412017-03-15 14:46:30 -04001121 self.pm_metrics.start_collector(_collect)
Nikolay Titov89004ec2017-06-19 18:22:42 -04001122
Stephane Barbarie35595062018-02-08 08:34:39 -05001123 def stop_kpi_collection(self):
1124 self.pm_metrics.stop_collector()
1125
Nikolay Titov89004ec2017-06-19 18:22:42 -04001126 def get_interface_config(self, data):
1127 interfaceConfig = InterfaceConfig()
1128 if isinstance(data, ChannelgroupConfig):
1129 interfaceConfig.channel_group_config.CopyFrom(data)
1130 elif isinstance(data, ChannelpartitionConfig):
1131 interfaceConfig.channel_partition_config.CopyFrom(data)
1132 elif isinstance(data, ChannelpairConfig):
1133 interfaceConfig.channel_pair_config.CopyFrom(data)
1134 elif isinstance(data, ChannelterminationConfig):
1135 interfaceConfig.channel_termination_config.CopyFrom(data)
1136 elif isinstance(data, OntaniConfig):
1137 interfaceConfig.ont_ani_config.CopyFrom(data)
1138 elif isinstance(data, VOntaniConfig):
1139 interfaceConfig.vont_ani_config.CopyFrom(data)
1140 elif isinstance(data, VEnetConfig):
1141 interfaceConfig.venet_config.CopyFrom(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -04001142 elif isinstance(data, TrafficDescriptorProfileData):
1143 interfaceConfig.traffic_descriptor_profile_config_data.CopyFrom(
1144 data)
1145 elif isinstance(data, TcontsConfigData):
1146 interfaceConfig.tconts_config_data.CopyFrom(data)
1147 elif isinstance(data, GemportsConfigData):
1148 interfaceConfig.gemports_config_data.CopyFrom(data)
1149 elif isinstance(data, MulticastGemportsConfigData):
1150 interfaceConfig.multicast_gemports_config_data.CopyFrom(data)
1151 elif isinstance(data, MulticastDistributionSetData):
1152 interfaceConfig.multicast_distribution_set_data.CopyFrom(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -04001153 else:
1154 return None
1155 return interfaceConfig
1156
Nikolay Titov176f1db2017-08-10 12:38:43 -04001157 def xpon_ponsim_olt_interface(self, method_name, data, data2=None):
Nikolay Titov89004ec2017-06-19 18:22:42 -04001158 interfaceConfig = self.get_interface_config(data)
1159 if interfaceConfig is not None:
Nikolay Titov176f1db2017-08-10 12:38:43 -04001160 self.log.info(
1161 'forwarding-{}-request-to-olt-for-interface-type'
khenaidoof3593a82018-06-01 16:41:31 -04001162 .format(self.xpon_ponsim_olt_itfs[method_name]['log']),
Nikolay Titov176f1db2017-08-10 12:38:43 -04001163 interface_type=type(data))
Scott Bakerd865fa22018-11-07 11:45:28 -08001164 stub = ponsim_pb2_grpc.XPonSimStub(self.get_channel())
Nikolay Titov176f1db2017-08-10 12:38:43 -04001165 _method = getattr(
1166 stub, self.xpon_ponsim_olt_itfs[method_name]['method_name'])
1167 if isinstance(data, TcontsConfigData):
1168 tcont_config = TcontInterfaceConfig()
1169 tcont_config.tconts_config_data.CopyFrom(data)
1170 tcont_config.traffic_descriptor_profile_config_data.CopyFrom(
1171 data2)
1172 _method(tcont_config)
1173 else:
1174 _method(interfaceConfig)
Nikolay Titov89004ec2017-06-19 18:22:42 -04001175 self.log.info('success')
1176
Nikolay Titov176f1db2017-08-10 12:38:43 -04001177 def create_interface(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001178 _method_name = sys._getframe().f_code.co_name
1179 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001180
Nikolay Titov89004ec2017-06-19 18:22:42 -04001181 def update_interface(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001182 _method_name = sys._getframe().f_code.co_name
1183 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov89004ec2017-06-19 18:22:42 -04001184
1185 def remove_interface(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001186 _method_name = sys._getframe().f_code.co_name
1187 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001188
1189 def create_tcont(self, tcont_data, traffic_descriptor_data):
khenaidoof3593a82018-06-01 16:41:31 -04001190 _method_name = sys._getframe().f_code.co_name
1191 self.xpon_ponsim_olt_interface(_method_name, tcont_data,
1192 traffic_descriptor_data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001193
1194 def update_tcont(self, tcont_data, traffic_descriptor_data):
khenaidoof3593a82018-06-01 16:41:31 -04001195 _method_name = sys._getframe().f_code.co_name
1196 self.xpon_ponsim_olt_interface(_method_name, tcont_data,
1197 traffic_descriptor_data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001198
1199 def remove_tcont(self, tcont_data, traffic_descriptor_data):
khenaidoof3593a82018-06-01 16:41:31 -04001200 _method_name = sys._getframe().f_code.co_name
1201 self.xpon_ponsim_olt_interface(_method_name, tcont_data,
1202 traffic_descriptor_data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001203
1204 def create_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001205 _method_name = sys._getframe().f_code.co_name
1206 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001207
1208 def update_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001209 _method_name = sys._getframe().f_code.co_name
1210 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001211
1212 def remove_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001213 _method_name = sys._getframe().f_code.co_name
1214 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001215
1216 def create_multicast_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001217 _method_name = sys._getframe().f_code.co_name
1218 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001219
1220 def update_multicast_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001221 _method_name = sys._getframe().f_code.co_name
1222 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001223
1224 def remove_multicast_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001225 _method_name = sys._getframe().f_code.co_name
1226 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001227
1228 def create_multicast_distribution_set(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001229 _method_name = sys._getframe().f_code.co_name
1230 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001231
1232 def update_multicast_distribution_set(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001233 _method_name = sys._getframe().f_code.co_name
1234 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001235
1236 def remove_multicast_distribution_set(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001237 _method_name = sys._getframe().f_code.co_name
1238 self.xpon_ponsim_olt_interface(_method_name, data);
Scott Baker50873cc2018-09-21 18:14:09 -07001239
1240 def simulate_alarm(self, alarm):
1241 # Ponsim_olt implements its own AdapterAlarms class, rather than using the Voltha alarm extension. Until that
1242 # has been reconciled, temporarily instantiate the Voltha alarm extension's AdapterAlarms here, for the
1243 # purpose of sending simulated alarms.
1244 alarms = VolthaAdapterAlarms(self.adapter_agent, self.device_id, self.logical_device_id)
1245 simulator = AdapterAlarmSimulator(alarms)
1246 simulator.simulate_alarm(alarm)