blob: d58ee03eb95361f212995a22b69492b5a57b1752 [file] [log] [blame]
magnussonl2b0e2d72020-02-04 10:52:46 +01001# Copyright 2020 ArctosLabs Scandinavia AB
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12# implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
beierlm6e06e752021-02-16 16:52:29 -050015
16# pylint: disable=E1120
17
magnussonl2b0e2d72020-02-04 10:52:46 +010018import asyncio
magnussonl31181aa2020-11-25 09:04:51 +010019import copy
magnussonl2b0e2d72020-02-04 10:52:46 +010020import os
21import sys
magnussonl2b0e2d72020-02-04 10:52:46 +010022from unittest import TestCase, mock
23from unittest.mock import Mock
24
magnussonl2b0e2d72020-02-04 10:52:46 +010025import yaml
26
27from osm_pla.placement.mznplacement import NsPlacementDataFactory, MznPlacementConductor
28from pathlib import Path
29
30# need to Mock the imports from osm_common made in Server and Config beforehand
garciadeblas20fc3b72022-11-14 00:48:32 +010031sys.modules["osm_common"] = Mock()
magnussonl2b0e2d72020-02-04 10:52:46 +010032from osm_pla.server.server import Server # noqa: E402
33from osm_pla.config.config import Config # noqa: E402
34
garciadeblas20fc3b72022-11-14 00:48:32 +010035nslcmop_record_wo_pinning = {
36 "statusEnteredTime": 1574625718.8280587,
37 "startTime": 1574625718.8280587,
38 "_admin": {
39 "created": 1574625718.8286533,
40 "projects_write": ["61e4bbab-9659-4abc-a01d-ba3a307becf9"],
41 "worker": "e5121e773e8b",
42 "modified": 1574625718.8286533,
43 "projects_read": ["61e4bbab-9659-4abc-a01d-ba3a307becf9"],
44 },
45 "operationState": "PROCESSING",
46 "nsInstanceId": "45f588bd-5bf4-4181-b13b-f16a55a23be4",
47 "lcmOperationType": "instantiate",
48 "isCancelPending": False,
49 "id": "a571b1de-19e5-48bd-b252-ba0ad7d540c9",
50 "_id": "a571b1de-19e5-48bd-b252-ba0ad7d540c9",
51 "isAutomaticInvocation": False,
52 "links": {
53 "nsInstance": "/osm/nslcm/v1/ns_instances/45f588bd-5bf4-4181-b13b-f16a55a23be4",
54 "self": "/osm/nslcm/v1/ns_lcm_op_occs/a571b1de-19e5-48bd-b252-ba0ad7d540c9",
55 },
56 "operationParams": {
57 "vimAccountId": "eb553051-5b6c-4ad6-939b-2ad23bd82e57",
58 "lcmOperationType": "instantiate",
59 "nsDescription": "just a test",
60 "nsdId": "0f4e658f-62a6-4f73-8623-270e8f0a18bc",
61 "nsName": "ThreeNsd plain placement",
62 "ssh_keys": [],
63 "validVimAccounts": [
64 "eb553051-5b6c-4ad6-939b-2ad23bd82e57",
65 "576bbe0a-b95d-4ced-a63e-f387f8e6e2ce",
66 "3d1ffc5d-b36d-4f69-8356-7f59c740ca2f",
67 "db54dcd4-9fc4-441c-8820-17bce0aef2c3",
68 ],
69 "nsr_id": "45f588bd-5bf4-4181-b13b-f16a55a23be4",
70 "placement-engine": "PLA",
71 "nsInstanceId": "45f588bd-5bf4-4181-b13b-f16a55a23be4",
72 },
73}
magnussonl2b0e2d72020-02-04 10:52:46 +010074
garciadeblas20fc3b72022-11-14 00:48:32 +010075nslcmop_record_w_pinning = {
76 "statusEnteredTime": 1574627411.420499,
77 "startTime": 1574627411.420499,
78 "_admin": {
79 "created": 1574627411.4209971,
80 "projects_write": ["61e4bbab-9659-4abc-a01d-ba3a307becf9"],
81 "worker": "e5121e773e8b",
82 "modified": 1574627411.4209971,
83 "projects_read": ["61e4bbab-9659-4abc-a01d-ba3a307becf9"],
84 },
85 "operationState": "PROCESSING",
86 "nsInstanceId": "61587478-ea25-44eb-9f13-7005046ddb08",
87 "lcmOperationType": "instantiate",
88 "isCancelPending": False,
89 "id": "80f95a17-6fa7-408d-930f-40aa4589d074",
90 "_id": "80f95a17-6fa7-408d-930f-40aa4589d074",
91 "isAutomaticInvocation": False,
92 "links": {
93 "nsInstance": "/osm/nslcm/v1/ns_instances/61587478-ea25-44eb-9f13-7005046ddb08",
94 "self": "/osm/nslcm/v1/ns_lcm_op_occs/80f95a17-6fa7-408d-930f-40aa4589d074",
95 },
96 "operationParams": {
97 "vimAccountId": "576bbe0a-b95d-4ced-a63e-f387f8e6e2ce",
98 "nsr_id": "61587478-ea25-44eb-9f13-7005046ddb08",
99 "nsDescription": "default description",
100 "nsdId": "0f4e658f-62a6-4f73-8623-270e8f0a18bc",
101 "validVimAccounts": [
102 "eb553051-5b6c-4ad6-939b-2ad23bd82e57",
103 "576bbe0a-b95d-4ced-a63e-f387f8e6e2ce",
104 "3d1ffc5d-b36d-4f69-8356-7f59c740ca2f",
105 "db54dcd4-9fc4-441c-8820-17bce0aef2c3",
106 ],
107 "nsName": "ThreeVnfTest2",
108 "wimAccountId": False,
109 "vnf": [
110 {
111 "vimAccountId": "3d1ffc5d-b36d-4f69-8356-7f59c740ca2f",
112 "member-vnf-index": "1",
113 }
114 ],
115 "placementEngine": "PLA",
116 "nsInstanceId": "61587478-ea25-44eb-9f13-7005046ddb08",
117 "lcmOperationType": "instantiate",
118 },
119}
magnussonl2b0e2d72020-02-04 10:52:46 +0100120
121nslcmop_record_w_pinning_and_order_constraints = {
garciadeblas20fc3b72022-11-14 00:48:32 +0100122 "links": {
123 "nsInstance": "/osm/nslcm/v1/ns_instances/7c4c3d94-ebb2-44e8-b236-d876b118434e",
124 "self": "/osm/nslcm/v1/ns_lcm_op_occs/fd7c9e15-38aa-4fc5-913c-417b26859fb0",
125 },
126 "id": "fd7c9e15-38aa-4fc5-913c-417b26859fb0",
127 "operationState": "PROCESSING",
128 "isAutomaticInvocation": False,
129 "nsInstanceId": "7c4c3d94-ebb2-44e8-b236-d876b118434e",
130 "_id": "fd7c9e15-38aa-4fc5-913c-417b26859fb0",
131 "isCancelPending": False,
132 "startTime": 1574772631.6932728,
133 "statusEnteredTime": 1574772631.6932728,
134 "lcmOperationType": "instantiate",
135 "operationParams": {
136 "placementEngine": "PLA",
137 "placement-constraints": {
138 "vld-constraints": [
139 {
140 "id": "three_vnf_constrained_vld_1",
141 "link-constraints": {"latency": 120, "jitter": 20},
142 },
143 {
144 "link_constraints": {"latency": 120, "jitter": 20},
145 "id": "three_vnf_constrained_nsd_vld_2",
146 },
147 ]
148 },
149 "nsName": "ThreeVnfTest2",
150 "nsDescription": "default description",
151 "nsr_id": "7c4c3d94-ebb2-44e8-b236-d876b118434e",
152 "nsdId": "0f4e658f-62a6-4f73-8623-270e8f0a18bc",
153 "validVimAccounts": [
154 "eb553051-5b6c-4ad6-939b-2ad23bd82e57",
155 "576bbe0a-b95d-4ced-a63e-f387f8e6e2ce",
156 "3d1ffc5d-b36d-4f69-8356-7f59c740ca2f",
157 "db54dcd4-9fc4-441c-8820-17bce0aef2c3",
158 ],
159 "wimAccountId": False,
160 "vnf": [
161 {
162 "member-vnf-index": "3",
163 "vimAccountId": "3d1ffc5d-b36d-4f69-8356-7f59c740ca2f",
164 }
165 ],
166 "nsInstanceId": "7c4c3d94-ebb2-44e8-b236-d876b118434e",
167 "lcmOperationType": "instantiate",
168 "vimAccountId": "576bbe0a-b95d-4ced-a63e-f387f8e6e2ce",
169 },
170 "_admin": {
171 "projects_read": ["61e4bbab-9659-4abc-a01d-ba3a307becf9"],
172 "modified": 1574772631.693885,
173 "projects_write": ["61e4bbab-9659-4abc-a01d-ba3a307becf9"],
174 "created": 1574772631.693885,
175 "worker": "e5121e773e8b",
176 },
177}
magnussonl2b0e2d72020-02-04 10:52:46 +0100178
garciadeblas20fc3b72022-11-14 00:48:32 +0100179list_of_vims = [
180 {
181 "_id": "73cd1a1b-333e-4e29-8db2-00d23bd9b644",
182 "vim_user": "admin",
183 "name": "OpenStack1",
184 "vim_url": "http://10.234.12.47:5000/v3",
185 "vim_type": "openstack",
186 "vim_tenant_name": "osm_demo",
187 "vim_password": "O/mHomfXPmCrTvUbYXVoyg==",
188 "schema_version": "1.1",
189 "_admin": {
190 "modified": 1565597984.3155663,
191 "deployed": {
192 "RO": "f0c1b516-bcd9-11e9-bb73-02420aff0030",
193 "RO-account": "f0d45496-bcd9-11e9-bb73-02420aff0030",
194 },
195 "projects_write": ["admin"],
196 "operationalState": "ENABLED",
197 "detailed-status": "Done",
198 "created": 1565597984.3155663,
199 "projects_read": ["admin"],
200 },
201 "config": {},
202 },
203 {
204 "_id": "684165ea-2cf9-4fbd-ac22-8464ca07d1d8",
205 "vim_user": "admin",
206 "name": "OpenStack2",
207 "vim_url": "http://10.234.12.44:5000/v3",
208 "vim_tenant_name": "osm_demo",
209 "vim_password": "Rw7gln9liP4ClMyHd5OFsw==",
210 "description": "Openstack on NUC",
211 "vim_type": "openstack",
212 "admin": {
213 "modified": 1566474766.7288046,
214 "deployed": {
215 "RO": "5bc59656-c4d3-11e9-b1e5-02420aff0006",
216 "RO-account": "5bd772e0-c4d3-11e9-b1e5-02420aff0006",
217 },
218 "projects_write": ["admin"],
219 "operationalState": "ENABLED",
220 "detailed-status": "Done",
221 "created": 1566474766.7288046,
222 "projects_read": ["admin"],
223 },
224 "config": {},
225 "schema_version": "1.1",
226 },
227 {
228 "_id": "8460b670-31cf-4fae-9f3e-d0dd6c57b61e",
229 "vim_user": "admin",
230 "name": "OpenStack1",
231 "vim_url": "http://10.234.12.47:5000/v3",
232 "vim_type": "openstack",
233 "vim_tenant_name": "osm_demo",
234 "vim_password": "NsgJJDlCdKreX30FQFNz7A==",
235 "description": "Openstack on Dell",
236 "_admin": {
237 "modified": 1566992449.5942867,
238 "deployed": {
239 "RO": "aed94f86-c988-11e9-bb38-02420aff0088",
240 "RO-account": "aee72fac-c988-11e9-bb38-02420aff0088",
241 },
242 "projects_write": ["0a5d0c5b-7e08-48a1-a686-642a038bbd70"],
243 "operationalState": "ENABLED",
244 "detailed-status": "Done",
245 "created": 1566992449.5942867,
246 "projects_read": ["0a5d0c5b-7e08-48a1-a686-642a038bbd70"],
247 },
248 "config": {},
249 "schema_version": "1.1",
250 },
251 {
252 "_id": "9b8b5268-acb7-4893-b494-a77656b418f2",
253 "vim_user": "admin",
254 "name": "OpenStack2",
255 "vim_url": "http://10.234.12.44:5000/v3",
256 "vim_type": "openstack",
257 "vim_tenant_name": "osm_demo",
258 "vim_password": "AnAV3xtoiwwdnAfv0KahSw==",
259 "description": "Openstack on NUC",
260 "_admin": {
261 "modified": 1566992484.9190753,
262 "deployed": {
263 "RO": "c3d61158-c988-11e9-bb38-02420aff0088",
264 "RO-account": "c3ec973e-c988-11e9-bb38-02420aff0088",
265 },
266 "projects_write": ["0a5d0c5b-7e08-48a1-a686-642a038bbd70"],
267 "operationalState": "ENABLED",
268 "detailed-status": "Done",
269 "created": 1566992484.9190753,
270 "projects_read": ["0a5d0c5b-7e08-48a1-a686-642a038bbd70"],
271 },
272 "config": {},
273 "schema_version": "1.1",
274 },
275 {
276 "_id": "3645f215-f32d-4355-b5ab-df0a2e2233c3",
277 "vim_user": "admin",
278 "name": "OpenStack3",
279 "vim_url": "http://10.234.12.46:5000/v3",
280 "vim_tenant_name": "osm_demo",
281 "vim_password": "XkG2w8e8/DiuohCFNp0+lQ==",
282 "description": "Openstack on NUC",
283 "vim_type": "openstack",
284 "_admin": {
285 "modified": 1567421247.7016313,
286 "deployed": {
287 "RO": "0e80f6a2-cd6f-11e9-bb50-02420aff00b6",
288 "RO-account": "0e974524-cd6f-11e9-bb50-02420aff00b6",
289 },
290 "projects_write": ["0a5d0c5b-7e08-48a1-a686-642a038bbd70"],
291 "operationalState": "ENABLED",
292 "detailed-status": "Done",
293 "created": 1567421247.7016313,
294 "projects_read": ["0a5d0c5b-7e08-48a1-a686-642a038bbd70"],
295 },
296 "schema_version": "1.1",
297 "config": {},
298 },
299 {
300 "_id": "53f8f2bb-88b5-4bf9-babf-556698b5261f",
301 "vim_user": "admin",
302 "name": "OpenStack4",
303 "vim_url": "http://10.234.12.43:5000/v3",
304 "vim_tenant_name": "osm_demo",
305 "vim_password": "GLrgVn8fMVneXMZq1r4yVA==",
306 "description": "Openstack on NUC",
307 "vim_type": "openstack",
308 "_admin": {
309 "modified": 1567421296.1576457,
310 "deployed": {
311 "RO": "2b43c756-cd6f-11e9-bb50-02420aff00b6",
312 "RO-account": "2b535aea-cd6f-11e9-bb50-02420aff00b6",
313 },
314 "projects_write": ["0a5d0c5b-7e08-48a1-a686-642a038bbd70"],
315 "operationalState": "ENABLED",
316 "detailed-status": "Done",
317 "created": 1567421296.1576457,
318 "projects_read": ["0a5d0c5b-7e08-48a1-a686-642a038bbd70"],
319 },
320 "schema_version": "1.1",
321 "config": {},
322 },
323]
magnussonl2b0e2d72020-02-04 10:52:46 +0100324
325# FIXME this is not correct re mgmt-network setting.
garciadeblas20fc3b72022-11-14 00:48:32 +0100326nsd_from_db = {
327 "_id": "15fc1941-f095-4cd8-af2d-1000bd6d9eaa",
328 "_admin": {
329 "modified": 1567672251.7531693,
330 "storage": {
331 "pkg-dir": "ns_constrained_nsd",
332 "fs": "local",
333 "descriptor": "ns_constrained_nsd/ns_constrained_nsd.yaml",
334 "zipfile": "package.tar.gz",
335 "folder": "15fc1941-f095-4cd8-af2d-1000bd6d9eaa",
336 "path": "/app/storage/",
337 },
338 "onboardingState": "ONBOARDED",
339 "usageState": "NOT_IN_USE",
340 "projects_write": ["0a5d0c5b-7e08-48a1-a686-642a038bbd70"],
341 "operationalState": "ENABLED",
342 "userDefinedData": {},
343 "created": 1567672251.7531693,
344 "projects_read": ["0a5d0c5b-7e08-48a1-a686-642a038bbd70"],
345 },
346 "id": "three_vnf_constrained_nsd_low",
347 "name": "three_vnf_constrained_nsd_low",
348 "description": "Placement constraints NSD",
349 "designer": "ArctosLabs",
350 "version": "1.0",
351 "vnfd-id": ["cirros_vnfd_v2"],
352 "df": [
353 {
354 "id": "default-df",
355 "vnf-profile": [
356 {
357 "id": "one",
358 "vnfd-id": "cirros_vnfd_v2",
359 "virtual-link-connectivity": [
360 {
361 "virtual-link-profile-id": "three_vnf_constrained_nsd_low_vld1",
362 "constituent-cpd-id": [
363 {
364 "constituent-base-element-id": "one",
365 "constituent-cpd-id": "vnf-cp0-ext",
366 }
367 ],
368 }
369 ],
370 },
371 {
372 "id": "two",
373 "vnfd-id": "cirros_vnfd_v2",
374 "virtual-link-connectivity": [
375 {
376 "virtual-link-profile-id": "three_vnf_constrained_nsd_low_vld1",
377 "constituent-cpd-id": [
378 {
379 "constituent-base-element-id": "two",
380 "constituent-cpd-id": "vnf-cp0-ext",
381 }
382 ],
383 },
384 {
385 "virtual-link-profile-id": "three_vnf_constrained_nsd_low_vld2",
386 "constituent-cpd-id": [
387 {
388 "constituent-base-element-id": "two",
389 "constituent-cpd-id": "vnf-cp0-ext",
390 }
391 ],
392 },
393 ],
394 },
395 {
396 "id": "three",
397 "vnfd-id": "cirros_vnfd_v2",
398 "virtual-link-connectivity": [
399 {
400 "virtual-link-profile-id": "three_vnf_constrained_nsd_low_vld2",
401 "constituent-cpd-id": [
402 {
403 "constituent-base-element-id": "three",
404 "constituent-cpd-id": "vnf-cp0-ext",
405 }
406 ],
407 }
408 ],
409 },
410 ],
411 }
412 ],
413 "virtual-link-desc": [
414 {
415 "id": "three_vnf_constrained_nsd_low_vld1",
416 "mgmt-network": True,
417 "vim-network-name": "external",
418 },
419 {
420 "id": "three_vnf_constrained_nsd_low_vld2",
421 "mgmt-network": True,
422 "vim-network-name": "lanretxe",
423 },
424 ],
425}
magnussonl2b0e2d72020-02-04 10:52:46 +0100426
427
428######################################################
429# These are helper functions to handle unittest of asyncio.
430# Inspired by: https://blog.miguelgrinberg.com/post/unit-testing-asyncio-code
431def _run(co_routine):
432 return asyncio.get_event_loop().run_until_complete(co_routine)
433
434
435def _async_mock(*args, **kwargs):
436 m = mock.MagicMock(*args, **kwargs)
437
438 async def mock_coro(*args, **kwargs):
439 return m(*args, **kwargs)
440
441 mock_coro.mock = m
442 return mock_coro
443
444
445######################################################
446
magnussonl2b0e2d72020-02-04 10:52:46 +0100447
garciadeblas20fc3b72022-11-14 00:48:32 +0100448class TestServer(TestCase):
magnussonl2b0e2d72020-02-04 10:52:46 +0100449 def _produce_ut_vim_accounts_info(self, list_of_vims):
450 """
451 FIXME temporary, we will need more control over vim_urls and _id for test purpose - make a generator
452 :return: vim_url and _id as dict, i.e. extract these from vim_accounts data
453 """
garciadeblas20fc3b72022-11-14 00:48:32 +0100454 return {_["name"]: _["_id"] for _ in list_of_vims}
magnussonl2b0e2d72020-02-04 10:52:46 +0100455
456 def _produce_ut_vnf_price_list(self):
457 price_list_file = "vnf_price_list.yaml"
458 with open(str(Path(price_list_file))) as pl_fd:
459 price_list_data = yaml.safe_load_all(pl_fd)
garciadeblas20fc3b72022-11-14 00:48:32 +0100460 return {
461 i["vnfd"]: {i1["vim_name"]: i1["price"] for i1 in i["prices"]}
462 for i in next(price_list_data)
463 }
magnussonl2b0e2d72020-02-04 10:52:46 +0100464
465 def _populate_pil_info(self, file):
466 """
467 FIXME we need more control over content in pil information - more files or generator and data
468 Note str(Path()) is a 3.5 thing
469 """
470 with open(str(Path(file))) as pp_fd:
471 test_data = yaml.safe_load_all(pp_fd)
472 return next(test_data)
473
garciadeblas20fc3b72022-11-14 00:48:32 +0100474 @mock.patch.object(Config, "_read_config_file")
475 @mock.patch.object(
476 Config,
477 "get",
478 side_effect=["doesnotmatter", "memory", "memory", "local", "doesnotmatter"],
479 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100480 def serverSetup(self, mock_get, mock__read_config_file):
481 """
482 Helper that returns a Server object
483 :return:
484 """
485 cfg = Config(None)
486 return Server(cfg)
487
488 def _adjust_path(self, file):
489 """In case we are not running from test directory,
Mark Beierl8b700832023-02-09 15:36:13 -0500490 then assume we are in top level directory (e.g. running from tox) and adjust file path accordingly
491 """
garciadeblas20fc3b72022-11-14 00:48:32 +0100492 path_component = "/osm_pla/test/"
magnussonl2b0e2d72020-02-04 10:52:46 +0100493 real_path = os.path.realpath(file)
494 if path_component not in real_path:
garciadeblas20fc3b72022-11-14 00:48:32 +0100495 return (
496 os.path.dirname(real_path)
497 + path_component
498 + os.path.basename(real_path)
499 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100500 else:
501 return real_path
502
503 def test__get_nslcmop(self):
504 server = self.serverSetup()
505 server.db = Mock()
506 _ = server._get_nslcmop(nslcmop_record_wo_pinning["id"])
garciadeblas20fc3b72022-11-14 00:48:32 +0100507 server.db.get_one.assert_called_with(
508 "nslcmops", {"_id": nslcmop_record_wo_pinning["id"]}
509 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100510
511 def test__get_nsd(self): # OK
512 server = self.serverSetup()
513 server.db = Mock()
garciadeblas20fc3b72022-11-14 00:48:32 +0100514 _ = server._get_nsd(nslcmop_record_wo_pinning["operationParams"]["nsdId"])
515 server.db.get_one.assert_called_with(
516 "nsds", {"_id": nslcmop_record_wo_pinning["operationParams"]["nsdId"]}
517 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100518
magnussonl31181aa2020-11-25 09:04:51 +0100519 def test__create_vnf_id_maps(self):
520 server = self.serverSetup()
521 server.db = Mock()
garciadeblas20fc3b72022-11-14 00:48:32 +0100522 expected_mvi2mzn = {"one": "VNF0", "two": "VNF1", "three": "VNF2"}
523 expected_mzn2mvi = {"VNF0": "one", "VNF1": "two", "VNF2": "three"}
magnussonl31181aa2020-11-25 09:04:51 +0100524
525 nsd_for_test = copy.deepcopy(nsd_from_db)
526 mvi2mzn, mzn2mvi = server._create_vnf_id_maps(nsd_for_test)
527
garciadeblas20fc3b72022-11-14 00:48:32 +0100528 self.assertDictEqual(
529 expected_mvi2mzn, mvi2mzn, "Faulty mzn2member-vnf-index mapping"
530 )
531 self.assertDictEqual(
532 expected_mzn2mvi, mzn2mvi, "Faulty mzn2member-vnf-index mapping"
533 )
magnussonl31181aa2020-11-25 09:04:51 +0100534
magnussonl2b0e2d72020-02-04 10:52:46 +0100535 def test__get_vim_accounts(self): # OK
536 server = self.serverSetup()
537 server.db = Mock()
garciadeblas20fc3b72022-11-14 00:48:32 +0100538 _ = server._get_vim_accounts(
539 nslcmop_record_wo_pinning["operationParams"]["validVimAccounts"]
540 )
541 server.db.get_list.assert_called_with(
542 "vim_accounts",
543 {"_id": nslcmop_record_wo_pinning["operationParams"]["validVimAccounts"]},
544 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100545
546 def test__get_vnf_price_list(self):
547 server = self.serverSetup()
garciadeblas20fc3b72022-11-14 00:48:32 +0100548 pl1 = server._get_vnf_price_list(
549 Path(self._adjust_path("./vnf_price_list.yaml"))
550 )
magnussonld8c1b392020-06-30 16:48:08 +0200551 self.assertIs(type(pl1), dict, "price list not a dictionary")
552 for k, v in pl1.items():
magnussonl2b0e2d72020-02-04 10:52:46 +0100553 self.assertIs(type(v), dict, "price list values not a dict")
554
garciadeblas20fc3b72022-11-14 00:48:32 +0100555 pl2 = server._get_vnf_price_list(
556 Path(self._adjust_path("./vnf_price_list_keys.yaml")), "hackfest_project_a"
557 )
magnussonld8c1b392020-06-30 16:48:08 +0200558 self.assertIs(type(pl2), dict, "price list not a dictionary")
559 for k, v in pl2.items():
560 self.assertIs(type(v), dict, "price list values not a dict")
561 self.assertEqual(pl1, pl2, "non-project and project price lists differ")
562
magnussonl2b0e2d72020-02-04 10:52:46 +0100563 def test__get_pil_info(self):
564 server = self.serverSetup()
garciadeblas20fc3b72022-11-14 00:48:32 +0100565 ppi = server._get_pil_info(Path(self._adjust_path("./pil_price_list.yaml")))
magnussonl2b0e2d72020-02-04 10:52:46 +0100566 self.assertIs(type(ppi), dict, "pil is not a dict")
garciadeblas20fc3b72022-11-14 00:48:32 +0100567 self.assertIn("pil", ppi.keys(), "pil has no pil key")
568 self.assertIs(type(ppi["pil"]), list, "pil does not contain a list")
magnussonl2b0e2d72020-02-04 10:52:46 +0100569 # check for expected keys
garciadeblas20fc3b72022-11-14 00:48:32 +0100570 expected_keys = {
571 "pil_description",
572 "pil_price",
573 "pil_latency",
574 "pil_jitter",
575 "pil_endpoints",
576 }
577 self.assertEqual(expected_keys, ppi["pil"][0].keys(), "expected keys not found")
magnussonl2b0e2d72020-02-04 10:52:46 +0100578
Dario Faccin0d0e80f2023-05-24 16:58:47 +0200579 # def test_handle_kafka_command(self): # OK
580 # server = self.serverSetup()
581 # server.loop.create_task = Mock()
582 # server.handle_kafka_command("pli", "get_placement", {})
583 # server.loop.create_task.assert_not_called()
584 # server.loop.create_task.reset_mock()
585 # server.handle_kafka_command(
586 # "pla", "get_placement", {"nslcmopId": nslcmop_record_wo_pinning["id"]}
587 # )
588 # self.assertTrue(server.loop.create_task.called, "create_task not called")
589 # args, kwargs = server.loop.create_task.call_args
590 # self.assertIn("Server.get_placement", str(args[0]), "get_placement not called")
magnussonl2b0e2d72020-02-04 10:52:46 +0100591
garciadeblas20fc3b72022-11-14 00:48:32 +0100592 @mock.patch.object(
593 NsPlacementDataFactory, "__init__", lambda x0, x1, x2, x3, x4, x5, x6: None
594 )
595 @mock.patch.object(MznPlacementConductor, "do_placement_computation")
596 @mock.patch.object(NsPlacementDataFactory, "create_ns_placement_data")
597 @mock.patch.object(Server, "_get_vim_accounts")
598 @mock.patch.object(Server, "_get_nsd")
599 @mock.patch.object(Server, "_get_nslcmop")
600 @mock.patch.object(Server, "_get_vnf_price_list")
601 @mock.patch.object(Server, "_get_pil_info")
602 @mock.patch.object(Server, "_get_projects")
603 def test_get_placement(
604 self,
605 mock_get_projects,
606 mock_get_pil_info,
607 mock_get_vnf_price_list,
608 mock__get_nslcmop,
609 mock__get_nsd,
610 mock__get_vim_accounts,
611 mock_create_ns_placement_data,
612 mock_do_placement_computation,
613 ):
magnussonl2b0e2d72020-02-04 10:52:46 +0100614 """
615 run _get_placement and check that things get called as expected
616 :return:
617 """
garciadeblas20fc3b72022-11-14 00:48:32 +0100618 placement_ret_val = [
619 {
620 "vimAccountId": "bbbbbbbb-38f5-438d-b8ee-3f93b3531f87",
621 "member-vnf-index": "VNF0",
622 },
623 {
624 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
625 "member-vnf-index": "VNF1",
626 },
627 {
628 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
629 "member-vnf-index": "VNF2",
630 },
631 ]
magnussonl2b0e2d72020-02-04 10:52:46 +0100632 server = self.serverSetup()
633
634 server.msgBus.aiowrite = _async_mock()
magnussonl31181aa2020-11-25 09:04:51 +0100635 nsd_for_test = copy.deepcopy(nsd_from_db)
636 mock__get_nsd.return_value = nsd_for_test
magnussonl2b0e2d72020-02-04 10:52:46 +0100637 mock__get_vim_accounts.return_value = list_of_vims
638
639 # FIXME need update to match nslcmop, not for test but for consistency
640 mock_do_placement_computation.return_value = placement_ret_val
garciadeblas20fc3b72022-11-14 00:48:32 +0100641 _run(server.get_placement(nslcmop_record_wo_pinning["id"]))
magnussonl2b0e2d72020-02-04 10:52:46 +0100642
garciadeblas20fc3b72022-11-14 00:48:32 +0100643 self.assertTrue(
644 mock_get_projects.called, "_get_projects not called as expected"
645 )
646 self.assertTrue(
647 mock_get_vnf_price_list.called, "_get_vnf_price_list not called as expected"
648 )
649 self.assertTrue(
650 mock_get_pil_info.called, "_get_pil_info not called as expected"
651 )
652 self.assertTrue(mock__get_nslcmop.called, "_get_nslcmop not called as expected")
magnussonl2b0e2d72020-02-04 10:52:46 +0100653 # mock_get_nsd.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100654 self.assertTrue(mock__get_nsd.called, "get_nsd not called as expected")
magnussonl2b0e2d72020-02-04 10:52:46 +0100655 # mock_get_enabled_vims.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100656 self.assertTrue(
657 mock__get_vim_accounts.called, "get_vim_accounts not called as expected"
658 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100659 # mock_create_ns_placement_data.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100660 self.assertTrue(
661 mock_create_ns_placement_data.called,
662 "create_ns_placement_data not called as expected",
663 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100664 # mock_do_placement_computation.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100665 self.assertTrue(
666 mock_do_placement_computation.called,
667 "do_placement_computation not called as expected",
668 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100669 self.assertTrue(server.msgBus.aiowrite.mock.called)
670
671 args, kwargs = server.msgBus.aiowrite.mock.call_args
garciadeblas20fc3b72022-11-14 00:48:32 +0100672 self.assertTrue(len(args) == 3, "invalid format")
673 self.assertEqual("pla", args[0], "topic invalid")
674 self.assertEqual("placement", args[1], "message invalid")
magnussonl2b0e2d72020-02-04 10:52:46 +0100675 # extract placement result and check content
676 rsp_payload = args[2]
677
garciadeblas20fc3b72022-11-14 00:48:32 +0100678 expected_rsp_keys = {"placement"}
679 self.assertEqual(
680 expected_rsp_keys,
681 set(rsp_payload.keys()),
682 "placement response missing keys",
683 )
684 self.assertIs(type(rsp_payload["placement"]), dict, "placement not a dict")
magnussonl2b0e2d72020-02-04 10:52:46 +0100685
garciadeblas20fc3b72022-11-14 00:48:32 +0100686 expected_placement_keys = {"vnf", "nslcmopId"}
687 self.assertEqual(
688 expected_placement_keys,
689 set(rsp_payload["placement"]),
690 "placement keys invalid",
691 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100692
garciadeblas20fc3b72022-11-14 00:48:32 +0100693 vim_account_candidates = [e["vimAccountId"] for e in placement_ret_val]
magnussonl2b0e2d72020-02-04 10:52:46 +0100694
garciadeblas20fc3b72022-11-14 00:48:32 +0100695 self.assertEqual(
696 nslcmop_record_wo_pinning["id"],
697 rsp_payload["placement"]["nslcmopId"],
698 "nslcmopId invalid",
699 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100700
garciadeblas20fc3b72022-11-14 00:48:32 +0100701 self.assertIs(type(rsp_payload["placement"]["vnf"]), list, "vnf not a list")
702 expected_vnf_keys = {"vimAccountId", "member-vnf-index"}
703 self.assertEqual(
704 expected_vnf_keys,
705 set(rsp_payload["placement"]["vnf"][0]),
706 "placement['vnf'] missing keys",
707 )
708 self.assertIn(
709 rsp_payload["placement"]["vnf"][0]["vimAccountId"],
710 vim_account_candidates,
711 "vimAccountId invalid",
712 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100713
garciadeblas20fc3b72022-11-14 00:48:32 +0100714 @mock.patch.object(
715 NsPlacementDataFactory, "__init__", lambda x0, x1, x2, x3, x4, x5, x6: None
716 )
717 @mock.patch.object(MznPlacementConductor, "do_placement_computation")
718 @mock.patch.object(NsPlacementDataFactory, "create_ns_placement_data")
719 @mock.patch.object(Server, "_get_vim_accounts")
720 @mock.patch.object(Server, "_get_nsd")
721 @mock.patch.object(Server, "_get_nslcmop")
722 @mock.patch.object(Server, "_get_vnf_price_list")
723 @mock.patch.object(Server, "_get_pil_info")
724 @mock.patch.object(Server, "_get_projects")
725 def test_get_placement_with_pinning(
726 self,
727 mock_get_projects,
728 mock_get_pil_info,
729 mock_get_vnf_price_list,
730 mock__get_nslcmop,
731 mock__get_nsd,
732 mock__get_vim_accounts,
733 mock_create_ns_placement_data,
734 mock_do_placement_computation,
735 ):
magnussonl2b0e2d72020-02-04 10:52:46 +0100736 """
737 run _get_placement and check that things get called as expected
738 :return:
739 """
garciadeblas20fc3b72022-11-14 00:48:32 +0100740 placement_ret_val = [
741 {
742 "vimAccountId": "bbbbbbbb-38f5-438d-b8ee-3f93b3531f87",
743 "member-vnf-index": "VNF0",
744 },
745 {
746 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
747 "member-vnf-index": "VNF1",
748 },
749 {
750 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
751 "member-vnf-index": "VNF2",
752 },
753 ]
magnussonl2b0e2d72020-02-04 10:52:46 +0100754 server = self.serverSetup()
755
756 server.msgBus.aiowrite = _async_mock()
magnussonl31181aa2020-11-25 09:04:51 +0100757 nsd_for_test = copy.deepcopy(nsd_from_db)
758 mock__get_nsd.return_value = nsd_for_test
magnussonl2b0e2d72020-02-04 10:52:46 +0100759 mock__get_vim_accounts.return_value = list_of_vims
760
761 # FIXME need update to match nslcmop, not for test but for consistency
762 mock_do_placement_computation.return_value = placement_ret_val
garciadeblas20fc3b72022-11-14 00:48:32 +0100763 _run(server.get_placement(nslcmop_record_w_pinning["id"]))
magnussonl2b0e2d72020-02-04 10:52:46 +0100764
garciadeblas20fc3b72022-11-14 00:48:32 +0100765 self.assertTrue(
766 (mock_get_projects.called, "_get_projects not called as expected")
767 )
768 self.assertTrue(
769 mock_get_vnf_price_list.called, "_get_vnf_price_list not called as expected"
770 )
771 self.assertTrue(
772 mock_get_pil_info.called, "_get_pil_info not called as expected"
773 )
774 self.assertTrue(mock__get_nslcmop.called, "_get_nslcmop not called as expected")
magnussonl2b0e2d72020-02-04 10:52:46 +0100775 # mock_get_nsd.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100776 self.assertTrue(mock__get_nsd.called, "get_nsd not called as expected")
magnussonl2b0e2d72020-02-04 10:52:46 +0100777 # mock_get_enabled_vims.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100778 self.assertTrue(
779 mock__get_vim_accounts.called, "get_vim_accounts not called as expected"
780 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100781 # mock_create_ns_placement_data.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100782 self.assertTrue(
783 mock_create_ns_placement_data.called,
784 "create_ns_placement_data not called as expected",
785 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100786 # mock_do_placement_computation.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100787 self.assertTrue(
788 mock_do_placement_computation.called,
789 "do_placement_computation not called as expected",
790 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100791 self.assertTrue(server.msgBus.aiowrite.mock.called)
792
793 args, kwargs = server.msgBus.aiowrite.mock.call_args
garciadeblas20fc3b72022-11-14 00:48:32 +0100794 self.assertTrue(len(args) == 3, "invalid format")
795 self.assertEqual("pla", args[0], "topic invalid")
796 self.assertEqual("placement", args[1], "message invalid")
magnussonl2b0e2d72020-02-04 10:52:46 +0100797 # extract placement result and check content
798 rsp_payload = args[2]
799
garciadeblas20fc3b72022-11-14 00:48:32 +0100800 expected_rsp_keys = {"placement"}
801 self.assertEqual(
802 expected_rsp_keys,
803 set(rsp_payload.keys()),
804 "placement response missing keys",
805 )
806 self.assertIs(type(rsp_payload["placement"]), dict, "placement not a dict")
magnussonl2b0e2d72020-02-04 10:52:46 +0100807
garciadeblas20fc3b72022-11-14 00:48:32 +0100808 expected_placement_keys = {"vnf", "nslcmopId"}
809 self.assertEqual(
810 expected_placement_keys,
811 set(rsp_payload["placement"]),
812 "placement keys invalid",
813 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100814
garciadeblas20fc3b72022-11-14 00:48:32 +0100815 vim_account_candidates = [e["vimAccountId"] for e in placement_ret_val]
magnussonl2b0e2d72020-02-04 10:52:46 +0100816
garciadeblas20fc3b72022-11-14 00:48:32 +0100817 self.assertEqual(
818 nslcmop_record_w_pinning["id"],
819 rsp_payload["placement"]["nslcmopId"],
820 "nslcmopId invalid",
821 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100822
garciadeblas20fc3b72022-11-14 00:48:32 +0100823 self.assertIs(type(rsp_payload["placement"]["vnf"]), list, "vnf not a list")
824 expected_vnf_keys = {"vimAccountId", "member-vnf-index"}
825 self.assertEqual(
826 expected_vnf_keys,
827 set(rsp_payload["placement"]["vnf"][0]),
828 "placement['vnf'] missing keys",
829 )
830 self.assertIn(
831 rsp_payload["placement"]["vnf"][0]["vimAccountId"],
832 vim_account_candidates,
833 "vimAccountId invalid",
834 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100835
836 # Note: does not mock reading of price list and pil_info
garciadeblas20fc3b72022-11-14 00:48:32 +0100837 @mock.patch.object(
838 NsPlacementDataFactory, "__init__", lambda x0, x1, x2, x3, x4, x5: None
839 )
840 @mock.patch.object(MznPlacementConductor, "do_placement_computation")
841 @mock.patch.object(NsPlacementDataFactory, "create_ns_placement_data")
842 @mock.patch.object(Server, "_get_vim_accounts")
843 @mock.patch.object(Server, "_get_nsd")
844 @mock.patch.object(Server, "_get_nslcmop")
845 def test_get_placement_w_exception(
846 self,
847 mock__get_nslcmop,
848 mock__get_nsd,
849 mock__get_vim_accounts,
850 mock_create_ns_placement_data,
851 mock_do_placement_computation,
852 ):
magnussonl2b0e2d72020-02-04 10:52:46 +0100853 """
854 check that raised exceptions are handled and response provided accordingly
855 """
856 server = self.serverSetup()
857
858 server.msgBus.aiowrite = _async_mock()
magnussonl31181aa2020-11-25 09:04:51 +0100859 nsd_for_test = copy.deepcopy(nsd_from_db)
860 mock__get_nsd.return_value = nsd_for_test
garciadeblas20fc3b72022-11-14 00:48:32 +0100861 mock__get_nsd.side_effect = RuntimeError("kaboom!")
magnussonl2b0e2d72020-02-04 10:52:46 +0100862 mock__get_vim_accounts.return_value = list_of_vims
garciadeblas20fc3b72022-11-14 00:48:32 +0100863 mock_do_placement_computation.return_value = [
864 {
865 "vimAccountId": "bbbbbbbb-38f5-438d-b8ee-3f93b3531f87",
866 "member-vnf-index": "1",
867 },
868 {
869 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
870 "member-vnf-index": "2",
871 },
872 {
873 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
874 "member-vnf-index": "3",
875 },
876 ]
magnussonl2b0e2d72020-02-04 10:52:46 +0100877
garciadeblas20fc3b72022-11-14 00:48:32 +0100878 _run(server.get_placement(nslcmop_record_w_pinning["id"]))
magnussonl2b0e2d72020-02-04 10:52:46 +0100879 self.assertTrue(server.msgBus.aiowrite.mock.called)
880 args, kwargs = server.msgBus.aiowrite.mock.call_args
881 rsp_payload = args[2]
garciadeblas20fc3b72022-11-14 00:48:32 +0100882 expected_keys = {"placement"}
883 self.assertEqual(
884 expected_keys, set(rsp_payload.keys()), "placement response missing keys"
885 )
886 self.assertIs(type(rsp_payload["placement"]["vnf"]), list, "vnf not a list")
887 self.assertEqual([], rsp_payload["placement"]["vnf"], "vnf list not empty")
888 self.assertEqual(
889 nslcmop_record_w_pinning["id"],
890 rsp_payload["placement"]["nslcmopId"],
891 "nslcmopId invalid",
892 )