blob: b18b7e28da7db0b0f68203f2e87234d42479367c [file] [log] [blame]
Zsolt Haraszti656ecc62016-12-28 15:08:23 -08001#
Zsolt Haraszti3eb27a52017-01-03 21:56:48 -08002# Copyright 2017 the original author or authors.
Zsolt Haraszti656ecc62016-12-28 15:08:23 -08003#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17"""
18Fully simulated OLT/ONU adapter.
19"""
Nikolay Titov176f1db2017-08-10 12:38:43 -040020import sys
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080021from uuid import uuid4
22
Sergio Slobodrian98eff412017-03-15 14:46:30 -040023import arrow
Jeff55a2f132018-08-05 21:10:41 -070024import voltha.core.flow_decomposer as fd
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080025import grpc
Stephane Barbarie4475a252017-03-31 13:49:20 -040026import json
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080027import structlog
28from scapy.layers.l2 import Ether, Dot1Q
Stephane Barbarie4475a252017-03-31 13:49:20 -040029from scapy.layers.inet import Raw
Stephane Barbarie35595062018-02-08 08:34:39 -050030from twisted.internet import reactor
Khen Nursimulud068d812017-03-06 11:44:18 -050031from twisted.internet.defer import inlineCallbacks
Stephane Barbarie35595062018-02-08 08:34:39 -050032from grpc._channel import _Rendezvous
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080033
34from common.frameio.frameio import BpfProgramFilter, hexify
Khen Nursimulud068d812017-03-06 11:44:18 -050035from common.utils.asleep import asleep
Sergio Slobodrian98eff412017-03-15 14:46:30 -040036from twisted.internet.task import LoopingCall
Shad Ansarid1aa9e72017-06-23 21:34:25 -070037from voltha.adapters.iadapter import OltAdapter
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080038from voltha.core.logical_device_agent import mac_str_to_tuple
39from voltha.protos import third_party
Jeff55a2f132018-08-05 21:10:41 -070040from voltha.protos import openflow_13_pb2 as ofp
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080041from voltha.protos import ponsim_pb2
Shad Ansari14bcd992017-06-13 14:27:20 -070042from voltha.protos.common_pb2 import OperStatus, ConnectStatus, AdminState
Scott Baker50873cc2018-09-21 18:14:09 -070043from voltha.protos.common_pb2 import OperationResp
Shad Ansari14bcd992017-06-13 14:27:20 -070044from voltha.protos.device_pb2 import Port, Device, PmConfig, PmConfigs
Sergio Slobodrian98eff412017-03-15 14:46:30 -040045from voltha.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080046from google.protobuf.empty_pb2 import Empty
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080047from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice
Stephane Barbarie5253c652017-03-22 16:29:46 -040048from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
49 OFPPF_1GB_FD, \
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080050 OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS, \
51 ofp_switch_features, ofp_desc
52from voltha.protos.openflow_13_pb2 import ofp_port
Stephane Barbarie35595062018-02-08 08:34:39 -050053from voltha.protos.ponsim_pb2 import FlowTable, PonSimFrame
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080054from voltha.registry import registry
55
Nikolay Titov89004ec2017-06-19 18:22:42 -040056from voltha.protos.bbf_fiber_base_pb2 import \
khenaidoof3593a82018-06-01 16:41:31 -040057 ChannelgroupConfig, ChannelpartitionConfig, ChannelpairConfig, \
Nikolay Titov176f1db2017-08-10 12:38:43 -040058 ChannelterminationConfig, OntaniConfig, VOntaniConfig, VEnetConfig
59from voltha.protos.bbf_fiber_traffic_descriptor_profile_body_pb2 import \
60 TrafficDescriptorProfileData
61from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
62from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
63from voltha.protos.bbf_fiber_multicast_gemport_body_pb2 import \
64 MulticastGemportsConfigData
Stephane Barbarie35595062018-02-08 08:34:39 -050065
Nikolay Titov176f1db2017-08-10 12:38:43 -040066from voltha.protos.bbf_fiber_multicast_distribution_set_body_pb2 import \
67 MulticastDistributionSetData
Nikolay Titov89004ec2017-06-19 18:22:42 -040068
Nikolay Titov176f1db2017-08-10 12:38:43 -040069from voltha.protos.ponsim_pb2 import InterfaceConfig, TcontInterfaceConfig
Nikolay Titov89004ec2017-06-19 18:22:42 -040070
Scott Baker50873cc2018-09-21 18:14:09 -070071from voltha.extensions.alarms.adapter_alarms import AdapterAlarms as VolthaAdapterAlarms
72from voltha.extensions.alarms.simulator.simulate_alarms import AdapterAlarmSimulator
73
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080074_ = third_party
75log = structlog.get_logger()
76
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080077PACKET_IN_VLAN = 4000
78is_inband_frame = BpfProgramFilter('(ether[14:2] & 0xfff) = 0x{:03x}'.format(
79 PACKET_IN_VLAN))
80
Stephane Barbarie5253c652017-03-22 16:29:46 -040081
Sergio Slobodrian98eff412017-03-15 14:46:30 -040082class AdapterPmMetrics:
Stephane Barbarie5253c652017-03-22 16:29:46 -040083 def __init__(self, device):
Sergio Slobodrianec6e3912017-04-02 11:46:55 -040084 self.pm_names = {'tx_64_pkts', 'tx_65_127_pkts', 'tx_128_255_pkts',
85 'tx_256_511_pkts', 'tx_512_1023_pkts',
86 'tx_1024_1518_pkts', 'tx_1519_9k_pkts',
87 'rx_64_pkts', 'rx_65_127_pkts',
88 'rx_128_255_pkts', 'rx_256_511_pkts',
89 'rx_512_1023_pkts', 'rx_1024_1518_pkts',
90 'rx_1519_9k_pkts'}
Sergio Slobodrian98eff412017-03-15 14:46:30 -040091 self.device = device
92 self.id = device.id
93 self.name = 'ponsim_olt'
Stephane Barbarie5253c652017-03-22 16:29:46 -040094 # self.id = "abc"
Sergio Slobodrian98eff412017-03-15 14:46:30 -040095 self.default_freq = 150
96 self.grouped = False
97 self.freq_override = False
98 self.pon_metrics_config = dict()
99 self.nni_metrics_config = dict()
100 self.lc = None
101 for m in self.pm_names:
102 self.pon_metrics_config[m] = PmConfig(name=m,
103 type=PmConfig.COUNTER,
104 enabled=True)
105 self.nni_metrics_config[m] = PmConfig(name=m,
106 type=PmConfig.COUNTER,
107 enabled=True)
108
109 def update(self, pm_config):
110 if self.default_freq != pm_config.default_freq:
111 # Update the callback to the new frequency.
112 self.default_freq = pm_config.default_freq
113 self.lc.stop()
Stephane Barbarie5253c652017-03-22 16:29:46 -0400114 self.lc.start(interval=self.default_freq / 10)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400115 for m in pm_config.metrics:
116 self.pon_metrics_config[m.name].enabled = m.enabled
117 self.nni_metrics_config[m.name].enabled = m.enabled
118
119 def make_proto(self):
120 pm_config = PmConfigs(
121 id=self.id,
122 default_freq=self.default_freq,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400123 grouped=False,
124 freq_override=False)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400125 for m in sorted(self.pon_metrics_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400126 pm = self.pon_metrics_config[m] # Either will do they're the same
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400127 pm_config.metrics.extend([PmConfig(name=pm.name,
128 type=pm.type,
129 enabled=pm.enabled)])
130 return pm_config
131
132 def collect_port_metrics(self, channel):
133 rtrn_port_metrics = dict()
134 stub = ponsim_pb2.PonSimStub(channel)
135 stats = stub.GetStats(Empty())
136 rtrn_port_metrics['pon'] = self.extract_pon_metrics(stats)
137 rtrn_port_metrics['nni'] = self.extract_nni_metrics(stats)
138 return rtrn_port_metrics
139
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400140 def extract_pon_metrics(self, stats):
141 rtrn_pon_metrics = dict()
142 for m in stats.metrics:
143 if m.port_name == "pon":
144 for p in m.packets:
145 if self.pon_metrics_config[p.name].enabled:
146 rtrn_pon_metrics[p.name] = p.value
147 return rtrn_pon_metrics
148
149 def extract_nni_metrics(self, stats):
150 rtrn_pon_metrics = dict()
151 for m in stats.metrics:
152 if m.port_name == "nni":
153 for p in m.packets:
154 if self.pon_metrics_config[p.name].enabled:
155 rtrn_pon_metrics[p.name] = p.value
156 return rtrn_pon_metrics
157
158 def start_collector(self, callback):
159 log.info("starting-pm-collection", device_name=self.name,
160 device_id=self.device.id)
161 prefix = 'voltha.{}.{}'.format(self.name, self.device.id)
162 self.lc = LoopingCall(callback, self.device.id, prefix)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400163 self.lc.start(interval=self.default_freq / 10)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400164
Stephane Barbarie35595062018-02-08 08:34:39 -0500165 def stop_collector(self):
166 log.info("stopping-pm-collection", device_name=self.name,
167 device_id=self.device.id)
168 self.lc.stop()
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800169
khenaidoof3593a82018-06-01 16:41:31 -0400170
Stephane Barbarie4475a252017-03-31 13:49:20 -0400171class AdapterAlarms:
172 def __init__(self, adapter, device):
173 self.adapter = adapter
174 self.device = device
175 self.lc = None
176
177 def send_alarm(self, context_data, alarm_data):
178 try:
179 current_context = {}
180 for key, value in context_data.__dict__.items():
181 current_context[key] = str(value)
182
183 alarm_event = self.adapter.adapter_agent.create_alarm(
184 resource_id=self.device.id,
khenaidoo032d3302017-06-09 14:50:04 -0400185 description="{}.{} - {}".format(self.adapter.name,
186 self.device.id,
187 alarm_data[
188 'description']) if 'description' in alarm_data else None,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400189 type=alarm_data['type'] if 'type' in alarm_data else None,
khenaidoo032d3302017-06-09 14:50:04 -0400190 category=alarm_data[
191 'category'] if 'category' in alarm_data else None,
192 severity=alarm_data[
193 'severity'] if 'severity' in alarm_data else None,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400194 state=alarm_data['state'] if 'state' in alarm_data else None,
195 raised_ts=alarm_data['ts'] if 'ts' in alarm_data else 0,
196 context=current_context
197 )
198
khenaidoo032d3302017-06-09 14:50:04 -0400199 self.adapter.adapter_agent.submit_alarm(self.device.id,
200 alarm_event)
Stephane Barbarie4475a252017-03-31 13:49:20 -0400201
202 except Exception as e:
203 log.exception('failed-to-send-alarm', e=e)
204
khenaidoof3593a82018-06-01 16:41:31 -0400205
Shad Ansarid1aa9e72017-06-23 21:34:25 -0700206class PonSimOltAdapter(OltAdapter):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800207 def __init__(self, adapter_agent, config):
Shad Ansari14bcd992017-06-13 14:27:20 -0700208 super(PonSimOltAdapter, self).__init__(adapter_agent=adapter_agent,
209 config=config,
Shad Ansari96f817b2017-06-18 23:17:44 -0700210 device_handler_class=PonSimOltHandler,
Shad Ansari14bcd992017-06-13 14:27:20 -0700211 name='ponsim_olt',
212 vendor='Voltha project',
Nikolay Titov89004ec2017-06-19 18:22:42 -0400213 version='0.4',
khenaidoof3593a82018-06-01 16:41:31 -0400214 device_type='ponsim_olt',
215 accepts_bulk_flow_update=True,
216 accepts_add_remove_flow_updates=False)
Shad Ansari96f817b2017-06-18 23:17:44 -0700217
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400218 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400219 log.info("adapter-update-pm-config", device=device,
220 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400221 handler = self.devices_handlers[device.id]
222 handler.update_pm_config(device, pm_config)
Sergio Slobodrianec864c62017-03-09 11:41:43 -0500223
Nikolay Titov89004ec2017-06-19 18:22:42 -0400224 def create_interface(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400225 if super(PonSimOltAdapter, self)._get_handler(device):
226 log.info('create-interface', device_id=device.id)
227 self.devices_handlers[device.id].create_interface(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400228
229 def update_interface(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400230 if super(PonSimOltAdapter, self)._get_handler(device):
231 log.info('update-interface', device_id=device.id)
232 self.devices_handlers[device.id].update_interface(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400233
234 def remove_interface(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400235 if super(PonSimOltAdapter, self)._get_handler(device):
236 log.info('remove-interface', device_id=device.id)
237 self.devices_handlers[device.id].remove_interface(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400238
Nikolay Titov176f1db2017-08-10 12:38:43 -0400239 def create_tcont(self, device, tcont_data, traffic_descriptor_data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400240 if super(PonSimOltAdapter, self)._get_handler(device):
241 log.info('create-tcont', device_id=device.id)
242 self.devices_handlers[device.id].create_tcont(
243 tcont_data, traffic_descriptor_data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400244
245 def update_tcont(self, device, tcont_data, traffic_descriptor_data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400246 if super(PonSimOltAdapter, self)._get_handler(device):
247 log.info('update-tcont', device_id=device.id)
248 self.devices_handlers[device.id].update_tcont(
249 tcont_data, traffic_descriptor_data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400250
251 def remove_tcont(self, device, tcont_data, traffic_descriptor_data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400252 if super(PonSimOltAdapter, self)._get_handler(device):
253 log.info('remove-tcont', device_id=device.id)
254 self.devices_handlers[device.id].remove_tcont(
255 tcont_data, traffic_descriptor_data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400256
257 def create_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400258 if super(PonSimOltAdapter, self)._get_handler(device):
259 log.info('create-gemport', device_id=device.id)
260 self.devices_handlers[device.id].create_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400261
262 def update_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400263 if super(PonSimOltAdapter, self)._get_handler(device):
264 log.info('update-gemport', device_id=device.id)
265 self.devices_handlers[device.id].update_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400266
267 def remove_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400268 if super(PonSimOltAdapter, self)._get_handler(device):
269 log.info('remove-gemport', device_id=device.id)
270 self.devices_handlers[device.id].remove_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400271
272 def create_multicast_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400273 if super(PonSimOltAdapter, self)._get_handler(device):
274 log.info('create-multicast-gemport', device_id=device.id)
275 self.devices_handlers[device.id].create_multicast_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400276
277 def update_multicast_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400278 if super(PonSimOltAdapter, self)._get_handler(device):
279 log.info('update-multicast-gemport', device_id=device.id)
280 self.devices_handlers[device.id].update_multicast_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400281
282 def remove_multicast_gemport(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400283 if super(PonSimOltAdapter, self)._get_handler(device):
284 log.info('remove-multicast-gemport', device_id=device.id)
285 self.devices_handlers[device.id].remove_multicast_gemport(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400286
287 def create_multicast_distribution_set(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400288 if super(PonSimOltAdapter, self)._get_handler(device):
289 log.info('create-multicast-distribution-set', device_id=device.id)
290 self.devices_handlers[device.id].create_multicast_distribution_set(
291 data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400292
293 def update_multicast_distribution_set(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400294 if super(PonSimOltAdapter, self)._get_handler(device):
295 log.info('update-multicast-distribution-set', device_id=device.id)
296 self.devices_handlers[device.id].update_multicast_distribution_set(
297 data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400298
299 def remove_multicast_distribution_set(self, device, data):
Nikolay Titov4e1c3702017-08-23 12:41:57 -0400300 if super(PonSimOltAdapter, self)._get_handler(device):
301 log.info('remove-multicast-distribution-set', device_id=device.id)
302 self.devices_handlers[device.id].remove_multicast_distribution_set(
303 data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400304
Scott Baker50873cc2018-09-21 18:14:09 -0700305 def simulate_alarm(self, device, alarm):
306 handler = self.devices_handlers[device.id]
307 handler.simulate_alarm(alarm)
308 return OperationResp(code=OperationResp.OPERATION_SUCCESS)
khenaidoof3593a82018-06-01 16:41:31 -0400309
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800310class PonSimOltHandler(object):
Nikolay Titov176f1db2017-08-10 12:38:43 -0400311 xpon_ponsim_olt_itfs = {
312 'create_interface': {
313 'method_name': 'CreateInterface',
314 'log': 'create-interface'},
315 'update_interface': {
316 'method_name': 'UpdateInterface',
317 'log': 'update-interface'},
318 'remove_interface': {
319 'method_name': 'RemoveInterface',
320 'log': 'remove-interface'},
321 'create_tcont': {
322 'method_name': 'CreateTcont',
323 'log': 'create-tconts-config-data'},
324 'update_tcont': {
325 'method_name': 'UpdateTcont',
326 'log': 'update-tconts-config-data'},
327 'remove_tcont': {
328 'method_name': 'RemoveTcont',
329 'log': 'remove-tconts-config-data'},
330 'create_gemport': {
331 'method_name': 'CreateGemport',
332 'log': 'create-gemports-config-data'},
333 'update_gemport': {
334 'method_name': 'UpdateGemport',
335 'log': 'update-gemports-config-data'},
336 'remove_gemport': {
337 'method_name': 'RemoveGemport',
338 'log': 'remove-gemports-config-data'},
339 'create_multicast_gemport': {
340 'method_name': 'CreateMulticastGemport',
341 'log': 'create-multicast-gemports-config-data'},
342 'update_multicast_gemport': {
343 'method_name': 'UpdateMulticastGemport',
344 'log': 'update-multicast-gemports-config-data'},
345 'remove_multicast_gemport': {
346 'method_name': 'RemoveMulticastGemport',
347 'log': 'remove-multicast-gemports-config-data'},
348 'create_multicast_distribution_set': {
349 'method_name': 'CreateMulticastDistributionSet',
350 'log': 'create-multicast-distribution-set-data'},
351 'update_multicast_distribution_set': {
352 'method_name': 'UpdateMulticastDistributionSet',
353 'log': 'update-multicast-distribution-set-data'},
354 'remove_multicast_distribution_set': {
355 'method_name': 'RemoveMulticastDistributionSet',
356 'log': 'remove-multicast-distribution-set-data'},
khenaidoof3593a82018-06-01 16:41:31 -0400357 }
Nikolay Titov176f1db2017-08-10 12:38:43 -0400358
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800359 def __init__(self, adapter, device_id):
360 self.adapter = adapter
361 self.adapter_agent = adapter.adapter_agent
362 self.device_id = device_id
363 self.log = structlog.get_logger(device_id=device_id)
364 self.channel = None
365 self.io_port = None
366 self.logical_device_id = None
Khen Nursimulud068d812017-03-06 11:44:18 -0500367 self.nni_port = None
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400368 self.ofp_port_no = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800369 self.interface = registry('main').get_args().interface
Stephane Barbarie35595062018-02-08 08:34:39 -0500370 self.ponsim_comm = registry('main').get_args().ponsim_comm
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400371 self.pm_metrics = None
Stephane Barbarie4475a252017-03-31 13:49:20 -0400372 self.alarms = None
Stephane Barbarie35595062018-02-08 08:34:39 -0500373 self.frames = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800374
375 def __del__(self):
376 if self.io_port is not None:
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800377 registry('frameio').close_port(self.io_port)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800378
379 def get_channel(self):
380 if self.channel is None:
381 device = self.adapter_agent.get_device(self.device_id)
schowdhury9e247752017-07-14 06:56:20 -0700382
Jeff70e8b2d2018-07-24 13:37:29 -0700383 self.channel = grpc.insecure_channel(device.host_and_port)
schowdhury9e247752017-07-14 06:56:20 -0700384
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800385 return self.channel
386
Stephane Barbarie35595062018-02-08 08:34:39 -0500387 def close_channel(self):
388 if self.channel is None:
389 self.log.info('grpc-channel-already-closed')
390 return
391 else:
392 if self.frames is not None:
393 self.frames.cancel()
394 self.frames = None
395 self.log.info('cancelled-grpc-frame-stream')
396
397 self.channel.unsubscribe(lambda *args: None)
398 self.channel = None
399
400 self.log.info('grpc-channel-closed')
401
khenaidoo032d3302017-06-09 14:50:04 -0400402 def _get_nni_port(self):
403 ports = self.adapter_agent.get_ports(self.device_id, Port.ETHERNET_NNI)
404 if ports:
405 # For now, we use on one NNI port
406 return ports[0]
407
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800408 def activate(self, device):
409 self.log.info('activating')
410
411 if not device.host_and_port:
412 device.oper_status = OperStatus.FAILED
413 device.reason = 'No host_and_port field provided'
414 self.adapter_agent.update_device(device)
415 return
416
417 stub = ponsim_pb2.PonSimStub(self.get_channel())
418 info = stub.GetDeviceInfo(Empty())
419 log.info('got-info', info=info)
420
421 device.root = True
422 device.vendor = 'ponsim'
Stephane Barbarie5253c652017-03-22 16:29:46 -0400423 device.model = 'n/a'
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800424 device.serial_number = device.host_and_port
425 device.connect_status = ConnectStatus.REACHABLE
426 self.adapter_agent.update_device(device)
427
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400428 # Now set the initial PM configuration for this device
Stephane Barbarie5253c652017-03-22 16:29:46 -0400429 self.pm_metrics = AdapterPmMetrics(device)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400430 pm_config = self.pm_metrics.make_proto()
431 log.info("initial-pm-config", pm_config=pm_config)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400432 self.adapter_agent.update_device_pm_config(pm_config, init=True)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400433
Stephane Barbarie4475a252017-03-31 13:49:20 -0400434 # Setup alarm handler
435 self.alarms = AdapterAlarms(self.adapter, device)
436
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800437 nni_port = Port(
Jonathan Hart32fe8812018-08-21 17:10:12 -0700438 port_no=info.nni_port,
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800439 label='NNI facing Ethernet port',
440 type=Port.ETHERNET_NNI,
441 admin_state=AdminState.ENABLED,
442 oper_status=OperStatus.ACTIVE
443 )
Khen Nursimulud068d812017-03-06 11:44:18 -0500444 self.nni_port = nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800445 self.adapter_agent.add_port(device.id, nni_port)
446 self.adapter_agent.add_port(device.id, Port(
447 port_no=1,
448 label='PON port',
449 type=Port.PON_OLT,
450 admin_state=AdminState.ENABLED,
451 oper_status=OperStatus.ACTIVE
452 ))
453
454 ld = LogicalDevice(
khenaidoo507d9222017-10-10 16:23:49 -0400455 # not setting id and datapath_id. Adapter agent will pick the id
456 # and will pick the datapath_id is it is not provided
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800457 desc=ofp_desc(
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800458 hw_desc='simualted pon',
459 sw_desc='simualted pon',
khenaidoof3593a82018-06-01 16:41:31 -0400460 # serial_num=uuid4().hex,
Jonathan Hart32fe8812018-08-21 17:10:12 -0700461 serial_num=device.serial_number,
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800462 dp_desc='n/a'
463 ),
464 switch_features=ofp_switch_features(
465 n_buffers=256, # TODO fake for now
466 n_tables=2, # TODO ditto
467 capabilities=( # TODO and ditto
khenaidoof3593a82018-06-01 16:41:31 -0400468 OFPC_FLOW_STATS
469 | OFPC_TABLE_STATS
470 | OFPC_PORT_STATS
471 | OFPC_GROUP_STATS
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800472 )
473 ),
474 root_device_id=device.id
475 )
khenaidoo507d9222017-10-10 16:23:49 -0400476 mac_address = "AA:BB:CC:DD:EE:FF"
477 ld_initialized = self.adapter_agent.create_logical_device(ld,
478 dpid=mac_address)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800479 cap = OFPPF_1GB_FD | OFPPF_FIBER
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400480 self.ofp_port_no = info.nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800481 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
482 id='nni',
483 ofp_port=ofp_port(
484 port_no=info.nni_port,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400485 hw_addr=mac_str_to_tuple(
486 '00:00:00:00:00:%02x' % info.nni_port),
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800487 name='nni',
488 config=0,
489 state=OFPPS_LIVE,
490 curr=cap,
491 advertised=cap,
492 peer=cap,
493 curr_speed=OFPPF_1GB_FD,
494 max_speed=OFPPF_1GB_FD
495 ),
496 device_id=device.id,
497 device_port_no=nni_port.port_no,
498 root_port=True
499 ))
500
501 device = self.adapter_agent.get_device(device.id)
502 device.parent_id = ld_initialized.id
503 device.oper_status = OperStatus.ACTIVE
504 self.adapter_agent.update_device(device)
505 self.logical_device_id = ld_initialized.id
506
Andy Bavierea82b462018-07-27 16:48:13 -0700507 # register ONUS
508 for onu in info.onus:
509 vlan_id = onu.uni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800510 self.adapter_agent.child_device_detected(
511 parent_device_id=device.id,
512 parent_port_no=1,
Niren R Chidrawarefcebcd2017-07-19 20:03:39 -0400513 child_device_type='ponsim_onu',
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800514 proxy_address=Device.ProxyAddress(
515 device_id=device.id,
516 channel_id=vlan_id
517 ),
Nikolay Titov89004ec2017-06-19 18:22:42 -0400518 admin_state=AdminState.ENABLED,
Andy Bavierea82b462018-07-27 16:48:13 -0700519 vlan=vlan_id,
520 serial_number=onu.serial_number
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800521 )
522
Stephane Barbarie35595062018-02-08 08:34:39 -0500523 if self.ponsim_comm == 'grpc':
524 self.log.info('starting-frame-grpc-stream')
525 reactor.callInThread(self.rcv_grpc)
526 self.log.info('started-frame-grpc-stream')
527 else:
528 # finally, open the frameio port to receive in-band packet_in messages
529 self.log.info('registering-frameio')
530 self.io_port = registry('frameio').open_port(
531 self.interface, self.rcv_io, is_inband_frame)
532 self.log.info('registered-frameio')
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800533
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400534 # Start collecting stats from the device after a brief pause
535 self.start_kpi_collection(device.id)
536
khenaidoo032d3302017-06-09 14:50:04 -0400537 def reconcile(self, device):
538 self.log.info('reconciling-OLT-device-starts')
539
540 if not device.host_and_port:
541 device.oper_status = OperStatus.FAILED
542 device.reason = 'No host_and_port field provided'
543 self.adapter_agent.update_device(device)
544 return
545
546 try:
547 stub = ponsim_pb2.PonSimStub(self.get_channel())
548 info = stub.GetDeviceInfo(Empty())
549 log.info('got-info', info=info)
550 # TODO: Verify we are connected to the same device we are
551 # reconciling - not much data in ponsim to differentiate at the
552 # time
553 device.oper_status = OperStatus.ACTIVE
554 self.adapter_agent.update_device(device)
555 self.ofp_port_no = info.nni_port
556 self.nni_port = self._get_nni_port()
557 except Exception, e:
558 log.exception('device-unreachable', e=e)
559 device.connect_status = ConnectStatus.UNREACHABLE
560 device.oper_status = OperStatus.UNKNOWN
561 self.adapter_agent.update_device(device)
562 return
563
564 # Now set the initial PM configuration for this device
565 self.pm_metrics = AdapterPmMetrics(device)
566 pm_config = self.pm_metrics.make_proto()
567 log.info("initial-pm-config", pm_config=pm_config)
568 self.adapter_agent.update_device_pm_config(pm_config, init=True)
569
570 # Setup alarm handler
571 self.alarms = AdapterAlarms(self.adapter, device)
572
573 # TODO: Is there anything required to verify nni and PON ports
574
575 # Set the logical device id
576 device = self.adapter_agent.get_device(device.id)
577 if device.parent_id:
578 self.logical_device_id = device.parent_id
579 self.adapter_agent.reconcile_logical_device(device.parent_id)
580 else:
581 self.log.info('no-logical-device-set')
582
583 # Reconcile child devices
584 self.adapter_agent.reconcile_child_devices(device.id)
585
Stephane Barbarie35595062018-02-08 08:34:39 -0500586 if self.ponsim_comm == 'grpc':
587 reactor.callInThread(self.rcv_grpc)
588 else:
589 # finally, open the frameio port to receive in-band packet_in messages
590 self.io_port = registry('frameio').open_port(
591 self.interface, self.rcv_io, is_inband_frame)
khenaidoo032d3302017-06-09 14:50:04 -0400592
593 # Start collecting stats from the device after a brief pause
594 self.start_kpi_collection(device.id)
595
596 self.log.info('reconciling-OLT-device-ends')
597
Stephane Barbarie35595062018-02-08 08:34:39 -0500598 def _rcv_frame(self, frame):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800599 pkt = Ether(frame)
Stephane Barbarie35595062018-02-08 08:34:39 -0500600
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800601 if pkt.haslayer(Dot1Q):
602 outer_shim = pkt.getlayer(Dot1Q)
Stephane Barbarie35595062018-02-08 08:34:39 -0500603
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800604 if isinstance(outer_shim.payload, Dot1Q):
605 inner_shim = outer_shim.payload
606 cvid = inner_shim.vlan
607 logical_port = cvid
608 popped_frame = (
khenaidoof3593a82018-06-01 16:41:31 -0400609 Ether(src=pkt.src, dst=pkt.dst, type=inner_shim.type) /
610 inner_shim.payload
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800611 )
612 kw = dict(
613 logical_device_id=self.logical_device_id,
614 logical_port_no=logical_port,
615 )
616 self.log.info('sending-packet-in', **kw)
617 self.adapter_agent.send_packet_in(
618 packet=str(popped_frame), **kw)
Stephane Barbarie4475a252017-03-31 13:49:20 -0400619 elif pkt.haslayer(Raw):
620 raw_data = json.loads(pkt.getlayer(Raw).load)
621 self.alarms.send_alarm(self, raw_data)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800622
Stephane Barbarie35595062018-02-08 08:34:39 -0500623 @inlineCallbacks
624 def rcv_grpc(self):
625 """
626 This call establishes a GRPC stream to receive frames.
627 """
628 stub = ponsim_pb2.PonSimStub(self.get_channel())
629
630 # Attempt to establish a grpc stream with the remote ponsim service
631 self.frames = stub.ReceiveFrames(Empty())
632
633 self.log.info('start-receiving-grpc-frames')
634
635 try:
636 for frame in self.frames:
khenaidoof3593a82018-06-01 16:41:31 -0400637 self.log.info('received-grpc-frame',
638 frame_len=len(frame.payload))
Stephane Barbarie35595062018-02-08 08:34:39 -0500639 self._rcv_frame(frame.payload)
640
641 except _Rendezvous, e:
khenaidoof3593a82018-06-01 16:41:31 -0400642 log.warn('grpc-connection-lost', message=e.message)
Stephane Barbarie35595062018-02-08 08:34:39 -0500643
644 self.log.info('stopped-receiving-grpc-frames')
645
646 def rcv_io(self, port, frame):
647 self.log.info('received-io-frame', iface_name=port.iface_name,
648 frame_len=len(frame))
649 self._rcv_frame(frame)
650
Jeff55a2f132018-08-05 21:10:41 -0700651 # VOLTHA's flow decomposition removes the information about which flows
652 # are trap flows where traffic should be forwarded to the controller.
653 # We'll go through the flows and change the output port of flows that we
654 # know to be trap flows to the OF CONTROLLER port.
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800655 def update_flow_table(self, flows):
656 stub = ponsim_pb2.PonSimStub(self.get_channel())
657 self.log.info('pushing-olt-flow-table')
Jeff55a2f132018-08-05 21:10:41 -0700658 for flow in flows:
659 classifier_info = {}
660 for field in fd.get_ofb_fields(flow):
661 if field.type == fd.ETH_TYPE:
662 classifier_info['eth_type'] = field.eth_type
663 self.log.debug('field-type-eth-type',
664 eth_type=classifier_info['eth_type'])
665 elif field.type == fd.IP_PROTO:
666 classifier_info['ip_proto'] = field.ip_proto
667 self.log.debug('field-type-ip-proto',
668 ip_proto=classifier_info['ip_proto'])
669 if ('ip_proto' in classifier_info and (
670 classifier_info['ip_proto'] == 17 or
671 classifier_info['ip_proto'] == 2)) or (
672 'eth_type' in classifier_info and
673 classifier_info['eth_type'] == 0x888e):
674 for action in fd.get_actions(flow):
675 if action.type == ofp.OFPAT_OUTPUT:
676 action.output.port = ofp.OFPP_CONTROLLER
677 self.log.info('out_port', out_port=fd.get_out_port(flow))
678
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800679 stub.UpdateFlowTable(FlowTable(
680 port=0,
681 flows=flows
682 ))
683 self.log.info('success')
684
khenaidoof3593a82018-06-01 16:41:31 -0400685 def remove_from_flow_table(self, flows):
686 self.log.debug('remove-from-flow-table', flows=flows)
687 # TODO: Update PONSIM code to accept incremental flow changes
688 # Once completed, the accepts_add_remove_flow_updates for this
689 # device type can be set to True
690
691 def add_to_flow_table(self, flows):
692 self.log.debug('add-to-flow-table', flows=flows)
693 # TODO: Update PONSIM code to accept incremental flow changes
694 # Once completed, the accepts_add_remove_flow_updates for this
695 # device type can be set to True
696
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400697 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400698 log.info("handler-update-pm-config", device=device,
699 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400700 self.pm_metrics.update(pm_config)
701
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800702 def send_proxied_message(self, proxy_address, msg):
703 self.log.info('sending-proxied-message')
704 if isinstance(msg, FlowTable):
705 stub = ponsim_pb2.PonSimStub(self.get_channel())
706 self.log.info('pushing-onu-flow-table', port=msg.port)
707 res = stub.UpdateFlowTable(msg)
708 self.adapter_agent.receive_proxied_message(proxy_address, res)
709
710 def packet_out(self, egress_port, msg):
711 self.log.info('sending-packet-out', egress_port=egress_port,
712 msg=hexify(msg))
713 pkt = Ether(msg)
Jonathan Hart32fe8812018-08-21 17:10:12 -0700714 out_pkt = pkt
715 if egress_port != self.nni_port.port_no:
716 # don't do the vlan manipulation for the NNI port, vlans are already correct
717 out_pkt = (
718 Ether(src=pkt.src, dst=pkt.dst) /
719 Dot1Q(vlan=egress_port, type=pkt.type) /
720 pkt.payload
721 )
722
723 # TODO need better way of mapping logical ports to PON ports
724 out_port = self.nni_port.port_no if egress_port == self.nni_port.port_no else 1
Stephane Barbarie35595062018-02-08 08:34:39 -0500725
726 if self.ponsim_comm == 'grpc':
727 # send over grpc stream
728 stub = ponsim_pb2.PonSimStub(self.get_channel())
Jonathan Hart32fe8812018-08-21 17:10:12 -0700729 frame = PonSimFrame(id=self.device_id, payload=str(out_pkt), out_port=out_port)
Stephane Barbarie35595062018-02-08 08:34:39 -0500730 stub.SendFrame(frame)
731 else:
732 # send over frameio
733 self.io_port.send(str(out_pkt))
734
Khen Nursimulud068d812017-03-06 11:44:18 -0500735 @inlineCallbacks
736 def reboot(self):
737 self.log.info('rebooting', device_id=self.device_id)
738
739 # Update the operational status to ACTIVATING and connect status to
740 # UNREACHABLE
741 device = self.adapter_agent.get_device(self.device_id)
742 previous_oper_status = device.oper_status
743 previous_conn_status = device.connect_status
744 device.oper_status = OperStatus.ACTIVATING
745 device.connect_status = ConnectStatus.UNREACHABLE
746 self.adapter_agent.update_device(device)
747
khenaidoo71d0a6c2017-03-22 21:46:04 -0400748 # Update the child devices connect state to UNREACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400749 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400750 connect_status=ConnectStatus.UNREACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400751
Khen Nursimulud068d812017-03-06 11:44:18 -0500752 # Sleep 10 secs, simulating a reboot
Stephane Barbarie5253c652017-03-22 16:29:46 -0400753 # TODO: send alert and clear alert after the reboot
Khen Nursimulud068d812017-03-06 11:44:18 -0500754 yield asleep(10)
755
756 # Change the operational status back to its previous state. With a
757 # real OLT the operational state should be the state the device is
758 # after a reboot.
759 # Get the latest device reference
760 device = self.adapter_agent.get_device(self.device_id)
761 device.oper_status = previous_oper_status
762 device.connect_status = previous_conn_status
763 self.adapter_agent.update_device(device)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400764
765 # Update the child devices connect state to REACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400766 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400767 connect_status=ConnectStatus.REACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400768
Khen Nursimulud068d812017-03-06 11:44:18 -0500769 self.log.info('rebooted', device_id=self.device_id)
770
sathishg5ae86222017-06-28 15:16:29 +0530771 def self_test_device(self, device):
772 """
773 This is called to Self a device based on a NBI call.
774 :param device: A Voltha.Device object.
775 :return: Will return result of self test
776 """
777 log.info('self-test-device', device=device.id)
778 raise NotImplementedError()
779
Khen Nursimulud068d812017-03-06 11:44:18 -0500780 def disable(self):
781 self.log.info('disabling', device_id=self.device_id)
782
Stephane Barbarie35595062018-02-08 08:34:39 -0500783 self.stop_kpi_collection()
784
Khen Nursimulud068d812017-03-06 11:44:18 -0500785 # Get the latest device reference
786 device = self.adapter_agent.get_device(self.device_id)
787
788 # Update the operational status to UNKNOWN
789 device.oper_status = OperStatus.UNKNOWN
790 device.connect_status = ConnectStatus.UNREACHABLE
791 self.adapter_agent.update_device(device)
792
793 # Remove the logical device
794 logical_device = self.adapter_agent.get_logical_device(
Stephane Barbarie5253c652017-03-22 16:29:46 -0400795 self.logical_device_id)
Khen Nursimulud068d812017-03-06 11:44:18 -0500796 self.adapter_agent.delete_logical_device(logical_device)
797
798 # Disable all child devices first
khenaidoo2d7af132017-03-23 15:45:51 -0400799 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400800 admin_state=AdminState.DISABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500801
Khen Nursimulud068d812017-03-06 11:44:18 -0500802 # Remove the peer references from this device
803 self.adapter_agent.delete_all_peer_references(self.device_id)
804
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400805 # Set all ports to disabled
806 self.adapter_agent.disable_all_ports(self.device_id)
807
Stephane Barbarie35595062018-02-08 08:34:39 -0500808 self.close_channel()
809 self.log.info('disabled-grpc-channel')
810
811 if self.ponsim_comm == 'frameio':
812 # close the frameio port
813 registry('frameio').close_port(self.io_port)
814 self.log.info('disabled-frameio-port')
Khen Nursimulud068d812017-03-06 11:44:18 -0500815
khenaidoo032d3302017-06-09 14:50:04 -0400816 # Update the logice device mapping
817 if self.logical_device_id in \
818 self.adapter.logical_device_id_to_root_device_id:
819 del self.adapter.logical_device_id_to_root_device_id[
820 self.logical_device_id]
821
Khen Nursimulud068d812017-03-06 11:44:18 -0500822 # TODO:
823 # 1) Remove all flows from the device
824 # 2) Remove the device from ponsim
825
826 self.log.info('disabled', device_id=device.id)
827
Khen Nursimulud068d812017-03-06 11:44:18 -0500828 def reenable(self):
829 self.log.info('re-enabling', device_id=self.device_id)
830
831 # Get the latest device reference
832 device = self.adapter_agent.get_device(self.device_id)
833
khenaidoo032d3302017-06-09 14:50:04 -0400834 # Set the ofp_port_no and nni_port in case we bypassed the reconcile
835 # process if the device was in DISABLED state on voltha restart
836 if not self.ofp_port_no and not self.nni_port:
837 stub = ponsim_pb2.PonSimStub(self.get_channel())
838 info = stub.GetDeviceInfo(Empty())
839 log.info('got-info', info=info)
840 self.ofp_port_no = info.nni_port
841 self.nni_port = self._get_nni_port()
842
Khen Nursimulud068d812017-03-06 11:44:18 -0500843 # Update the connect status to REACHABLE
844 device.connect_status = ConnectStatus.REACHABLE
845 self.adapter_agent.update_device(device)
846
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400847 # Set all ports to enabled
848 self.adapter_agent.enable_all_ports(self.device_id)
849
Khen Nursimulud068d812017-03-06 11:44:18 -0500850 ld = LogicalDevice(
851 # not setting id and datapth_id will let the adapter agent pick id
852 desc=ofp_desc(
Khen Nursimulud068d812017-03-06 11:44:18 -0500853 hw_desc='simulated pon',
854 sw_desc='simulated pon',
855 serial_num=uuid4().hex,
856 dp_desc='n/a'
857 ),
858 switch_features=ofp_switch_features(
859 n_buffers=256, # TODO fake for now
860 n_tables=2, # TODO ditto
861 capabilities=( # TODO and ditto
khenaidoof3593a82018-06-01 16:41:31 -0400862 OFPC_FLOW_STATS
863 | OFPC_TABLE_STATS
864 | OFPC_PORT_STATS
865 | OFPC_GROUP_STATS
Khen Nursimulud068d812017-03-06 11:44:18 -0500866 )
867 ),
868 root_device_id=device.id
869 )
khenaidoo507d9222017-10-10 16:23:49 -0400870 mac_address = "AA:BB:CC:DD:EE:FF"
871 ld_initialized = self.adapter_agent.create_logical_device(ld,
872 dpid=mac_address)
Khen Nursimulud068d812017-03-06 11:44:18 -0500873 cap = OFPPF_1GB_FD | OFPPF_FIBER
874 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
875 id='nni',
876 ofp_port=ofp_port(
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400877 port_no=self.ofp_port_no,
Khen Nursimulud068d812017-03-06 11:44:18 -0500878 hw_addr=mac_str_to_tuple(
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400879 '00:00:00:00:00:%02x' % self.ofp_port_no),
Khen Nursimulud068d812017-03-06 11:44:18 -0500880 name='nni',
881 config=0,
882 state=OFPPS_LIVE,
883 curr=cap,
884 advertised=cap,
885 peer=cap,
886 curr_speed=OFPPF_1GB_FD,
887 max_speed=OFPPF_1GB_FD
888 ),
889 device_id=device.id,
890 device_port_no=self.nni_port.port_no,
891 root_port=True
892 ))
893
894 device = self.adapter_agent.get_device(device.id)
895 device.parent_id = ld_initialized.id
896 device.oper_status = OperStatus.ACTIVE
897 self.adapter_agent.update_device(device)
898 self.logical_device_id = ld_initialized.id
899
900 # Reenable all child devices
khenaidoo2d7af132017-03-23 15:45:51 -0400901 self.adapter_agent.update_child_devices_state(device.id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400902 admin_state=AdminState.ENABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500903
Stephane Barbarie35595062018-02-08 08:34:39 -0500904 if self.ponsim_comm == 'grpc':
905 # establish frame grpc-stream
906 reactor.callInThread(self.rcv_grpc)
907 else:
908 # finally, open the frameio port to receive in-band packet_in messages
909 self.io_port = registry('frameio').open_port(
910 self.interface, self.rcv_io, is_inband_frame)
911
912 self.start_kpi_collection(device.id)
Khen Nursimulud068d812017-03-06 11:44:18 -0500913
914 self.log.info('re-enabled', device_id=device.id)
915
Khen Nursimulud068d812017-03-06 11:44:18 -0500916 def delete(self):
917 self.log.info('deleting', device_id=self.device_id)
918
919 # Remove all child devices
920 self.adapter_agent.delete_all_child_devices(self.device_id)
921
Stephane Barbarie35595062018-02-08 08:34:39 -0500922 self.close_channel()
923 self.log.info('disabled-grpc-channel')
924
925 if self.ponsim_comm == 'frameio':
926 # close the frameio port
927 registry('frameio').close_port(self.io_port)
928 self.log.info('disabled-frameio-port')
929
Khen Nursimulud068d812017-03-06 11:44:18 -0500930 # TODO:
931 # 1) Remove all flows from the device
932 # 2) Remove the device from ponsim
933
934 self.log.info('deleted', device_id=self.device_id)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400935
936 def start_kpi_collection(self, device_id):
937
938 def _collect(device_id, prefix):
939
940 try:
941 # Step 1: gather metrics from device
942 port_metrics = \
Stephane Barbarie5253c652017-03-22 16:29:46 -0400943 self.pm_metrics.collect_port_metrics(self.get_channel())
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400944
945 # Step 2: prepare the KpiEvent for submission
946 # we can time-stamp them here (or could use time derived from OLT
947 ts = arrow.utcnow().timestamp
948 kpi_event = KpiEvent(
949 type=KpiEventType.slice,
950 ts=ts,
951 prefixes={
952 # OLT NNI port
953 prefix + '.nni': MetricValuePairs(
954 metrics=port_metrics['nni']),
955 # OLT PON port
956 prefix + '.pon': MetricValuePairs(
957 metrics=port_metrics['pon'])
958 }
959 )
960
961 # Step 3: submit
962 self.adapter_agent.submit_kpis(kpi_event)
963
964 except Exception as e:
965 log.exception('failed-to-submit-kpis', e=e)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400966
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400967 self.pm_metrics.start_collector(_collect)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400968
Stephane Barbarie35595062018-02-08 08:34:39 -0500969 def stop_kpi_collection(self):
970 self.pm_metrics.stop_collector()
971
Nikolay Titov89004ec2017-06-19 18:22:42 -0400972 def get_interface_config(self, data):
973 interfaceConfig = InterfaceConfig()
974 if isinstance(data, ChannelgroupConfig):
975 interfaceConfig.channel_group_config.CopyFrom(data)
976 elif isinstance(data, ChannelpartitionConfig):
977 interfaceConfig.channel_partition_config.CopyFrom(data)
978 elif isinstance(data, ChannelpairConfig):
979 interfaceConfig.channel_pair_config.CopyFrom(data)
980 elif isinstance(data, ChannelterminationConfig):
981 interfaceConfig.channel_termination_config.CopyFrom(data)
982 elif isinstance(data, OntaniConfig):
983 interfaceConfig.ont_ani_config.CopyFrom(data)
984 elif isinstance(data, VOntaniConfig):
985 interfaceConfig.vont_ani_config.CopyFrom(data)
986 elif isinstance(data, VEnetConfig):
987 interfaceConfig.venet_config.CopyFrom(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400988 elif isinstance(data, TrafficDescriptorProfileData):
989 interfaceConfig.traffic_descriptor_profile_config_data.CopyFrom(
990 data)
991 elif isinstance(data, TcontsConfigData):
992 interfaceConfig.tconts_config_data.CopyFrom(data)
993 elif isinstance(data, GemportsConfigData):
994 interfaceConfig.gemports_config_data.CopyFrom(data)
995 elif isinstance(data, MulticastGemportsConfigData):
996 interfaceConfig.multicast_gemports_config_data.CopyFrom(data)
997 elif isinstance(data, MulticastDistributionSetData):
998 interfaceConfig.multicast_distribution_set_data.CopyFrom(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400999 else:
1000 return None
1001 return interfaceConfig
1002
Nikolay Titov176f1db2017-08-10 12:38:43 -04001003 def xpon_ponsim_olt_interface(self, method_name, data, data2=None):
Nikolay Titov89004ec2017-06-19 18:22:42 -04001004 interfaceConfig = self.get_interface_config(data)
1005 if interfaceConfig is not None:
Nikolay Titov176f1db2017-08-10 12:38:43 -04001006 self.log.info(
1007 'forwarding-{}-request-to-olt-for-interface-type'
khenaidoof3593a82018-06-01 16:41:31 -04001008 .format(self.xpon_ponsim_olt_itfs[method_name]['log']),
Nikolay Titov176f1db2017-08-10 12:38:43 -04001009 interface_type=type(data))
Nikolay Titov89004ec2017-06-19 18:22:42 -04001010 stub = ponsim_pb2.XPonSimStub(self.get_channel())
Nikolay Titov176f1db2017-08-10 12:38:43 -04001011 _method = getattr(
1012 stub, self.xpon_ponsim_olt_itfs[method_name]['method_name'])
1013 if isinstance(data, TcontsConfigData):
1014 tcont_config = TcontInterfaceConfig()
1015 tcont_config.tconts_config_data.CopyFrom(data)
1016 tcont_config.traffic_descriptor_profile_config_data.CopyFrom(
1017 data2)
1018 _method(tcont_config)
1019 else:
1020 _method(interfaceConfig)
Nikolay Titov89004ec2017-06-19 18:22:42 -04001021 self.log.info('success')
1022
Nikolay Titov176f1db2017-08-10 12:38:43 -04001023 def create_interface(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001024 _method_name = sys._getframe().f_code.co_name
1025 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001026
Nikolay Titov89004ec2017-06-19 18:22:42 -04001027 def update_interface(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001028 _method_name = sys._getframe().f_code.co_name
1029 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov89004ec2017-06-19 18:22:42 -04001030
1031 def remove_interface(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001032 _method_name = sys._getframe().f_code.co_name
1033 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001034
1035 def create_tcont(self, tcont_data, traffic_descriptor_data):
khenaidoof3593a82018-06-01 16:41:31 -04001036 _method_name = sys._getframe().f_code.co_name
1037 self.xpon_ponsim_olt_interface(_method_name, tcont_data,
1038 traffic_descriptor_data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001039
1040 def update_tcont(self, tcont_data, traffic_descriptor_data):
khenaidoof3593a82018-06-01 16:41:31 -04001041 _method_name = sys._getframe().f_code.co_name
1042 self.xpon_ponsim_olt_interface(_method_name, tcont_data,
1043 traffic_descriptor_data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001044
1045 def remove_tcont(self, tcont_data, traffic_descriptor_data):
khenaidoof3593a82018-06-01 16:41:31 -04001046 _method_name = sys._getframe().f_code.co_name
1047 self.xpon_ponsim_olt_interface(_method_name, tcont_data,
1048 traffic_descriptor_data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001049
1050 def create_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001051 _method_name = sys._getframe().f_code.co_name
1052 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001053
1054 def update_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001055 _method_name = sys._getframe().f_code.co_name
1056 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001057
1058 def remove_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001059 _method_name = sys._getframe().f_code.co_name
1060 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001061
1062 def create_multicast_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001063 _method_name = sys._getframe().f_code.co_name
1064 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001065
1066 def update_multicast_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001067 _method_name = sys._getframe().f_code.co_name
1068 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001069
1070 def remove_multicast_gemport(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001071 _method_name = sys._getframe().f_code.co_name
1072 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001073
1074 def create_multicast_distribution_set(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001075 _method_name = sys._getframe().f_code.co_name
1076 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001077
1078 def update_multicast_distribution_set(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001079 _method_name = sys._getframe().f_code.co_name
1080 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov176f1db2017-08-10 12:38:43 -04001081
1082 def remove_multicast_distribution_set(self, data):
khenaidoof3593a82018-06-01 16:41:31 -04001083 _method_name = sys._getframe().f_code.co_name
1084 self.xpon_ponsim_olt_interface(_method_name, data);
Scott Baker50873cc2018-09-21 18:14:09 -07001085
1086 def simulate_alarm(self, alarm):
1087 # Ponsim_olt implements its own AdapterAlarms class, rather than using the Voltha alarm extension. Until that
1088 # has been reconciled, temporarily instantiate the Voltha alarm extension's AdapterAlarms here, for the
1089 # purpose of sending simulated alarms.
1090 alarms = VolthaAdapterAlarms(self.adapter_agent, self.device_id, self.logical_device_id)
1091 simulator = AdapterAlarmSimulator(alarms)
1092 simulator.simulate_alarm(alarm)