Fix Bug 2086 Updating VNF status configurable
[osm/RO.git] / NG-RO / osm_ng_ro / tests / test_ns_thread.py
index 41a6ff1..b120901 100644 (file)
@@ -19,10 +19,245 @@ import logging
 import unittest
 from unittest.mock import MagicMock, patch
 
-from osm_ng_ro.ns_thread import VimInteractionNet
+from osm_common.dbmemory import DbMemory
+from osm_ng_ro.ns_thread import (
+    ConfigValidate,
+    NsWorker,
+    VimInteractionAffinityGroup,
+    VimInteractionMigration,
+    VimInteractionNet,
+    VimInteractionResize,
+)
 from osm_ro_plugin.vimconn import VimConnConnectionException, VimConnException
 
 
+class TestConfigValidate(unittest.TestCase):
+    def setUp(self):
+        self.config_dict = {
+            "period": {
+                "refresh_active": 65,
+                "refresh_build": 20,
+                "refresh_image": 3600,
+                "refresh_error": 300,
+                "queue_size": 50,
+            }
+        }
+
+    def test__get_configuration(self):
+        with self.subTest(i=1, t="Get config attributes with config input"):
+            configuration = ConfigValidate(self.config_dict)
+            self.assertEqual(configuration.active, 65)
+            self.assertEqual(configuration.build, 20)
+            self.assertEqual(configuration.image, 3600)
+            self.assertEqual(configuration.error, 300)
+            self.assertEqual(configuration.queue_size, 50)
+
+        with self.subTest(i=2, t="Unallowed refresh active input"):
+            # > 60  (except -1) is not allowed to set, so it should return default value 60
+            self.config_dict["period"]["refresh_active"] = 20
+            configuration = ConfigValidate(self.config_dict)
+            self.assertEqual(configuration.active, 60)
+
+        with self.subTest(i=3, t="Config to disable VM status periodic checks"):
+            # -1 is allowed to set to disable VM status updates
+            self.config_dict["period"]["refresh_active"] = -1
+            configuration = ConfigValidate(self.config_dict)
+            self.assertEqual(configuration.active, -1)
+
+
+class TestNsWorker(unittest.TestCase):
+    def setUp(self):
+        self.task_depends = None
+        self.plugins = {}
+        self.worker_index = "worker-3"
+        self.config = {
+            "period": {
+                "refresh_active": 60,
+                "refresh_build": 20,
+                "refresh_image": 3600,
+                "refresh_error": 600,
+                "queue_size": 100,
+            },
+            "process_id": "343435353",
+            "global": {"task_locked_time": 16373242100.994312},
+        }
+
+        self.ro_task = {
+            "_id": "122436:1",
+            "locked_by": None,
+            "locked_at": 0.0,
+            "target_id": "vim_openstack_1",
+            "vim_info": {
+                "created": False,
+                "created_items": None,
+                "vim_id": "test-vim-id",
+                "vim_name": "test-vim",
+                "vim_status": "DONE",
+                "vim_details": "",
+                "vim_message": None,
+                "refresh_at": None,
+            },
+            "modified_at": 1637324200.994312,
+            "created_at": 1637324200.994312,
+            "to_check_at": 16373242400.994312,
+            "tasks": [
+                {
+                    "target_id": 0,
+                    "action_id": "123456",
+                    "nsr_id": "654321",
+                    "task_id": "123456:1",
+                    "status": "DONE",
+                    "action": "CREATE",
+                    "item": "test_item",
+                    "target_record": "test_target_record",
+                    "target_record_id": "test_target_record_id",
+                },
+            ],
+        }
+
+    def get_disabled_tasks(self, db, status):
+        db_disabled_tasks = db.get_list(
+            "ro_tasks",
+            q_filter={
+                "tasks.status": status,
+                "to_check_at.lt": 0,
+            },
+        )
+        return db_disabled_tasks
+
+    def test__update_vm_refresh(self):
+        with self.subTest(
+            i=1,
+            t="1 disabled task with status BUILD in DB, refresh_active parameter is not equal to -1",
+        ):
+            # Disabled task with status build will not enabled again
+            db = DbMemory()
+            self.ro_task["tasks"][0]["status"] = "BUILD"
+            self.ro_task["to_check_at"] = -1
+            db.create("ro_tasks", self.ro_task)
+            disabled_tasks_count = len(self.get_disabled_tasks(db, "BUILD"))
+            instance = NsWorker(self.worker_index, self.config, self.plugins, db)
+            with patch.object(instance, "logger", logging):
+                instance.update_vm_refresh()
+                self.assertEqual(
+                    len(self.get_disabled_tasks(db, "BUILD")), disabled_tasks_count
+                )
+
+        with self.subTest(
+            i=2,
+            t="1 disabled task with status DONE in DB, refresh_active parameter is equal to -1",
+        ):
+            # As refresh_active parameter is equal to -1, task will not be enabled to process again
+            db = DbMemory()
+            self.config["period"]["refresh_active"] = -1
+            self.ro_task["tasks"][0]["status"] = "DONE"
+            self.ro_task["to_check_at"] = -1
+            db.create("ro_tasks", self.ro_task)
+            disabled_tasks_count = len(self.get_disabled_tasks(db, "DONE"))
+            instance = NsWorker(self.worker_index, self.config, self.plugins, db)
+            with patch.object(instance, "logger", logging):
+                instance.update_vm_refresh()
+                self.assertEqual(
+                    len(self.get_disabled_tasks(db, "DONE")), disabled_tasks_count
+                )
+
+        with self.subTest(
+            i=3,
+            t="2 disabled task with status DONE in DB, refresh_active parameter is not equal to -1",
+        ):
+            # Disabled tasks should be enabled to process again
+            db = DbMemory()
+            self.config["period"]["refresh_active"] = 66
+            self.ro_task["tasks"][0]["status"] = "DONE"
+            self.ro_task["to_check_at"] = -1
+            db.create("ro_tasks", self.ro_task)
+            self.ro_task2 = self.ro_task
+            self.ro_task2["_id"] = "122437:1"
+            db.create("ro_tasks", self.ro_task2)
+            disabled_tasks_count = len(self.get_disabled_tasks(db, "DONE"))
+            instance = NsWorker(self.worker_index, self.config, self.plugins, db)
+            with patch.object(instance, "logger", logging):
+                instance.update_vm_refresh()
+                self.assertEqual(
+                    len(self.get_disabled_tasks(db, "DONE")), disabled_tasks_count - 2
+                )
+
+        with self.subTest(
+            i=4,
+            t="No disabled task with status DONE in DB, refresh_active parameter is not equal to -1",
+        ):
+            # If there is not any disabled task, method will not change anything
+            db = DbMemory()
+            self.config["period"]["refresh_active"] = 66
+            self.ro_task["tasks"][0]["status"] = "DONE"
+            self.ro_task["to_check_at"] = 16373242400.994312
+            db.create("ro_tasks", self.ro_task)
+            self.ro_task2 = self.ro_task
+            self.ro_task2["_id"] = "122437:1"
+            db.create("ro_tasks", self.ro_task2)
+            disabled_tasks_count = len(self.get_disabled_tasks(db, "DONE"))
+            instance = NsWorker(self.worker_index, self.config, self.plugins, db)
+            with patch.object(instance, "logger", logging):
+                instance.update_vm_refresh()
+                self.assertEqual(
+                    len(self.get_disabled_tasks(db, "DONE")), disabled_tasks_count
+                )
+
+    def test__process_pending_tasks(self):
+        with self.subTest(
+            i=1,
+            t="refresh_active parameter is equal to -1, task status is DONE",
+        ):
+            # Task should be disabled to process again
+            db = DbMemory()
+            self.config["period"]["refresh_active"] = -1
+            self.ro_task["tasks"][0]["status"] = "DONE"
+            self.ro_task["to_check_at"] = 16373242400.994312
+            db.create("ro_tasks", self.ro_task)
+            # Number of disabled tasks in DB
+            disabled_tasks_count = len(self.get_disabled_tasks(db, "DONE"))
+            instance = NsWorker(self.worker_index, self.config, self.plugins, db)
+            with patch.object(instance, "logger", logging):
+                instance._process_pending_tasks(self.ro_task)
+                self.assertEqual(
+                    len(self.get_disabled_tasks(db, "DONE")), disabled_tasks_count + 1
+                )
+
+        with self.subTest(
+            i=2, t="refresh_active parameter is equal to -1, task status is FAILED"
+        ):
+            # Task will not be disabled to process as task status is not DONE
+            db = DbMemory()
+            self.config["period"]["refresh_active"] = -1
+            self.ro_task["tasks"][0]["status"] = "FAILED"
+            self.ro_task["to_check_at"] = 16373242400.994312
+            db.create("ro_tasks", self.ro_task)
+            disabled_tasks_count = len(self.get_disabled_tasks(db, "FAILED"))
+            instance = NsWorker(self.worker_index, self.config, self.plugins, db)
+            with patch.object(instance, "logger", logging):
+                instance._process_pending_tasks(self.ro_task)
+                self.assertEqual(
+                    len(self.get_disabled_tasks(db, "FAILED")), disabled_tasks_count
+                )
+
+        with self.subTest(
+            i=3, t="refresh_active parameter is not equal to -1, task status is DONE"
+        ):
+            # Task will not be disabled to process as refresh_active parameter is not -1
+            db = DbMemory()
+            self.config["period"]["refresh_active"] = 70
+            self.ro_task["tasks"][0]["status"] = "DONE"
+            self.ro_task["to_check_at"] = 16373242400.994312
+            db.create("ro_tasks", self.ro_task)
+            disabled_tasks_count = len(self.get_disabled_tasks(db, "DONE"))
+            instance = NsWorker(self.worker_index, self.config, self.plugins, db)
+            with patch.object(instance, "logger", logging):
+                instance._process_pending_tasks(self.ro_task)
+                self.assertEqual(
+                    len(self.get_disabled_tasks(db, "DONE")), disabled_tasks_count
+                )
+
+
 class TestVimInteractionNet(unittest.TestCase):
     def setUp(self):
         module_name = "osm_ro_plugin"
