Feature 10960 Performance optimizations for the polling of VM status in RO
[osm/RO.git] / NG-RO / osm_ng_ro / tests / test_ns_thread.py
1 #######################################################################################
2 # Copyright ETSI Contributors and Others.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 # implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #######################################################################################
17 import logging
18 import unittest
19 from unittest.mock import MagicMock, Mock, patch
20
21 from osm_common.dbmemory import DbMemory
22 from osm_ng_ro.ns_thread import (
23 ConfigValidate,
24 NsWorker,
25 VimInteractionAffinityGroup,
26 )
27
28 # Variables used in tests
29 db_vims_openstack = {
30 "my_target_vim": {"vim_type": "openstack"},
31 }
32 db_vims_aws = {
33 "my_target_vim": {"vim_type": "aws"},
34 }
35
36
37 class TestConfigValidate(unittest.TestCase):
38 def setUp(self):
39 self.config_dict = {
40 "period": {
41 "refresh_active": 65,
42 "refresh_build": 20,
43 "refresh_image": 3600,
44 "refresh_error": 300,
45 "queue_size": 50,
46 }
47 }
48
49 def test_get_configuration(self):
50 with self.subTest(i=1, t="Get config attributes with config input"):
51 configuration = ConfigValidate(self.config_dict)
52 self.assertEqual(configuration.active, 65)
53 self.assertEqual(configuration.build, 20)
54 self.assertEqual(configuration.image, 3600)
55 self.assertEqual(configuration.error, 300)
56 self.assertEqual(configuration.queue_size, 50)
57
58 with self.subTest(i=2, t="Unallowed refresh active input"):
59 # > 60 (except -1) is not allowed to set, so it should return default value 60
60 self.config_dict["period"]["refresh_active"] = 20
61 configuration = ConfigValidate(self.config_dict)
62 self.assertEqual(configuration.active, 60)
63
64 with self.subTest(i=3, t="Config to disable VM status periodic checks"):
65 # -1 is allowed to set to disable VM status updates
66 self.config_dict["period"]["refresh_active"] = -1
67 configuration = ConfigValidate(self.config_dict)
68 self.assertEqual(configuration.active, -1)
69
70
71 class TestNsWorker(unittest.TestCase):
72 @patch("logging.getLogger", autospec=True)
73 def setUp(self, mock_logger):
74 mock_logger = logging.getLogger()
75 mock_logger.disabled = True
76 self.task_depends = None
77 self.plugins = {}
78 self.db_vims = db_vims_openstack
79 self.db = Mock(DbMemory())
80 self.worker_index = "worker-3"
81 self.config = {
82 "period": {
83 "refresh_active": 60,
84 "refresh_build": 20,
85 "refresh_image": 3600,
86 "refresh_error": 600,
87 "queue_size": 100,
88 },
89 "process_id": "343435353",
90 "global": {"task_locked_time": 16373242100.994312},
91 }
92
93 self.ro_task = {
94 "_id": "122436:1",
95 "locked_by": None,
96 "locked_at": 0.0,
97 "target_id": "my_target_vim",
98 "vim_info": {
99 "created": False,
100 "created_items": None,
101 "vim_id": "test-vim-id",
102 "vim_name": "test-vim",
103 "vim_status": "DONE",
104 "vim_details": "",
105 "vim_message": None,
106 "refresh_at": None,
107 },
108 "modified_at": 1637324200.994312,
109 "created_at": 1637324200.994312,
110 "to_check_at": 16373242400.994312,
111 "tasks": [
112 {
113 "target_id": 0,
114 "action_id": "123456",
115 "nsr_id": "654321",
116 "task_id": "123456:1",
117 "status": "DONE",
118 "action": "CREATE",
119 "item": "test_item",
120 "target_record": "test_target_record",
121 "target_record_id": "test_target_record_id",
122 },
123 ],
124 }
125 self.instance = NsWorker(self.worker_index, self.config, self.plugins, self.db)
126 self.instance.db_vims = db_vims_openstack
127 self.instance.refresh_config = Mock()
128
129 def get_disabled_tasks(self, db, status):
130 db_disabled_tasks = db.get_list(
131 "ro_tasks",
132 q_filter={
133 "tasks.status": status,
134 "to_check_at.lt": 0,
135 },
136 )
137 return db_disabled_tasks
138
139 def test_update_vm_refresh_disabled_task_with_status_build_vim_openstack_with_refresh(
140 self,
141 ):
142 """1 disabled task with status BUILD in DB, refresh_active parameter is not equal to -1."""
143 # Disabled task with status build is not enabled again
144 db = DbMemory()
145 self.ro_task["tasks"][0]["status"] = "BUILD"
146 self.config["period"]["refresh_active"] = 70
147 self.ro_task["to_check_at"] = -1
148 db.create("ro_tasks", self.ro_task)
149 disabled_tasks_count = len(self.get_disabled_tasks(db, "BUILD"))
150 instance = NsWorker(self.worker_index, self.config, self.plugins, db)
151 instance.update_vm_refresh(self.ro_task)
152 self.assertEqual(
153 len(self.get_disabled_tasks(db, "BUILD")), disabled_tasks_count
154 )
155
156 def test_update_vm_refresh_disabled_task_with_status_done_vim_openstack_no_refresh(
157 self,
158 ):
159 """1 disabled task with status DONE in DB, refresh_active parameter is equal to -1."""
160 # As refresh_active parameter is equal to -1, task is not be enabled to process again
161 db = DbMemory()
162 self.config["period"]["refresh_active"] = -1
163 self.ro_task["tasks"][0]["status"] = "DONE"
164 self.ro_task["to_check_at"] = -1
165 db.create("ro_tasks", self.ro_task)
166 disabled_tasks_count = len(self.get_disabled_tasks(db, "DONE"))
167 instance = NsWorker(self.worker_index, self.config, self.plugins, db)
168 instance.update_vm_refresh(self.ro_task)
169 self.assertEqual(len(self.get_disabled_tasks(db, "DONE")), disabled_tasks_count)
170
171 def test_update_vm_refresh_disabled_task_with_status_done_vim_aws_with_refresh(
172 self,
173 ):
174 """2 disabled task with status DONE in DB, refresh_active parameter is not equal to -1."""
175 # Disabled tasks should be enabled to process again as vim type aws
176 db = DbMemory()
177 self.config["period"]["refresh_active"] = 66
178 self.ro_task["tasks"][0]["status"] = "DONE"
179 self.ro_task["to_check_at"] = -1
180 db.create("ro_tasks", self.ro_task)
181 self.ro_task2 = self.ro_task
182 self.ro_task2["_id"] = "122437:1"
183 db.create("ro_tasks", self.ro_task2)
184 disabled_tasks_count = len(self.get_disabled_tasks(db, "DONE"))
185 instance = NsWorker(self.worker_index, self.config, self.plugins, db)
186 with patch.object(instance, "db_vims", db_vims_aws):
187 instance.update_vm_refresh(self.ro_task)
188 self.assertEqual(
189 len(self.get_disabled_tasks(db, "DONE")), disabled_tasks_count - 2
190 )
191
192 def test_update_vm_refresh_no_disabled_task_with_status_done_vim_openstack_with_refresh(
193 self,
194 ):
195 """No disabled task with status DONE in DB, refresh_active parameter is not equal to -1."""
196 # There is not any disabled task, method does not change anything
197 db = DbMemory()
198 self.config["period"]["refresh_active"] = 66
199 self.ro_task["tasks"][0]["status"] = "DONE"
200 self.ro_task["to_check_at"] = 16373242400.994312
201 db.create("ro_tasks", self.ro_task)
202 self.ro_task2 = self.ro_task
203 self.ro_task2["_id"] = "122437:1"
204 db.create("ro_tasks", self.ro_task2)
205 disabled_tasks_count = len(self.get_disabled_tasks(db, "DONE"))
206 instance = NsWorker(self.worker_index, self.config, self.plugins, db)
207 instance.update_vm_refresh(self.ro_task)
208 self.assertEqual(len(self.get_disabled_tasks(db, "DONE")), disabled_tasks_count)
209
210 def test_update_vm_refresh_disabled_task_with_status_done_vim_openstack_with_refresh(
211 self,
212 ):
213 """1 disabled task with status DONE in DB, refresh_active parameter is equal to -1, vim type is Openstack."""
214 # Disabled task with status done is not enabled again as vim type is openstack
215 db = DbMemory()
216 self.ro_task["tasks"][0]["status"] = "DONE"
217 self.ro_task["to_check_at"] = -1
218 db.create("ro_tasks", self.ro_task)
219 disabled_tasks_count = len(self.get_disabled_tasks(db, "DONE"))
220 instance = NsWorker(self.worker_index, self.config, self.plugins, db)
221 instance.update_vm_refresh(self.ro_task)
222 self.assertEqual(len(self.get_disabled_tasks(db, "DONE")), disabled_tasks_count)
223
224 def test_process_pending_tasks_status_done_vim_aws_no_refresh(self):
225 """Refresh_active parameter is equal to -1, task status is DONE."""
226 # Task should be disabled to process again
227 db = DbMemory()
228 self.config["period"]["refresh_active"] = -1
229 self.ro_task["tasks"][0]["status"] = "DONE"
230 self.ro_task["to_check_at"] = 16373242400.994312
231 db.create("ro_tasks", self.ro_task)
232 # Number of disabled tasks in DB
233 disabled_tasks_count = len(self.get_disabled_tasks(db, "DONE"))
234 instance = NsWorker(self.worker_index, self.config, self.plugins, db)
235 with patch.object(instance, "db_vims", db_vims_aws):
236 instance._process_pending_tasks(self.ro_task)
237 self.assertEqual(
238 len(self.get_disabled_tasks(db, "DONE")), disabled_tasks_count + 1
239 )
240
241 def test_process_pending_tasks_status_failed_vim_aws_no_refresh(self):
242 """Refresh_active parameter is equal to -1, task status is FAILED."""
243 # Task is not disabled to process as task status is not DONE
244 db = DbMemory()
245 self.config["period"]["refresh_active"] = -1
246 self.ro_task["tasks"][0]["status"] = "FAILED"
247 self.ro_task["to_check_at"] = 16373242400.994312
248 db.create("ro_tasks", self.ro_task)
249 disabled_tasks_count = len(self.get_disabled_tasks(db, "FAILED"))
250 instance = NsWorker(self.worker_index, self.config, self.plugins, db)
251 with patch.object(instance, "db_vims", db_vims_aws):
252 instance._process_pending_tasks(self.ro_task)
253 self.assertEqual(
254 len(self.get_disabled_tasks(db, "FAILED")), disabled_tasks_count
255 )
256
257 def test_process_pending_tasks_status_done_vim_aws_with_refresh(self):
258 """Refresh_active parameter is not equal to -1, task status is DONE."""
259 # Task is not disabled to process as refresh_active parameter is not -1
260 db = DbMemory()
261 self.config["period"]["refresh_active"] = 70
262 self.ro_task["tasks"][0]["status"] = "DONE"
263 self.ro_task["to_check_at"] = 16373242400.994312
264 db.create("ro_tasks", self.ro_task)
265 disabled_tasks_count = len(self.get_disabled_tasks(db, "DONE"))
266 instance = NsWorker(self.worker_index, self.config, self.plugins, db)
267 with patch.object(instance, "db_vims", db_vims_aws):
268 instance._process_pending_tasks(self.ro_task)
269 self.assertEqual(
270 len(self.get_disabled_tasks(db, "DONE")), disabled_tasks_count
271 )
272
273
274 class TestVimInteractionAffinityGroup(unittest.TestCase):
275 def setUp(self):
276 module_name = "osm_ro_plugin"
277 self.target_vim = MagicMock(name=f"{module_name}.vimconn.VimConnector")
278 self.task_depends = None
279
280 patches = [patch(f"{module_name}.vimconn.VimConnector", self.target_vim)]
281
282 # Enabling mocks and add cleanups
283 for mock in patches:
284 mock.start()
285 self.addCleanup(mock.stop)
286
287 def test__new_affinity_group_ok(self):
288 """
289 create affinity group with attributes set in params
290 """
291 db = "test_db"
292 logger = "test_logger"
293 my_vims = "test-vim"
294 db_vims = {
295 0: {
296 "config": {},
297 },
298 }
299
300 instance = VimInteractionAffinityGroup(db, logger, my_vims, db_vims)
301 with patch.object(instance, "my_vims", [self.target_vim]), patch.object(
302 instance, "logger", logging
303 ), patch.object(instance, "db_vims", db_vims):
304 ro_task = {
305 "target_id": 0,
306 "tasks": {
307 "task_index_1": {
308 "target_id": 0,
309 "action_id": "123456",
310 "nsr_id": "654321",
311 "task_id": "123456:1",
312 "status": "SCHEDULED",
313 "action": "CREATE",
314 "item": "test_item",
315 "target_record": "test_target_record",
316 "target_record_id": "test_target_record_id",
317 # values coming from extra_dict
318 "params": {
319 "affinity_group_data": {
320 "name": "affinity_group_1",
321 "type": "affinity",
322 "scope": "nfvi-node",
323 }
324 },
325 "find_params": {},
326 "depends_on": "test_depends_on",
327 },
328 },
329 }
330
331 task_index = "task_index_1"
332 self.target_vim.new_affinity_group.return_value = (
333 "sample_affinity_group_id_1"
334 )
335 result = instance.new(ro_task, task_index, self.task_depends)
336 self.assertEqual(result[0], "DONE")
337 self.assertEqual(result[1].get("vim_id"), "sample_affinity_group_id_1")
338 self.assertEqual(result[1].get("created"), True)
339 self.assertEqual(result[1].get("vim_status"), "DONE")
340
341 def test__new_affinity_group_failed(self):
342 """
343 create affinity group with no attributes set in params
344 """
345 db = "test_db"
346 logger = "test_logger"
347 my_vims = "test-vim"
348 db_vims = {
349 0: {
350 "config": {},
351 },
352 }
353
354 instance = VimInteractionAffinityGroup(db, logger, my_vims, db_vims)
355 with patch.object(instance, "my_vims", [self.target_vim]), patch.object(
356 instance, "logger", logging
357 ), patch.object(instance, "db_vims", db_vims):
358 ro_task = {
359 "target_id": 0,
360 "tasks": {
361 "task_index_2": {
362 "target_id": 0,
363 "action_id": "123456",
364 "nsr_id": "654321",
365 "task_id": "123456:1",
366 "status": "SCHEDULED",
367 "action": "CREATE",
368 "item": "test_item",
369 "target_record": "test_target_record",
370 "target_record_id": "test_target_record_id",
371 # values coming from extra_dict
372 "params": {},
373 "find_params": {},
374 "depends_on": "test_depends_on",
375 },
376 },
377 }
378
379 task_index = "task_index_2"
380 self.target_vim.new_affinity_group.return_value = (
381 "sample_affinity_group_id_1"
382 )
383 result = instance.new(ro_task, task_index, self.task_depends)
384 self.assertEqual(result[0], "DONE")
385 self.assertEqual(result[1].get("vim_id"), None)
386 self.assertEqual(result[1].get("created"), False)
387 self.assertEqual(result[1].get("vim_status"), "DONE")
388
389 def test__delete_affinity_group_ok(self):
390 """
391 delete affinity group with a proper vim_id
392 """
393 db = "test_db"
394 logger = "test_logger"
395 my_vims = "test-vim"
396 db_vims = {
397 0: {
398 "config": {},
399 },
400 }
401
402 instance = VimInteractionAffinityGroup(db, logger, my_vims, db_vims)
403 with patch.object(instance, "my_vims", [self.target_vim]), patch.object(
404 instance, "logger", logging
405 ), patch.object(instance, "db_vims", db_vims):
406 ro_task = {
407 "target_id": 0,
408 "tasks": {
409 "task_index_3": {
410 "target_id": 0,
411 "task_id": "123456:1",
412 },
413 },
414 "vim_info": {
415 "created": False,
416 "created_items": None,
417 "vim_id": "sample_affinity_group_id_3",
418 "vim_name": "sample_affinity_group_id_3",
419 "vim_status": None,
420 "vim_details": "some-details",
421 "refresh_at": None,
422 },
423 }
424
425 task_index = "task_index_3"
426 self.target_vim.delete_affinity_group.return_value = (
427 "sample_affinity_group_id_3"
428 )
429 result = instance.delete(ro_task, task_index)
430 self.assertEqual(result[0], "DONE")
431 self.assertEqual(result[1].get("vim_details"), "DELETED")
432 self.assertEqual(result[1].get("created"), False)
433 self.assertEqual(result[1].get("vim_status"), "DELETED")
434
435 def test__delete_affinity_group_failed(self):
436 """
437 delete affinity group with missing vim_id
438 """
439 db = "test_db"
440 logger = "test_logger"
441 my_vims = "test-vim"
442 db_vims = {
443 0: {
444 "config": {},
445 },
446 }
447
448 instance = VimInteractionAffinityGroup(db, logger, my_vims, db_vims)
449 with patch.object(instance, "my_vims", [self.target_vim]), patch.object(
450 instance, "logger", logging
451 ), patch.object(instance, "db_vims", db_vims):
452 ro_task = {
453 "target_id": 0,
454 "tasks": {
455 "task_index_4": {
456 "target_id": 0,
457 "task_id": "123456:1",
458 },
459 },
460 "vim_info": {
461 "created": False,
462 "created_items": None,
463 "vim_id": None,
464 "vim_name": None,
465 "vim_status": None,
466 "vim_details": "some-details",
467 "refresh_at": None,
468 },
469 }
470
471 task_index = "task_index_4"
472 self.target_vim.delete_affinity_group.return_value = ""
473 result = instance.delete(ro_task, task_index)
474 self.assertEqual(result[0], "DONE")
475 self.assertEqual(result[1].get("vim_details"), "DELETED")
476 self.assertEqual(result[1].get("created"), False)
477 self.assertEqual(result[1].get("vim_status"), "DELETED")