Chip Boling | f5af85d | 2019-02-12 15:36:17 -0600 | [diff] [blame] | 1 | # Copyright 2017-present Adtran, Inc. |
| 2 | # |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | # you may not use this file except in compliance with the License. |
| 5 | # You may obtain a copy of the License at |
| 6 | # |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 9 | # Unless required by applicable law or agreed to in writing, software |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | # See the License for the specific language governing permissions and |
| 13 | # limitations under the License. |
| 14 | # |
| 15 | import structlog |
| 16 | |
| 17 | log = structlog.get_logger() |
| 18 | |
| 19 | |
| 20 | class OltState(object): |
| 21 | """ |
| 22 | Class to wrap decode of olt-state container from the ADTRAN |
| 23 | gpon-olt-hw.yang YANG model |
| 24 | """ |
| 25 | |
| 26 | def __init__(self, packet): |
| 27 | self._packet = packet |
| 28 | self._pons = None |
| 29 | |
| 30 | def __str__(self): |
| 31 | return "OltState: {}".format(self.software_version) |
| 32 | |
| 33 | @property |
| 34 | def software_version(self): |
| 35 | """The software version of olt driver""" |
| 36 | return self._packet.get('software-version', '') |
| 37 | |
| 38 | @property |
| 39 | def pons(self): |
| 40 | if self._pons is None: |
| 41 | self._pons = OltState.Pon.decode(self._packet.get('pon', None)) |
| 42 | return self._pons |
| 43 | |
| 44 | ############################################################# |
| 45 | # Act like a container for simple access into PON list |
| 46 | |
| 47 | def __len__(self): |
| 48 | return len(self.pons) |
| 49 | |
| 50 | def __getitem__(self, key): |
| 51 | if not isinstance(key, int): |
| 52 | raise TypeError('Key should be of type int') |
| 53 | if key not in self.pons: |
| 54 | raise KeyError("key '{}' not found".format(key)) |
| 55 | |
| 56 | return self.pons[key] |
| 57 | |
| 58 | def __iter__(self): |
| 59 | raise NotImplementedError('TODO: Not yet implemented') |
| 60 | |
| 61 | def __contains__(self, item): |
| 62 | if not isinstance(item, int): |
| 63 | raise TypeError('Item should be of type int') |
| 64 | return item in self.pons |
| 65 | |
| 66 | # TODO: Look at generator support and if it is useful |
| 67 | |
| 68 | class Pon(object): |
| 69 | """ |
| 70 | Provides decode of PON list from within |
| 71 | """ |
| 72 | def __init__(self, packet): |
| 73 | assert 'pon-id' in packet |
| 74 | self._packet = packet |
| 75 | self._onus = None |
| 76 | self._gems = None |
| 77 | |
| 78 | def __str__(self): |
| 79 | return "OltState.Pon: pon-id: {}".format(self.pon_id) |
| 80 | |
| 81 | @staticmethod |
| 82 | def decode(pon_list): |
| 83 | # log.debug('Decoding PON List:{}{}'.format(os.linesep, |
| 84 | # pprint.PrettyPrinter().pformat(pon_list))) |
| 85 | pons = {} |
| 86 | for pon_data in pon_list: |
| 87 | pon = OltState.Pon(pon_data) |
| 88 | assert pon.pon_id not in pons |
| 89 | pons[pon.pon_id] = pon |
| 90 | |
| 91 | return pons |
| 92 | |
| 93 | @property |
| 94 | def pon_id(self): |
| 95 | """PON identifier""" |
| 96 | return self._packet['pon-id'] |
| 97 | |
| 98 | @property |
| 99 | def downstream_wavelength(self): |
| 100 | """The wavelength, in nanometers, being used in the downstream direction""" |
| 101 | return self._packet.get('downstream-wavelength', 0) |
| 102 | |
| 103 | @property |
| 104 | def upstream_wavelength(self): |
| 105 | """The wavelength, in nanometers, being used in the upstream direction""" |
| 106 | return self._packet.get('upstream-wavelength', 0) |
| 107 | |
| 108 | @property |
| 109 | def downstream_channel_id(self): |
| 110 | """Downstream wavelength channel identifier associated with this PON.""" |
| 111 | return self._packet.get('downstream-channel-id', 0) |
| 112 | |
| 113 | @property |
| 114 | def rx_packets(self): |
| 115 | """Sum all of the RX Packets of GEM ports that are not base TCONT's""" |
| 116 | return int(self._packet.get('rx-packets', 0)) |
| 117 | |
| 118 | @property |
| 119 | def tx_packets(self): |
| 120 | """Sum all of the TX Packets of GEM ports that are not base TCONT's""" |
| 121 | return int(self._packet.get('tx-packets', 0)) |
| 122 | |
| 123 | @property |
| 124 | def rx_bytes(self): |
| 125 | """Sum all of the RX Octets of GEM ports that are not base TCONT's""" |
| 126 | return int(self._packet.get('rx-bytes', 0)) |
| 127 | |
| 128 | @property |
| 129 | def tx_bytes(self): |
| 130 | """Sum all of the TX Octets of GEM ports that are not base TCONT's""" |
| 131 | return int(self._packet.get('tx-bytes', 0)) |
| 132 | |
| 133 | @property |
| 134 | def tx_bip_errors(self): |
| 135 | """Sum the TX ONU bip errors to get TX BIP's per PON""" |
| 136 | return int(self._packet.get('tx-bip-errors', 0)) |
| 137 | |
| 138 | @property |
| 139 | def wm_tuned_out_onus(self): |
| 140 | """ |
| 141 | bit array indicates the list of tuned out ONU's that are in wavelength |
| 142 | mobility protecting state. |
| 143 | onu-bit-octects: |
| 144 | type binary { length "4 .. 1024"; } |
| 145 | description each bit position indicates corresponding ONU's status |
| 146 | (true or false) whether that ONU's is in |
| 147 | wavelength mobility protecting state or not |
| 148 | For 128 ONTs per PON, the size of this |
| 149 | array will be 16. onu-bit-octects[0] and MSB bit in that byte |
| 150 | represents ONU 0 etc. |
| 151 | """ |
| 152 | return self._packet.get('wm-tuned-out-onus', bytes(0)) |
| 153 | |
| 154 | @property |
| 155 | def ont_los(self): |
| 156 | """List of configured ONTs that have been previously discovered and are in a los of signal state""" |
| 157 | return self._packet.get('ont-los', []) |
| 158 | |
| 159 | @property |
| 160 | def discovered_onu(self): |
| 161 | """ |
| 162 | Immutable Set of each Optical Network Unit(ONU) that has been activated via discovery |
| 163 | key/value: serial-number (string) |
| 164 | """ |
| 165 | return frozenset([sn['serial-number'] for sn in self._packet.get('discovered-onu', []) |
| 166 | if 'serial-number' in sn and sn['serial-number'] != 'AAAAAAAAAAA=']) |
| 167 | |
| 168 | @property |
| 169 | def gems(self): |
| 170 | """This list is not in the proposed BBF model, the stats are part of ietf-interfaces""" |
| 171 | if self._gems is None: |
| 172 | self._gems = OltState.Pon.Gem.decode(self._packet.get('gem', [])) |
| 173 | return self._gems |
| 174 | |
| 175 | @property |
| 176 | def onus(self): |
| 177 | """ |
| 178 | The map of each Optical Network Unit(ONU). Key: ONU ID (int) |
| 179 | """ |
| 180 | if self._onus is None: |
| 181 | self._onus = OltState.Pon.Onu.decode(self._packet.get('onu', [])) |
| 182 | return self._onus |
| 183 | |
| 184 | class Onu(object): |
| 185 | """ |
| 186 | Provides decode of onu list for a PON port |
| 187 | """ |
| 188 | def __init__(self, packet): |
| 189 | assert 'onu-id' in packet, 'onu-id not found in packet' |
| 190 | self._packet = packet |
| 191 | |
| 192 | def __str__(self): |
| 193 | return "OltState.Pon.Onu: onu-id: {}".format(self.onu_id) |
| 194 | |
| 195 | @staticmethod |
| 196 | def decode(onu_list): |
| 197 | # log.debug('onus:{}{}'.format(os.linesep, |
| 198 | # pprint.PrettyPrinter().pformat(onu_list))) |
| 199 | onus = {} |
| 200 | for onu_data in onu_list: |
| 201 | onu = OltState.Pon.Onu(onu_data) |
| 202 | assert onu.onu_id not in onus |
| 203 | onus[onu.onu_id] = onu |
| 204 | |
| 205 | return onus |
| 206 | |
| 207 | @property |
| 208 | def onu_id(self): |
| 209 | """The ID used to identify the ONU""" |
| 210 | return self._packet['onu-id'] |
| 211 | |
| 212 | @property |
| 213 | def oper_status(self): |
| 214 | """The operational state of each ONU""" |
| 215 | return self._packet.get('oper-status', 'unknown') |
| 216 | |
| 217 | @property |
| 218 | def reported_password(self): |
| 219 | """The password reported by the ONU (binary)""" |
| 220 | return self._packet.get('reported-password', bytes(0)) |
| 221 | |
| 222 | @property |
| 223 | def rssi(self): |
| 224 | """The received signal strength indication of the ONU""" |
| 225 | return self._packet.get('rssi', -9999) |
| 226 | |
| 227 | @property |
| 228 | def equalization_delay(self): |
| 229 | """Equalization delay (bits)""" |
| 230 | return self._packet.get('equalization-delay', 0) |
| 231 | |
| 232 | @property |
| 233 | def fiber_length(self): |
| 234 | """Distance to ONU""" |
| 235 | return self._packet.get('fiber-length', 0) |
| 236 | |
| 237 | class Gem(object): |
| 238 | """ |
| 239 | Provides decode of onu list for a PON port |
| 240 | """ |
| 241 | def __init__(self, packet): |
| 242 | assert 'onu-id' in packet, 'onu-id not found in packet' |
| 243 | assert 'port-id' in packet, 'port-id not found in packet' |
| 244 | assert 'alloc-id' in packet, 'alloc-id not found in packet' |
| 245 | self._packet = packet |
| 246 | |
| 247 | def __str__(self): |
| 248 | return "OltState.Pon.Gem: onu-id: {}, gem-id".\ |
| 249 | format(self.onu_id, self.gem_id) |
| 250 | |
| 251 | @staticmethod |
| 252 | def decode(gem_list): |
| 253 | # log.debug('gems:{}{}'.format(os.linesep, |
| 254 | # pprint.PrettyPrinter().pformat(gem_list))) |
| 255 | gems = {} |
| 256 | for gem_data in gem_list: |
| 257 | gem = OltState.Pon.Gem(gem_data) |
| 258 | assert gem.gem_id not in gems |
| 259 | gems[gem.gem_id] = gem |
| 260 | |
| 261 | return gems |
| 262 | |
| 263 | @property |
| 264 | def onu_id(self): |
| 265 | """The ID used to identify the ONU""" |
| 266 | return self._packet['onu-id'] |
| 267 | |
| 268 | @property |
| 269 | def alloc_id(self): |
| 270 | return self._packet['alloc-id'] |
| 271 | |
| 272 | @property |
| 273 | def gem_id(self): |
| 274 | return self._packet['port-id'] |
| 275 | |
| 276 | @property |
| 277 | def tx_packets(self): |
| 278 | return int(self._packet.get('tx-packets', 0)) |
| 279 | |
| 280 | @property |
| 281 | def tx_bytes(self): |
| 282 | return int(self._packet.get('tx-bytes', 0)) |
| 283 | |
| 284 | @property |
| 285 | def rx_packets(self): |
| 286 | return int(self._packet.get('rx-packets', 0)) |
| 287 | |
| 288 | @property |
| 289 | def rx_bytes(self): |
| 290 | return int(self._packet.get('rx-bytes', 0)) |