@@ -817,6 +1052,7 @@ class TestVimInteractionNet(unittest.TestCase):
                     "vim_name": "test-vim",
                     "vim_status": None,
                     "vim_details": "some-details",
+                    "vim_message": None,
                     "refresh_at": None,
                 },
                 "modified_at": 1637324200.994312,
@@ -871,6 +1107,7 @@ class TestVimInteractionNet(unittest.TestCase):
                     "vim_name": "test-vim",
                     "vim_status": "BUILD",
                     "vim_details": "",
+                    "vim_message": None,
                     "refresh_at": None,
                 },
                 "modified_at": 1637324200.994312,
@@ -926,6 +1163,7 @@ class TestVimInteractionNet(unittest.TestCase):
                     "vim_name": "test-vim",
                     "vim_status": "BUILD",
                     "vim_details": "",
+                    "vim_message": None,
                     "refresh_at": None,
                 },
                 "modified_at": 1637324200.994312,
@@ -945,7 +1183,7 @@ class TestVimInteractionNet(unittest.TestCase):
             task_status = "FAILED"
             ro_vim_item_update = {
                 "vim_status": "ERROR",
-                "vim_details": "some error message",
+                "vim_message": "some error message",
             }
             result = instance.refresh(ro_task)
             self.assertEqual(result[0], task_status)
