blob: d6421f05c65db4b935deba7d19a5376282a20db4 [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
Zsolt Haraszti217a12e2016-12-19 16:37:55 -080024from klein import Klein
Zsolt Haraszti8925d1f2016-12-21 00:45:19 -080025from scapy.layers.l2 import Ether, EAPOL, Padding
Zsolt Haraszti217a12e2016-12-19 16:37:55 -080026from twisted.internet import endpoints
Zsolt Haraszti66862032016-11-28 14:28:39 -080027from twisted.internet import reactor
28from twisted.internet.defer import inlineCallbacks
Zsolt Haraszti749b0952017-01-18 09:02:35 -080029from twisted.internet.task import LoopingCall
Zsolt Haraszti217a12e2016-12-19 16:37:55 -080030from twisted.web.server import Site
Zsolt Haraszti66862032016-11-28 14:28:39 -080031from zope.interface import implementer
32
33from common.utils.asleep import asleep
34from voltha.adapters.interface import IAdapterInterface
Zsolt Haraszti85f12852016-12-24 08:30:58 -080035from voltha.core.flow_decomposer import *
Zsolt Haraszti66862032016-11-28 14:28:39 -080036from voltha.core.logical_device_agent import mac_str_to_tuple
37from voltha.protos.adapter_pb2 import Adapter, AdapterConfig
Sergio Slobodriana2eb52b2017-03-07 12:24:46 -050038from voltha.protos.device_pb2 import DeviceType, DeviceTypes, Device, Port, \
39PmConfigs, PmConfig, PmGroupConfig
Zsolt Haraszti749b0952017-01-18 09:02:35 -080040from voltha.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs
Zsolt Haraszti66862032016-11-28 14:28:39 -080041from voltha.protos.health_pb2 import HealthStatus
42from voltha.protos.common_pb2 import LogLevel, OperStatus, ConnectStatus, \
43 AdminState
44from voltha.protos.logical_device_pb2 import LogicalDevice, LogicalPort
45from voltha.protos.openflow_13_pb2 import ofp_desc, ofp_port, OFPPF_1GB_FD, \
46 OFPPF_FIBER, OFPPS_LIVE, ofp_switch_features, OFPC_PORT_STATS, \
47 OFPC_GROUP_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS
Stephane Barbariecc6b2e62017-03-02 14:35:55 -050048from voltha.protos.events_pb2 import AlarmEvent, AlarmEventType, \
49 AlarmEventSeverity, AlarmEventState, AlarmEventCategory
Sergio Slobodriana2eb52b2017-03-07 12:24:46 -050050import sys
Zsolt Haraszti66862032016-11-28 14:28:39 -080051
52log = structlog.get_logger()
53
54
Sergio Slobodrianef635a22017-03-10 22:44:39 -050055class AdapterPmMetrics:
56 class Metrics:
57 def __init__(self, config, value):
58 self.config = config
59 self.value = value
60
61 def __init__(self,device):
Sergio Slobodrianec6e3912017-04-02 11:46:55 -040062 self.pm_names = {'tx_64_pkts','tx_65_127_pkts', 'tx_128_255_pkts',
63 'tx_256_511_pkts', 'tx_512_1023_pkts',
64 'tx_1024_1518_pkts', 'tx_1519_9k_pkts', 'rx_64_pkts',
65 'rx_65_127_pkts', 'rx_128_255_pkts', 'rx_256_511_pkts',
66 'rx_512_1023_pkts', 'rx_1024_1518_pkts',
67 'rx_1519_9k_pkts', 'tx_pkts', 'rx_pkts',
Sergio Slobodrianef635a22017-03-10 22:44:39 -050068 'tx_bytes', 'rx_bytes'}
69 # This is just to generate more realistic looking values. This would
70 # not be implemented in a normal adapter.
71 self.rand_ranges = dict (
Sergio Slobodrianec6e3912017-04-02 11:46:55 -040072 tx_64_pkts=[50, 55],
73 tx_65_127_pkts=[55,60],
74 tx_128_255_pkts=[60,65],
75 tx_256_511_pkts=[85,90],
76 tx_512_1023_pkts=[90,95],
77 tx_1024_1518_pkts=[60,65],
78 tx_1519_9k_pkts=[50,55],
79 rx_64_pkts=[50, 55],
80 rx_65_127_pkts=[55,60],
81 rx_128_255_pkts=[60,65],
82 rx_256_511_pkts=[85,90],
83 rx_512_1023_pkts=[90,95],
84 rx_1024_1518_pkts=[60,65],
85 rx_1519_9k_pkts=[50,55],
Sergio Slobodrianef635a22017-03-10 22:44:39 -050086 tx_pkts=[90,100],
87 rx_pkts=[90,100],
88 rx_bytes=[90000,100000],
89 tx_bytes=[90000,100000]
90 )
91 self.device = device
92 self.id = device.id
93 self.default_freq = 150
94 self.grouped = False
95 self.freq_override = False
96 self.pon_metrics = dict()
97 self.nni_metrics = dict()
98 self.lc = None
99 for m in self.pm_names:
100 self.pon_metrics[m] = \
101 self.Metrics(config = PmConfig(name=m,
102 type=PmConfig.COUNTER,
103 enabled=True), value = 0)
104 self.nni_metrics[m] = \
105 self.Metrics(config = PmConfig(name=m,
106 type=PmConfig.COUNTER,
107 enabled=True), value = 0)
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()
114 self.lc.start(interval=self.default_freq/10)
115 for m in pm_config.metrics:
116 self.pon_metrics[m.name].config.enabled = m.enabled
117 self.nni_metrics[m.name].config.enabled = m.enabled
118
119 def make_proto(self):
120 pm_config = PmConfigs(
121 id=self.id,
122 default_freq=self.default_freq,
123 grouped = False,
124 freq_override = False)
125 for m in sorted(self.pon_metrics):
126 pm=self.pon_metrics[m]
127 pm_config.metrics.extend([PmConfig(name=pm.config.name,
128 type=pm.config.type,
129 enabled=pm.config.enabled)])
130 return pm_config
131
132 def collect_pon_metrics(self):
133 import random
134 rtrn_pon_metrics = dict()
135 for m in self.pm_names:
136 if self.pon_metrics[m].config.enabled:
137 self.pon_metrics[m].value += \
138 random.randint(self.rand_ranges[m][0], self.rand_ranges[m][1])
139 rtrn_pon_metrics[m] = self.pon_metrics[m].value
140 return rtrn_pon_metrics
141
142 def collect_nni_metrics(self):
143 import random
144 rtrn_nni_metrics = dict()
145 for m in self.pm_names:
146 if self.nni_metrics[m].config.enabled:
147 self.nni_metrics[m].value += \
148 random.randint(self.rand_ranges[m][0], self.rand_ranges[m][1])
149 rtrn_nni_metrics[m] = self.nni_metrics[m].value
150 return rtrn_nni_metrics
151
152 def start_collector(self, device_name, device_id, callback):
153 prefix = 'voltha.{}.{}'.format(device_name, device_id)
154 self.lc = LoopingCall(callback, device_id, prefix)
155 self.lc.start(interval=self.default_freq/10)
156
157
Zsolt Haraszti66862032016-11-28 14:28:39 -0800158@implementer(IAdapterInterface)
159class SimulatedOltAdapter(object):
Zsolt Haraszti66862032016-11-28 14:28:39 -0800160 name = 'simulated_olt'
161
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800162 supported_device_types = [
163 DeviceType(
164 id='simulated_olt',
165 adapter=name,
166 accepts_bulk_flow_update=True
167 )
168 ]
169
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800170 app = Klein()
171
Sergio Slobodrianef635a22017-03-10 22:44:39 -0500172
Zsolt Haraszti66862032016-11-28 14:28:39 -0800173 def __init__(self, adapter_agent, config):
174 self.adapter_agent = adapter_agent
175 self.config = config
176 self.descriptor = Adapter(
177 id=self.name,
178 vendor='Voltha project',
179 version='0.1',
180 config=AdapterConfig(log_level=LogLevel.INFO)
181 )
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800182 self.control_endpoint = None
Sergio Slobodrianf39aaf82017-02-28 16:10:16 -0500183 # Faked PM metrics for testing PM functionality
Sergio Slobodrianef635a22017-03-10 22:44:39 -0500184 self.pm_metrics = None
Zsolt Haraszti66862032016-11-28 14:28:39 -0800185
186 def start(self):
187 log.debug('starting')
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800188
189 # setup a basic web server for test control
190 self.control_endpoint = endpoints.TCP4ServerEndpoint(reactor, 18880)
191 self.control_endpoint.listen(self.get_test_control_site())
192
Zsolt Haraszti66862032016-11-28 14:28:39 -0800193 # TODO tmp: populate some devices and logical devices
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800194 # reactor.callLater(0, self._tmp_populate_stuff)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800195 log.info('started')
196
197 def stop(self):
198 log.debug('stopping')
199 log.info('stopped')
200
201 def adapter_descriptor(self):
202 return self.descriptor
203
204 def device_types(self):
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800205 return DeviceTypes(items=self.supported_device_types)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800206
207 def health(self):
208 return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
209
210 def change_master_state(self, master):
211 raise NotImplementedError()
212
213 def adopt_device(self, device):
214 # We kick of a simulated activation scenario
215 reactor.callLater(0.2, self._simulate_device_activation, device)
216 return device
217
218 def abandon_device(self, device):
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800219 raise NotImplementedError()
220
Khen Nursimulud068d812017-03-06 11:44:18 -0500221 def disable_device(self, device):
222 raise NotImplementedError()
223
224 def reenable_device(self, device):
225 raise NotImplementedError()
226
227 def reboot_device(self, device):
228 raise NotImplementedError()
229
230 def delete_device(self, device):
231 raise NotImplementedError()
232
233 def get_device_details(self, device):
Zsolt Haraszti66862032016-11-28 14:28:39 -0800234 raise NotImplementedError()
235
Sergio Slobodrianef635a22017-03-10 22:44:39 -0500236 def update_pm_config(self, device, pm_config):
237 log.info("adapter-update-pm-config", device=device, pm_config=pm_config)
238 self.pm_metrics.update(pm_config)
239
240
Sergio Slobodrianec864c62017-03-09 11:41:43 -0500241
Zsolt Haraszti66862032016-11-28 14:28:39 -0800242 def _tmp_populate_stuff(self):
243 """
244 pretend that we discovered some devices and create:
245 - devices
246 - device ports for each
247 - logical device
248 - logical device ports
249 """
250
251 olt = Device(
252 id='simulated_olt_1',
253 type='simulated_olt',
254 root=True,
255 vendor='simulated',
256 model='n/a',
257 hardware_version='n/a',
258 firmware_version='n/a',
259 software_version='1.0',
260 serial_number=uuid4().hex,
261 adapter=self.name,
262 oper_status=OperStatus.DISCOVERED
263 )
264 self.adapter_agent.add_device(olt)
265 self.adapter_agent.add_port(
266 olt.id, Port(port_no=1, label='pon', type=Port.PON_OLT))
267 self.adapter_agent.add_port(
268 olt.id, Port(port_no=2, label='eth', type=Port.ETHERNET_NNI))
269
270 onu1 = Device(
271 id='simulated_onu_1',
272 type='simulated_onu',
273 root=False,
274 parent_id=olt.id,
275 parent_port_no=1,
276 vendor='simulated',
277 model='n/a',
278 hardware_version='n/a',
279 firmware_version='n/a',
280 software_version='1.0',
281 serial_number=uuid4().hex,
282 adapter='simulated_onu',
283 oper_status=OperStatus.DISCOVERED,
284 vlan=101
285 )
286 self.adapter_agent.add_device(onu1)
287 self.adapter_agent.add_port(onu1.id, Port(
288 port_no=2, label='eth', type=Port.ETHERNET_UNI))
289 self.adapter_agent.add_port(onu1.id, Port(
290 port_no=1,
291 label='pon',
292 type=Port.PON_ONU,
293 peers=[Port.PeerPort(device_id=olt.id, port_no=1)]))
294
295 onu2 = Device(
296 id='simulated_onu_2',
297 type='simulated_onu',
298 root=False,
299 parent_id=olt.id,
300 parent_port_no=1,
301 vendor='simulated',
302 model='n/a',
303 hardware_version='n/a',
304 firmware_version='n/a',
305 software_version='1.0',
306 serial_number=uuid4().hex,
307 adapter='simulated_onu',
308 oper_status=OperStatus.DISCOVERED,
309 vlan=102
310 )
311 self.adapter_agent.add_device(onu2)
312 self.adapter_agent.add_port(onu2.id, Port(
313 port_no=2, label='eth', type=Port.ETHERNET_UNI))
314 self.adapter_agent.add_port(onu2.id, Port(
315 port_no=1,
316 label='pon',
317 type=Port.PON_ONU,
318 peers=[Port.PeerPort(device_id=olt.id, port_no=1)]))
319
320 ld = LogicalDevice(
321 id='simulated1',
322 datapath_id=1,
323 desc=ofp_desc(
alshabib9fbb2232016-12-23 00:40:08 -0800324 mfr_desc='cord project',
Zsolt Haraszti66862032016-11-28 14:28:39 -0800325 hw_desc='simualted pon',
326 sw_desc='simualted pon',
327 serial_num=uuid4().hex,
328 dp_desc='n/a'
329 ),
330 switch_features=ofp_switch_features(
331 n_buffers=256, # TODO fake for now
332 n_tables=2, # TODO ditto
333 capabilities=( # TODO and ditto
334 OFPC_FLOW_STATS
335 | OFPC_TABLE_STATS
336 | OFPC_PORT_STATS
337 | OFPC_GROUP_STATS
338 )
339 ),
340 root_device_id=olt.id
341 )
342 self.adapter_agent.create_logical_device(ld)
343
344 cap = OFPPF_1GB_FD | OFPPF_FIBER
345 for port_no, name, device_id, device_port_no, root_port in [
346 (1, 'onu1', onu1.id, 2, False),
347 (2, 'onu2', onu2.id, 2, False),
348 (129, 'olt1', olt.id, 2, True)]:
349 port = LogicalPort(
350 id=name,
351 ofp_port=ofp_port(
352 port_no=port_no,
353 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
354 name=name,
355 config=0,
356 state=OFPPS_LIVE,
357 curr=cap,
358 advertised=cap,
359 peer=cap,
360 curr_speed=OFPPF_1GB_FD,
361 max_speed=OFPPF_1GB_FD
362 ),
363 device_id=device_id,
364 device_port_no=device_port_no,
365 root_port=root_port
366 )
367 self.adapter_agent.add_logical_port(ld.id, port)
368
Zsolt Haraszti8925d1f2016-12-21 00:45:19 -0800369 olt.parent_id = ld.id
370 self.adapter_agent.update_device(olt)
371
Zsolt Haraszti66862032016-11-28 14:28:39 -0800372 @inlineCallbacks
373 def _simulate_device_activation(self, device):
374
375 # first we pretend that we were able to contact the device and obtain
376 # additional information about it
377 device.root = True
378 device.vendor = 'simulated'
379 device.model = 'n/a'
380 device.hardware_version = 'n/a'
381 device.firmware_version = 'n/a'
382 device.software_version = '1.0'
383 device.serial_number = uuid4().hex
384 device.connect_status = ConnectStatus.REACHABLE
Sergio Slobodrian2db4c102017-03-09 22:29:23 -0500385
Zsolt Haraszti66862032016-11-28 14:28:39 -0800386 self.adapter_agent.update_device(device)
387
Sergio Slobodrian2db4c102017-03-09 22:29:23 -0500388 # Now set the initial PM configuration for this device
Sergio Slobodrianef635a22017-03-10 22:44:39 -0500389 self.pm_metrics=AdapterPmMetrics(device)
390 pm_config = self.pm_metrics.make_proto()
391 log.info("initial-pm-config", pm_config=pm_config)
392 self.adapter_agent.update_device_pm_config(pm_config,init=True)
Sergio Slobodrian2db4c102017-03-09 22:29:23 -0500393
Zsolt Haraszti66862032016-11-28 14:28:39 -0800394 # then shortly after we create some ports for the device
395 yield asleep(0.05)
396 nni_port = Port(
397 port_no=2,
398 label='NNI facing Ethernet port',
399 type=Port.ETHERNET_NNI,
400 admin_state=AdminState.ENABLED,
401 oper_status=OperStatus.ACTIVE
402 )
403 self.adapter_agent.add_port(device.id, nni_port)
404 self.adapter_agent.add_port(device.id, Port(
405 port_no=1,
406 label='PON port',
407 type=Port.PON_OLT,
408 admin_state=AdminState.ENABLED,
409 oper_status=OperStatus.ACTIVE
410 ))
411
Sergio Slobodriana2eb52b2017-03-07 12:24:46 -0500412
Zsolt Haraszti66862032016-11-28 14:28:39 -0800413 # then shortly after we create the logical device with one port
414 # that will correspond to the NNI port
415 yield asleep(0.05)
alshabib9fbb2232016-12-23 00:40:08 -0800416 logical_device_id = uuid4().hex[:12]
Zsolt Haraszti66862032016-11-28 14:28:39 -0800417 ld = LogicalDevice(
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800418 # not setting id and datapth_id will let the adapter agent pick id
Zsolt Haraszti66862032016-11-28 14:28:39 -0800419 desc=ofp_desc(
420 mfr_desc='cord porject',
421 hw_desc='simualted pon',
422 sw_desc='simualted pon',
423 serial_num=uuid4().hex,
424 dp_desc='n/a'
425 ),
426 switch_features=ofp_switch_features(
427 n_buffers=256, # TODO fake for now
428 n_tables=2, # TODO ditto
429 capabilities=( # TODO and ditto
430 OFPC_FLOW_STATS
431 | OFPC_TABLE_STATS
432 | OFPC_PORT_STATS
433 | OFPC_GROUP_STATS
434 )
435 ),
436 root_device_id=device.id
437 )
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800438 ld_initialized = self.adapter_agent.create_logical_device(ld)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800439 cap = OFPPF_1GB_FD | OFPPF_FIBER
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800440 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
Zsolt Haraszti66862032016-11-28 14:28:39 -0800441 id='nni',
442 ofp_port=ofp_port(
443 port_no=129,
444 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % 129),
445 name='nni',
446 config=0,
447 state=OFPPS_LIVE,
448 curr=cap,
449 advertised=cap,
450 peer=cap,
451 curr_speed=OFPPF_1GB_FD,
452 max_speed=OFPPF_1GB_FD
453 ),
454 device_id=device.id,
455 device_port_no=nni_port.port_no,
456 root_port=True
457 ))
458
459 # and finally update to active
460 device = self.adapter_agent.get_device(device.id)
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800461 device.parent_id = ld_initialized.id
Zsolt Haraszti66862032016-11-28 14:28:39 -0800462 device.oper_status = OperStatus.ACTIVE
463 self.adapter_agent.update_device(device)
464
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800465 # reactor.callLater(0.1, self._simulate_detection_of_onus, device.id)
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800466 self.start_kpi_collection(device.id)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800467
Stephane Barbarie52198b92017-03-02 13:44:46 -0500468 self.start_alarm_simulation(device.id)
469
Zsolt Haraszti66862032016-11-28 14:28:39 -0800470 @inlineCallbacks
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800471 def _simulate_detection_of_onus(self, device_id):
Zsolt Haraszti66862032016-11-28 14:28:39 -0800472 for i in xrange(1, 5):
473 log.info('activate-olt-for-onu-{}'.format(i))
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800474 vlan_id = self._olt_side_onu_activation(i)
Zsolt Haraszti66862032016-11-28 14:28:39 -0800475 yield asleep(0.05)
476 self.adapter_agent.child_device_detected(
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800477 parent_device_id=device_id,
Zsolt Haraszti66862032016-11-28 14:28:39 -0800478 parent_port_no=1,
479 child_device_type='simulated_onu',
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800480 proxy_address=Device.ProxyAddress(
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800481 device_id=device_id,
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800482 channel_id=vlan_id
483 ),
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800484 vlan=vlan_id
Zsolt Haraszti66862032016-11-28 14:28:39 -0800485 )
486
487 def _olt_side_onu_activation(self, seq):
488 """
489 This is where if this was a real OLT, the OLT-side activation for
490 the new ONU should be performed. By the time we return, the OLT shall
491 be able to provide tunneled (proxy) communication to the given ONU,
492 using the returned information.
493 """
Zsolt Haraszti66862032016-11-28 14:28:39 -0800494 vlan_id = seq + 100
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800495 return vlan_id
Zsolt Haraszti66862032016-11-28 14:28:39 -0800496
Sergio Slobodriana2eb52b2017-03-07 12:24:46 -0500497 #def update_pm_collection(self, device, pm_collection_config):
498 # This is where the metrics to be collected are configured and where
499 # the sampling frequency is set.
500 #TODO: Here.
501 # pass
502
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800503 def update_flows_bulk(self, device, flows, groups):
504 log.debug('bulk-flow-update', device_id=device.id,
505 flows=flows, groups=groups)
506
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800507 # sample code that analyzes the incoming flow table
508 assert len(groups.items) == 0, "Cannot yet deal with groups"
509
510 for flow in flows.items:
511 in_port = get_in_port(flow)
512 assert in_port is not None
513
514 if in_port == 2:
515
516 # Downstream rule
517
518 for field in get_ofb_fields(flow):
519 if field.type == ETH_TYPE:
520 _type = field.eth_type
521 pass # construct ether type based condition here
522
523 elif field.type == IP_PROTO:
524 _proto = field.ip_proto
525 pass # construct ip_proto based condition here
526
527 elif field.type == IN_PORT:
528 _port = field.port
529 pass # construct in_port based condition here
530
531 elif field.type == VLAN_VID:
532 _vlan_vid = field.vlan_vid
533 pass # construct VLAN ID based filter condition here
534
535 elif field.type == VLAN_PCP:
536 _vlan_pcp = field.vlan_pcp
537 pass # construct VLAN PCP based filter condition here
538
Zsolt Haraszti6a5107c2017-01-09 23:42:41 -0800539 elif field.type == METADATA:
540 pass # safe to ignore
541
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800542 # TODO
543 else:
544 raise NotImplementedError('field.type={}'.format(
545 field.type))
546
547 for action in get_actions(flow):
548
549 if action.type == OUTPUT:
550 pass # construct packet emit rule here
551
552 elif action.type == PUSH_VLAN:
553 if action.push.ethertype != 0x8100:
554 log.error('unhandled-ether-type',
555 ethertype=action.push.ethertype)
556 pass # construct vlan push command here
557
558 elif action.type == POP_VLAN:
559 pass # construct vlan pop command here
560
561 elif action.type == SET_FIELD:
562 assert (action.set_field.field.oxm_class ==
563 ofp.OFPXMC_OPENFLOW_BASIC)
564 field = action.set_field.field.ofb_field
565 if field.type == VLAN_VID:
566 pass # construct vlan_id set command here
567 else:
568 log.error('unsupported-action-set-field-type',
569 field_type=field.type)
570
571 else:
572 log.error('unsupported-action-type',
573 action_type=action.type)
574
575 # final assembly of low level device flow rule and pushing it
576 # down to device
577 pass
578
579 elif in_port == 1:
580
581 # Upstream rule
582
583 for field in get_ofb_fields(flow):
584
585 if field.type == ETH_TYPE:
586 _type = field.eth_type
587 pass # construct ether type based condition here
588
589 elif field.type == IP_PROTO:
590 _proto = field.ip_proto
591 pass # construct ip_proto based condition here
592
593 elif field.type == IN_PORT:
594 _port = field.port
595 pass # construct in_port based condition here
596
597 elif field.type == VLAN_VID:
598 _vlan_vid = field.vlan_vid
599 pass # construct VLAN ID based filter condition here
600
601 elif field.type == VLAN_PCP:
602 _vlan_pcp = field.vlan_pcp
603 pass # construct VLAN PCP based filter condition here
604
Zsolt Haraszti3578a1c2017-01-10 15:29:02 -0800605 elif field.type == UDP_SRC:
606 _udp_src = field.udp_src
607 pass # construct UDP SRC based filter here
608
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800609 elif field.type == UDP_DST:
610 _udp_dst = field.udp_dst
Zsolt Haraszti3578a1c2017-01-10 15:29:02 -0800611 pass # construct UDP DST based filter here
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800612
613 # TODO
614 else:
615 raise NotImplementedError('field.type={}'.format(
616 field.type))
617
618 for action in get_actions(flow):
619
620 if action.type == OUTPUT:
621 pass # construct packet emit rule here
622
623 elif action.type == PUSH_VLAN:
624 if action.push.ethertype != 0x8100:
625 log.error('unhandled-ether-type',
626 ethertype=action.push.ethertype)
627 pass # construct vlan push command here
628
629 elif action.type == SET_FIELD:
630 assert (action.set_field.field.oxm_class ==
631 ofp.OFPXMC_OPENFLOW_BASIC)
632 field = action.set_field.field.ofb_field
633 if field.type == VLAN_VID:
634 pass # construct vlan_id set command here
635 else:
636 log.error('unsupported-action-set-field-type',
637 field_type=field.type)
638
639 else:
640 log.error('unsupported-action-type',
641 action_type=action.type)
642
643 # final assembly of low level device flow rule and pushing it
644 # down to device
645 pass
646
647 else:
648 raise Exception('Port should be 1 or 2 by our convention')
649
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800650 def update_flows_incrementally(self, device, flow_changes, group_changes):
651 raise NotImplementedError()
652
653 def send_proxied_message(self, proxy_address, msg):
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800654 log.info('send-proxied-message', proxy_address=proxy_address, msg=msg)
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800655 # we mimic a response by sending the same message back in a short time
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800656 reactor.callLater(
657 0.2,
658 self.adapter_agent.receive_proxied_message,
659 proxy_address,
660 msg
661 )
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800662
663 def receive_proxied_message(self, proxy_address, msg):
664 raise NotImplementedError()
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800665
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800666 def receive_packet_out(self, logical_device_id, egress_port_no, msg):
667 log.info('packet-out', logical_device_id=logical_device_id,
668 egress_port_no=egress_port_no, msg_len=len(msg))
669
Peter Shafik9107f2e2017-05-02 15:54:39 -0400670 def receive_inter_adapter_message(self, msg):
671 raise NotImplementedError()
672
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800673 def start_kpi_collection(self, device_id):
Zsolt Harasztic5f740b2017-01-18 09:53:17 -0800674
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800675 """Simulate periodic KPI metric collection from the device"""
676 import random
677
678 @inlineCallbacks # pretend that we need to do async calls
679 def _collect(device_id, prefix):
680
681 try:
682 # Step 1: gather metrics from device (pretend it here) - examples
Sergio Slobodrianf39aaf82017-02-28 16:10:16 -0500683 # upgraded the metrics to include packet statistics for
684 # testing.
Sergio Slobodrianef635a22017-03-10 22:44:39 -0500685 nni_port_metrics = self.pm_metrics.collect_nni_metrics()
686 pon_port_metrics = self.pm_metrics.collect_pon_metrics()
Sergio Slobodrianf39aaf82017-02-28 16:10:16 -0500687
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800688 olt_metrics = yield dict(
689 cpu_util=20 + 5 * random.random(),
690 buffer_util=10 + 10 * random.random()
691 )
692
693 # Step 2: prepare the KpiEvent for submission
694 # we can time-stamp them here (or could use time derived from OLT
695 ts = arrow.utcnow().timestamp
696 kpi_event = KpiEvent(
697 type=KpiEventType.slice,
698 ts=ts,
699 prefixes={
700 # OLT-level
701 prefix: MetricValuePairs(metrics=olt_metrics),
702 # OLT NNI port
Stephane Barbariecc6b2e62017-03-02 14:35:55 -0500703 prefix + '.nni': MetricValuePairs(
704 metrics=nni_port_metrics),
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800705 # OLT PON port
Stephane Barbariecc6b2e62017-03-02 14:35:55 -0500706 prefix + '.pon': MetricValuePairs(
707 metrics=pon_port_metrics)
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800708 }
709 )
710
711 # Step 3: submit
712 self.adapter_agent.submit_kpis(kpi_event)
713
714 except Exception as e:
715 log.exception('failed-to-submit-kpis', e=e)
716
Sergio Slobodrianef635a22017-03-10 22:44:39 -0500717 self.pm_metrics.start_collector(self.name, device_id ,_collect)
718 #prefix = 'voltha.{}.{}'.format(self.name, device_id)
719 #lc = LoopingCall(_collect, device_id, prefix)
720 #lc.start(interval=15) # TODO make this configurable
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800721
Stephane Barbarie52198b92017-03-02 13:44:46 -0500722 def start_alarm_simulation(self, device_id):
723
724 """Simulate periodic device alarms"""
725 import random
726
727 def _generate_alarm(device_id):
728
729 try:
730 # Randomly choose values for each enum types
Stephane Barbariecc6b2e62017-03-02 14:35:55 -0500731 severity = random.choice(list(
732 v for k, v in
733 AlarmEventSeverity.DESCRIPTOR.enum_values_by_name.items()))
Stephane Barbarie52198b92017-03-02 13:44:46 -0500734
Stephane Barbariecc6b2e62017-03-02 14:35:55 -0500735 state = random.choice(list(
736 v for k, v in
737 AlarmEventState.DESCRIPTOR.enum_values_by_name.items()))
738
739 type = random.choice(list(
740 v for k, v in
741 AlarmEventType.DESCRIPTOR.enum_values_by_name.items()))
742
743 category = random.choice(list(
744 v for k, v in
745 AlarmEventCategory.DESCRIPTOR.enum_values_by_name.items()))
746
747 description = "Simulated alarm - " \
748 "device:{} " \
749 "type:{} " \
750 "severity:{} " \
751 "state:{} " \
752 "category:{}".format(device_id,
753 type.name,
754 severity.name,
755 state.name,
756 category.name)
Stephane Barbarie52198b92017-03-02 13:44:46 -0500757
758 current_context = {}
759 for key, value in self.__dict__.items():
760 current_context[key] = str(value)
761
Stephane Barbariecc6b2e62017-03-02 14:35:55 -0500762 alarm_event = self.adapter_agent.create_alarm(
763 resource_id=device_id,
764 type=type.number,
765 category=category.number,
766 severity=severity.number,
767 state=state.number,
768 description=description,
769 context=current_context)
Stephane Barbarie52198b92017-03-02 13:44:46 -0500770
Stephane Barbarie4db8ca22017-04-24 10:30:20 -0400771 self.adapter_agent.submit_alarm(device_id, alarm_event)
Stephane Barbarie52198b92017-03-02 13:44:46 -0500772
773 except Exception as e:
774 log.exception('failed-to-submit-alarm', e=e)
775
776 alarm_lc = LoopingCall(_generate_alarm, device_id)
777 alarm_lc.start(30)
Zsolt Haraszti749b0952017-01-18 09:02:35 -0800778
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800779 # ~~~~~~~~~~~~~~~~~~~~ Embedded test Klein rest server ~~~~~~~~~~~~~~~~~~~~
780
Zsolt Haraszti8925d1f2016-12-21 00:45:19 -0800781 def get_test_control_site(self):
782 return Site(self.app.resource())
783
784 @app.route('/devices/<string:device_id>/detect_onus')
785 def detect_onus(self, request, device_id):
786 log.info('detect-onus', request=request, device_id=device_id)
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800787 self._simulate_detection_of_onus(device_id)
788 return '{"status": "OK"}'
789
Zsolt Haraszti8925d1f2016-12-21 00:45:19 -0800790 @app.route('/devices/<string:device_id>/test_eapol_in')
791 def test_eapol_in(self, request, device_id):
792 """Simulate a packet in message posted upstream"""
793 log.info('test_eapol_in', request=request, device_id=device_id)
794 eapol_start = str(
Zsolt Harasztid036b7e2016-12-23 15:36:01 -0800795 Ether(src='00:11:22:33:44:55') /
796 EAPOL(type=1, len=0) /
Stephane Barbarie52198b92017-03-02 13:44:46 -0500797 Padding(load=42 * '\x00')
Zsolt Haraszti8925d1f2016-12-21 00:45:19 -0800798 )
Zsolt Haraszti8925d1f2016-12-21 00:45:19 -0800799 device = self.adapter_agent.get_device(device_id)
800 self.adapter_agent.send_packet_in(logical_device_id=device.parent_id,
801 logical_port_no=1,
802 packet=eapol_start)
803 return '{"status": "sent"}'
Sergio Slobodrianef635a22017-03-10 22:44:39 -0500804
805