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

Change-Id: I9d62d0607a011ce642e379fd92b35ec48b300070
diff --git a/adapters/adtran_common/net/mock_netconf_client.py b/adapters/adtran_common/net/mock_netconf_client.py
new file mode 100644
index 0000000..314f2a0
--- /dev/null
+++ b/adapters/adtran_common/net/mock_netconf_client.py
@@ -0,0 +1,199 @@
+# 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
+import random
+import time
+from adtran_netconf import AdtranNetconfClient
+from pyvoltha.common.utils.asleep import asleep
+from ncclient.operations.rpc import RPCReply, RPCError
+from twisted.internet.defer import inlineCallbacks, returnValue
+
+log = structlog.get_logger()
+
+_dummy_xml = '<rpc-reply message-id="br-549" ' + \
+      'xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" ' + \
+      'xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">' + \
+      '<data/>' + \
+      '</rpc-reply>'
+
+
+class MockNetconfClient(AdtranNetconfClient):
+    """
+    Performs NETCONF requests
+    """
+    def __init__(self, host_ip, port=830, username='', password='', timeout=20):
+        super(MockNetconfClient, self).__init__(host_ip, port=port, username=username,
+                                                password=password, timeout=timeout)
+        self._connected = False
+        self._locked = {}
+
+    def __str__(self):
+        return "MockNetconfClient {}@{}:{}".format(self._username, self._ip, self._port)
+
+    @property
+    def capabilities(self):
+        """
+        Get the server's NETCONF capabilities
+
+        :return: (ncclient.capabilities.Capabilities) object representing the server's capabilities.
+        """
+        return None
+
+    @property
+    def connected(self):
+        """
+        Is this client connected to a NETCONF server
+        :return: (boolean) True if connected
+        """
+        return self._connected
+
+    @inlineCallbacks
+    def connect(self, connect_timeout=None):
+        """
+        Connect to the NETCONF server
+          o To disable attempting publickey authentication altogether, call with
+            allow_agent and look_for_keys as False.`
+
+          o hostkey_verify enables hostkey verification from ~/.ssh/known_hosts
+
+        :return: (deferred) Deferred request
+        """
+        yield asleep(random.uniform(0.1, 5.0))   # Simulate NETCONF request delay
+        self._connected = True
+        self._locked = {}
+        returnValue(True)
+
+    @inlineCallbacks
+    def close(self):
+        """
+        Close the connection to the NETCONF server
+        :return:  (deferred) Deferred request
+        """
+        yield asleep(random.uniform(0.1, 0.5))   # Simulate NETCONF request delay
+        self._connected = False
+        self._locked = {}
+        returnValue(True)
+
+    @inlineCallbacks
+    def get_config(self, source='running'):
+        """
+        Get the configuration from the specified source
+
+        :param source: (string) Configuration source, 'running', 'candidate', ...
+        :return: (deferred) Deferred request that wraps the GetReply class
+        """
+        yield asleep(random.uniform(0.1, 4.0))   # Simulate NETCONF request delay
+
+        # TODO: Customize if needed...
+        xml = _dummy_xml
+        returnValue(RPCReply(xml))
+
+    @inlineCallbacks
+    def get(self, payload):
+        """
+        Get the requested data from the server
+
+        :param payload: Payload/filter
+        :return: (defeered) for GetReply
+        """
+        yield asleep(random.uniform(0.1, 3.0))   # Simulate NETCONF request delay
+
+        # TODO: Customize if needed...
+        xml = _dummy_xml
+        returnValue(RPCReply(xml))
+
+    @inlineCallbacks
+    def lock(self, source, lock_timeout):
+        """
+        Lock the configuration system
+        :param source: is the name of the configuration datastore accessed
+        :param lock_timeout: timeout in seconds for holding the lock
+        :return: (defeered) for RpcReply
+        """
+        expire_time = time.time() + lock_timeout
+
+        if source not in self._locked:
+            self._locked[source] = None
+
+        while self._locked[source] is not None:
+            # Watch for lock timeout
+            if time.time() >= self._locked[source]:
+                self._locked[source] = None
+                break
+            yield asleep(0.1)
+
+        if time.time() < expire_time:
+            yield asleep(random.uniform(0.1, 0.5))   # Simulate NETCONF request delay
+            self._locked[source] = expire_time
+
+        returnValue(RPCReply(_dummy_xml) if expire_time > time.time() else RPCError('TODO'))
+
+    @inlineCallbacks
+    def unlock(self, source):
+        """
+        Get the requested data from the server
+        :param rpc_string: RPC request
+        :param source: is the name of the configuration datastore accessed
+        :return: (defeered) for RpcReply
+        """
+        if source not in self._locked:
+            self._locked[source] = None
+
+        if self._locked[source] is not None:
+            yield asleep(random.uniform(0.1, 0.5))   # Simulate NETCONF request delay
+
+        self._locked[source] = None
+        returnValue(RPCReply(_dummy_xml))
+
+    @inlineCallbacks
+    def edit_config(self, config, target='running', default_operation='merge',
+                    test_option=None, error_option=None):
+        """
+        Loads all or part of the specified config to the target configuration datastore with the ability to lock
+        the datastore during the edit.
+
+        :param config is the configuration, which must be rooted in the config element. It can be specified
+                      either as a string or an Element.format="xml"
+        :param target is the name of the configuration datastore being edited
+        :param default_operation if specified must be one of { 'merge', 'replace', or 'none' }
+        :param test_option if specified must be one of { 'test_then_set', 'set' }
+        :param error_option if specified must be one of { 'stop-on-error', 'continue-on-error', 'rollback-on-error' }
+                            The 'rollback-on-error' error_option depends on the :rollback-on-error capability.
+
+        :return: (defeered) for RpcReply
+        """
+        try:
+            yield asleep(random.uniform(0.1, 2.0))  # Simulate NETCONF request delay
+
+        except Exception as e:
+            log.exception('edit_config', e=e)
+            raise
+
+        # TODO: Customize if needed...
+        xml = _dummy_xml
+        returnValue(RPCReply(xml))
+
+    @inlineCallbacks
+    def rpc(self, rpc_string):
+        """
+        Custom RPC request
+        :param rpc_string: (string) RPC request
+        :return: (defeered) for GetReply
+        """
+        yield asleep(random.uniform(0.1, 2.0))   # Simulate NETCONF request delay
+
+        # TODO: Customize if needed...
+        xml = _dummy_xml
+        returnValue(RPCReply(xml))