@@ -981,6 +1219,7 @@ class TestVimInteractionNet(unittest.TestCase):
                     "vim_name": "test-vim",
                     "vim_status": "BUILD",
                     "vim_details": "",
+                    "vim_message": None,
                     "refresh_at": None,
                 },
                 "modified_at": 1637324200.994312,
@@ -1025,6 +1264,7 @@ class TestVimInteractionNet(unittest.TestCase):
                     "vim_name": "test-vim",
                     "vim_status": "BUILD",
                     "vim_details": "",
+                    "vim_message": None,
                     "refresh_at": None,
                 },
                 "modified_at": 163724200.994312,
@@ -1043,7 +1283,7 @@ class TestVimInteractionNet(unittest.TestCase):
             task_status = "FAILED"
             ro_vim_item_update = {
                 "vim_status": "DELETED",
-                "vim_details": "Deleted externally",
+                "vim_message": "Deleted externally",
                 "vim_id": None,
             }
             result = instance.refresh(ro_task)
@@ -1081,6 +1321,7 @@ class TestVimInteractionNet(unittest.TestCase):
                     "vim_name": "test-vim",
                     "vim_status": "BUILD",
                     "vim_details": "",
+                    "vim_message": None,
                     "refresh_at": None,
                 },
                 "modified_at": 163724211.994312,
