| # |
| # Copyright 2017-present Adtran, Inc. |
| # |
| # 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 pprint |
| |
| import os |
| import structlog |
| |
| log = structlog.get_logger() |
| |
| |
| class OltState(object): |
| """ |
| Class to wrap decode of olt-state container from the ADTRAN |
| gpon-olt-hw.yang YANG model |
| """ |
| |
| def __init__(self, packet): |
| self._packet = packet |
| self._pons = None |
| |
| def __str__(self): |
| return "OltState: {}".format(self.software_version) |
| |
| @property |
| def software_version(self): |
| """The software version of olt driver""" |
| return self._packet.get('software-version', '') |
| |
| @property |
| def pons(self): |
| if self._pons is None: |
| self._pons = OltState.Pon.decode(self._packet.get('pon', None)) |
| return self._pons |
| |
| ############################################################# |
| # Act like a container for simple access into PON list |
| |
| def __len__(self): |
| return len(self.pons) |
| |
| def __getitem__(self, key): |
| if not isinstance(key, int): |
| raise TypeError('Key should be of type int') |
| if key not in self.pons: |
| raise KeyError("key '{}' not found".format(key)) |
| |
| return self.pons[key] |
| |
| def __iter__(self): |
| raise NotImplementedError('TODO: Not yet implemented') |
| |
| def __contains__(self, item): |
| if not isinstance(item, int): |
| raise TypeError('Item should be of type int') |
| return item in self.pons |
| |
| # TODO: Look at generator support and if it is useful |
| |
| class Pon(object): |
| """ |
| Provides decode of PON list from within |
| """ |
| |
| def __init__(self, packet): |
| assert 'pon-id' in packet |
| self._packet = packet |
| self._onus = None |
| |
| def __str__(self): |
| return "OltState.Pon: pon-id: {}".format(self.pon_id) |
| |
| @staticmethod |
| def decode(pon_list): |
| log.info('Decoding PON List:{}{}'.format(os.linesep, |
| pprint.PrettyPrinter().pformat(pon_list))) |
| pons = {} |
| for pon_data in pon_list: |
| pon = OltState.Pon(pon_data) |
| assert pon.pon_id not in pons |
| pons[pon.pon_id] = pon |
| |
| return pons |
| |
| @property |
| def pon_id(self): |
| """PON identifier""" |
| return self._packet['pon-id'] |
| |
| @property |
| def downstream_wavelength(self): |
| """The wavelength, in nanometers, being used in the downstream direction""" |
| return self._packet.get('downstream-wavelength', 0) |
| |
| @property |
| def upstream_wavelength(self): |
| """The wavelength, in nanometers, being used in the upstream direction""" |
| return self._packet.get('upstream-wavelength', 0) |
| |
| @property |
| def downstream_channel_id(self): |
| """Downstream wavelength channel identifier associated with this PON.""" |
| return self._packet.get('downstream-channel-id', 0) |
| |
| @property |
| def rx_packets(self): |
| """Sum all of the RX Packets of GEM ports that are not base TCONT's""" |
| return self._packet.get('rx-packets', 0) |
| |
| @property |
| def tx_packets(self): |
| """Sum all of the TX Packets of GEM ports that are not base TCONT's""" |
| return self._packet.get('tx-packets', 0) |
| |
| @property |
| def rx_bytes(self): |
| """Sum all of the RX Octets of GEM ports that are not base TCONT's""" |
| return self._packet.get('rx-bytes', 0) |
| |
| @property |
| def tx_bytes(self): |
| """Sum all of the TX Octets of GEM ports that are not base TCONT's""" |
| return self._packet.get('tx-bytes', 0) |
| |
| @property |
| def tx_bip_errors(self): |
| """Sum the TX ONU bip errors to get TX BIP's per PON""" |
| return self._packet.get('tx-bip-errors', 0) |
| |
| @property |
| def wm_tuned_out_onus(self): |
| """ |
| bit array indicates the list of tuned out ONU's that are in wavelength |
| mobility protecting state. |
| onu-bit-octects: |
| type binary { length "4 .. 1024"; } |
| description each bit position indicates corresponding ONU's status |
| (true or false) whether that ONU's is in |
| wavelength mobility protecting state or not |
| For 128 ONTs per PON, the size of this |
| array will be 16. onu-bit-octects[0] and MSB bit in that byte |
| represents ONU 0 etc. |
| """ |
| return self._packet.get('wm-tuned-out-onus', bytes(0)) |
| |
| @property |
| def ont_los(self): |
| """List of configured ONTs that have been previously discovered and are in a los of signal state""" |
| return self._packet.get('ont-los', []) |
| |
| @property |
| def discovered_onu(self): |
| """ |
| Immutable Set of each Optical Network Unit(ONU) that has been activated via discovery |
| key/value: serial-number (string) |
| """ |
| return frozenset([sn['serial-number'] for sn in self._packet.get('discovered-onu', []) |
| if 'serial-number' in sn]) |
| |
| @property |
| def gems(self): |
| """This list is not in the proposed BBF model, the stats are part of ietf-interfaces""" |
| raise NotImplementedError('TODO: not yet supported') |
| |
| @property |
| def onus(self): |
| """ |
| The map of each Optical Network Unit(ONU). Key: ONU ID (int) |
| """ |
| if self._onus is None: |
| self._onus = OltState.Pon.Onu.decode(self._packet.get('onu', [])) |
| return self._onus |
| |
| class Onu(object): |
| """ |
| Provides decode of onu list for a PON port |
| """ |
| |
| def __init__(self, packet): |
| assert 'onu-id' in packet |
| self._packet = packet |
| |
| def __str__(self): |
| return "OltState.Pon.Onu: onu-id: {}".format(self.onu_id) |
| |
| @staticmethod |
| def decode(onu_list): |
| log.debug('onus:{}{}'.format(os.linesep, |
| pprint.PrettyPrinter().pformat(onu_list))) |
| onus = {} |
| for onu_data in onu_list: |
| onu = OltState.Pon.Onu(onu_data) |
| assert onu.onu_id not in onus |
| onus[onu.onu_id] = onu |
| |
| return onus |
| |
| @property |
| def onu_id(self): |
| """The ID used to identify the ONU""" |
| return self._packet['onu-id'] |
| |
| @property |
| def oper_status(self): |
| """The operational state of each ONU""" |
| return self._packet.get('oper-status', 'unknown') |
| |
| @property |
| def reported_password(self): |
| """The password reported by the ONU (binary)""" |
| return self._packet.get('reported-password', bytes(0)) |
| |
| @property |
| def rssi(self): |
| """The received signal strength indication of the ONU""" |
| return self._packet.get('rssi', -9999) |