VOL-1395: Common shared libraries needed for Python based device adapters.
This is an initial check-in of code from the master branch. Additional work
is expected on a few items to work with the new go-core and will be covered
by separate JIRAs and commits.
Change-Id: I0856ec6b79b8d3e49082c609eb9c7eedd75b1708
diff --git a/python/adapters/extensions/omci/onu_configuration.py b/python/adapters/extensions/omci/onu_configuration.py
new file mode 100644
index 0000000..1fa00fe
--- /dev/null
+++ b/python/adapters/extensions/omci/onu_configuration.py
@@ -0,0 +1,509 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import structlog
+
+from voltha.protos.device_pb2 import Image
+from omci_entities import *
+from database.mib_db_api import *
+from enum import IntEnum
+
+
+class OMCCVersion(IntEnum):
+ Unknown = 0 # Unknown or unsupported version
+ G_984_4 = 0x80 # (06/04)
+ G_984_4_2005_Amd_1 = 0x81 # Amd.1 (06/05)
+ G_984_4_2006_Amd_2 = 0x82 # Amd.2 (03/06)
+ G_984_4_2006_Amd_3 = 0x83 # Amd.3 (12/06)
+ G_984_4_2008 = 0x84 # (02/08)
+ G_984_4_2009_Amd_1 = 0x85 # Amd.1 (06/09)
+ G_984_4_2009_Amd_2_Base = 0x86 # Amd.2 (2009) Baseline message set only, w/o the extended message set option
+ G_984_4_2009_Amd_2 = 0x96 # Amd.2 (2009) Extended message set option + baseline message set.
+ G_988_2010_Base = 0xA0 # (2010) Baseline message set only, w/o the extended message set option
+ G_988_2011_Amd_1_Base = 0xA1 # Amd.1 (2011) Baseline message set only
+ G_988_2012_Amd_2_Base = 0xA2 # Amd.2 (2012) Baseline message set only
+ G_988_2012_Base = 0xA3 # (2012) Baseline message set only
+ G_988_2010 = 0xB0 # (2010) Baseline and extended message set
+ G_988_2011_Amd_1 = 0xB1 # Amd.1 (2011) Baseline and extended message set
+ G_988_2012_Amd_2 = 0xB2 # Amd.2 (2012) Baseline and extended message set
+ G_988_2012 = 0xB3 # (2012)Baseline and extended message set
+
+ @staticmethod
+ def values():
+ return {OMCCVersion[member].value for member in OMCCVersion.__members__.keys()}
+
+ @staticmethod
+ def to_enum(value):
+ return next((v for k, v in OMCCVersion.__members__.items()
+ if v.value == value), OMCCVersion.Unknown)
+
+
+class OnuConfiguration(object):
+ """
+ Utility class to query OMCI MIB Database for various ONU/OMCI Configuration
+ and capabilties. These capabilities revolve around read-only MEs discovered
+ during the MIB Upload process.
+
+ There is also a 'omci_onu_capabilities' State Machine and an
+ 'onu_capabilities_task.py' OMCI Task that will query the ONU, via the
+ OMCI (ME#287) Managed entity to get the full list of supported OMCI ME's
+ and available actions/message-types supported.
+
+ NOTE: Currently this class is optimized/tested for ONUs that support the
+ OpenOMCI implementation.
+ """
+ def __init__(self, omci_agent, device_id):
+ """
+ Initialize this instance of the OnuConfiguration class
+
+ :param omci_agent: (OpenOMCIAgent) agent reference
+ :param device_id: (str) ONU Device ID
+
+ :raises KeyError: If ONU Device is not registered with OpenOMCI
+ """
+ self.log = structlog.get_logger(device_id=device_id)
+ self._device_id = device_id
+ self._onu_device = omci_agent.get_device(device_id)
+
+ # The capabilities
+ self._attributes = None
+ self.reset()
+
+ def _get_capability(self, attr, class_id, instance_id=None):
+ """
+ Get the OMCI capabilities for this device
+
+ :param attr: (str) OnuConfiguration attribute field
+ :param class_id: (int) ME Class ID
+ :param instance_id: (int) Instance ID. If not provided, all instances of the
+ specified class ID are returned if present in the DB.
+
+ :return: (dict) Class and/or Instances. None is returned if the CLASS is not present
+ """
+ try:
+ assert self._onu_device.mib_synchronizer.last_mib_db_sync is not None, \
+ 'MIB Database for ONU {} has never been synchronized'.format(self._device_id)
+
+ # Get the requested information
+ if self._attributes[attr] is None:
+ value = self._onu_device.query_mib(class_id, instance_id=instance_id)
+
+ if isinstance(value, dict) and len(value) > 0:
+ self._attributes[attr] = value
+
+ return self._attributes[attr]
+
+ except Exception as e:
+ self.log.exception('onu-capabilities', e=e, class_id=class_id,
+ instance_id=instance_id)
+ raise
+
+ def reset(self):
+ """
+ Reset the cached database entries to None. This method should be
+ called after any communications loss to the ONU (reboot, PON down, ...)
+ in case a new software load with different capabilities is available.
+ """
+ self._attributes = {
+ '_ont_g': None,
+ '_ont_2g': None,
+ '_ani_g': None,
+ '_uni_g': None,
+ '_cardholder': None,
+ '_circuit_pack': None,
+ '_software': None,
+ '_pptp': None,
+ '_veip': None
+ }
+
+ @property
+ def version(self):
+ """
+ This attribute identifies the version of the ONU as defined by the vendor
+ """
+ ontg = self._get_capability('_ont_g', OntG.class_id, 0)
+ if ontg is None or ATTRIBUTES_KEY not in ontg:
+ return None
+
+ return ontg[ATTRIBUTES_KEY].get('version')
+
+ @property
+ def serial_number(self):
+ """
+ The serial number is unique for each ONU
+ """
+ ontg = self._get_capability('_ont_g', OntG.class_id, 0)
+ if ontg is None or ATTRIBUTES_KEY not in ontg:
+ return None
+
+ return ontg[ATTRIBUTES_KEY].get('serial_number')
+
+ @property
+ def traffic_management_option(self):
+ """
+ This attribute identifies the upstream traffic management function
+ implemented in the ONU. There are three options:
+
+ 0 Priority controlled and flexibly scheduled upstream traffic. The traffic
+ scheduler and priority queue mechanism are used for upstream traffic.
+
+ 1 Rate controlled upstream traffic. The maximum upstream traffic of each
+ individual connection is guaranteed by shaping.
+
+ 2 Priority and rate controlled. The traffic scheduler and priority queue
+ mechanism are used for upstream traffic. The maximum upstream traffic
+ of each individual connection is guaranteed by shaping.
+ """
+ ontg = self._get_capability('_ont_g', OntG.class_id, 0)
+ if ontg is None or ATTRIBUTES_KEY not in ontg:
+ return None
+
+ return ontg[ATTRIBUTES_KEY].get('traffic_management_option')
+
+ @property
+ def onu_survival_time(self):
+ """
+ This attribute indicates the minimum guaranteed time in milliseconds
+ between the loss of external power and the silence of the ONU. This does not
+ include survival time attributable to a backup battery. The value zero implies that
+ the actual time is not known.
+
+ Optional
+ """
+ ontg = self._get_capability('_ont_g', OntG.class_id, 0)
+ if ontg is None or ATTRIBUTES_KEY not in ontg:
+ return None
+
+ return ontg[ATTRIBUTES_KEY].get('onu_survival_time', 0)
+
+ @property
+ def equipment_id(self):
+ """
+ This attribute may be used to identify the specific type of ONU. In some
+ environments, this attribute may include the equipment CLEI code.
+ """
+ ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
+ if ont2g is None or ATTRIBUTES_KEY not in ont2g:
+ return None
+
+ return ont2g[ATTRIBUTES_KEY].get('equipment_id')
+
+ @property
+ def omcc_version(self):
+ """
+ This attribute identifies the version of the OMCC protocol being used by the
+ ONU. This allows the OLT to manage a network with ONUs that support different
+ OMCC versions. Release levels of [ITU-T G.984.4] are supported with code
+ points of the form 0x8y and 0x9y, where y is a hexadecimal digit in the range
+ 0..F. Support for continuing revisions of this Recommendation is defined in
+ the 0xAy range.
+ """
+ ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
+ if ont2g is None or ATTRIBUTES_KEY not in ont2g:
+ return None
+
+ return OMCCVersion.to_enum(ont2g[ATTRIBUTES_KEY].get('omcc_version', 0))
+
+ @property
+ def vendor_product_code(self):
+ """
+ This attribute contains a vendor-specific product code for the ONU
+ """
+ ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
+ if ont2g is None or ATTRIBUTES_KEY not in ont2g:
+ return None
+
+ return ont2g[ATTRIBUTES_KEY].get('vendor_product_code')
+
+ @property
+ def total_priority_queues(self):
+ """
+ This attribute reports the total number of upstream priority queues
+ that are not associated with a circuit pack, but with the ONU in its entirety
+ """
+ ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
+ if ont2g is None or ATTRIBUTES_KEY not in ont2g:
+ return None
+
+ return ont2g[ATTRIBUTES_KEY].get('total_priority_queue_number')
+
+ @property
+ def total_traffic_schedulers(self):
+ """
+ This attribute reports the total number of traffic schedulers that
+ are not associated with a circuit pack, but with the ONU in its entirety.
+ """
+ ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
+ if ont2g is None or ATTRIBUTES_KEY not in ont2g:
+ return None
+
+ return ont2g[ATTRIBUTES_KEY].get('total_traffic_scheduler_number')
+
+ @property
+ def total_gem_ports(self):
+ """
+ This attribute reports the total number of GEM port-IDs supported
+ by the ONU.
+ """
+ ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
+ if ont2g is None or ATTRIBUTES_KEY not in ont2g:
+ return None
+
+ return ont2g[ATTRIBUTES_KEY].get('total_gem_port_id_number')
+
+ @property
+ def uptime(self):
+ """
+ This attribute counts 10 ms intervals since the ONU was last initialized.
+ It rolls over to 0 when full
+ """
+ ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
+ if ont2g is None or ATTRIBUTES_KEY not in ont2g:
+ return None
+
+ return ont2g[ATTRIBUTES_KEY].get('sys_uptime')
+
+ @property
+ def connectivity_capability(self):
+ """
+ This attribute indicates the Ethernet connectivity models that the ONU
+ can support. The value 0 indicates that the capability is not supported; 1 signifies
+ support.
+
+ Bit Model [Figure reference ITU-T 988]
+ 1 (LSB) N:1 bridging, Figure 8.2.2-3
+ 2 1:M mapping, Figure 8.2.2-4
+ 3 1:P filtering, Figure 8.2.2-5
+ 4 N:M bridge-mapping, Figure 8.2.2-6
+ 5 1:MP map-filtering, Figure 8.2.2-7
+ 6 N:P bridge-filtering, Figure 8.2.2-8
+ 7 to refer to N:MP bridge-map-filtering, Figure 8.2.2-9
+ 8...16 Reserved
+ """
+ ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
+ if ont2g is None or ATTRIBUTES_KEY not in ont2g:
+ return None
+
+ return ont2g[ATTRIBUTES_KEY].get('connectivity_capability')
+
+ @property
+ def qos_configuration_flexibility(self):
+ """
+ This attribute reports whether various managed entities in the
+ ONU are fixed by the ONU's architecture or whether they are configurable. For
+ backward compatibility, and if the ONU does not support this attribute, all such
+ attributes are understood to be hard-wired.
+
+ Bit Interpretation when bit value = 1
+ 1 (LSB) Priority queue ME: Port field of related port attribute is
+ read-write and can point to any T-CONT or UNI port in the
+ same slot
+ 2 Priority queue ME: The traffic scheduler pointer is permitted
+ to refer to any other traffic scheduler in the same slot
+ 3 Traffic scheduler ME: T-CONT pointer is read-write
+ 4 Traffic scheduler ME: Policy attribute is read-write
+ 5 T-CONT ME: Policy attribute is read-write
+ 6 Priority queue ME: Priority field of related port attribute is
+ read-write
+ 7..16 Reserved
+ """
+ ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
+ if ont2g is None or ATTRIBUTES_KEY not in ont2g:
+ return None
+
+ return ont2g[ATTRIBUTES_KEY].get('qos_configuration_flexibility')
+
+ @property
+ def priority_queue_scale_factor(self):
+ """
+ This specifies the scale factor of several attributes of the priority
+ queue managed entity of section 5.2.8
+ """
+ ont2g = self._get_capability('_ont_2g', Ont2G.class_id, 0)
+ if ont2g is None or ATTRIBUTES_KEY not in ont2g:
+ return None
+
+ return ont2g[ATTRIBUTES_KEY].get('priority_queue_scale_factor', 1)
+
+ @property
+ def cardholder_entities(self):
+ """
+ Return a dictionary containing some overall information on the CardHolder
+ instances for this ONU.
+ """
+ ch = self._get_capability('_cardholder', Cardholder.class_id)
+ results = dict()
+
+ if ch is not None:
+ for inst, inst_data in ch.items():
+ if isinstance(inst, int):
+ results[inst] = {
+ 'entity-id': inst,
+ 'is-single-piece': inst >= 256,
+ 'slot-number': inst & 0xff,
+ 'actual-plug-in-type': inst_data[ATTRIBUTES_KEY].get('actual_plugin_unit_type', 0),
+ 'actual-equipment-id': inst_data[ATTRIBUTES_KEY].get('actual_equipment_id', 0),
+ 'protection-profile-ptr': inst_data[ATTRIBUTES_KEY].get('protection_profile_pointer', 0),
+ }
+ return results if len(results) else None
+
+ @property
+ def circuitpack_entities(self):
+ """
+ This specifies the scale factor of several attributes of the priority
+ queue managed entity of section 5.2.8
+ """
+ cp = self._get_capability('_circuit_pack', CircuitPack.class_id)
+ results = dict()
+
+ if cp is not None:
+ for inst, inst_data in cp.items():
+ if isinstance(inst, int):
+ results[inst] = {
+ 'entity-id': inst,
+ 'number-of-ports': inst_data[ATTRIBUTES_KEY].get('number_of_ports', 0),
+ 'serial-number': inst_data[ATTRIBUTES_KEY].get('serial_number', 0),
+ 'version': inst_data[ATTRIBUTES_KEY].get('version', 0),
+ 'vendor-id': inst_data[ATTRIBUTES_KEY].get('vendor_id', 0),
+ 'total-tcont-count': inst_data[ATTRIBUTES_KEY].get('total_tcont_buffer_number', 0),
+ 'total-priority-queue-count': inst_data[ATTRIBUTES_KEY].get('total_priority_queue_number', 0),
+ 'total-traffic-sched-count': inst_data[ATTRIBUTES_KEY].get('total_traffic_scheduler_number', 0),
+ }
+
+ return results if len(results) else None
+
+ @property
+ def software_images(self):
+ """
+ Get a list of software image information for the ONU. The information is provided
+ so that it may be directly added to the protobuf Device information software list.
+ """
+ sw = self._get_capability('_software', SoftwareImage.class_id)
+ images = list()
+
+ if sw is not None:
+ for inst, inst_data in sw.items():
+ if isinstance(inst, int):
+ is_active = inst_data[ATTRIBUTES_KEY].get('is_active', False)
+
+ images.append(Image(name='running-revision' if is_active else 'candidate-revision',
+ version=str(inst_data[ATTRIBUTES_KEY].get('version',
+ 'Not Available').rstrip('\0')),
+ is_active=is_active,
+ is_committed=inst_data[ATTRIBUTES_KEY].get('is_committed',
+ False),
+ is_valid=inst_data[ATTRIBUTES_KEY].get('is_valid',
+ False),
+ install_datetime='Not Available',
+ hash=str(inst_data[ATTRIBUTES_KEY].get('image_hash',
+ 'Not Available').rstrip('\0'))))
+ return images if len(images) else None
+
+ @property
+ def ani_g_entities(self):
+ """
+ This managed entity organizes data associated with each access network
+ interface supported by a G-PON ONU. The ONU automatically creates one
+ instance of this managed entity for each PON physical port.
+ """
+ ag = self._get_capability('_ani_g', AniG.class_id)
+ results = dict()
+
+ if ag is not None:
+ for inst, inst_data in ag.items():
+ if isinstance(inst, int):
+ results[inst] = {
+ 'entity-id': inst,
+ 'slot-number': (inst >> 8) & 0xff,
+ 'port-number': inst & 0xff,
+ 'total-tcont-count': inst_data[ATTRIBUTES_KEY].get('total_tcont_number', 0),
+ 'piggyback-dba-reporting': inst_data[ATTRIBUTES_KEY].get('piggyback_dba_reporting', 0),
+ }
+ return results if len(results) else None
+
+ @property
+ def uni_g_entities(self):
+ """
+ This managed entity organizes data associated with user network interfaces
+ (UNIs) supported by GEM. One instance of the UNI-G managed entity exists
+ for each UNI supported by the ONU.
+
+ The ONU automatically creates or deletes instances of this managed entity
+ upon the creation or deletion of a real or virtual circuit pack managed
+ entity, one per port.
+ """
+ ug = self._get_capability('_uni_g', UniG.class_id)
+ results = dict()
+
+ if ug is not None:
+ for inst, inst_data in ug.items():
+ if isinstance(inst, int):
+ results[inst] = {
+ 'entity-id': inst,
+ 'management-capability': inst_data[ATTRIBUTES_KEY].get('management_capability', 0)
+ }
+ return results if len(results) else None
+
+ @property
+ def pptp_entities(self):
+ """
+ Returns discovered PPTP Ethernet entities. TODO more detail here
+ """
+ pptp = self._get_capability('_pptp', PptpEthernetUni.class_id)
+ results = dict()
+
+ if pptp is not None:
+ for inst, inst_data in pptp.items():
+ if isinstance(inst, int):
+ results[inst] = {
+ 'entity-id': inst,
+ 'expected-type': inst_data[ATTRIBUTES_KEY].get('expected_type', 0),
+ 'sensed-type': inst_data[ATTRIBUTES_KEY].get('sensed_type', 0),
+ 'autodetection-config': inst_data[ATTRIBUTES_KEY].get('auto_detection_configuration', 0),
+ 'ethernet-loopback-config': inst_data[ATTRIBUTES_KEY].get('ethernet_loopback_configuration', 0),
+ 'administrative-state': inst_data[ATTRIBUTES_KEY].get('administrative_state', 0),
+ 'operational-state': inst_data[ATTRIBUTES_KEY].get('operational_state', 0),
+ 'config-ind': inst_data[ATTRIBUTES_KEY].get('configuration_ind', 0),
+ 'max-frame-size': inst_data[ATTRIBUTES_KEY].get('max_frame_size', 0),
+ 'dte-dce-ind': inst_data[ATTRIBUTES_KEY].get('dte_or_dce_ind', 0),
+ 'pause-time': inst_data[ATTRIBUTES_KEY].get('pause_time', 0),
+ 'bridged-ip-ind': inst_data[ATTRIBUTES_KEY].get('bridged_or_ip_ind', 0),
+ 'arc': inst_data[ATTRIBUTES_KEY].get('arc', 0),
+ 'arc-interval': inst_data[ATTRIBUTES_KEY].get('arc_interval', 0),
+ 'pppoe-filter': inst_data[ATTRIBUTES_KEY].get('ppoe_filter', 0),
+ 'power-control': inst_data[ATTRIBUTES_KEY].get('power_control', 0)
+ }
+ return results if len(results) else None
+
+ @property
+ def veip_entities(self):
+ """
+ Returns discovered VEIP entities. TODO more detail here
+ """
+ veip = self._get_capability('_veip', VeipUni.class_id)
+ results = dict()
+
+ if veip is not None:
+ for inst, inst_data in veip.items():
+ if isinstance(inst, int):
+ results[inst] = {
+ 'entity-id': inst,
+ 'administrative-state': inst_data[ATTRIBUTES_KEY].get('administrative_state', 0),
+ 'operational-state': inst_data[ATTRIBUTES_KEY].get('operational_state', 0),
+ 'interdomain-name': inst_data[ATTRIBUTES_KEY].get('interdomain_name', ""),
+ 'tcp-udp-pointer': inst_data[ATTRIBUTES_KEY].get('tcp_udp_pointer', 0),
+ 'iana-assigned-port': inst_data[ATTRIBUTES_KEY].get('iana_assigned_port', 0)
+ }
+ return results if len(results) else None