VOL-1397: Adtran-OLT - Initial containerization commit
 - Need to move VERSION to base directory

Change-Id: I9d62d0607a011ce642e379fd92b35ec48b300070
diff --git a/adapters/adtran_olt/codec/olt_state.py b/adapters/adtran_olt/codec/olt_state.py
new file mode 100644
index 0000000..74413a4
--- /dev/null
+++ b/adapters/adtran_olt/codec/olt_state.py
@@ -0,0 +1,290 @@
+# 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 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
+            self._gems = None
+
+        def __str__(self):
+            return "OltState.Pon: pon-id: {}".format(self.pon_id)
+
+        @staticmethod
+        def decode(pon_list):
+            # log.debug('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 int(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 int(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 int(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 int(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 int(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 and sn['serial-number'] != 'AAAAAAAAAAA='])
+
+        @property
+        def gems(self):
+            """This list is not in the proposed BBF model, the stats are part of ietf-interfaces"""
+            if self._gems is None:
+                self._gems = OltState.Pon.Gem.decode(self._packet.get('gem', []))
+            return self._gems
+
+        @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, 'onu-id not found 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)
+
+            @property
+            def equalization_delay(self):
+                """Equalization delay (bits)"""
+                return self._packet.get('equalization-delay', 0)
+
+            @property
+            def fiber_length(self):
+                """Distance to ONU"""
+                return self._packet.get('fiber-length', 0)
+
+        class Gem(object):
+            """
+            Provides decode of onu list for a PON port
+            """
+            def __init__(self, packet):
+                assert 'onu-id' in packet, 'onu-id not found in packet'
+                assert 'port-id' in packet, 'port-id not found in packet'
+                assert 'alloc-id' in packet, 'alloc-id not found in packet'
+                self._packet = packet
+
+            def __str__(self):
+                return "OltState.Pon.Gem: onu-id: {}, gem-id".\
+                    format(self.onu_id, self.gem_id)
+
+            @staticmethod
+            def decode(gem_list):
+                # log.debug('gems:{}{}'.format(os.linesep,
+                #                              pprint.PrettyPrinter().pformat(gem_list)))
+                gems = {}
+                for gem_data in gem_list:
+                    gem = OltState.Pon.Gem(gem_data)
+                    assert gem.gem_id not in gems
+                    gems[gem.gem_id] = gem
+
+                return gems
+
+            @property
+            def onu_id(self):
+                """The ID used to identify the ONU"""
+                return self._packet['onu-id']
+
+            @property
+            def alloc_id(self):
+                return self._packet['alloc-id']
+
+            @property
+            def gem_id(self):
+                return self._packet['port-id']
+
+            @property
+            def tx_packets(self):
+                return int(self._packet.get('tx-packets', 0))
+
+            @property
+            def tx_bytes(self):
+                return int(self._packet.get('tx-bytes', 0))
+
+            @property
+            def rx_packets(self):
+                return int(self._packet.get('rx-packets', 0))
+
+            @property
+            def rx_bytes(self):
+                return int(self._packet.get('rx-bytes', 0))