<reponame>iecedge/xos<gh_stars>0
# 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
import mock
import pdb

import os
import sys
from xosconfig import Config

test_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
sync_lib_dir = os.path.join(test_path, "..", "xossynchronizer")
xos_dir = os.path.join(test_path, "..", "..", "..", "xos")

class TestModelPolicyTenantWithContainer(unittest.TestCase):
    def setUp(self):
        global TenantWithContainerPolicy, LeastLoadedNodeScheduler, MockObjectList

        self.sys_path_save = sys.path
        self.cwd_save = os.getcwd()

        config = basic_conf = os.path.abspath(
            os.path.dirname(os.path.realpath(__file__)) + "/test_config.yaml"
        )
        Config.clear()  # in case left unclean by a previous test case
        Config.init(config, "synchronizer-config-schema.yaml")

        from xossynchronizer.mock_modelaccessor_build import (
            build_mock_modelaccessor,
        )

        build_mock_modelaccessor(sync_lib_dir, xos_dir, services_dir=None, service_xprotos=[])

        import xossynchronizer.model_policies.model_policy_tenantwithcontainer
        from xossynchronizer.model_policies.model_policy_tenantwithcontainer import (
            TenantWithContainerPolicy,
            LeastLoadedNodeScheduler,
        )

        from mock_modelaccessor import MockObjectList

        # import all class names to globals
        for (
            k,
            v,
        ) in xossynchronizer.model_policies.model_policy_tenantwithcontainer.model_accessor.all_model_classes.items():
            globals()[k] = v

        from xossynchronizer.modelaccessor import model_accessor

        self.policy = TenantWithContainerPolicy(model_accessor=model_accessor)
        self.user = User(email="<EMAIL>")
        self.tenant = TenantWithContainer(creator=self.user)
        self.flavor = Flavor(name="m1.small")

    def tearDown(self):
        Config.clear()
        sys.path = self.sys_path_save
        os.chdir(self.cwd_save)

    def test_manage_container_no_slices(self):
        with patch.object(TenantWithContainer, "owner") as owner:
            owner.slices.count.return_value = 0
            with self.assertRaises(Exception) as e:
                self.policy.manage_container(self.tenant)
            self.assertEqual(e.exception.message, "The service has no slices")

    def test_manage_container(self):
        with patch.object(TenantWithContainer, "owner") as owner, patch.object(
            TenantWithContainer, "save"
        ) as tenant_save, patch.object(
            Node, "site_deployment"
        ) as site_deployment, patch.object(
            Instance, "save"
        ) as instance_save, patch.object(
            Instance, "delete"
        ) as instance_delete, patch.object(
            TenantWithContainerPolicy, "get_image"
        ) as get_image, patch.object(
            LeastLoadedNodeScheduler, "pick"
        ) as pick:
            # setup mocks
            node = Node(hostname="my.node.com")
            slice = Slice(
                name="mysite_test1", default_flavor=self.flavor, default_isolation="vm"
            )
            image = Image(name="trusty-server-multi-nic")
            deployment = Deployment(name="testdeployment")
            owner.slices.count.return_value = 1
            owner.slices.all.return_value = [slice]
            owner.slices.first.return_value = slice
            get_image.return_value = image
            pick.return_value = (node, None)
            site_deployment.deployment = deployment
            # done setup mocks

            # call manage_container
            self.policy.manage_container(self.tenant)

            # make sure manage_container did what it is supposed to do
            self.assertNotEqual(self.tenant.instance, None)
            self.assertEqual(self.tenant.instance.creator.email, "<EMAIL>")
            self.assertEqual(self.tenant.instance.image.name, "trusty-server-multi-nic")
            self.assertEqual(self.tenant.instance.flavor.name, "m1.small")
            self.assertEqual(self.tenant.instance.isolation, "vm")
            self.assertEqual(self.tenant.instance.node.hostname, "my.node.com")
            self.assertEqual(self.tenant.instance.slice.name, "mysite_test1")
            self.assertEqual(self.tenant.instance.parent, None)
            instance_save.assert_called()
            instance_delete.assert_not_called()
            tenant_save.assert_called()

    def test_manage_container_delete(self):
        self.tenant.deleted = True

        # call manage_container
        self.policy.manage_container(self.tenant)

        # make sure manage_container did what it is supposed to do
        self.assertEqual(self.tenant.instance, None)

    def test_manage_container_no_m1_small(self):
        with patch.object(TenantWithContainer, "owner") as owner, patch.object(
            Node, "site_deployment"
        ) as site_deployment, patch.object(
            Flavor, "objects"
        ) as flavor_objects, patch.object(
            TenantWithContainerPolicy, "get_image"
        ) as get_image, patch.object(
            LeastLoadedNodeScheduler, "pick"
        ) as pick:
            # setup mocks
            node = Node(hostname="my.node.com")
            slice = Slice(
                name="mysite_test1", default_flavor=None, default_isolation="vm"
            )
            image = Image(name="trusty-server-multi-nic")
            deployment = Deployment(name="testdeployment")
            owner.slices.count.return_value = 1
            owner.slices.all.return_value = [slice]
            owner.slices.first.return_value = slice
            get_image.return_value = image
            pick.return_value = (node, None)
            site_deployment.deployment = deployment
            flavor_objects.filter.return_value = []
            # done setup mocks

            with self.assertRaises(Exception) as e:
                self.policy.manage_container(self.tenant)
            self.assertEqual(e.exception.message, "No m1.small flavor")

    def test_least_loaded_node_scheduler(self):
        with patch.object(Node.objects, "get_items") as node_objects:
            slice = Slice(
                name="mysite_test1", default_flavor=None, default_isolation="vm"
            )
            node = Node(hostname="my.node.com", id=4567)
            node.instances = MockObjectList(initial=[])
            node_objects.return_value = [node]

            sched = LeastLoadedNodeScheduler(slice)
            (picked_node, parent) = sched.pick()

            self.assertNotEqual(picked_node, None)
            self.assertEqual(picked_node.id, node.id)

    def test_least_loaded_node_scheduler_two_nodes(self):
        with patch.object(Node.objects, "get_items") as node_objects:
            slice = Slice(
                name="mysite_test1", default_flavor=None, default_isolation="vm"
            )
            instance1 = Instance(id=1)
            node1 = Node(hostname="my.node.com", id=4567)
            node1.instances = MockObjectList(initial=[])
            node2 = Node(hostname="my.node.com", id=8910)
            node2.instances = MockObjectList(initial=[instance1])
            node_objects.return_value = [node1, node2]

            # should pick the node with the fewest instance (node1)

            sched = LeastLoadedNodeScheduler(slice)
            (picked_node, parent) = sched.pick()

            self.assertNotEqual(picked_node, None)
            self.assertEqual(picked_node.id, node1.id)

    def test_least_loaded_node_scheduler_two_nodes_multi(self):
        with patch.object(Node.objects, "get_items") as node_objects:
            slice = Slice(
                name="mysite_test1", default_flavor=None, default_isolation="vm"
            )
            instance1 = Instance(id=1)
            instance2 = Instance(id=2)
            instance3 = Instance(id=3)
            node1 = Node(hostname="my.node.com", id=4567)
            node1.instances = MockObjectList(initial=[instance2, instance3])
            node2 = Node(hostname="my.node.com", id=8910)
            node2.instances = MockObjectList(initial=[instance1])
            node_objects.return_value = [node1, node2]

            # should pick the node with the fewest instance (node2)

            sched = LeastLoadedNodeScheduler(slice)
            (picked_node, parent) = sched.pick()

            self.assertNotEqual(picked_node, None)
            self.assertEqual(picked_node.id, node2.id)

    def test_least_loaded_node_scheduler_with_label(self):
        with patch.object(Node.objects, "get_items") as node_objects:
            slice = Slice(
                name="mysite_test1", default_flavor=None, default_isolation="vm"
            )
            instance1 = Instance(id=1)
            node1 = Node(hostname="my.node.com", id=4567)
            node1.instances = MockObjectList(initial=[])
            node2 = Node(hostname="my.node.com", id=8910)
            node2.instances = MockObjectList(initial=[instance1])
            # Fake out the existence of a NodeLabel object. TODO: Extend the mock framework to support the model__field
            # syntax.
            node1.nodelabels__name = None
            node2.nodelabels__name = "foo"
            node_objects.return_value = [node1, node2]

            # should pick the node with the label, even if it has a greater number of instances

            sched = LeastLoadedNodeScheduler(slice, label="foo")
            (picked_node, parent) = sched.pick()

            self.assertNotEqual(picked_node, None)
            self.assertEqual(picked_node.id, node2.id)

    def test_least_loaded_node_scheduler_create_label(self):
        with patch.object(Node.objects, "get_items") as node_objects, patch.object(
            NodeLabel, "save", autospec=True
        ) as nodelabel_save, patch.object(NodeLabel, "node") as nodelabel_node_add:
            slice = Slice(
                name="mysite_test1", default_flavor=None, default_isolation="vm"
            )
            instance1 = Instance(id=1)
            node1 = Node(hostname="my.node.com", id=4567)
            node1.instances = MockObjectList(initial=[])
            node2 = Node(hostname="my.node.com", id=8910)
            node2.instances = MockObjectList(initial=[instance1])
            # Fake out the existence of a NodeLabel object. TODO: Extend the mock framework to support the model__field
            # syntax.
            node1.nodelabels__name = None
            node2.nodelabels__name = None
            node_objects.return_value = [node1, node2]

            # should pick the node with the least number of instances

            sched = LeastLoadedNodeScheduler(
                slice, label="foo", constrain_by_service_instance=True
            )
            (picked_node, parent) = sched.pick()

            self.assertNotEqual(picked_node, None)
            self.assertEqual(picked_node.id, node1.id)

            # NodeLabel should have been created and saved

            self.assertEqual(nodelabel_save.call_count, 1)
            self.assertEqual(nodelabel_save.call_args[0][0].name, "foo")

            # The NodeLabel's node field should have been added to

            NodeLabel.node.add.assert_called_with(node1)


if __name__ == "__main__":
    unittest.main()
