blob: e3860b06dc023b5f813887c80eb3d792c22299b1 [file] [log] [blame]
Zsolt Haraszti66862032016-11-28 14:28:39 -08001#
Zsolt Haraszti3eb27a52017-01-03 21:56:48 -08002# Copyright 2017 the original author or authors.
Zsolt Haraszti66862032016-11-28 14:28:39 -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"""
18Mock device adapter for testing.
19"""
20from uuid import uuid4
21
Zsolt Haraszti749b0952017-01-18 09:02:35 -080022import arrow
Zsolt Haraszti66862032016-11-28 14:28:39 -080023import structlog
ggowdru236bd952017-06-20 20:32:55 -070024import datetime
sathishg5ae86222017-06-28 15:16:29 +053025import random
Zsolt Haraszti217a12e2016-12-19 16:37:55 -080026from klein import Klein
Zsolt Haraszti8925d1f2016-12-21 00:45:19 -080027from scapy.layers.l2 import Ether, EAPOL, Padding
Zsolt Haraszti217a12e2016-12-19 16:37:55 -080028from twisted.internet import endpoints
Zsolt Haraszti66862032016-11-28 14:28:39 -080029from twisted.internet import reactor
30from twisted.internet.defer import inlineCallbacks
Zsolt Haraszti749b0952017-01-18 09:02:35 -080031from twisted.internet.task import LoopingCall
Zsolt Haraszti217a12e2016-12-19 16:37:55 -080032from twisted.web.server import Site
Zsolt Haraszti66862032016-11-28 14:28:39 -080033from zope.interface import implementer
34
35from common.utils.asleep import asleep
36from voltha.adapters.interface import IAdapterInterface
Zsolt Haraszti85f12852016-12-24 08:30:58 -080037from voltha.core.flow_decomposer import *
Zsolt Haraszti66862032016-11-28 14:28:39 -080038from voltha.core.logical_device_agent import mac_str_to_tuple
39from voltha.protos.adapter_pb2 import Adapter, AdapterConfig
Sergio Slobodriana2eb52b2017-03-07 12:24:46 -050040from voltha.protos.device_pb2 import DeviceType, DeviceTypes, Device, Port, \
ggowdru236bd952017-06-20 20:32:55 -070041PmConfigs, PmConfig, PmGroupConfig, Image
sathishg5ae86222017-06-28 15:16:29 +053042from voltha.protos.voltha_pb2 import SelfTestResponse
Zsolt Haraszti749b0952017-01-18 09:02:35 -080043from voltha.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs
Zsolt Haraszti66862032016-11-28 14:28:39 -080044from voltha.protos.health_pb2 import HealthStatus
45from voltha.protos.common_pb2 import LogLevel, OperStatus, ConnectStatus, \
46 AdminState
47from voltha.protos.logical_device_pb2 import LogicalDevice, LogicalPort
48from voltha.protos.openflow_13_pb2 import ofp_desc, ofp_port, OFPPF_1GB_FD, \
49 OFPPF_FIBER, OFPPS_LIVE, ofp_switch_features, OFPC_PORT_STATS, \
50 OFPC_GROUP_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS
Stephane Barbariecc6b2e62017-03-02 14:35:55 -050051from voltha.protos.events_pb2 import AlarmEvent, AlarmEventType, \
52 AlarmEventSeverity, AlarmEventState, AlarmEventCategory
Sergio Slobodriana2eb52b2017-03-07 12:24:46 -050053import sys
Zsolt Haraszti66862032016-11-28 14:28:39 -080054
55log = structlog.get_logger()
56
57
Sergio Slobodrianef635a22017-03-10 22:44:39 -050058class AdapterPmMetrics:
59 class Metrics:
60 def __init__(self, config, value):
61 self.config = config
62 self.value = value
63
64 def __init__(self,device):
Sergio Slobodrianec6e3912017-04-02 11:46:55 -040065 self.pm_names = {'tx_64_pkts','tx_65_127_pkts', 'tx_128_255_pkts',
66 'tx_256_511_pkts', 'tx_512_1023_pkts',
67 'tx_1024_1518_pkts', 'tx_1519_9k_pkts', 'rx_64_pkts',
68 'rx_65_127_pkts', 'rx_128_255_pkts', 'rx_256_511_pkts',
69 'rx_512_1023_pkts', 'rx_1024_1518_pkts',
70 'rx_1519_9k_pkts', 'tx_pkts', 'rx_pkts',
Sergio Slobodrianef635a22017-03-10 22:44:39 -050071 'tx_bytes', 'rx_bytes'}
72 # This is just to generate more realistic looking values. This would
73 # not be implemented in a normal adapter.
74 self.rand_ranges = dict (
Sergio Slobodrianec6e3912017-04-02 11:46:55 -040075 tx_64_pkts=[50, 55],
76 tx_65_127_pkts=[55,60],
77 tx_128_255_pkts=[60,65],
78 tx_256_511_pkts=[85,90],
79 tx_512_1023_pkts=[90,95],
80 tx_1024_1518_pkts=[60,65],
81 tx_1519_9k_pkts=[50,55],
82 rx_64_pkts=[50, 55],
83 rx_65_127_pkts=[55,60],
84 rx_128_255_pkts=[60,65],
85 rx_256_511_pkts=[85,90],
86 rx_512_1023_pkts=[90,95],
87 rx_1024_1518_pkts=[60,65],
88 rx_1519_9k_pkts=[50,55],
Sergio Slobodrianef635a22017-03-10 22:44:39 -050089 tx_pkts=[90,100],
90 rx_pkts=[90,100],
91 rx_bytes=[90000,100000],
92 tx_bytes=[90000,100000]
93 )
94 self.device = device
95 self.id = device.id
96 self.default_freq = 150
97 self.grouped = False
98 self.freq_override = False
99 self.pon_metrics = dict()
100 self.nni_metrics = dict()
101 self.lc = None
102 for m in self.pm_names:
103 self.pon_metrics[m] = \
104 self.Metrics(config = PmConfig(name=m,
105 type=PmConfig.COUNTER,
106 enabled=True), value = 0)
107 self.nni_metrics[m] = \
108 self.Metrics(config = PmConfig(name=m,
109 type=PmConfig.COUNTER,
110 enabled=True), value = 0)
111
112 def update(self, pm_config):
113 if self.default_freq != pm_config.default_freq:
114 # Update the callback to the new frequency.
115 self.default_freq = pm_config.default_freq
116 self.lc.stop()
117 self.lc.start(interval=self.default_freq/10)
118 for m in pm_config.metrics:
119 self.pon_metrics[m.name].config.enabled = m.enabled
120 self.nni_metrics[m.name].config.enabled = m.enabled
121
122 def make_proto(self):
123 pm_config = PmConfigs(
124 id=self.id,
125 default_freq=self.default_freq,
126 grouped = False,
127 freq_override = False)
128 for m in sorted(self.pon_metrics):
129 pm=self.pon_metrics[m]
130 pm_config.metrics.extend([PmConfig(name=pm.config.name,
131 type=pm.config.type,
132 enabled=pm.config.enabled)])
133 return pm_config
134
135 def collect_pon_metrics(self):
136 import random
137 rtrn_pon_metrics = dict()
138 for m in self.pm_names:
139 if self.pon_metrics[m].config.enabled:
140 self.pon_metrics[m].value += \
141 random.randint(self.rand_ranges[m][0], self.rand_ranges[m][1])
142 rtrn_pon_metrics[m] = self.pon_metrics[m].value
143 return rtrn_pon_metrics
144
145 def collect_nni_metrics(self):
146 import random
147 rtrn_nni_metrics = dict()
148 for m in self.pm_names:
149 if self.nni_metrics[m].config.enabled:
150 self.nni_metrics[m].value += \
151 random.randint(self.rand_ranges[m][0], self.rand_ranges[m][1])
152 rtrn_nni_metrics[m] = self.nni_metrics[m].value
153 return rtrn_nni_metrics
154
155 def start_collector(self, device_name, device_id, callback):
156 prefix = 'voltha.{}.{}'.format(device_name, device_id)
157 self.lc = LoopingCall(callback, device_id, prefix)
158 self.lc.start(interval=self.default_freq/10)
159
160
Zsolt Haraszti66862032016-11-28 14:28:39 -0800161@implementer(IAdapterInterface)
162class SimulatedOltAdapter(object):
Zsolt Haraszti66862032016-11-28 14:28:39 -0800163 name = 'simulated_olt'
164
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800165 supported_device_types = [
166 DeviceType(
167 id='simulated_olt',
168 adapter=name,
169 accepts_bulk_flow_update=True
170 )
171 ]
172
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800173 app = Klein()
174
Sergio Slobodrianef635a22017-03-10 22:44:39 -0500175
Zsolt Haraszti66862032016-11-28 14:28:39 -0800176 def __init__(self, adapter_agent, config):
177 self.adapter_agent = adapter_agent
178 self.config = config
179 self.descriptor = Adapter(
180 id=self.name,
181 vendor='Voltha project',
182 version='0.1',
183 config=AdapterConfig(log_level=LogLevel.INFO)
184 )
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800185 self.control_endpoint = None
Sergio Slobodrianf39aaf82017-02-28 16:10:16 -0500186 # Faked PM metrics for testing PM functionality
Sergio Slobodrianef635a22017-03-10 22:44:39 -0500187 self.pm_metrics = None
Zsolt Haraszti66862032016-11-28 14:28:39 -0800188
189 def start(self):
190 log.debug('starting')
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800191
192 # setup a basic web server for test control
193 self.control_endpoint = endpoints.TCP4ServerEndpoint(reactor, 18880)
194 self.control_endpoint.listen(self.get_test_control_site())
195
Zsolt Haraszti66862032016-11-28 14:28:39 -0800196 # TODO tmp: populate some devices and logical devices
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800197 # reactor.callLater(0, self._tmp_populate_stuff)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800198 log.info('started')
199
200 def stop(self):
201 log.debug('stopping')
202 log.info('stopped')
203
204 def adapter_descriptor(self):
205 return self.descriptor
206
207 def device_types(self):
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800208 return DeviceTypes(items=self.supported_device_types)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800209
210 def health(self):
211 return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
212
213 def change_master_state(self, master):
214 raise NotImplementedError()
215
216 def adopt_device(self, device):
217 # We kick of a simulated activation scenario
218 reactor.callLater(0.2, self._simulate_device_activation, device)
219 return device
220
khenaidoo032d3302017-06-09 14:50:04 -0400221 def reconcile_device(self, device):
222 raise NotImplementedError()
223
Zsolt Haraszti66862032016-11-28 14:28:39 -0800224 def abandon_device(self, device):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800225 raise NotImplementedError()
226
Khen Nursimulud068d812017-03-06 11:44:18 -0500227 def disable_device(self, device):
228 raise NotImplementedError()
229
230 def reenable_device(self, device):
231 raise NotImplementedError()
232
233 def reboot_device(self, device):
234 raise NotImplementedError()
235
236 def delete_device(self, device):
237 raise NotImplementedError()
238
239 def get_device_details(self, device):
Zsolt Haraszti66862032016-11-28 14:28:39 -0800240 raise NotImplementedError()
241
Sergio Slobodrianef635a22017-03-10 22:44:39 -0500242 def update_pm_config(self, device, pm_config):
243 log.info("adapter-update-pm-config", device=device, pm_config=pm_config)
244 self.pm_metrics.update(pm_config)
245
sathishg5ae86222017-06-28 15:16:29 +0530246 def self_test_device(self, device):
247 log.info("run-self-test-on-device", device=device.id)
248 result = SelfTestResponse(result = random.choice([
249 SelfTestResponse.SUCCESS,
250 SelfTestResponse.FAILURE,
251 SelfTestResponse.NOT_SUPPORTED,
252 SelfTestResponse.UNKNOWN_ERROR]))
253 return result
Sergio Slobodrianec864c62017-03-09 11:41:43 -0500254
Zsolt Haraszti66862032016-11-28 14:28:39 -0800255 def _tmp_populate_stuff(self):
256 """
257 pretend that we discovered some devices and create:
258 - devices
259 - device ports for each
260 - logical device
261 - logical device ports
262 """
263
264 olt = Device(
265 id='simulated_olt_1',
266 type='simulated_olt',
267 root=True,
268 vendor='simulated',
269 model='n/a',
270 hardware_version='n/a',
271 firmware_version='n/a',
272 software_version='1.0',
273 serial_number=uuid4().hex,
274 adapter=self.name,
275 oper_status=OperStatus.DISCOVERED
276 )
277 self.adapter_agent.add_device(olt)
278 self.adapter_agent.add_port(
279 olt.id, Port(port_no=1, label='pon', type=Port.PON_OLT))
280 self.adapter_agent.add_port(
281 olt.id, Port(port_no=2, label='eth', type=Port.ETHERNET_NNI))
282
283 onu1 = Device(
284 id='simulated_onu_1',
285 type='simulated_onu',
286 root=False,
287 parent_id=olt.id,
288 parent_port_no=1,
289 vendor='simulated',
290 model='n/a',
291 hardware_version='n/a',
292 firmware_version='n/a',
293 software_version='1.0',
294 serial_number=uuid4().hex,
295 adapter='simulated_onu',
296 oper_status=OperStatus.DISCOVERED,
297 vlan=101
298 )
299 self.adapter_agent.add_device(onu1)
300 self.adapter_agent.add_port(onu1.id, Port(
301 port_no=2, label='eth', type=Port.ETHERNET_UNI))
302 self.adapter_agent.add_port(onu1.id, Port(
303 port_no=1,
304 label='pon',
305 type=Port.PON_ONU,
306 peers=[Port.PeerPort(device_id=olt.id, port_no=1)]))
307
308 onu2 = Device(
309 id='simulated_onu_2',
310 type='simulated_onu',
311 root=False,
312 parent_id=olt.id,
313 parent_port_no=1,
314 vendor='simulated',
315 model='n/a',
316 hardware_version='n/a',
317 firmware_version='n/a',
318 software_version='1.0',
319 serial_number=uuid4().hex,
320 adapter='simulated_onu',
321 oper_status=OperStatus.DISCOVERED,
322 vlan=102
323 )
324 self.adapter_agent.add_device(onu2)
325 self.adapter_agent.add_port(onu2.id, Port(
326 port_no=2, label='eth', type=Port.ETHERNET_UNI))
327 self.adapter_agent.add_port(onu2.id, Port(
328 port_no=1,
329 label='pon',
330 type=Port.PON_ONU,
331 peers=[Port.PeerPort(device_id=olt.id, port_no=1)]))
332
333 ld = LogicalDevice(
334 id='simulated1',
335 datapath_id=1,
336 desc=ofp_desc(
alshabib9fbb2232016-12-23 00:40:08 -0800337 mfr_desc='cord project',
Zsolt Haraszti66862032016-11-28 14:28:39 -0800338 hw_desc='simualted pon',
339 sw_desc='simualted pon',
340 serial_num=uuid4().hex,
341 dp_desc='n/a'
342 ),
343 switch_features=ofp_switch_features(
344 n_buffers=256, # TODO fake for now
345 n_tables=2, # TODO ditto
346 capabilities=( # TODO and ditto
347 OFPC_FLOW_STATS
348 | OFPC_TABLE_STATS
349 | OFPC_PORT_STATS
350 | OFPC_GROUP_STATS
351 )
352 ),
353 root_device_id=olt.id
354 )
355 self.adapter_agent.create_logical_device(ld)
356
357 cap = OFPPF_1GB_FD | OFPPF_FIBER
358 for port_no, name, device_id, device_port_no, root_port in [
359 (1, 'onu1', onu1.id, 2, False),
360 (2, 'onu2', onu2.id, 2, False),
361 (129, 'olt1', olt.id, 2, True)]:
362 port = LogicalPort(
363 id=name,
364 ofp_port=ofp_port(
365 port_no=port_no,
366 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
367 name=name,
368 config=0,
369 state=OFPPS_LIVE,
370 curr=cap,
371 advertised=cap,
372 peer=cap,
373 curr_speed=OFPPF_1GB_FD,
374 max_speed=OFPPF_1GB_FD
375 ),
376 device_id=device_id,
377 device_port_no=device_port_no,
378 root_port=root_port
379 )
380 self.adapter_agent.add_logical_port(ld.id, port)
381
Zsolt Haraszti8925d1f2016-12-21 00:45:19 -0800382 olt.parent_id = ld.id
383 self.adapter_agent.update_device(olt)
384
Zsolt Haraszti66862032016-11-28 14:28:39 -0800385 @inlineCallbacks
386 def _simulate_device_activation(self, device):
387
388 # first we pretend that we were able to contact the device and obtain
389 # additional information about it
390 device.root = True
391 device.vendor = 'simulated'
392 device.model = 'n/a'
393 device.hardware_version = 'n/a'
394 device.firmware_version = 'n/a'
Zsolt Haraszti66862032016-11-28 14:28:39 -0800395 device.serial_number = uuid4().hex
396 device.connect_status = ConnectStatus.REACHABLE
Sergio Slobodrian2db4c102017-03-09 22:29:23 -0500397
ggowdru236bd952017-06-20 20:32:55 -0700398 image1 = Image(name="olt_candidate1",
399 version="1.0",
400 hash="",
401 install_datetime=datetime.datetime.utcnow().isoformat(),
402 is_active=True,
403 is_committed=True,
404 is_valid=True)
405
406 image2 = Image(name="olt_candidate2",
407 version="1.0",
408 hash="",
409 install_datetime=datetime.datetime.utcnow().isoformat(),
410 is_active=False,
411 is_committed=False,
412 is_valid=True)
413
414 device.images.image.extend([image1, image2])
415
Zsolt Haraszti66862032016-11-28 14:28:39 -0800416 self.adapter_agent.update_device(device)
417
Sergio Slobodrian2db4c102017-03-09 22:29:23 -0500418 # Now set the initial PM configuration for this device
Sergio Slobodrianef635a22017-03-10 22:44:39 -0500419 self.pm_metrics=AdapterPmMetrics(device)
420 pm_config = self.pm_metrics.make_proto()
421 log.info("initial-pm-config", pm_config=pm_config)
422 self.adapter_agent.update_device_pm_config(pm_config,init=True)
Sergio Slobodrian2db4c102017-03-09 22:29:23 -0500423
Zsolt Haraszti66862032016-11-28 14:28:39 -0800424 # then shortly after we create some ports for the device
425 yield asleep(0.05)
426 nni_port = Port(
427 port_no=2,
428 label='NNI facing Ethernet port',
429 type=Port.ETHERNET_NNI,
430 admin_state=AdminState.ENABLED,
431 oper_status=OperStatus.ACTIVE
432 )
433 self.adapter_agent.add_port(device.id, nni_port)
434 self.adapter_agent.add_port(device.id, Port(
435 port_no=1,
436 label='PON port',
437 type=Port.PON_OLT,
438 admin_state=AdminState.ENABLED,
439 oper_status=OperStatus.ACTIVE
440 ))
441
Sergio Slobodriana2eb52b2017-03-07 12:24:46 -0500442
Zsolt Haraszti66862032016-11-28 14:28:39 -0800443 # then shortly after we create the logical device with one port
444 # that will correspond to the NNI port
445 yield asleep(0.05)
alshabib9fbb2232016-12-23 00:40:08 -0800446 logical_device_id = uuid4().hex[:12]
Zsolt Haraszti66862032016-11-28 14:28:39 -0800447 ld = LogicalDevice(
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800448 # not setting id and datapth_id will let the adapter agent pick id
Zsolt Haraszti66862032016-11-28 14:28:39 -0800449 desc=ofp_desc(
450 mfr_desc='cord porject',
451 hw_desc='simualted pon',
452 sw_desc='simualted pon',
453 serial_num=uuid4().hex,
454 dp_desc='n/a'
455 ),
456 switch_features=ofp_switch_features(
457 n_buffers=256, # TODO fake for now
458 n_tables=2, # TODO ditto
459 capabilities=( # TODO and ditto
460 OFPC_FLOW_STATS
461 | OFPC_TABLE_STATS
462 | OFPC_PORT_STATS
463 | OFPC_GROUP_STATS
464 )
465 ),
466 root_device_id=device.id
467 )
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800468 ld_initialized = self.adapter_agent.create_logical_device(ld)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800469 cap = OFPPF_1GB_FD | OFPPF_FIBER
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800470 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
Zsolt Haraszti66862032016-11-28 14:28:39 -0800471 id='nni',
472 ofp_port=ofp_port(
473 port_no=129,
474 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % 129),
475 name='nni',
476 config=0,
477 state=OFPPS_LIVE,
478 curr=cap,
479 advertised=cap,
480 peer=cap,
481 curr_speed=OFPPF_1GB_FD,
482 max_speed=OFPPF_1GB_FD
483 ),
484 device_id=device.id,
485 device_port_no=nni_port.port_no,
486 root_port=True
487 ))
488
489 # and finally update to active
490 device = self.adapter_agent.get_device(device.id)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800491 device.parent_id = ld_initialized.id
Zsolt Haraszti66862032016-11-28 14:28:39 -0800492 device.oper_status = OperStatus.ACTIVE
493 self.adapter_agent.update_device(device)
494
khenaidoob96ee0a2017-06-28 15:39:16 -0400495 reactor.callLater(0.1, self._simulate_detection_of_onus, device.id)
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800496 self.start_kpi_collection(device.id)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800497
Stephane Barbarie52198b92017-03-02 13:44:46 -0500498 self.start_alarm_simulation(device.id)
499
Zsolt Haraszti66862032016-11-28 14:28:39 -0800500 @inlineCallbacks
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800501 def _simulate_detection_of_onus(self, device_id):
khenaidoo08d48d22017-06-29 19:42:49 -0400502 try:
503 for i in xrange(1, 5):
504 log.info('activate-olt-for-onu-{}'.format(i))
505 vlan_id = self._olt_side_onu_activation(i)
506 yield asleep(0.05)
507 self.adapter_agent.child_device_detected(
508 parent_device_id=device_id,
509 parent_port_no=1,
510 child_device_type='simulated_onu',
511 proxy_address=Device.ProxyAddress(
512 device_id=device_id,
513 channel_id=vlan_id
514 ),
515 vlan=vlan_id
516 )
517 except Exception as e:
518 log.exception('error', e=e)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800519
520 def _olt_side_onu_activation(self, seq):
521 """
522 This is where if this was a real OLT, the OLT-side activation for
523 the new ONU should be performed. By the time we return, the OLT shall
524 be able to provide tunneled (proxy) communication to the given ONU,
525 using the returned information.
526 """
Zsolt Haraszti66862032016-11-28 14:28:39 -0800527 vlan_id = seq + 100
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800528 return vlan_id
Zsolt Haraszti66862032016-11-28 14:28:39 -0800529
Sergio Slobodriana2eb52b2017-03-07 12:24:46 -0500530 #def update_pm_collection(self, device, pm_collection_config):
531 # This is where the metrics to be collected are configured and where
532 # the sampling frequency is set.
533 #TODO: Here.
534 # pass
535
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800536 def update_flows_bulk(self, device, flows, groups):
537 log.debug('bulk-flow-update', device_id=device.id,
538 flows=flows, groups=groups)
539
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800540 # sample code that analyzes the incoming flow table
541 assert len(groups.items) == 0, "Cannot yet deal with groups"
542
543 for flow in flows.items:
544 in_port = get_in_port(flow)
545 assert in_port is not None
546
547 if in_port == 2:
548
549 # Downstream rule
550
551 for field in get_ofb_fields(flow):
552 if field.type == ETH_TYPE:
553 _type = field.eth_type
554 pass # construct ether type based condition here
555
556 elif field.type == IP_PROTO:
557 _proto = field.ip_proto
558 pass # construct ip_proto based condition here
559
560 elif field.type == IN_PORT:
561 _port = field.port
562 pass # construct in_port based condition here
563
564 elif field.type == VLAN_VID:
565 _vlan_vid = field.vlan_vid
566 pass # construct VLAN ID based filter condition here
567
568 elif field.type == VLAN_PCP:
569 _vlan_pcp = field.vlan_pcp
570 pass # construct VLAN PCP based filter condition here
571
Zsolt Haraszti6a5107c2017-01-09 23:42:41 -0800572 elif field.type == METADATA:
573 pass # safe to ignore
574
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800575 # TODO
576 else:
577 raise NotImplementedError('field.type={}'.format(
578 field.type))
579
580 for action in get_actions(flow):
581
582 if action.type == OUTPUT:
583 pass # construct packet emit rule here
584
585 elif action.type == PUSH_VLAN:
586 if action.push.ethertype != 0x8100:
587 log.error('unhandled-ether-type',
588 ethertype=action.push.ethertype)
589 pass # construct vlan push command here
590
591 elif action.type == POP_VLAN:
592 pass # construct vlan pop command here
593
594 elif action.type == SET_FIELD:
595 assert (action.set_field.field.oxm_class ==
596 ofp.OFPXMC_OPENFLOW_BASIC)
597 field = action.set_field.field.ofb_field
598 if field.type == VLAN_VID:
599 pass # construct vlan_id set command here
600 else:
601 log.error('unsupported-action-set-field-type',
602 field_type=field.type)
603
604 else:
605 log.error('unsupported-action-type',
606 action_type=action.type)
607
608 # final assembly of low level device flow rule and pushing it
609 # down to device
610 pass
611
612 elif in_port == 1:
613
614 # Upstream rule
615
616 for field in get_ofb_fields(flow):
617
618 if field.type == ETH_TYPE:
619 _type = field.eth_type
620 pass # construct ether type based condition here
621
622 elif field.type == IP_PROTO:
623 _proto = field.ip_proto
624 pass # construct ip_proto based condition here
625
626 elif field.type == IN_PORT:
627 _port = field.port
628 pass # construct in_port based condition here
629
630 elif field.type == VLAN_VID:
631 _vlan_vid = field.vlan_vid
632 pass # construct VLAN ID based filter condition here
633
634 elif field.type == VLAN_PCP:
635 _vlan_pcp = field.vlan_pcp
636 pass # construct VLAN PCP based filter condition here
637
Zsolt Haraszti3578a1c2017-01-10 15:29:02 -0800638 elif field.type == UDP_SRC:
639 _udp_src = field.udp_src
640 pass # construct UDP SRC based filter here
641
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800642 elif field.type == UDP_DST:
643 _udp_dst = field.udp_dst
Zsolt Haraszti3578a1c2017-01-10 15:29:02 -0800644 pass # construct UDP DST based filter here
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800645
646 # TODO
647 else:
648 raise NotImplementedError('field.type={}'.format(
649 field.type))
650
651 for action in get_actions(flow):
652
653 if action.type == OUTPUT:
654 pass # construct packet emit rule here
655
656 elif action.type == PUSH_VLAN:
657 if action.push.ethertype != 0x8100:
658 log.error('unhandled-ether-type',
659 ethertype=action.push.ethertype)
660 pass # construct vlan push command here
661
662 elif action.type == SET_FIELD:
663 assert (action.set_field.field.oxm_class ==
664 ofp.OFPXMC_OPENFLOW_BASIC)
665 field = action.set_field.field.ofb_field
666 if field.type == VLAN_VID:
667 pass # construct vlan_id set command here
668 else:
669 log.error('unsupported-action-set-field-type',
670 field_type=field.type)
671
672 else:
673 log.error('unsupported-action-type',
674 action_type=action.type)
675
676 # final assembly of low level device flow rule and pushing it
677 # down to device
678 pass
679
680 else:
681 raise Exception('Port should be 1 or 2 by our convention')
682
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800683 def update_flows_incrementally(self, device, flow_changes, group_changes):
684 raise NotImplementedError()
685
686 def send_proxied_message(self, proxy_address, msg):
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800687 log.info('send-proxied-message', proxy_address=proxy_address, msg=msg)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800688 # we mimic a response by sending the same message back in a short time
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800689 reactor.callLater(
690 0.2,
691 self.adapter_agent.receive_proxied_message,
692 proxy_address,
693 msg
694 )
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800695
696 def receive_proxied_message(self, proxy_address, msg):
697 raise NotImplementedError()
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800698
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800699 def receive_packet_out(self, logical_device_id, egress_port_no, msg):
700 log.info('packet-out', logical_device_id=logical_device_id,
701 egress_port_no=egress_port_no, msg_len=len(msg))
702
Peter Shafik9107f2e2017-05-02 15:54:39 -0400703 def receive_inter_adapter_message(self, msg):
704 raise NotImplementedError()
705
Stephane Barbarie980a0912017-05-11 11:27:06 -0400706 def suppress_alarm(self, filter):
707 raise NotImplementedError()
708
709 def unsuppress_alarm(self, filter):
710 raise NotImplementedError()
711
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800712 def start_kpi_collection(self, device_id):
Zsolt Harasztic5f740b2017-01-18 09:53:17 -0800713
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800714 """Simulate periodic KPI metric collection from the device"""
715 import random
716
717 @inlineCallbacks # pretend that we need to do async calls
718 def _collect(device_id, prefix):
719
720 try:
721 # Step 1: gather metrics from device (pretend it here) - examples
Sergio Slobodrianf39aaf82017-02-28 16:10:16 -0500722 # upgraded the metrics to include packet statistics for
723 # testing.
Sergio Slobodrianef635a22017-03-10 22:44:39 -0500724 nni_port_metrics = self.pm_metrics.collect_nni_metrics()
725 pon_port_metrics = self.pm_metrics.collect_pon_metrics()
Sergio Slobodrianf39aaf82017-02-28 16:10:16 -0500726
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800727 olt_metrics = yield dict(
728 cpu_util=20 + 5 * random.random(),
729 buffer_util=10 + 10 * random.random()
730 )
731
732 # Step 2: prepare the KpiEvent for submission
733 # we can time-stamp them here (or could use time derived from OLT
734 ts = arrow.utcnow().timestamp
735 kpi_event = KpiEvent(
736 type=KpiEventType.slice,
737 ts=ts,
738 prefixes={
739 # OLT-level
740 prefix: MetricValuePairs(metrics=olt_metrics),
741 # OLT NNI port
Stephane Barbariecc6b2e62017-03-02 14:35:55 -0500742 prefix + '.nni': MetricValuePairs(
743 metrics=nni_port_metrics),
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800744 # OLT PON port
Stephane Barbariecc6b2e62017-03-02 14:35:55 -0500745 prefix + '.pon': MetricValuePairs(
746 metrics=pon_port_metrics)
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800747 }
748 )
749
750 # Step 3: submit
751 self.adapter_agent.submit_kpis(kpi_event)
752
753 except Exception as e:
754 log.exception('failed-to-submit-kpis', e=e)
755
Sergio Slobodrianef635a22017-03-10 22:44:39 -0500756 self.pm_metrics.start_collector(self.name, device_id ,_collect)
757 #prefix = 'voltha.{}.{}'.format(self.name, device_id)
758 #lc = LoopingCall(_collect, device_id, prefix)
759 #lc.start(interval=15) # TODO make this configurable
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800760
Stephane Barbarie52198b92017-03-02 13:44:46 -0500761 def start_alarm_simulation(self, device_id):
762
763 """Simulate periodic device alarms"""
764 import random
765
766 def _generate_alarm(device_id):
767
768 try:
769 # Randomly choose values for each enum types
Stephane Barbariecc6b2e62017-03-02 14:35:55 -0500770 severity = random.choice(list(
771 v for k, v in
772 AlarmEventSeverity.DESCRIPTOR.enum_values_by_name.items()))
Stephane Barbarie52198b92017-03-02 13:44:46 -0500773
Stephane Barbariecc6b2e62017-03-02 14:35:55 -0500774 state = random.choice(list(
775 v for k, v in
776 AlarmEventState.DESCRIPTOR.enum_values_by_name.items()))
777
778 type = random.choice(list(
779 v for k, v in
780 AlarmEventType.DESCRIPTOR.enum_values_by_name.items()))
781
782 category = random.choice(list(
783 v for k, v in
784 AlarmEventCategory.DESCRIPTOR.enum_values_by_name.items()))
785
786 description = "Simulated alarm - " \
787 "device:{} " \
788 "type:{} " \
789 "severity:{} " \
790 "state:{} " \
791 "category:{}".format(device_id,
792 type.name,
793 severity.name,
794 state.name,
795 category.name)
Stephane Barbarie52198b92017-03-02 13:44:46 -0500796
797 current_context = {}
798 for key, value in self.__dict__.items():
799 current_context[key] = str(value)
800
Stephane Barbariecc6b2e62017-03-02 14:35:55 -0500801 alarm_event = self.adapter_agent.create_alarm(
802 resource_id=device_id,
803 type=type.number,
804 category=category.number,
805 severity=severity.number,
806 state=state.number,
807 description=description,
808 context=current_context)
Stephane Barbarie52198b92017-03-02 13:44:46 -0500809
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400810 self.adapter_agent.submit_alarm(device_id, alarm_event)
Stephane Barbarie52198b92017-03-02 13:44:46 -0500811
812 except Exception as e:
813 log.exception('failed-to-submit-alarm', e=e)
814
815 alarm_lc = LoopingCall(_generate_alarm, device_id)
816 alarm_lc.start(30)
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800817
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800818 # ~~~~~~~~~~~~~~~~~~~~ Embedded test Klein rest server ~~~~~~~~~~~~~~~~~~~~
819
Zsolt Haraszti8925d1f2016-12-21 00:45:19 -0800820 def get_test_control_site(self):
821 return Site(self.app.resource())
822
823 @app.route('/devices/<string:device_id>/detect_onus')
824 def detect_onus(self, request, device_id):
825 log.info('detect-onus', request=request, device_id=device_id)
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800826 self._simulate_detection_of_onus(device_id)
827 return '{"status": "OK"}'
828
Zsolt Haraszti8925d1f2016-12-21 00:45:19 -0800829 @app.route('/devices/<string:device_id>/test_eapol_in')
830 def test_eapol_in(self, request, device_id):
831 """Simulate a packet in message posted upstream"""
832 log.info('test_eapol_in', request=request, device_id=device_id)
833 eapol_start = str(
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800834 Ether(src='00:11:22:33:44:55') /
835 EAPOL(type=1, len=0) /
Stephane Barbarie52198b92017-03-02 13:44:46 -0500836 Padding(load=42 * '\x00')
Zsolt Haraszti8925d1f2016-12-21 00:45:19 -0800837 )
Zsolt Haraszti8925d1f2016-12-21 00:45:19 -0800838 device = self.adapter_agent.get_device(device_id)
839 self.adapter_agent.send_packet_in(logical_device_id=device.parent_id,
840 logical_port_no=1,
841 packet=eapol_start)
842 return '{"status": "sent"}'
Sergio Slobodrianef635a22017-03-10 22:44:39 -0500843
844