@@ -1091,3 +1332,334 @@ class TestVimInteractionNet(unittest.TestCase):
             self.target_vim.refresh_nets_status.return_value = {}
             with self.assertRaises(KeyError):
                 instance.refresh(ro_task)
+
+
+class TestVimInteractionAffinityGroup(unittest.TestCase):
+    def setUp(self):
+        module_name = "osm_ro_plugin"
+        self.target_vim = MagicMock(name=f"{module_name}.vimconn.VimConnector")
+        self.task_depends = None
+
+        patches = [patch(f"{module_name}.vimconn.VimConnector", self.target_vim)]
+
+        # Enabling mocks and add cleanups
+        for mock in patches:
+            mock.start()
+            self.addCleanup(mock.stop)
+
+    def test__new_affinity_group_ok(self):
+        """
+        create affinity group with attributes set in params
+        """
+        db = "test_db"
+        logger = "test_logger"
+        my_vims = "test-vim"
+        db_vims = {
+            0: {
+                "config": {},
+            },
+        }
+
+        instance = VimInteractionAffinityGroup(db, logger, my_vims, db_vims)
+        with patch.object(instance, "my_vims", [self.target_vim]), patch.object(
+            instance, "logger", logging
+        ), patch.object(instance, "db_vims", db_vims):
+            ro_task = {
+                "target_id": 0,
+                "tasks": {
+                    "task_index_1": {
+                        "target_id": 0,
+                        "action_id": "123456",
+                        "nsr_id": "654321",
+                        "task_id": "123456:1",
+                        "status": "SCHEDULED",
+                        "action": "CREATE",
+                        "item": "test_item",
+                        "target_record": "test_target_record",
+                        "target_record_id": "test_target_record_id",
+                        # values coming from extra_dict
+                        "params": {
+                            "affinity_group_data": {
+                                "name": "affinity_group_1",
+                                "type": "affinity",
+                                "scope": "nfvi-node",
+                            }
+                        },
+                        "find_params": {},
+                        "depends_on": "test_depends_on",
+                    },
+                },
+            }
+
+            task_index = "task_index_1"
+            self.target_vim.new_affinity_group.return_value = (
+                "sample_affinity_group_id_1"
+            )
+            result = instance.new(ro_task, task_index, self.task_depends)
+            self.assertEqual(result[0], "DONE")
+            self.assertEqual(result[1].get("vim_id"), "sample_affinity_group_id_1")
+            self.assertEqual(result[1].get("created"), True)
+            self.assertEqual(result[1].get("vim_status"), "DONE")
+
+    def test__new_affinity_group_failed(self):
+        """
+        create affinity group with no attributes set in params
+        """
+        db = "test_db"
+        logger = "test_logger"
+        my_vims = "test-vim"
+        db_vims = {
+            0: {
+                "config": {},
+            },
+        }
+
+        instance = VimInteractionAffinityGroup(db, logger, my_vims, db_vims)
+        with patch.object(instance, "my_vims", [self.target_vim]), patch.object(
+            instance, "logger", logging
+        ), patch.object(instance, "db_vims", db_vims):
+            ro_task = {
+                "target_id": 0,
+                "tasks": {
+                    "task_index_2": {
+                        "target_id": 0,
+                        "action_id": "123456",
+                        "nsr_id": "654321",
+                        "task_id": "123456:1",
+                        "status": "SCHEDULED",
+                        "action": "CREATE",
+                        "item": "test_item",
+                        "target_record": "test_target_record",
+                        "target_record_id": "test_target_record_id",
+                        # values coming from extra_dict
+                        "params": {},
+                        "find_params": {},
+                        "depends_on": "test_depends_on",
+                    },
+                },
+            }
+
+            task_index = "task_index_2"
+            self.target_vim.new_affinity_group.return_value = (
+                "sample_affinity_group_id_1"
+            )
+            result = instance.new(ro_task, task_index, self.task_depends)
+            self.assertEqual(result[0], "DONE")
+            self.assertEqual(result[1].get("vim_id"), None)
+            self.assertEqual(result[1].get("created"), False)
+            self.assertEqual(result[1].get("vim_status"), "DONE")
+
+    def test__delete_affinity_group_ok(self):
+        """
+        delete affinity group with a proper vim_id
+        """
+        db = "test_db"
+        logger = "test_logger"
+        my_vims = "test-vim"
+        db_vims = {
+            0: {
+                "config": {},
+            },
+        }
+
+        instance = VimInteractionAffinityGroup(db, logger, my_vims, db_vims)
+        with patch.object(instance, "my_vims", [self.target_vim]), patch.object(
+            instance, "logger", logging
+        ), patch.object(instance, "db_vims", db_vims):
+            ro_task = {
+                "target_id": 0,
+                "tasks": {
+                    "task_index_3": {
+                        "target_id": 0,
+                        "task_id": "123456:1",
+                    },
+                },
+                "vim_info": {
+                    "created": False,
+                    "created_items": None,
+                    "vim_id": "sample_affinity_group_id_3",
+                    "vim_name": "sample_affinity_group_id_3",
+                    "vim_status": None,
+                    "vim_details": "some-details",
+                    "vim_message": None,
+                    "refresh_at": None,
+                },
+            }
+
+            task_index = "task_index_3"
+            self.target_vim.delete_affinity_group.return_value = (
+                "sample_affinity_group_id_3"
+            )
+            result = instance.delete(ro_task, task_index)
+            self.assertEqual(result[0], "DONE")
+            self.assertEqual(result[1].get("vim_message"), "DELETED")
+            self.assertEqual(result[1].get("created"), False)
+            self.assertEqual(result[1].get("vim_status"), "DELETED")
+
+    def test__delete_affinity_group_failed(self):
+        """
+        delete affinity group with missing vim_id
+        """
+        db = "test_db"
+        logger = "test_logger"
+        my_vims = "test-vim"
+        db_vims = {
+            0: {
+                "config": {},
+            },
+        }
+
+        instance = VimInteractionAffinityGroup(db, logger, my_vims, db_vims)
+        with patch.object(instance, "my_vims", [self.target_vim]), patch.object(
+            instance, "logger", logging
+        ), patch.object(instance, "db_vims", db_vims):
+            ro_task = {
+                "target_id": 0,
+                "tasks": {
+                    "task_index_4": {
+                        "target_id": 0,
+                        "task_id": "123456:1",
+                    },
+                },
+                "vim_info": {
+                    "created": False,
+                    "created_items": None,
+                    "vim_id": None,
+                    "vim_name": None,
+                    "vim_status": None,
+                    "vim_details": "some-details",
+                    "vim_message": None,
+                    "refresh_at": None,
+                },
+            }
+
+            task_index = "task_index_4"
+            self.target_vim.delete_affinity_group.return_value = ""
+            result = instance.delete(ro_task, task_index)
+            self.assertEqual(result[0], "DONE")
+            self.assertEqual(result[1].get("vim_message"), "DELETED")
+            self.assertEqual(result[1].get("created"), False)
+            self.assertEqual(result[1].get("vim_status"), "DELETED")
+
+
+class TestVimInteractionResize(unittest.TestCase):
+    def setUp(self):
+        module_name = "osm_ro_plugin"
+        self.target_vim = MagicMock(name=f"{module_name}.vimconn.VimConnector")
+        self.task_depends = None
+
+        patches = [patch(f"{module_name}.vimconn.VimConnector", self.target_vim)]
+
+        # Enabling mocks and add cleanups
+        for mock in patches:
+            mock.start()
+            self.addCleanup(mock.stop)
+
+    def test__exec_resize_done(self):
+        """
+        create verticalscale task
+        """
+        db = "test_db"
+        logger = "test_logger"
+        my_vims = "test-vim"
+        db_vims = {
+            0: {
+                "config": {},
+            },
+        }
+        target_record_id = (
+            "vnfrs:665b4165-ce24-4320-bf19-b9a45bade49f:"
+            "vdur.bb9c43f9-10a2-4569-a8a8-957c3528b6d1"
+        )
+
+        instance = VimInteractionResize(db, logger, my_vims, db_vims)
+        with patch.object(instance, "my_vims", [self.target_vim]), patch.object(
+            instance, "logger", logging
+        ), patch.object(instance, "db_vims", db_vims):
+            ro_task = {
+                "target_id": 0,
+                "tasks": {
+                    "task_index_1": {
+                        "target_id": 0,
+                        "action_id": "bb937f49-3870-4169-b758-9732e1ff40f3",
+                        "nsr_id": "993166fe-723e-4680-ac4b-b1af2541ae31",
+                        "task_id": "bb937f49-3870-4169-b758-9732e1ff40f3:0",
+                        "status": "SCHEDULED",
+                        "action": "EXEC",
+                        "item": "verticalscale",
+                        "target_record": "vnfrs:665b4165-ce24-4320-bf19-b9a45bade49f:vdur.0",
+                        "target_record_id": target_record_id,
+                        "params": {
+                            "vim_vm_id": "f37b18ef-3caa-4dc9-ab91-15c669b16396",
+                            "flavor_dict": "flavor_dict",
+                        },
+                    }
+                },
+            }
+            task_index = "task_index_1"
+            result = instance.exec(ro_task, task_index, self.task_depends)
+            self.assertEqual(result[0], "DONE")
+            self.assertEqual(result[1].get("vim_status"), "DONE")
+
+
+class TestVimInteractionMigration(unittest.TestCase):
+    def setUp(self):
+        module_name = "osm_ro_plugin"
+        self.target_vim = MagicMock(name=f"{module_name}.vimconn.VimConnector")
+        self.task_depends = None
+
+        patches = [patch(f"{module_name}.vimconn.VimConnector", self.target_vim)]
+
+        # Enabling mocks and add cleanups
+        for mock in patches:
+            mock.start()
+            self.addCleanup(mock.stop)
+
+    def test__exec_migration_done(self):
+        """
+        create migrate task
+        """
+        db = "test_db"
+        logger = "test_logger"
+        my_vims = "test-vim"
+        db_vims = {
+            0: {
+                "config": {},
+            },
+        }
+        target_record_id = (
+            "vnfrs:665b4165-ce24-4320-bf19-b9a45bade49f:"
+            "vdur.bb9c43f9-10a2-4569-a8a8-957c3528b6d1"
+        )
+
+        instance = VimInteractionMigration(db, logger, my_vims, db_vims)
+        with patch.object(instance, "my_vims", [self.target_vim]), patch.object(
+            instance, "logger", logging
+        ), patch.object(instance, "db_vims", db_vims):
+            ro_task = {
+                "target_id": 0,
+                "tasks": {
+                    "task_index_1": {
+                        "target_id": 0,
+                        "action_id": "bb937f49-3870-4169-b758-9732e1ff40f3",
+                        "nsr_id": "993166fe-723e-4680-ac4b-b1af2541ae31",
+                        "task_id": "bb937f49-3870-4169-b758-9732e1ff40f3:0",
+                        "status": "SCHEDULED",
+                        "action": "EXEC",
+                        "item": "migrate",
+                        "target_record": "vnfrs:665b4165-ce24-4320-bf19-b9a45bade49f:vdur.0",
+                        "target_record_id": target_record_id,
+                        "params": {
+                            "vim_vm_id": "f37b18ef-3caa-4dc9-ab91-15c669b16396",
+                            "migrate_host": "osm-test2",
+                            "vdu_vim_info": {0: {"interfaces": []}},
+                        },
+                    }
+                },
+            }
+            self.target_vim.migrate_instance.return_value = "ACTIVE", "test"
+
+            task_index = "task_index_1"
+            result = instance.exec(ro_task, task_index, self.task_depends)
+            self.assertEqual(result[0], "DONE")
+            self.assertEqual(result[1].get("vim_status"), "ACTIVE")