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