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

Change-Id: I9d62d0607a011ce642e379fd92b35ec48b300070
diff --git a/adapters/adtran_common/flow/flow_tables.py b/adapters/adtran_common/flow/flow_tables.py
new file mode 100644
index 0000000..48e2e7e
--- /dev/null
+++ b/adapters/adtran_common/flow/flow_tables.py
@@ -0,0 +1,163 @@
+# 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.
+#
+from flow_entry import FlowEntry
+from evc import EVC
+
+
+class DeviceFlows(object):
+    """ Tracks existing flows on the device """
+
+    def __init__(self):
+        self._flow_table = dict()   # Key = (str)Flow ID, Value = FlowEntry
+
+    def __getitem__(self, item):
+        flow_id = item.flow_id if isinstance(item, FlowEntry) else item
+        return self._flow_table[flow_id]
+
+    def __iter__(self):
+        for _flow_id, _flow in self._flow_table.items():
+            yield _flow_id, _flow
+
+    def itervalues(self):
+        for _flow in self._flow_table.values():
+            yield _flow
+
+    def iterkeys(self):
+        for _id in self._flow_table.keys():
+            yield _id
+
+    def items(self):
+        return self._flow_table.items()
+
+    def values(self):
+        return self._flow_table.values()
+
+    def keys(self):
+        return self._flow_table.keys()
+
+    def __len__(self):
+        return len(self._flow_table)
+
+    def add(self, flow):
+        assert isinstance(flow, FlowEntry)
+        if flow.flow_id not in self._flow_table:
+            self._flow_table[flow.flow_id] = flow
+        return flow
+
+    def get(self, item):
+        flow_id = item.flow_id if isinstance(item, FlowEntry) else item
+        return self._flow_table.get(flow_id)
+
+    def remove(self, item):
+        flow_id = item.flow_id if isinstance(item, FlowEntry) else item
+        return self._flow_table.pop(flow_id, None)
+
+    def clear_all(self):
+        self._flow_table = dict()
+
+
+class DownstreamFlows(object):
+    """
+    Tracks existing flows that are downstream (NNI as source port)
+
+    The downstream table is slightly different than the base DeviceFlows
+    table as it is used to track flows that will become EVCs.  The base
+    table tracks flows that will be EVC-maps (or related to them).
+
+    The downstream table is also indexed by a downstream signature that
+    is composed as follows:
+
+        <dev-id>.<ingress-port-number>.<s-tag>.*
+
+    In comparison, the upstream flows is similar, but instead of '*' it has the
+    c-tag (if any).
+
+    TODO: Drop device ID from signatures once flow tables are unique to a device handler
+    """
+    def __init__(self):
+        self._signature_table = dict()  # Key = (str)Downstream signature
+                                        #  |
+                                        #  +-> downstream-signature
+                                        #      |
+                                        #      +-> 'evc' -> EVC
+                                        #      |
+                                        #      +-> flow-ids -> flow-entries...
+
+    def __getitem__(self, signature):
+        assert isinstance(signature, str)
+        return self._signature_table[signature]
+
+    def __iter__(self):
+        for _flow_id, _flow in self._signature_table.items():
+            yield _flow_id, _flow
+
+    def itervalues(self):
+        for _flow in self._signature_table.values():
+            yield _flow
+
+    def iterkeys(self):
+        for _id in self._signature_table.keys():
+            yield _id
+
+    def items(self):
+        return self._signature_table.items()
+
+    def values(self):
+        return self._signature_table.values()
+
+    def keys(self):
+        return self._signature_table.keys()
+
+    def __len__(self):
+        return len(self._signature_table)
+
+    def get(self, signature):
+        assert isinstance(signature, str)
+        return self._signature_table.get(signature)
+
+    def add(self, signature):
+        assert isinstance(signature, str)
+        """
+        Can be called by upstream flow to reserve a slot
+        """
+        if signature not in self._signature_table:
+            self._signature_table[signature] = DownstreamFlows.SignatureTableEntry(signature)
+        return self._signature_table[signature]
+
+    def remove(self, signature):
+        assert isinstance(signature, str)
+        return self._signature_table.pop(signature)
+
+    def clear_all(self):
+        self._signature_table = dict()
+
+    class SignatureTableEntry(object):
+        def __init__(self, signature):
+            self._signature = signature
+            self._evc = None
+            self._flow_table = DeviceFlows()
+
+        @property
+        def evc(self):
+            return self._evc
+
+        @evc.setter
+        def evc(self, evc):
+            assert isinstance(evc, (EVC, type(None)))
+            self._evc = evc
+
+        @property
+        def flows(self):
+            return self._flow_table