blob: 1fa00fe302141eed85f3b95782ca783684450482 [file] [log] [blame]
Chip Boling32aab302019-01-23 10:50:18 -06001#
2# Copyright 2018 the original author or authors.
3#
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#
16import structlog
17
18from voltha.protos.device_pb2 import Image
19from omci_entities import *
20from database.mib_db_api import *
21from enum import IntEnum
22
23
24class OMCCVersion(IntEnum):
25 Unknown = 0 # Unknown or unsupported version
26 G_984_4 = 0x80 # (06/04)
27 G_984_4_2005_Amd_1 = 0x81 # Amd.1 (06/05)
28 G_984_4_2006_Amd_2 = 0x82 # Amd.2 (03/06)
29 G_984_4_2006_Amd_3 = 0x83 # Amd.3 (12/06)
30 G_984_4_2008 = 0x84 # (02/08)
31 G_984_4_2009_Amd_1 = 0x85 # Amd.1 (06/09)
32 G_984_4_2009_Amd_2_Base = 0x86 # Amd.2 (2009) Baseline message set only, w/o the extended message set option
33 G_984_4_2009_Amd_2 = 0x96 # Amd.2 (2009) Extended message set option + baseline message set.
34 G_988_2010_Base = 0xA0 # (2010) Baseline message set only, w/o the extended message set option
35 G_988_2011_Amd_1_Base = 0xA1 # Amd.1 (2011) Baseline message set only
36 G_988_2012_Amd_2_Base = 0xA2 # Amd.2 (2012) Baseline message set only
37 G_988_2012_Base = 0xA3 # (2012) Baseline message set only
38 G_988_2010 = 0xB0 # (2010) Baseline and extended message set
39 G_988_2011_Amd_1 = 0xB1 # Amd.1 (2011) Baseline and extended message set
40 G_988_2012_Amd_2 = 0xB2 # Amd.2 (2012) Baseline and extended message set
41 G_988_2012 = 0xB3 # (2012)Baseline and extended message set
42
43 @staticmethod
44 def values():
45 return {OMCCVersion[member].value for member in OMCCVersion.__members__.keys()}
46
47 @staticmethod
48 def to_enum(value):
49 return next((v for k, v in OMCCVersion.__members__.items()
50 if v.value == value), OMCCVersion.Unknown)
51
52
53class OnuConfiguration(object):
54 """
55 Utility class to query OMCI MIB Database for various ONU/OMCI Configuration
56 and capabilties. These capabilities revolve around read-only MEs discovered
57 during the MIB Upload process.
58
59 There is also a 'omci_onu_capabilities' State Machine and an
60 'onu_capabilities_task.py' OMCI Task that will query the ONU, via the
61 OMCI (ME#287) Managed entity to get the full list of supported OMCI ME's
62 and available actions/message-types supported.
63
64 NOTE: Currently this class is optimized/tested for ONUs that support the
65 OpenOMCI implementation.
66 """
67 def __init__(self, omci_agent, device_id):
68 """
69 Initialize this instance of the OnuConfiguration class
70
71 :param omci_agent: (OpenOMCIAgent) agent reference
72 :param device_id: (str) ONU Device ID
73
74 :raises KeyError: If ONU Device is not registered with OpenOMCI
75 """
76 self.log = structlog.get_logger(device_id=device_id)
77 self._device_id = device_id
78 self._onu_device = omci_agent.get_device(device_id)
79
80 # The capabilities
81 self._attributes = None
82 self.reset()
83
84 def _get_capability(self, attr, class_id, instance_id=None):
85 """
86 Get the OMCI capabilities for this device
87
88 :param attr: (str) OnuConfiguration attribute field
89 :param class_id: (int) ME Class ID
90 :param instance_id: (int) Instance ID. If not provided, all instances of the
91 specified class ID are returned if present in the DB.
92
93 :return: (dict) Class and/or Instances. None is returned if the CLASS is not present
94 """
95 try:
96 assert self._onu_device.mib_synchronizer.last_mib_db_sync is not None, \
97 'MIB Database for ONU {} has never been synchronized'.format(self._device_id)
98
99 # Get the requested information
100 if self._attributes[attr] is None:
101 value = self._onu_device.query_mib(class_id, instance_id=instance_id)
102
103 if isinstance(value, dict) and len(value) > 0:
104 self._attributes[attr] = value
105
106 return self._attributes[attr]
107
108 except Exception as e:
109 self.log.exception('onu-capabilities', e=e, class_id=class_id,
110 instance_id=instance_id)
111 raise
112
113 def reset(self):
114 """
115 Reset the cached database entries to None. This method should be
116 called after any communications loss to the ONU (reboot, PON down, ...)
117 in case a new software load with different capabilities is available.
118 """
119 self._attributes = {
120 '_ont_g': None,
121 '_ont_2g': None,
122 '_ani_g': None,
123 '_uni_g': None,
124 '_cardholder': None,
125 '_circuit_pack': None,
126 '_software': None,
127 '_pptp': None,
128 '_veip': None
129 }
130
131 @property
132 def version(self):
133 """
134 This attribute identifies the version of the ONU as defined by the vendor
135 """
136 ontg = self._get_capability('_ont_g', OntG.class_id, 0)
137 if ontg is None or ATTRIBUTES_KEY not in ontg:
138 return None
139
140 return ontg[ATTRIBUTES_KEY].get('version')
141
142 @property
143 def serial_number(self):
144 """
145 The serial number is unique for each ONU
146 """
147 ontg = self._get_capability('_ont_g', OntG.class_id, 0)
148 if ontg is None or ATTRIBUTES_KEY not in ontg:
149 return None
150
151 return ontg[ATTRIBUTES_KEY].get('serial_number')
152
153 @property
154 def traffic_management_option(self):
155 """
156 This attribute identifies the upstream traffic management function
157 implemented in the ONU. There are three options:
158
159 0 Priority controlled and flexibly scheduled upstream traffic. The traffic
160 scheduler and priority queue mechanism are used for upstream traffic.
161
162 1 Rate controlled upstream traffic. The maximum upstream traffic of each
163 individual connection is guaranteed by shaping.
164
165 2 Priority and rate controlled. The traffic scheduler and priority queue
166 mechanism are used for upstream traffic. The maximum upstream traffic
167 of each individual connection is guaranteed by shaping.
168 """
169 ontg = self._get_capability('_ont_g', OntG.class_id, 0)
170 if ontg is None or ATTRIBUTES_KEY not in ontg:
171 return None
172
173 return ontg[ATTRIBUTES_KEY].get('traffic_management_option')
174
175 @property
176 def onu_survival_time(self):
177 """
178 This attribute indicates the minimum guaranteed time in milliseconds
179 between the loss of external power and the silence of the ONU. This does not
180 include survival time attributable to a backup battery. The value zero implies that
181 the actual time is not known.
182
183 Optional
184 """
185 ontg = self._get_capability('_ont_g', OntG.class_id, 0)
186 if ontg is None or ATTRIBUTES_KEY not in ontg:
187 return None
188
189 return ontg[ATTRIBUTES_KEY].get('onu_survival_time', 0)
190
191 @property
192 def equipment_id(self):
193 """
194 This attribute may be used to identify the specific type of ONU. In some
195 environments, this attribute may include the equipment CLEI code.
196 """
197 ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
198 if ont2g is None or ATTRIBUTES_KEY not in ont2g:
199 return None
200
201 return ont2g[ATTRIBUTES_KEY].get('equipment_id')
202
203 @property
204 def omcc_version(self):
205 """
206 This attribute identifies the version of the OMCC protocol being used by the
207 ONU. This allows the OLT to manage a network with ONUs that support different
208 OMCC versions. Release levels of [ITU-T G.984.4] are supported with code
209 points of the form 0x8y and 0x9y, where y is a hexadecimal digit in the range
210 0..F. Support for continuing revisions of this Recommendation is defined in
211 the 0xAy range.
212 """
213 ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
214 if ont2g is None or ATTRIBUTES_KEY not in ont2g:
215 return None
216
217 return OMCCVersion.to_enum(ont2g[ATTRIBUTES_KEY].get('omcc_version', 0))
218
219 @property
220 def vendor_product_code(self):
221 """
222 This attribute contains a vendor-specific product code for the ONU
223 """
224 ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
225 if ont2g is None or ATTRIBUTES_KEY not in ont2g:
226 return None
227
228 return ont2g[ATTRIBUTES_KEY].get('vendor_product_code')
229
230 @property
231 def total_priority_queues(self):
232 """
233 This attribute reports the total number of upstream priority queues
234 that are not associated with a circuit pack, but with the ONU in its entirety
235 """
236 ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
237 if ont2g is None or ATTRIBUTES_KEY not in ont2g:
238 return None
239
240 return ont2g[ATTRIBUTES_KEY].get('total_priority_queue_number')
241
242 @property
243 def total_traffic_schedulers(self):
244 """
245 This attribute reports the total number of traffic schedulers that
246 are not associated with a circuit pack, but with the ONU in its entirety.
247 """
248 ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
249 if ont2g is None or ATTRIBUTES_KEY not in ont2g:
250 return None
251
252 return ont2g[ATTRIBUTES_KEY].get('total_traffic_scheduler_number')
253
254 @property
255 def total_gem_ports(self):
256 """
257 This attribute reports the total number of GEM port-IDs supported
258 by the ONU.
259 """
260 ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
261 if ont2g is None or ATTRIBUTES_KEY not in ont2g:
262 return None
263
264 return ont2g[ATTRIBUTES_KEY].get('total_gem_port_id_number')
265
266 @property
267 def uptime(self):
268 """
269 This attribute counts 10 ms intervals since the ONU was last initialized.
270 It rolls over to 0 when full
271 """
272 ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
273 if ont2g is None or ATTRIBUTES_KEY not in ont2g:
274 return None
275
276 return ont2g[ATTRIBUTES_KEY].get('sys_uptime')
277
278 @property
279 def connectivity_capability(self):
280 """
281 This attribute indicates the Ethernet connectivity models that the ONU
282 can support. The value 0 indicates that the capability is not supported; 1 signifies
283 support.
284
285 Bit Model [Figure reference ITU-T 988]
286 1 (LSB) N:1 bridging, Figure 8.2.2-3
287 2 1:M mapping, Figure 8.2.2-4
288 3 1:P filtering, Figure 8.2.2-5
289 4 N:M bridge-mapping, Figure 8.2.2-6
290 5 1:MP map-filtering, Figure 8.2.2-7
291 6 N:P bridge-filtering, Figure 8.2.2-8
292 7 to refer to N:MP bridge-map-filtering, Figure 8.2.2-9
293 8...16 Reserved
294 """
295 ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
296 if ont2g is None or ATTRIBUTES_KEY not in ont2g:
297 return None
298
299 return ont2g[ATTRIBUTES_KEY].get('connectivity_capability')
300
301 @property
302 def qos_configuration_flexibility(self):
303 """
304 This attribute reports whether various managed entities in the
305 ONU are fixed by the ONU's architecture or whether they are configurable. For
306 backward compatibility, and if the ONU does not support this attribute, all such
307 attributes are understood to be hard-wired.
308
309 Bit Interpretation when bit value = 1
310 1 (LSB) Priority queue ME: Port field of related port attribute is
311 read-write and can point to any T-CONT or UNI port in the
312 same slot
313 2 Priority queue ME: The traffic scheduler pointer is permitted
314 to refer to any other traffic scheduler in the same slot
315 3 Traffic scheduler ME: T-CONT pointer is read-write
316 4 Traffic scheduler ME: Policy attribute is read-write
317 5 T-CONT ME: Policy attribute is read-write
318 6 Priority queue ME: Priority field of related port attribute is
319 read-write
320 7..16 Reserved
321 """
322 ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
323 if ont2g is None or ATTRIBUTES_KEY not in ont2g:
324 return None
325
326 return ont2g[ATTRIBUTES_KEY].get('qos_configuration_flexibility')
327
328 @property
329 def priority_queue_scale_factor(self):
330 """
331 This specifies the scale factor of several attributes of the priority
332 queue managed entity of section 5.2.8
333 """
334 ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
335 if ont2g is None or ATTRIBUTES_KEY not in ont2g:
336 return None
337
338 return ont2g[ATTRIBUTES_KEY].get('priority_queue_scale_factor', 1)
339
340 @property
341 def cardholder_entities(self):
342 """
343 Return a dictionary containing some overall information on the CardHolder
344 instances for this ONU.
345 """
346 ch = self._get_capability('_cardholder', Cardholder.class_id)
347 results = dict()
348
349 if ch is not None:
350 for inst, inst_data in ch.items():
351 if isinstance(inst, int):
352 results[inst] = {
353 'entity-id': inst,
354 'is-single-piece': inst >= 256,
355 'slot-number': inst & 0xff,
356 'actual-plug-in-type': inst_data[ATTRIBUTES_KEY].get('actual_plugin_unit_type', 0),
357 'actual-equipment-id': inst_data[ATTRIBUTES_KEY].get('actual_equipment_id', 0),
358 'protection-profile-ptr': inst_data[ATTRIBUTES_KEY].get('protection_profile_pointer', 0),
359 }
360 return results if len(results) else None
361
362 @property
363 def circuitpack_entities(self):
364 """
365 This specifies the scale factor of several attributes of the priority
366 queue managed entity of section 5.2.8
367 """
368 cp = self._get_capability('_circuit_pack', CircuitPack.class_id)
369 results = dict()
370
371 if cp is not None:
372 for inst, inst_data in cp.items():
373 if isinstance(inst, int):
374 results[inst] = {
375 'entity-id': inst,
376 'number-of-ports': inst_data[ATTRIBUTES_KEY].get('number_of_ports', 0),
377 'serial-number': inst_data[ATTRIBUTES_KEY].get('serial_number', 0),
378 'version': inst_data[ATTRIBUTES_KEY].get('version', 0),
379 'vendor-id': inst_data[ATTRIBUTES_KEY].get('vendor_id', 0),
380 'total-tcont-count': inst_data[ATTRIBUTES_KEY].get('total_tcont_buffer_number', 0),
381 'total-priority-queue-count': inst_data[ATTRIBUTES_KEY].get('total_priority_queue_number', 0),
382 'total-traffic-sched-count': inst_data[ATTRIBUTES_KEY].get('total_traffic_scheduler_number', 0),
383 }
384
385 return results if len(results) else None
386
387 @property
388 def software_images(self):
389 """
390 Get a list of software image information for the ONU. The information is provided
391 so that it may be directly added to the protobuf Device information software list.
392 """
393 sw = self._get_capability('_software', SoftwareImage.class_id)
394 images = list()
395
396 if sw is not None:
397 for inst, inst_data in sw.items():
398 if isinstance(inst, int):
399 is_active = inst_data[ATTRIBUTES_KEY].get('is_active', False)
400
401 images.append(Image(name='running-revision' if is_active else 'candidate-revision',
402 version=str(inst_data[ATTRIBUTES_KEY].get('version',
403 'Not Available').rstrip('\0')),
404 is_active=is_active,
405 is_committed=inst_data[ATTRIBUTES_KEY].get('is_committed',
406 False),
407 is_valid=inst_data[ATTRIBUTES_KEY].get('is_valid',
408 False),
409 install_datetime='Not Available',
410 hash=str(inst_data[ATTRIBUTES_KEY].get('image_hash',
411 'Not Available').rstrip('\0'))))
412 return images if len(images) else None
413
414 @property
415 def ani_g_entities(self):
416 """
417 This managed entity organizes data associated with each access network
418 interface supported by a G-PON ONU. The ONU automatically creates one
419 instance of this managed entity for each PON physical port.
420 """
421 ag = self._get_capability('_ani_g', AniG.class_id)
422 results = dict()
423
424 if ag is not None:
425 for inst, inst_data in ag.items():
426 if isinstance(inst, int):
427 results[inst] = {
428 'entity-id': inst,
429 'slot-number': (inst >> 8) & 0xff,
430 'port-number': inst & 0xff,
431 'total-tcont-count': inst_data[ATTRIBUTES_KEY].get('total_tcont_number', 0),
432 'piggyback-dba-reporting': inst_data[ATTRIBUTES_KEY].get('piggyback_dba_reporting', 0),
433 }
434 return results if len(results) else None
435
436 @property
437 def uni_g_entities(self):
438 """
439 This managed entity organizes data associated with user network interfaces
440 (UNIs) supported by GEM. One instance of the UNI-G managed entity exists
441 for each UNI supported by the ONU.
442
443 The ONU automatically creates or deletes instances of this managed entity
444 upon the creation or deletion of a real or virtual circuit pack managed
445 entity, one per port.
446 """
447 ug = self._get_capability('_uni_g', UniG.class_id)
448 results = dict()
449
450 if ug is not None:
451 for inst, inst_data in ug.items():
452 if isinstance(inst, int):
453 results[inst] = {
454 'entity-id': inst,
455 'management-capability': inst_data[ATTRIBUTES_KEY].get('management_capability', 0)
456 }
457 return results if len(results) else None
458
459 @property
460 def pptp_entities(self):
461 """
462 Returns discovered PPTP Ethernet entities. TODO more detail here
463 """
464 pptp = self._get_capability('_pptp', PptpEthernetUni.class_id)
465 results = dict()
466
467 if pptp is not None:
468 for inst, inst_data in pptp.items():
469 if isinstance(inst, int):
470 results[inst] = {
471 'entity-id': inst,
472 'expected-type': inst_data[ATTRIBUTES_KEY].get('expected_type', 0),
473 'sensed-type': inst_data[ATTRIBUTES_KEY].get('sensed_type', 0),
474 'autodetection-config': inst_data[ATTRIBUTES_KEY].get('auto_detection_configuration', 0),
475 'ethernet-loopback-config': inst_data[ATTRIBUTES_KEY].get('ethernet_loopback_configuration', 0),
476 'administrative-state': inst_data[ATTRIBUTES_KEY].get('administrative_state', 0),
477 'operational-state': inst_data[ATTRIBUTES_KEY].get('operational_state', 0),
478 'config-ind': inst_data[ATTRIBUTES_KEY].get('configuration_ind', 0),
479 'max-frame-size': inst_data[ATTRIBUTES_KEY].get('max_frame_size', 0),
480 'dte-dce-ind': inst_data[ATTRIBUTES_KEY].get('dte_or_dce_ind', 0),
481 'pause-time': inst_data[ATTRIBUTES_KEY].get('pause_time', 0),
482 'bridged-ip-ind': inst_data[ATTRIBUTES_KEY].get('bridged_or_ip_ind', 0),
483 'arc': inst_data[ATTRIBUTES_KEY].get('arc', 0),
484 'arc-interval': inst_data[ATTRIBUTES_KEY].get('arc_interval', 0),
485 'pppoe-filter': inst_data[ATTRIBUTES_KEY].get('ppoe_filter', 0),
486 'power-control': inst_data[ATTRIBUTES_KEY].get('power_control', 0)
487 }
488 return results if len(results) else None
489
490 @property
491 def veip_entities(self):
492 """
493 Returns discovered VEIP entities. TODO more detail here
494 """
495 veip = self._get_capability('_veip', VeipUni.class_id)
496 results = dict()
497
498 if veip is not None:
499 for inst, inst_data in veip.items():
500 if isinstance(inst, int):
501 results[inst] = {
502 'entity-id': inst,
503 'administrative-state': inst_data[ATTRIBUTES_KEY].get('administrative_state', 0),
504 'operational-state': inst_data[ATTRIBUTES_KEY].get('operational_state', 0),
505 'interdomain-name': inst_data[ATTRIBUTES_KEY].get('interdomain_name', ""),
506 'tcp-udp-pointer': inst_data[ATTRIBUTES_KEY].get('tcp_udp_pointer', 0),
507 'iana-assigned-port': inst_data[ATTRIBUTES_KEY].get('iana_assigned_port', 0)
508 }
509 return results if len(results) else None