blob: 1e5d0426ec1ff83597bd71d9d1ee18befaf90116 [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
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080024import grpc
Stephane Barbarie4475a252017-03-31 13:49:20 -040025import json
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080026import structlog
27from scapy.layers.l2 import Ether, Dot1Q
Stephane Barbarie4475a252017-03-31 13:49:20 -040028from scapy.layers.inet import Raw
Khen Nursimulud068d812017-03-06 11:44:18 -050029from twisted.internet.defer import inlineCallbacks
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080030
31from common.frameio.frameio import BpfProgramFilter, hexify
Khen Nursimulud068d812017-03-06 11:44:18 -050032from common.utils.asleep import asleep
Sergio Slobodrian98eff412017-03-15 14:46:30 -040033from twisted.internet.task import LoopingCall
Shad Ansarid1aa9e72017-06-23 21:34:25 -070034from voltha.adapters.iadapter import OltAdapter
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080035from voltha.core.logical_device_agent import mac_str_to_tuple
36from voltha.protos import third_party
37from voltha.protos import ponsim_pb2
Shad Ansari14bcd992017-06-13 14:27:20 -070038from voltha.protos.common_pb2 import OperStatus, ConnectStatus, AdminState
39from voltha.protos.device_pb2 import Port, Device, PmConfig, PmConfigs
Sergio Slobodrian98eff412017-03-15 14:46:30 -040040from voltha.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080041from google.protobuf.empty_pb2 import Empty
42
43from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice
Stephane Barbarie5253c652017-03-22 16:29:46 -040044from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
45 OFPPF_1GB_FD, \
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080046 OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS, \
47 ofp_switch_features, ofp_desc
48from voltha.protos.openflow_13_pb2 import ofp_port
49from voltha.protos.ponsim_pb2 import FlowTable
50from voltha.registry import registry
51
Nikolay Titov89004ec2017-06-19 18:22:42 -040052from voltha.protos.bbf_fiber_base_pb2 import \
Nikolay Titov176f1db2017-08-10 12:38:43 -040053 ChannelgroupConfig, ChannelpartitionConfig, ChannelpairConfig,\
54 ChannelterminationConfig, OntaniConfig, VOntaniConfig, VEnetConfig
55from voltha.protos.bbf_fiber_traffic_descriptor_profile_body_pb2 import \
56 TrafficDescriptorProfileData
57from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
58from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
59from voltha.protos.bbf_fiber_multicast_gemport_body_pb2 import \
60 MulticastGemportsConfigData
61from voltha.protos.bbf_fiber_multicast_distribution_set_body_pb2 import \
62 MulticastDistributionSetData
Nikolay Titov89004ec2017-06-19 18:22:42 -040063
Nikolay Titov176f1db2017-08-10 12:38:43 -040064from voltha.protos.ponsim_pb2 import InterfaceConfig, TcontInterfaceConfig
Nikolay Titov89004ec2017-06-19 18:22:42 -040065
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080066_ = third_party
67log = structlog.get_logger()
68
Zsolt Haraszti656ecc62016-12-28 15:08:23 -080069PACKET_IN_VLAN = 4000
70is_inband_frame = BpfProgramFilter('(ether[14:2] & 0xfff) = 0x{:03x}'.format(
71 PACKET_IN_VLAN))
72
Stephane Barbarie5253c652017-03-22 16:29:46 -040073
Sergio Slobodrian98eff412017-03-15 14:46:30 -040074class AdapterPmMetrics:
Stephane Barbarie5253c652017-03-22 16:29:46 -040075 def __init__(self, device):
Sergio Slobodrianec6e3912017-04-02 11:46:55 -040076 self.pm_names = {'tx_64_pkts', 'tx_65_127_pkts', 'tx_128_255_pkts',
77 'tx_256_511_pkts', 'tx_512_1023_pkts',
78 'tx_1024_1518_pkts', 'tx_1519_9k_pkts',
79 'rx_64_pkts', 'rx_65_127_pkts',
80 'rx_128_255_pkts', 'rx_256_511_pkts',
81 'rx_512_1023_pkts', 'rx_1024_1518_pkts',
82 'rx_1519_9k_pkts'}
Sergio Slobodrian98eff412017-03-15 14:46:30 -040083 self.device = device
84 self.id = device.id
85 self.name = 'ponsim_olt'
Stephane Barbarie5253c652017-03-22 16:29:46 -040086 # self.id = "abc"
Sergio Slobodrian98eff412017-03-15 14:46:30 -040087 self.default_freq = 150
88 self.grouped = False
89 self.freq_override = False
90 self.pon_metrics_config = dict()
91 self.nni_metrics_config = dict()
92 self.lc = None
93 for m in self.pm_names:
94 self.pon_metrics_config[m] = PmConfig(name=m,
95 type=PmConfig.COUNTER,
96 enabled=True)
97 self.nni_metrics_config[m] = PmConfig(name=m,
98 type=PmConfig.COUNTER,
99 enabled=True)
100
101 def update(self, pm_config):
102 if self.default_freq != pm_config.default_freq:
103 # Update the callback to the new frequency.
104 self.default_freq = pm_config.default_freq
105 self.lc.stop()
Stephane Barbarie5253c652017-03-22 16:29:46 -0400106 self.lc.start(interval=self.default_freq / 10)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400107 for m in pm_config.metrics:
108 self.pon_metrics_config[m.name].enabled = m.enabled
109 self.nni_metrics_config[m.name].enabled = m.enabled
110
111 def make_proto(self):
112 pm_config = PmConfigs(
113 id=self.id,
114 default_freq=self.default_freq,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400115 grouped=False,
116 freq_override=False)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400117 for m in sorted(self.pon_metrics_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400118 pm = self.pon_metrics_config[m] # Either will do they're the same
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400119 pm_config.metrics.extend([PmConfig(name=pm.name,
120 type=pm.type,
121 enabled=pm.enabled)])
122 return pm_config
123
124 def collect_port_metrics(self, channel):
125 rtrn_port_metrics = dict()
126 stub = ponsim_pb2.PonSimStub(channel)
127 stats = stub.GetStats(Empty())
128 rtrn_port_metrics['pon'] = self.extract_pon_metrics(stats)
129 rtrn_port_metrics['nni'] = self.extract_nni_metrics(stats)
130 return rtrn_port_metrics
131
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400132 def extract_pon_metrics(self, stats):
133 rtrn_pon_metrics = dict()
134 for m in stats.metrics:
135 if m.port_name == "pon":
136 for p in m.packets:
137 if self.pon_metrics_config[p.name].enabled:
138 rtrn_pon_metrics[p.name] = p.value
139 return rtrn_pon_metrics
140
141 def extract_nni_metrics(self, stats):
142 rtrn_pon_metrics = dict()
143 for m in stats.metrics:
144 if m.port_name == "nni":
145 for p in m.packets:
146 if self.pon_metrics_config[p.name].enabled:
147 rtrn_pon_metrics[p.name] = p.value
148 return rtrn_pon_metrics
149
150 def start_collector(self, callback):
151 log.info("starting-pm-collection", device_name=self.name,
152 device_id=self.device.id)
153 prefix = 'voltha.{}.{}'.format(self.name, self.device.id)
154 self.lc = LoopingCall(callback, self.device.id, prefix)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400155 self.lc.start(interval=self.default_freq / 10)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400156
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800157
Stephane Barbarie4475a252017-03-31 13:49:20 -0400158class AdapterAlarms:
159 def __init__(self, adapter, device):
160 self.adapter = adapter
161 self.device = device
162 self.lc = None
163
164 def send_alarm(self, context_data, alarm_data):
165 try:
166 current_context = {}
167 for key, value in context_data.__dict__.items():
168 current_context[key] = str(value)
169
170 alarm_event = self.adapter.adapter_agent.create_alarm(
171 resource_id=self.device.id,
khenaidoo032d3302017-06-09 14:50:04 -0400172 description="{}.{} - {}".format(self.adapter.name,
173 self.device.id,
174 alarm_data[
175 'description']) if 'description' in alarm_data else None,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400176 type=alarm_data['type'] if 'type' in alarm_data else None,
khenaidoo032d3302017-06-09 14:50:04 -0400177 category=alarm_data[
178 'category'] if 'category' in alarm_data else None,
179 severity=alarm_data[
180 'severity'] if 'severity' in alarm_data else None,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400181 state=alarm_data['state'] if 'state' in alarm_data else None,
182 raised_ts=alarm_data['ts'] if 'ts' in alarm_data else 0,
183 context=current_context
184 )
185
khenaidoo032d3302017-06-09 14:50:04 -0400186 self.adapter.adapter_agent.submit_alarm(self.device.id,
187 alarm_event)
Stephane Barbarie4475a252017-03-31 13:49:20 -0400188
189 except Exception as e:
190 log.exception('failed-to-send-alarm', e=e)
191
Shad Ansarid1aa9e72017-06-23 21:34:25 -0700192class PonSimOltAdapter(OltAdapter):
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800193 def __init__(self, adapter_agent, config):
Shad Ansari14bcd992017-06-13 14:27:20 -0700194 super(PonSimOltAdapter, self).__init__(adapter_agent=adapter_agent,
195 config=config,
Shad Ansari96f817b2017-06-18 23:17:44 -0700196 device_handler_class=PonSimOltHandler,
Shad Ansari14bcd992017-06-13 14:27:20 -0700197 name='ponsim_olt',
198 vendor='Voltha project',
Nikolay Titov89004ec2017-06-19 18:22:42 -0400199 version='0.4',
200 device_type='ponsim_olt')
Shad Ansari96f817b2017-06-18 23:17:44 -0700201
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400202 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400203 log.info("adapter-update-pm-config", device=device,
204 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400205 handler = self.devices_handlers[device.id]
206 handler.update_pm_config(device, pm_config)
Sergio Slobodrianec864c62017-03-09 11:41:43 -0500207
Nikolay Titov89004ec2017-06-19 18:22:42 -0400208 def create_interface(self, device, data):
209 log.info('create-interface', device_id=device.id)
210 self.devices_handlers[device.id].create_interface(data)
211
212 def update_interface(self, device, data):
213 log.info('update-interface', device_id=device.id)
214 self.devices_handlers[device.id].update_interface(data)
215
216 def remove_interface(self, device, data):
217 log.info('remove-interface', device_id=device.id)
218 self.devices_handlers[device.id].remove_interface(data)
219
Nikolay Titov176f1db2017-08-10 12:38:43 -0400220 def create_tcont(self, device, tcont_data, traffic_descriptor_data):
221 log.info('create-tcont', device_id=device.id)
222 self.devices_handlers[device.id].create_tcont(tcont_data,
223 traffic_descriptor_data)
224
225 def update_tcont(self, device, tcont_data, traffic_descriptor_data):
226 log.info('update-tcont', device_id=device.id)
227 self.devices_handlers[device.id].update_tcont(tcont_data,
228 traffic_descriptor_data)
229
230 def remove_tcont(self, device, tcont_data, traffic_descriptor_data):
231 log.info('remove-tcont', device_id=device.id)
232 self.devices_handlers[device.id].remove_tcont(tcont_data,
233 traffic_descriptor_data)
234
235 def create_gemport(self, device, data):
236 log.info('create-gemport', device_id=device.id)
237 self.devices_handlers[device.id].create_gemport(data)
238
239 def update_gemport(self, device, data):
240 log.info('update-gemport', device_id=device.id)
241 self.devices_handlers[device.id].update_gemport(data)
242
243 def remove_gemport(self, device, data):
244 log.info('remove-gemport', device_id=device.id)
245 self.devices_handlers[device.id].remove_gemport(data)
246
247 def create_multicast_gemport(self, device, data):
248 log.info('create-multicast-gemport', device_id=device.id)
249 self.devices_handlers[device.id].create_multicast_gemport(data)
250
251 def update_multicast_gemport(self, device, data):
252 log.info('update-multicast-gemport', device_id=device.id)
253 self.devices_handlers[device.id].update_multicast_gemport(data)
254
255 def remove_multicast_gemport(self, device, data):
256 log.info('remove-multicast-gemport', device_id=device.id)
257 self.devices_handlers[device.id].remove_multicast_gemport(data)
258
259 def create_multicast_distribution_set(self, device, data):
260 log.info('create-multicast-distribution-set', device_id=device.id)
261 self.devices_handlers[device.id].create_multicast_distribution_set(
262 data)
263
264 def update_multicast_distribution_set(self, device, data):
265 log.info('update-multicast-distribution-set', device_id=device.id)
266 self.devices_handlers[device.id].update_multicast_distribution_set(
267 data)
268
269 def remove_multicast_distribution_set(self, device, data):
270 log.info('remove-multicast-distribution-set', device_id=device.id)
271 self.devices_handlers[device.id].remove_multicast_distribution_set(
272 data)
273
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800274class PonSimOltHandler(object):
Nikolay Titov176f1db2017-08-10 12:38:43 -0400275 xpon_ponsim_olt_itfs = {
276 'create_interface': {
277 'method_name': 'CreateInterface',
278 'log': 'create-interface'},
279 'update_interface': {
280 'method_name': 'UpdateInterface',
281 'log': 'update-interface'},
282 'remove_interface': {
283 'method_name': 'RemoveInterface',
284 'log': 'remove-interface'},
285 'create_tcont': {
286 'method_name': 'CreateTcont',
287 'log': 'create-tconts-config-data'},
288 'update_tcont': {
289 'method_name': 'UpdateTcont',
290 'log': 'update-tconts-config-data'},
291 'remove_tcont': {
292 'method_name': 'RemoveTcont',
293 'log': 'remove-tconts-config-data'},
294 'create_gemport': {
295 'method_name': 'CreateGemport',
296 'log': 'create-gemports-config-data'},
297 'update_gemport': {
298 'method_name': 'UpdateGemport',
299 'log': 'update-gemports-config-data'},
300 'remove_gemport': {
301 'method_name': 'RemoveGemport',
302 'log': 'remove-gemports-config-data'},
303 'create_multicast_gemport': {
304 'method_name': 'CreateMulticastGemport',
305 'log': 'create-multicast-gemports-config-data'},
306 'update_multicast_gemport': {
307 'method_name': 'UpdateMulticastGemport',
308 'log': 'update-multicast-gemports-config-data'},
309 'remove_multicast_gemport': {
310 'method_name': 'RemoveMulticastGemport',
311 'log': 'remove-multicast-gemports-config-data'},
312 'create_multicast_distribution_set': {
313 'method_name': 'CreateMulticastDistributionSet',
314 'log': 'create-multicast-distribution-set-data'},
315 'update_multicast_distribution_set': {
316 'method_name': 'UpdateMulticastDistributionSet',
317 'log': 'update-multicast-distribution-set-data'},
318 'remove_multicast_distribution_set': {
319 'method_name': 'RemoveMulticastDistributionSet',
320 'log': 'remove-multicast-distribution-set-data'},
321 }
322
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800323 def __init__(self, adapter, device_id):
324 self.adapter = adapter
325 self.adapter_agent = adapter.adapter_agent
326 self.device_id = device_id
327 self.log = structlog.get_logger(device_id=device_id)
328 self.channel = None
329 self.io_port = None
330 self.logical_device_id = None
Khen Nursimulud068d812017-03-06 11:44:18 -0500331 self.nni_port = None
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400332 self.ofp_port_no = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800333 self.interface = registry('main').get_args().interface
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400334 self.pm_metrics = None
Stephane Barbarie4475a252017-03-31 13:49:20 -0400335 self.alarms = None
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800336
337 def __del__(self):
338 if self.io_port is not None:
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800339 registry('frameio').close_port(self.io_port)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800340
341 def get_channel(self):
342 if self.channel is None:
343 device = self.adapter_agent.get_device(self.device_id)
schowdhury9e247752017-07-14 06:56:20 -0700344
345 # read in certificate
346 try:
347 with open('/voltha/pki/voltha-CA.pem') as f:
348 trusted_certs = f.read()
349
350 with open('/voltha/pki/voltha.crt') as f:
351 client_cert = f.read()
352
353 with open('/voltha/pki/voltha.key') as f:
354 client_key = f.read()
355 except Exception as e:
356 log.error('failed-to-read-cert-keys', reason=e)
357
358 # create credentials
359 credentials = grpc.ssl_channel_credentials( root_certificates=trusted_certs, private_key=client_key, certificate_chain=client_cert)
360
361 # create channel using ssl credentials
362 my_server_host_override_string = "ABCD" # Server's CN Name, Ugly but no other Choice.
363 self.channel = grpc.secure_channel(device.host_and_port, credentials, options=(('grpc.ssl_target_name_override', my_server_host_override_string,),))
364
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800365 return self.channel
366
khenaidoo032d3302017-06-09 14:50:04 -0400367 def _get_nni_port(self):
368 ports = self.adapter_agent.get_ports(self.device_id, Port.ETHERNET_NNI)
369 if ports:
370 # For now, we use on one NNI port
371 return ports[0]
372
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800373 def activate(self, device):
374 self.log.info('activating')
375
376 if not device.host_and_port:
377 device.oper_status = OperStatus.FAILED
378 device.reason = 'No host_and_port field provided'
379 self.adapter_agent.update_device(device)
380 return
381
382 stub = ponsim_pb2.PonSimStub(self.get_channel())
383 info = stub.GetDeviceInfo(Empty())
384 log.info('got-info', info=info)
385
386 device.root = True
387 device.vendor = 'ponsim'
Stephane Barbarie5253c652017-03-22 16:29:46 -0400388 device.model = 'n/a'
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800389 device.serial_number = device.host_and_port
390 device.connect_status = ConnectStatus.REACHABLE
391 self.adapter_agent.update_device(device)
392
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400393 # Now set the initial PM configuration for this device
Stephane Barbarie5253c652017-03-22 16:29:46 -0400394 self.pm_metrics = AdapterPmMetrics(device)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400395 pm_config = self.pm_metrics.make_proto()
396 log.info("initial-pm-config", pm_config=pm_config)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400397 self.adapter_agent.update_device_pm_config(pm_config, init=True)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400398
Stephane Barbarie4475a252017-03-31 13:49:20 -0400399 # Setup alarm handler
400 self.alarms = AdapterAlarms(self.adapter, device)
401
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800402 nni_port = Port(
403 port_no=2,
404 label='NNI facing Ethernet port',
405 type=Port.ETHERNET_NNI,
406 admin_state=AdminState.ENABLED,
407 oper_status=OperStatus.ACTIVE
408 )
Khen Nursimulud068d812017-03-06 11:44:18 -0500409 self.nni_port = nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800410 self.adapter_agent.add_port(device.id, nni_port)
411 self.adapter_agent.add_port(device.id, Port(
412 port_no=1,
413 label='PON port',
414 type=Port.PON_OLT,
415 admin_state=AdminState.ENABLED,
416 oper_status=OperStatus.ACTIVE
417 ))
418
419 ld = LogicalDevice(
420 # not setting id and datapth_id will let the adapter agent pick id
421 desc=ofp_desc(
422 mfr_desc='cord porject',
423 hw_desc='simualted pon',
424 sw_desc='simualted pon',
425 serial_num=uuid4().hex,
426 dp_desc='n/a'
427 ),
428 switch_features=ofp_switch_features(
429 n_buffers=256, # TODO fake for now
430 n_tables=2, # TODO ditto
431 capabilities=( # TODO and ditto
432 OFPC_FLOW_STATS
433 | OFPC_TABLE_STATS
434 | OFPC_PORT_STATS
435 | OFPC_GROUP_STATS
436 )
437 ),
438 root_device_id=device.id
439 )
440 ld_initialized = self.adapter_agent.create_logical_device(ld)
441 cap = OFPPF_1GB_FD | OFPPF_FIBER
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400442 self.ofp_port_no = info.nni_port
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800443 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
444 id='nni',
445 ofp_port=ofp_port(
446 port_no=info.nni_port,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400447 hw_addr=mac_str_to_tuple(
448 '00:00:00:00:00:%02x' % info.nni_port),
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800449 name='nni',
450 config=0,
451 state=OFPPS_LIVE,
452 curr=cap,
453 advertised=cap,
454 peer=cap,
455 curr_speed=OFPPF_1GB_FD,
456 max_speed=OFPPF_1GB_FD
457 ),
458 device_id=device.id,
459 device_port_no=nni_port.port_no,
460 root_port=True
461 ))
462
463 device = self.adapter_agent.get_device(device.id)
464 device.parent_id = ld_initialized.id
465 device.oper_status = OperStatus.ACTIVE
466 self.adapter_agent.update_device(device)
467 self.logical_device_id = ld_initialized.id
468
469 # register ONUS per uni port
470 for port_no in info.uni_ports:
471 vlan_id = port_no
472 self.adapter_agent.child_device_detected(
473 parent_device_id=device.id,
474 parent_port_no=1,
Niren R Chidrawarefcebcd2017-07-19 20:03:39 -0400475 child_device_type='ponsim_onu',
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800476 proxy_address=Device.ProxyAddress(
477 device_id=device.id,
478 channel_id=vlan_id
479 ),
Nikolay Titov89004ec2017-06-19 18:22:42 -0400480 admin_state=AdminState.ENABLED,
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800481 vlan=vlan_id
482 )
483
484 # finally, open the frameio port to receive in-band packet_in messages
485 self.log.info('registering-frameio')
Zsolt Haraszti3e6f0892017-01-19 11:51:40 -0800486 self.io_port = registry('frameio').open_port(
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800487 self.interface, self.rcv_io, is_inband_frame)
488 self.log.info('registered-frameio')
489
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400490 # Start collecting stats from the device after a brief pause
491 self.start_kpi_collection(device.id)
492
khenaidoo032d3302017-06-09 14:50:04 -0400493 def reconcile(self, device):
494 self.log.info('reconciling-OLT-device-starts')
495
496 if not device.host_and_port:
497 device.oper_status = OperStatus.FAILED
498 device.reason = 'No host_and_port field provided'
499 self.adapter_agent.update_device(device)
500 return
501
502 try:
503 stub = ponsim_pb2.PonSimStub(self.get_channel())
504 info = stub.GetDeviceInfo(Empty())
505 log.info('got-info', info=info)
506 # TODO: Verify we are connected to the same device we are
507 # reconciling - not much data in ponsim to differentiate at the
508 # time
509 device.oper_status = OperStatus.ACTIVE
510 self.adapter_agent.update_device(device)
511 self.ofp_port_no = info.nni_port
512 self.nni_port = self._get_nni_port()
513 except Exception, e:
514 log.exception('device-unreachable', e=e)
515 device.connect_status = ConnectStatus.UNREACHABLE
516 device.oper_status = OperStatus.UNKNOWN
517 self.adapter_agent.update_device(device)
518 return
519
520 # Now set the initial PM configuration for this device
521 self.pm_metrics = AdapterPmMetrics(device)
522 pm_config = self.pm_metrics.make_proto()
523 log.info("initial-pm-config", pm_config=pm_config)
524 self.adapter_agent.update_device_pm_config(pm_config, init=True)
525
526 # Setup alarm handler
527 self.alarms = AdapterAlarms(self.adapter, device)
528
529 # TODO: Is there anything required to verify nni and PON ports
530
531 # Set the logical device id
532 device = self.adapter_agent.get_device(device.id)
533 if device.parent_id:
534 self.logical_device_id = device.parent_id
535 self.adapter_agent.reconcile_logical_device(device.parent_id)
536 else:
537 self.log.info('no-logical-device-set')
538
539 # Reconcile child devices
540 self.adapter_agent.reconcile_child_devices(device.id)
541
542 # finally, open the frameio port to receive in-band packet_in messages
543 self.io_port = registry('frameio').open_port(
544 self.interface, self.rcv_io, is_inband_frame)
545
546 # Start collecting stats from the device after a brief pause
547 self.start_kpi_collection(device.id)
548
549 self.log.info('reconciling-OLT-device-ends')
550
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800551 def rcv_io(self, port, frame):
khenaidoo032d3302017-06-09 14:50:04 -0400552 self.log.info('received', iface_name=port.iface_name,
Stephane Barbarie5253c652017-03-22 16:29:46 -0400553 frame_len=len(frame))
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800554 pkt = Ether(frame)
555 if pkt.haslayer(Dot1Q):
556 outer_shim = pkt.getlayer(Dot1Q)
557 if isinstance(outer_shim.payload, Dot1Q):
558 inner_shim = outer_shim.payload
559 cvid = inner_shim.vlan
560 logical_port = cvid
561 popped_frame = (
562 Ether(src=pkt.src, dst=pkt.dst, type=inner_shim.type) /
563 inner_shim.payload
564 )
565 kw = dict(
566 logical_device_id=self.logical_device_id,
567 logical_port_no=logical_port,
568 )
569 self.log.info('sending-packet-in', **kw)
570 self.adapter_agent.send_packet_in(
571 packet=str(popped_frame), **kw)
Stephane Barbarie4475a252017-03-31 13:49:20 -0400572 elif pkt.haslayer(Raw):
573 raw_data = json.loads(pkt.getlayer(Raw).load)
574 self.alarms.send_alarm(self, raw_data)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800575
576 def update_flow_table(self, flows):
577 stub = ponsim_pb2.PonSimStub(self.get_channel())
578 self.log.info('pushing-olt-flow-table')
579 stub.UpdateFlowTable(FlowTable(
580 port=0,
581 flows=flows
582 ))
583 self.log.info('success')
584
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400585 def update_pm_config(self, device, pm_config):
Stephane Barbarie5253c652017-03-22 16:29:46 -0400586 log.info("handler-update-pm-config", device=device,
587 pm_config=pm_config)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400588 self.pm_metrics.update(pm_config)
589
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800590 def send_proxied_message(self, proxy_address, msg):
591 self.log.info('sending-proxied-message')
592 if isinstance(msg, FlowTable):
593 stub = ponsim_pb2.PonSimStub(self.get_channel())
594 self.log.info('pushing-onu-flow-table', port=msg.port)
595 res = stub.UpdateFlowTable(msg)
596 self.adapter_agent.receive_proxied_message(proxy_address, res)
597
598 def packet_out(self, egress_port, msg):
599 self.log.info('sending-packet-out', egress_port=egress_port,
600 msg=hexify(msg))
601 pkt = Ether(msg)
602 out_pkt = (
603 Ether(src=pkt.src, dst=pkt.dst) /
604 Dot1Q(vlan=4000) /
605 Dot1Q(vlan=egress_port, type=pkt.type) /
606 pkt.payload
607 )
608 self.io_port.send(str(out_pkt))
Khen Nursimulud068d812017-03-06 11:44:18 -0500609
610 @inlineCallbacks
611 def reboot(self):
612 self.log.info('rebooting', device_id=self.device_id)
613
614 # Update the operational status to ACTIVATING and connect status to
615 # UNREACHABLE
616 device = self.adapter_agent.get_device(self.device_id)
617 previous_oper_status = device.oper_status
618 previous_conn_status = device.connect_status
619 device.oper_status = OperStatus.ACTIVATING
620 device.connect_status = ConnectStatus.UNREACHABLE
621 self.adapter_agent.update_device(device)
622
khenaidoo71d0a6c2017-03-22 21:46:04 -0400623 # Update the child devices connect state to UNREACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400624 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400625 connect_status=ConnectStatus.UNREACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400626
Khen Nursimulud068d812017-03-06 11:44:18 -0500627 # Sleep 10 secs, simulating a reboot
Stephane Barbarie5253c652017-03-22 16:29:46 -0400628 # TODO: send alert and clear alert after the reboot
Khen Nursimulud068d812017-03-06 11:44:18 -0500629 yield asleep(10)
630
631 # Change the operational status back to its previous state. With a
632 # real OLT the operational state should be the state the device is
633 # after a reboot.
634 # Get the latest device reference
635 device = self.adapter_agent.get_device(self.device_id)
636 device.oper_status = previous_oper_status
637 device.connect_status = previous_conn_status
638 self.adapter_agent.update_device(device)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400639
640 # Update the child devices connect state to REACHABLE
khenaidoo2d7af132017-03-23 15:45:51 -0400641 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400642 connect_status=ConnectStatus.REACHABLE)
khenaidoo71d0a6c2017-03-22 21:46:04 -0400643
Khen Nursimulud068d812017-03-06 11:44:18 -0500644 self.log.info('rebooted', device_id=self.device_id)
645
sathishg5ae86222017-06-28 15:16:29 +0530646 def self_test_device(self, device):
647 """
648 This is called to Self a device based on a NBI call.
649 :param device: A Voltha.Device object.
650 :return: Will return result of self test
651 """
652 log.info('self-test-device', device=device.id)
653 raise NotImplementedError()
654
Khen Nursimulud068d812017-03-06 11:44:18 -0500655 def disable(self):
656 self.log.info('disabling', device_id=self.device_id)
657
658 # Get the latest device reference
659 device = self.adapter_agent.get_device(self.device_id)
660
661 # Update the operational status to UNKNOWN
662 device.oper_status = OperStatus.UNKNOWN
663 device.connect_status = ConnectStatus.UNREACHABLE
664 self.adapter_agent.update_device(device)
665
666 # Remove the logical device
667 logical_device = self.adapter_agent.get_logical_device(
Stephane Barbarie5253c652017-03-22 16:29:46 -0400668 self.logical_device_id)
Khen Nursimulud068d812017-03-06 11:44:18 -0500669 self.adapter_agent.delete_logical_device(logical_device)
670
671 # Disable all child devices first
khenaidoo2d7af132017-03-23 15:45:51 -0400672 self.adapter_agent.update_child_devices_state(self.device_id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400673 admin_state=AdminState.DISABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500674
Khen Nursimulud068d812017-03-06 11:44:18 -0500675 # Remove the peer references from this device
676 self.adapter_agent.delete_all_peer_references(self.device_id)
677
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400678 # Set all ports to disabled
679 self.adapter_agent.disable_all_ports(self.device_id)
680
Khen Nursimulud068d812017-03-06 11:44:18 -0500681 # close the frameio port
682 registry('frameio').close_port(self.io_port)
683
khenaidoo032d3302017-06-09 14:50:04 -0400684 # Update the logice device mapping
685 if self.logical_device_id in \
686 self.adapter.logical_device_id_to_root_device_id:
687 del self.adapter.logical_device_id_to_root_device_id[
688 self.logical_device_id]
689
Khen Nursimulud068d812017-03-06 11:44:18 -0500690 # TODO:
691 # 1) Remove all flows from the device
692 # 2) Remove the device from ponsim
693
694 self.log.info('disabled', device_id=device.id)
695
Khen Nursimulud068d812017-03-06 11:44:18 -0500696 def reenable(self):
697 self.log.info('re-enabling', device_id=self.device_id)
698
699 # Get the latest device reference
700 device = self.adapter_agent.get_device(self.device_id)
701
khenaidoo032d3302017-06-09 14:50:04 -0400702 # Set the ofp_port_no and nni_port in case we bypassed the reconcile
703 # process if the device was in DISABLED state on voltha restart
704 if not self.ofp_port_no and not self.nni_port:
705 stub = ponsim_pb2.PonSimStub(self.get_channel())
706 info = stub.GetDeviceInfo(Empty())
707 log.info('got-info', info=info)
708 self.ofp_port_no = info.nni_port
709 self.nni_port = self._get_nni_port()
710
Khen Nursimulud068d812017-03-06 11:44:18 -0500711 # Update the connect status to REACHABLE
712 device.connect_status = ConnectStatus.REACHABLE
713 self.adapter_agent.update_device(device)
714
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400715 # Set all ports to enabled
716 self.adapter_agent.enable_all_ports(self.device_id)
717
Khen Nursimulud068d812017-03-06 11:44:18 -0500718 ld = LogicalDevice(
719 # not setting id and datapth_id will let the adapter agent pick id
720 desc=ofp_desc(
721 mfr_desc='cord porject',
722 hw_desc='simulated pon',
723 sw_desc='simulated pon',
724 serial_num=uuid4().hex,
725 dp_desc='n/a'
726 ),
727 switch_features=ofp_switch_features(
728 n_buffers=256, # TODO fake for now
729 n_tables=2, # TODO ditto
730 capabilities=( # TODO and ditto
731 OFPC_FLOW_STATS
732 | OFPC_TABLE_STATS
733 | OFPC_PORT_STATS
734 | OFPC_GROUP_STATS
735 )
736 ),
737 root_device_id=device.id
738 )
739 ld_initialized = self.adapter_agent.create_logical_device(ld)
740 cap = OFPPF_1GB_FD | OFPPF_FIBER
741 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
742 id='nni',
743 ofp_port=ofp_port(
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400744 port_no=self.ofp_port_no,
Khen Nursimulud068d812017-03-06 11:44:18 -0500745 hw_addr=mac_str_to_tuple(
Khen Nursimuluc60afa12017-03-13 14:33:50 -0400746 '00:00:00:00:00:%02x' % self.ofp_port_no),
Khen Nursimulud068d812017-03-06 11:44:18 -0500747 name='nni',
748 config=0,
749 state=OFPPS_LIVE,
750 curr=cap,
751 advertised=cap,
752 peer=cap,
753 curr_speed=OFPPF_1GB_FD,
754 max_speed=OFPPF_1GB_FD
755 ),
756 device_id=device.id,
757 device_port_no=self.nni_port.port_no,
758 root_port=True
759 ))
760
761 device = self.adapter_agent.get_device(device.id)
762 device.parent_id = ld_initialized.id
763 device.oper_status = OperStatus.ACTIVE
764 self.adapter_agent.update_device(device)
765 self.logical_device_id = ld_initialized.id
766
767 # Reenable all child devices
khenaidoo2d7af132017-03-23 15:45:51 -0400768 self.adapter_agent.update_child_devices_state(device.id,
Stephane Barbarie4475a252017-03-31 13:49:20 -0400769 admin_state=AdminState.ENABLED)
Khen Nursimulud068d812017-03-06 11:44:18 -0500770
Khen Nursimulud068d812017-03-06 11:44:18 -0500771 # finally, open the frameio port to receive in-band packet_in messages
Khen Nursimulud068d812017-03-06 11:44:18 -0500772 self.io_port = registry('frameio').open_port(
773 self.interface, self.rcv_io, is_inband_frame)
Khen Nursimulud068d812017-03-06 11:44:18 -0500774
775 self.log.info('re-enabled', device_id=device.id)
776
Khen Nursimulud068d812017-03-06 11:44:18 -0500777 def delete(self):
778 self.log.info('deleting', device_id=self.device_id)
779
780 # Remove all child devices
781 self.adapter_agent.delete_all_child_devices(self.device_id)
782
783 # TODO:
784 # 1) Remove all flows from the device
785 # 2) Remove the device from ponsim
786
787 self.log.info('deleted', device_id=self.device_id)
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400788
789 def start_kpi_collection(self, device_id):
790
791 def _collect(device_id, prefix):
792
793 try:
794 # Step 1: gather metrics from device
795 port_metrics = \
Stephane Barbarie5253c652017-03-22 16:29:46 -0400796 self.pm_metrics.collect_port_metrics(self.get_channel())
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400797
798 # Step 2: prepare the KpiEvent for submission
799 # we can time-stamp them here (or could use time derived from OLT
800 ts = arrow.utcnow().timestamp
801 kpi_event = KpiEvent(
802 type=KpiEventType.slice,
803 ts=ts,
804 prefixes={
805 # OLT NNI port
806 prefix + '.nni': MetricValuePairs(
807 metrics=port_metrics['nni']),
808 # OLT PON port
809 prefix + '.pon': MetricValuePairs(
810 metrics=port_metrics['pon'])
811 }
812 )
813
814 # Step 3: submit
815 self.adapter_agent.submit_kpis(kpi_event)
816
817 except Exception as e:
818 log.exception('failed-to-submit-kpis', e=e)
Stephane Barbarie5253c652017-03-22 16:29:46 -0400819
Sergio Slobodrian98eff412017-03-15 14:46:30 -0400820 self.pm_metrics.start_collector(_collect)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400821
822 def get_interface_config(self, data):
823 interfaceConfig = InterfaceConfig()
824 if isinstance(data, ChannelgroupConfig):
825 interfaceConfig.channel_group_config.CopyFrom(data)
826 elif isinstance(data, ChannelpartitionConfig):
827 interfaceConfig.channel_partition_config.CopyFrom(data)
828 elif isinstance(data, ChannelpairConfig):
829 interfaceConfig.channel_pair_config.CopyFrom(data)
830 elif isinstance(data, ChannelterminationConfig):
831 interfaceConfig.channel_termination_config.CopyFrom(data)
832 elif isinstance(data, OntaniConfig):
833 interfaceConfig.ont_ani_config.CopyFrom(data)
834 elif isinstance(data, VOntaniConfig):
835 interfaceConfig.vont_ani_config.CopyFrom(data)
836 elif isinstance(data, VEnetConfig):
837 interfaceConfig.venet_config.CopyFrom(data)
Nikolay Titov176f1db2017-08-10 12:38:43 -0400838 elif isinstance(data, TrafficDescriptorProfileData):
839 interfaceConfig.traffic_descriptor_profile_config_data.CopyFrom(
840 data)
841 elif isinstance(data, TcontsConfigData):
842 interfaceConfig.tconts_config_data.CopyFrom(data)
843 elif isinstance(data, GemportsConfigData):
844 interfaceConfig.gemports_config_data.CopyFrom(data)
845 elif isinstance(data, MulticastGemportsConfigData):
846 interfaceConfig.multicast_gemports_config_data.CopyFrom(data)
847 elif isinstance(data, MulticastDistributionSetData):
848 interfaceConfig.multicast_distribution_set_data.CopyFrom(data)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400849 else:
850 return None
851 return interfaceConfig
852
Nikolay Titov176f1db2017-08-10 12:38:43 -0400853 def xpon_ponsim_olt_interface(self, method_name, data, data2=None):
Nikolay Titov89004ec2017-06-19 18:22:42 -0400854 interfaceConfig = self.get_interface_config(data)
855 if interfaceConfig is not None:
Nikolay Titov176f1db2017-08-10 12:38:43 -0400856 self.log.info(
857 'forwarding-{}-request-to-olt-for-interface-type'
858 .format(self.xpon_ponsim_olt_itfs[method_name]['log']),
859 interface_type=type(data))
Nikolay Titov89004ec2017-06-19 18:22:42 -0400860 stub = ponsim_pb2.XPonSimStub(self.get_channel())
Nikolay Titov176f1db2017-08-10 12:38:43 -0400861 _method = getattr(
862 stub, self.xpon_ponsim_olt_itfs[method_name]['method_name'])
863 if isinstance(data, TcontsConfigData):
864 tcont_config = TcontInterfaceConfig()
865 tcont_config.tconts_config_data.CopyFrom(data)
866 tcont_config.traffic_descriptor_profile_config_data.CopyFrom(
867 data2)
868 _method(tcont_config)
869 else:
870 _method(interfaceConfig)
Nikolay Titov89004ec2017-06-19 18:22:42 -0400871 self.log.info('success')
872
Nikolay Titov176f1db2017-08-10 12:38:43 -0400873 def create_interface(self, data):
874 _method_name = sys._getframe().f_code.co_name
875 self.xpon_ponsim_olt_interface(_method_name, data);
876
Nikolay Titov89004ec2017-06-19 18:22:42 -0400877 def update_interface(self, data):
Nikolay Titov176f1db2017-08-10 12:38:43 -0400878 _method_name = sys._getframe().f_code.co_name
879 self.xpon_ponsim_olt_interface(_method_name, data);
Nikolay Titov89004ec2017-06-19 18:22:42 -0400880
881 def remove_interface(self, data):
Nikolay Titov176f1db2017-08-10 12:38:43 -0400882 _method_name = sys._getframe().f_code.co_name
883 self.xpon_ponsim_olt_interface(_method_name, data);
884
885 def create_tcont(self, tcont_data, traffic_descriptor_data):
886 _method_name = sys._getframe().f_code.co_name
887 self.xpon_ponsim_olt_interface(_method_name, tcont_data,
888 traffic_descriptor_data);
889
890 def update_tcont(self, tcont_data, traffic_descriptor_data):
891 _method_name = sys._getframe().f_code.co_name
892 self.xpon_ponsim_olt_interface(_method_name, tcont_data,
893 traffic_descriptor_data);
894
895 def remove_tcont(self, tcont_data, traffic_descriptor_data):
896 _method_name = sys._getframe().f_code.co_name
897 self.xpon_ponsim_olt_interface(_method_name, tcont_data,
898 traffic_descriptor_data);
899
900 def create_gemport(self, data):
901 _method_name = sys._getframe().f_code.co_name
902 self.xpon_ponsim_olt_interface(_method_name, data);
903
904 def update_gemport(self, data):
905 _method_name = sys._getframe().f_code.co_name
906 self.xpon_ponsim_olt_interface(_method_name, data);
907
908 def remove_gemport(self, data):
909 _method_name = sys._getframe().f_code.co_name
910 self.xpon_ponsim_olt_interface(_method_name, data);
911
912 def create_multicast_gemport(self, data):
913 _method_name = sys._getframe().f_code.co_name
914 self.xpon_ponsim_olt_interface(_method_name, data);
915
916 def update_multicast_gemport(self, data):
917 _method_name = sys._getframe().f_code.co_name
918 self.xpon_ponsim_olt_interface(_method_name, data);
919
920 def remove_multicast_gemport(self, data):
921 _method_name = sys._getframe().f_code.co_name
922 self.xpon_ponsim_olt_interface(_method_name, data);
923
924 def create_multicast_distribution_set(self, data):
925 _method_name = sys._getframe().f_code.co_name
926 self.xpon_ponsim_olt_interface(_method_name, data);
927
928 def update_multicast_distribution_set(self, data):
929 _method_name = sys._getframe().f_code.co_name
930 self.xpon_ponsim_olt_interface(_method_name, data);
931
932 def remove_multicast_distribution_set(self, data):
933 _method_name = sys._getframe().f_code.co_name
934 self.xpon_ponsim_olt_interface(_method_name, data);