CORD-2238: unit tests for vnaas syncsteps

Change-Id: Ic06b72ddd6d8934dfe0cd56e4d8386eccff19ccf
(cherry picked from commit b3a355468615d9a6eed596a0c698d97856abea98)
diff --git a/xos/nose2-plugins/__init__.py b/xos/nose2-plugins/__init__.py
new file mode 100644
index 0000000..42722a8
--- /dev/null
+++ b/xos/nose2-plugins/__init__.py
@@ -0,0 +1,14 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
diff --git a/xos/nose2-plugins/exclude.py b/xos/nose2-plugins/exclude.py
new file mode 100644
index 0000000..241eadb
--- /dev/null
+++ b/xos/nose2-plugins/exclude.py
@@ -0,0 +1,32 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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 logging
+import os
+
+from nose2.events import Plugin
+
+log = logging.getLogger('nose2.plugins.excludeignoredfiles')
+
+class ExcludeIgnoredFiles(Plugin):
+    commandLineSwitch = (None, 'exclude-ignored-files', 'Exclude that which should be excluded')
+
+    def matchPath(self, event):
+        if event.path.endswith(".py"):
+            text = open(event.path, "r").read()
+            if "test_framework: ignore" in text.lower():
+                log.info("Ignoring %s" % event.path)
+                event.handled = True
+                return False
diff --git a/xos/synchronizer/steps/test_config.yaml b/xos/synchronizer/steps/test_config.yaml
new file mode 100644
index 0000000..270d0a9
--- /dev/null
+++ b/xos/synchronizer/steps/test_config.yaml
@@ -0,0 +1,30 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+name: test-model-policies
+accessor:
+  username: xosadmin@opencord.org
+  password: "sample"
+  kind: "testframework"
+logging:
+  version: 1
+  handlers:
+    console:
+      class: logging.StreamHandler
+  loggers:
+    'multistructlog':
+      handlers:
+          - console
diff --git a/xos/synchronizer/steps/test_private_key b/xos/synchronizer/steps/test_private_key
new file mode 100644
index 0000000..3cb08a3
--- /dev/null
+++ b/xos/synchronizer/steps/test_private_key
@@ -0,0 +1 @@
+some_key
diff --git a/xos/synchronizer/steps/test_sync_vnaaseline.py b/xos/synchronizer/steps/test_sync_vnaaseline.py
new file mode 100644
index 0000000..b02b94d
--- /dev/null
+++ b/xos/synchronizer/steps/test_sync_vnaaseline.py
@@ -0,0 +1,172 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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 unittest
+from mock import patch, call, Mock, MagicMock, PropertyMock
+import mock
+
+import os, sys
+
+test_path=os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+service_dir=os.path.join(test_path, "../../../..")
+xos_dir=os.path.join(test_path, "../../..")
+if not os.path.exists(os.path.join(test_path, "new_base")):
+    xos_dir=os.path.join(test_path, "../../../../../../orchestration/xos/xos")
+    services_dir=os.path.join(xos_dir, "../../xos_services")
+
+# chosen to match https://github.com/opencord/ecord/blob/master/examples/vnaasglobal-service-reference.yaml
+ONOS_NAME = "onfGlobalONOS"
+ONOS_IP = "onos-cord"
+ONOS_PORT = 8182
+ONOS_USERNAME = "onos"
+ONOS_PASSWORD = "rocks"
+ONOS_TYPE = "global"
+
+BWP_GOLD_CBS = 2000
+BWP_GOLD_EBS = 2700
+BWP_GOLD_CIR = 20000
+BWP_GOLD_EIR = 5000
+BWP_GOLD_NAME = "gold"
+
+ELINE_VLANIDS = "100"
+ELINE_CONNECT_POINT_1_ID = "domain:10.90.1.30-cord-onos/1"
+ELINE_CONNECT_POINT_2_ID = "domain:10.90.1.50-cord-onos/1"
+ELINE_NAME = "testeline"
+
+UNI_TENANT = "onf"
+UNI_NAME = "onf"
+UNI_LATLNG = [37.773972, -122.431297]
+UNI_CPE_ID = "netconf:192.168.56.20:830/0"
+
+
+class TestSyncvNaaSEline(unittest.TestCase):
+    def setUp(self):
+        global SyncvNaaSEline, MockObjectList
+
+        self.sys_path_save = sys.path
+        sys.path.append(xos_dir)
+        sys.path.append(os.path.join(xos_dir, 'synchronizers', 'new_base'))
+
+        config = os.path.join(test_path, "test_config.yaml")
+        from xosconfig import Config
+        Config.clear()
+        Config.init(config, 'synchronizer-config-schema.yaml')
+
+        from synchronizers.new_base.mock_modelaccessor_build import build_mock_modelaccessor
+        build_mock_modelaccessor(xos_dir, services_dir, ["vnaas/xos/vnaas.xproto"])
+
+        import synchronizers.new_base.modelaccessor
+        import synchronizers.new_base.model_policies.model_policy_tenantwithcontainer
+        import sync_vnaaseline
+        from sync_vnaaseline import SyncvNaaSEline, model_accessor
+
+        from mock_modelaccessor import MockObjectList
+
+        # import all class names to globals
+        for (k, v) in model_accessor.all_model_classes.items():
+            globals()[k] = v
+
+        # Some of the functions we call have side-effects. For example, creating a VSGServiceInstance may lead to creation of
+        # tags. Ideally, this wouldn't happen, but it does. So make sure we reset the world.
+        model_accessor.reset_all_object_stores()
+
+        self.syncstep = SyncvNaaSEline()
+
+        self.onosModel = OnosModel(name=ONOS_NAME,
+                                   onos_ip=ONOS_IP,
+                                   onos_port=ONOS_PORT,
+                                   onos_username=ONOS_USERNAME,
+                                   onos_password=ONOS_PASSWORD,
+                                   onos_type=ONOS_TYPE)
+        self.bandwidthProfile = BandwidthProfile(cbs=BWP_GOLD_CBS,
+                                                 ebs=BWP_GOLD_EBS,
+                                                 cir=BWP_GOLD_CIR,
+                                                 eir=BWP_GOLD_EIR,
+                                                 name=BWP_GOLD_NAME)
+        self.userNetworkInterface = UserNetworkInterface(tenant=UNI_TENANT,
+                                                         name=UNI_NAME,
+                                                         latlng=UNI_LATLNG,
+                                                         cpe_id=UNI_CPE_ID)
+
+        self.eline = ELine(name=ELINE_NAME,
+                           connect_point_1_id=ELINE_CONNECT_POINT_1_ID,
+                           connect_point_2_id=ELINE_CONNECT_POINT_2_ID,
+                           vlanids=ELINE_VLANIDS,
+                           cord_site_name=ONOS_NAME,
+                           bwp=BWP_GOLD_NAME)
+
+    def tearDown(self):
+        sys.path = self.sys_path_save
+
+    def test_sync_record(self):
+        with patch.object(BandwidthProfile.objects, "get_items") as bwp_objects, \
+             patch.object(OnosModel.objects, "get_items") as onos_objects, \
+             patch("requests.post") as requests_post:
+
+            bwp_objects.return_value = [self.bandwidthProfile]
+            onos_objects.return_value = [self.onosModel]
+
+            requests_post.return_value = Mock(status_code=200)
+
+            self.syncstep.sync_record(self.eline)
+
+            requests_post.assert_called()
+
+            attrs = requests_post.call_args[1]["data"]
+            attrs = eval(attrs) # convert POST string back into a dict
+
+            desired_attrs = {"evcCfgId": ELINE_NAME,
+                             "eir": BWP_GOLD_EIR,
+                             "cir": BWP_GOLD_CIR,
+                             "uniList": [ELINE_CONNECT_POINT_1_ID, ELINE_CONNECT_POINT_2_ID],
+                             "ebs": BWP_GOLD_EBS,
+                             "vlanId": int(ELINE_VLANIDS),
+                             "cbs": BWP_GOLD_CBS,
+                             "evcId": ELINE_NAME,
+                             "evcType": "POINT_TO_POINT"}
+
+            self.assertDictContainsSubset(desired_attrs, attrs)
+
+    def test_delete_record(self):
+        with patch.object(BandwidthProfile.objects, "get_items") as bwp_objects, \
+             patch.object(OnosModel.objects, "get_items") as onos_objects, \
+             patch("requests.delete") as requests_delete:
+
+            bwp_objects.return_value = [self.bandwidthProfile]
+            onos_objects.return_value = [self.onosModel]
+
+            requests_delete.return_value = Mock(status_code=200)
+
+            self.syncstep.delete_record(self.eline)
+
+            requests_delete.assert_called()
+
+            url = requests_delete.call_args[0][0]
+            self.assertEqual(url, "http://%s:%d/carrierethernet/evc/testeline" % (ONOS_IP, ONOS_PORT))
+
+    def test_get_onos_global_addr(self):
+        addr = self.syncstep.get_onos_global_addr(self.onosModel)
+        self.assertEqual(addr, 'http://%s:%d/carrierethernet/evc' % (ONOS_IP, ONOS_PORT))
+
+    def test_get_onos_global_auth(self):
+        auth = self.syncstep.get_onos_global_auth(self.onosModel)
+        self.assertEqual(auth.username, ONOS_USERNAME)
+        self.assertEqual(auth.password, ONOS_PASSWORD)
+
+if __name__ == '__main__':
+    unittest.main()
+
+
diff --git a/xos/unittest.cfg b/xos/unittest.cfg
new file mode 100644
index 0000000..71be7ca
--- /dev/null
+++ b/xos/unittest.cfg
@@ -0,0 +1,5 @@
+[unittest]
+plugins=nose2-plugins.exclude
+code-directories=synchronizer
+                 model_policies
+                 steps