CORD-2238 unit tests for veg syncsteps
Change-Id: I05e3360f61fdd6248a8538e88a2f68693e858230
(cherry picked from commit 60671dcbf9f44605bf7378373a9bbf844a60a5c5)
diff --git a/xos/synchronizer/steps/sync_vegtenant.py b/xos/synchronizer/steps/sync_vegtenant.py
index 44db1ff..98400f3 100644
--- a/xos/synchronizer/steps/sync_vegtenant.py
+++ b/xos/synchronizer/steps/sync_vegtenant.py
@@ -157,12 +157,12 @@
def sync_record(self, o):
if (not o.policed) or (o.policed<o.updated):
- defer_sync("waiting on model policy")
+ self.defer_sync(o, "waiting on model policy")
super(SyncVEGTenant, self).sync_record(o)
def delete_record(self, o):
if (not o.policed) or (o.policed<o.updated):
- defer_sync("waiting on model policy")
+ self.defer_sync(o, "waiting on model policy")
# do not call super, as we don't want to re-run the playbook
def handle_service_monitoringagentinfo_watch_notification(self, monitoring_agent_info):
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_vegtenant.py b/xos/synchronizer/steps/test_sync_vegtenant.py
new file mode 100644
index 0000000..05d83f6
--- /dev/null
+++ b/xos/synchronizer/steps/test_sync_vegtenant.py
@@ -0,0 +1,326 @@
+
+# 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")
+
+class TestSyncVEGServiceInstance(unittest.TestCase):
+ def setUp(self):
+ global SyncVEGTenant, LeastLoadedNodeScheduler, 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, ["vEG/xos/veg.xproto", "addressmanager/xos/addressmanager.xproto"])
+
+ import synchronizers.new_base.modelaccessor
+ import synchronizers.new_base.model_policies.model_policy_tenantwithcontainer
+ import sync_vegtenant
+ from sync_vegtenant import SyncVEGTenant, 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 VEGTenant 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()
+
+ # attic functions that are not present in the mock model accessor
+ AddressManagerServiceInstance.set_attribute = Mock()
+
+ self.syncstep = SyncVEGTenant()
+
+ # set up an object hierarchy that represents a Service and ServiceInstance
+
+ self.user = User(email="testadmin@test.org")
+ self.service = VEGService(name="the_veg_service",
+ id=1,
+ docker_image_name="reg/veg_docker",
+ docker_insecure_registry=True,
+ dns_servers="dnsone,dnstwo",
+ url_filter_kind=None,
+ private_key_fn=os.path.join(test_path, "test_private_key"))
+ # E-CORD / vEG doesn't support these subscriber fields at this time, but keep them in the test for now since the
+ # synchronizer supports them to maintain similarity with vSG. If/when E-CORD introduces its own subscriber
+ # object, deal with the differences here.
+ self.subscriber = MagicMock(firewall_rules = "rule1",
+ firewall_enable = True,
+ url_filter_enable = True,
+ url_filter_level="R",
+ cdn_enable=True,
+ uplink_speed=1234,
+ downlink_speed=5678,
+ enable_uverse=False,
+ status="suspended",
+ sync_attributes=["firewall_rules", "firewall_enable", "url_filter_enable",
+ "url_filter_level", "cdn_enable", "uplink_speed",
+ "downlink_speed", "enable_uverse", "status"])
+ self.volt = MagicMock(s_tag=111, c_tag=222, subscriber=self.subscriber)
+ self.tenant = VEGTenant(creator=self.user,
+ id=401,
+ volt=self.volt,
+ owner=self.service,
+ wan_container_ip="10.7.1.3",
+ wan_container_netbits="24",
+ wan_container_mac="02:42:0a:07:01:03",
+ wan_container_gateway_ip="10.7.1.1",
+ wan_vm_ip="10.7.1.2",
+ wan_vm_mac="02:42:0a:07:01:02",
+ sync_attributes = ["wan_container_ip", "wan_container_netbits", "wan_container_mac",
+ "wan_container_gateway_ip", "wan_vm_ip", "wan_vm_mac"])
+ self.flavor = Flavor(name="m1.small")
+ self.npt_ctag = NetworkParameterType(name="c_tag", id=1)
+ self.npt_stag = NetworkParameterType(name="s_tag", id=2)
+ self.npt_neutron_port_name = NetworkParameterType(name="neutron_port_name", id=501)
+ self.priv_template = NetworkTemplate(name="access_network", visibility="private")
+ self.priv_network = Network(name="mysite_test1_private", template=self.priv_template)
+ self.image = Image(name="trusty-server-multi-nic")
+ self.deployment = Deployment(name="testdeployment")
+ self.user = User(email="smbaker", id=701)
+ self.controller = Controller(id=101)
+ self.node = Node(name="testnode")
+ self.slice = Slice(name="mysite_test1", default_flavor=self.flavor, default_isolation="vm", service=self.service, id=301)
+ self.instance = Instance(slice=self.slice,
+ instance_name="testinstance1_instance_name",
+ instance_id="testinstance1_instance_id",
+ name="testinstance1_name",
+ node=self.node,
+ creator=self.user,
+ controller=self.controller)
+ self.tenant.instance = self.instance
+ self.instance.get_ssh_ip = Mock(return_value="1.2.3.4")
+ self.controllerslice = ControllerSlice(slice_id=self.slice.id, controller_id=self.controller.id, id=201)
+ self.controlleruser = ControllerUser(user_id=self.user.id, controller_id=self.controller.id, id=601)
+
+ def tearDown(self):
+ sys.path = self.sys_path_save
+
+ def test_get_veg_service(self):
+ with patch.object(VEGService.objects, "get_items") as vegservice_objects:
+ vegservice_objects.return_value = [self.service]
+
+ self.tenant.owner = self.service
+
+ self.assertEqual(self.syncstep.get_veg_service(self.tenant), self.service)
+
+ def test_get_extra_attributes(self):
+ with patch.object(VEGService.objects, "get_items") as vegservice_objects:
+ vegservice_objects.return_value = [self.service]
+
+ attrs = self.syncstep.get_extra_attributes(self.tenant)
+
+ desired_attrs = {"s_tags": [111],
+ "c_tags": [222],
+ "docker_remote_image_name": "reg/veg_docker",
+ "docker_local_image_name": "reg/veg_docker",
+ "docker_opts": "--insecure-registry reg",
+ "dnsdemux_ip": "none",
+ "cdn_prefixes": [],
+ "full_setup": True,
+ "isolation": "vm",
+ "safe_browsing_macs": [],
+ "container_name": "veg-111-222",
+ "dns_servers": ["dnsone", "dnstwo"],
+ "url_filter_kind": None,
+
+ "firewall_rules": "rule1",
+ "firewall_enable": True,
+ "url_filter_enable": True,
+ "url_filter_level": "R",
+ "cdn_enable": True,
+ "uplink_speed": 1234,
+ "downlink_speed": 5678,
+ "enable_uverse": False,
+ "status": "suspended"}
+
+ self.assertDictContainsSubset(desired_attrs, attrs)
+
+
+ def test_sync_record(self):
+ with patch.object(VEGService.objects, "get_items") as vegservice_objects, \
+ patch.object(Slice.objects, "get_items") as slice_objects, \
+ patch.object(User.objects, "get_items") as user_objects, \
+ patch.object(ControllerSlice.objects, "get_items") as controllerslice_objects, \
+ patch.object(ControllerUser.objects, "get_items") as controlleruser_objects, \
+ patch.object(SyncVEGTenant, "run_playbook") as run_playbook:
+ slice_objects.return_value = [self.slice]
+ vegservice_objects.return_value = [self.service]
+ controllerslice_objects.return_value = [self.controllerslice]
+ controlleruser_objects.return_value = [self.controlleruser]
+ user_objects.return_value = [self.user]
+
+ self.tenant.updated = 10
+ self.tenant.policed = 20
+ self.tenant.enacted = None
+
+ run_playbook.return_value = True
+
+ self.syncstep.sync_record(self.tenant)
+
+ run_playbook.assert_called()
+
+ attrs = run_playbook.call_args[0][1]
+
+ desired_attrs = {"username": "ubuntu",
+ "ansible_tag": "VEGTenant_401",
+ "instance_name": "testinstance1_name",
+ "hostname": "testnode",
+ "private_key": "some_key\n",
+ "ssh_ip": "1.2.3.4",
+ "instance_id": "testinstance1_instance_id",
+
+ "wan_container_ip": "10.7.1.3",
+ "wan_container_netbits": "24",
+ "wan_container_mac": "02:42:0a:07:01:03",
+ "wan_container_gateway_ip": "10.7.1.1",
+ "wan_vm_ip": "10.7.1.2",
+ "wan_vm_mac": "02:42:0a:07:01:02",
+
+ "s_tags": [111],
+ "c_tags": [222],
+ "docker_remote_image_name": "reg/veg_docker",
+ "docker_local_image_name": "reg/veg_docker",
+ "docker_opts": "--insecure-registry reg",
+ "dnsdemux_ip": "none",
+ "cdn_prefixes": [],
+ "full_setup": True,
+ "isolation": "vm",
+ "safe_browsing_macs": [],
+ "container_name": "veg-111-222",
+ "dns_servers": ["dnsone", "dnstwo"],
+ "url_filter_kind": None,
+
+ "firewall_rules": "rule1",
+ "firewall_enable": True,
+ "url_filter_enable": True,
+ "url_filter_level": "R",
+ "cdn_enable": True,
+ "uplink_speed": 1234,
+ "downlink_speed": 5678,
+ "enable_uverse": False,
+ "status": "suspended"}
+
+ self.assertDictContainsSubset(desired_attrs, attrs)
+
+ def test_sync_record_emptysubscriber(self):
+ with patch.object(VEGService.objects, "get_items") as vegservice_objects, \
+ patch.object(Slice.objects, "get_items") as slice_objects, \
+ patch.object(User.objects, "get_items") as user_objects, \
+ patch.object(ControllerSlice.objects, "get_items") as controllerslice_objects, \
+ patch.object(ControllerUser.objects, "get_items") as controlleruser_objects, \
+ patch.object(SyncVEGTenant, "run_playbook") as run_playbook:
+ slice_objects.return_value = [self.slice]
+ vegservice_objects.return_value = [self.service]
+ controllerslice_objects.return_value = [self.controllerslice]
+ controlleruser_objects.return_value = [self.controlleruser]
+ user_objects.return_value = [self.user]
+
+ self.tenant.updated = 10
+ self.tenant.policed = 20
+ self.tenant.enacted = None
+
+ self.volt.subscriber = MagicMock()
+
+ run_playbook.return_value = True
+
+ self.syncstep.sync_record(self.tenant)
+
+ run_playbook.assert_called()
+
+ attrs = run_playbook.call_args[0][1]
+
+ desired_attrs = {"firewall_rules": "",
+ "firewall_enable": False,
+ "url_filter_enable": False,
+ "url_filter_level": "PG",
+ "cdn_enable": False,
+ "uplink_speed": 1000000000,
+ "downlink_speed": 1000000000,
+ "enable_uverse": True,
+ "status": "enabled"}
+
+ self.assertDictContainsSubset(desired_attrs, attrs)
+
+ def test_sync_record_no_policy(self):
+ with patch.object(SyncVEGTenant, "run_playbook") as run_playbook:
+
+ self.tenant.updated = 10
+ self.tenant.policed = 5 # policies need to be run
+ self.tenant.enacted = None
+
+ with self.assertRaises(Exception) as e:
+ self.syncstep.sync_record(self.tenant)
+ self.assertIn("due to waiting on model policy", e.exception.message)
+
+ run_playbook.assert_not_called()
+
+ def test_sync_record_instance_not_ready(self):
+ with patch.object(SyncVEGTenant, "run_playbook") as run_playbook:
+
+ self.tenant.updated = 10
+ self.tenant.policed = 20
+ self.tenant.enacted = None
+
+ self.instance.instance_name = None # no instance_name means instance is not ready
+
+ with self.assertRaises(Exception) as e:
+ self.syncstep.sync_record(self.tenant)
+ self.assertIn("due to waiting on instance.instance_name", e.exception.message)
+
+ run_playbook.assert_not_called()
+
+ def test_delete_record_no_policy(self):
+ self.tenant.updated = 10
+ self.tenant.policed = 20
+ self.tenant.enacted = None
+
+ self.syncstep.delete_record(self.tenant)
+
+ # delete doesn't actually do anything, so nothing to further test.
+
+ def test_delete_record_no_policy(self):
+ self.tenant.updated = 10
+ self.tenant.policed = 5 # policies need to be run
+ self.tenant.enacted = None
+
+ with self.assertRaises(Exception) as e:
+ self.syncstep.delete_record(self.tenant)
+ self.assertIn("due to waiting on model policy", e.exception.message)
+
+if __name__ == '__main__':
+ unittest.main()
+
+
diff --git a/xos/unittest.cfg b/xos/unittest.cfg
index a5bdb18..71be7ca 100644
--- a/xos/unittest.cfg
+++ b/xos/unittest.cfg
@@ -2,3 +2,4 @@
plugins=nose2-plugins.exclude
code-directories=synchronizer
model_policies
+ steps