blob: 3ba29798ec1dd2d39fc3c244c43e2ca97b7d7bcf [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,
490 then assume we are in top level directory (e.g. running from tox) and adjust file path accordingly"""
garciadeblas20fc3b72022-11-14 00:48:32 +0100491 path_component = "/osm_pla/test/"
magnussonl2b0e2d72020-02-04 10:52:46 +0100492 real_path = os.path.realpath(file)
493 if path_component not in real_path:
garciadeblas20fc3b72022-11-14 00:48:32 +0100494 return (
495 os.path.dirname(real_path)
496 + path_component
497 + os.path.basename(real_path)
498 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100499 else:
500 return real_path
501
502 def test__get_nslcmop(self):
503 server = self.serverSetup()
504 server.db = Mock()
505 _ = server._get_nslcmop(nslcmop_record_wo_pinning["id"])
garciadeblas20fc3b72022-11-14 00:48:32 +0100506 server.db.get_one.assert_called_with(
507 "nslcmops", {"_id": nslcmop_record_wo_pinning["id"]}
508 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100509
510 def test__get_nsd(self): # OK
511 server = self.serverSetup()
512 server.db = Mock()
garciadeblas20fc3b72022-11-14 00:48:32 +0100513 _ = server._get_nsd(nslcmop_record_wo_pinning["operationParams"]["nsdId"])
514 server.db.get_one.assert_called_with(
515 "nsds", {"_id": nslcmop_record_wo_pinning["operationParams"]["nsdId"]}
516 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100517
magnussonl31181aa2020-11-25 09:04:51 +0100518 def test__create_vnf_id_maps(self):
519 server = self.serverSetup()
520 server.db = Mock()
garciadeblas20fc3b72022-11-14 00:48:32 +0100521 expected_mvi2mzn = {"one": "VNF0", "two": "VNF1", "three": "VNF2"}
522 expected_mzn2mvi = {"VNF0": "one", "VNF1": "two", "VNF2": "three"}
magnussonl31181aa2020-11-25 09:04:51 +0100523
524 nsd_for_test = copy.deepcopy(nsd_from_db)
525 mvi2mzn, mzn2mvi = server._create_vnf_id_maps(nsd_for_test)
526
garciadeblas20fc3b72022-11-14 00:48:32 +0100527 self.assertDictEqual(
528 expected_mvi2mzn, mvi2mzn, "Faulty mzn2member-vnf-index mapping"
529 )
530 self.assertDictEqual(
531 expected_mzn2mvi, mzn2mvi, "Faulty mzn2member-vnf-index mapping"
532 )
magnussonl31181aa2020-11-25 09:04:51 +0100533
magnussonl2b0e2d72020-02-04 10:52:46 +0100534 def test__get_vim_accounts(self): # OK
535 server = self.serverSetup()
536 server.db = Mock()
garciadeblas20fc3b72022-11-14 00:48:32 +0100537 _ = server._get_vim_accounts(
538 nslcmop_record_wo_pinning["operationParams"]["validVimAccounts"]
539 )
540 server.db.get_list.assert_called_with(
541 "vim_accounts",
542 {"_id": nslcmop_record_wo_pinning["operationParams"]["validVimAccounts"]},
543 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100544
545 def test__get_vnf_price_list(self):
546 server = self.serverSetup()
garciadeblas20fc3b72022-11-14 00:48:32 +0100547 pl1 = server._get_vnf_price_list(
548 Path(self._adjust_path("./vnf_price_list.yaml"))
549 )
magnussonld8c1b392020-06-30 16:48:08 +0200550 self.assertIs(type(pl1), dict, "price list not a dictionary")
551 for k, v in pl1.items():
magnussonl2b0e2d72020-02-04 10:52:46 +0100552 self.assertIs(type(v), dict, "price list values not a dict")
553
garciadeblas20fc3b72022-11-14 00:48:32 +0100554 pl2 = server._get_vnf_price_list(
555 Path(self._adjust_path("./vnf_price_list_keys.yaml")), "hackfest_project_a"
556 )
magnussonld8c1b392020-06-30 16:48:08 +0200557 self.assertIs(type(pl2), dict, "price list not a dictionary")
558 for k, v in pl2.items():
559 self.assertIs(type(v), dict, "price list values not a dict")
560 self.assertEqual(pl1, pl2, "non-project and project price lists differ")
561
magnussonl2b0e2d72020-02-04 10:52:46 +0100562 def test__get_pil_info(self):
563 server = self.serverSetup()
garciadeblas20fc3b72022-11-14 00:48:32 +0100564 ppi = server._get_pil_info(Path(self._adjust_path("./pil_price_list.yaml")))
magnussonl2b0e2d72020-02-04 10:52:46 +0100565 self.assertIs(type(ppi), dict, "pil is not a dict")
garciadeblas20fc3b72022-11-14 00:48:32 +0100566 self.assertIn("pil", ppi.keys(), "pil has no pil key")
567 self.assertIs(type(ppi["pil"]), list, "pil does not contain a list")
magnussonl2b0e2d72020-02-04 10:52:46 +0100568 # check for expected keys
garciadeblas20fc3b72022-11-14 00:48:32 +0100569 expected_keys = {
570 "pil_description",
571 "pil_price",
572 "pil_latency",
573 "pil_jitter",
574 "pil_endpoints",
575 }
576 self.assertEqual(expected_keys, ppi["pil"][0].keys(), "expected keys not found")
magnussonl2b0e2d72020-02-04 10:52:46 +0100577
578 def test_handle_kafka_command(self): # OK
579 server = self.serverSetup()
580 server.loop.create_task = Mock()
garciadeblas20fc3b72022-11-14 00:48:32 +0100581 server.handle_kafka_command("pli", "get_placement", {})
magnussonl2b0e2d72020-02-04 10:52:46 +0100582 server.loop.create_task.assert_not_called()
583 server.loop.create_task.reset_mock()
garciadeblas20fc3b72022-11-14 00:48:32 +0100584 server.handle_kafka_command(
585 "pla", "get_placement", {"nslcmopId": nslcmop_record_wo_pinning["id"]}
586 )
587 self.assertTrue(server.loop.create_task.called, "create_task not called")
magnussonl2b0e2d72020-02-04 10:52:46 +0100588 args, kwargs = server.loop.create_task.call_args
garciadeblas20fc3b72022-11-14 00:48:32 +0100589 self.assertIn("Server.get_placement", str(args[0]), "get_placement not called")
magnussonl2b0e2d72020-02-04 10:52:46 +0100590
garciadeblas20fc3b72022-11-14 00:48:32 +0100591 @mock.patch.object(
592 NsPlacementDataFactory, "__init__", lambda x0, x1, x2, x3, x4, x5, x6: None
593 )
594 @mock.patch.object(MznPlacementConductor, "do_placement_computation")
595 @mock.patch.object(NsPlacementDataFactory, "create_ns_placement_data")
596 @mock.patch.object(Server, "_get_vim_accounts")
597 @mock.patch.object(Server, "_get_nsd")
598 @mock.patch.object(Server, "_get_nslcmop")
599 @mock.patch.object(Server, "_get_vnf_price_list")
600 @mock.patch.object(Server, "_get_pil_info")
601 @mock.patch.object(Server, "_get_projects")
602 def test_get_placement(
603 self,
604 mock_get_projects,
605 mock_get_pil_info,
606 mock_get_vnf_price_list,
607 mock__get_nslcmop,
608 mock__get_nsd,
609 mock__get_vim_accounts,
610 mock_create_ns_placement_data,
611 mock_do_placement_computation,
612 ):
magnussonl2b0e2d72020-02-04 10:52:46 +0100613 """
614 run _get_placement and check that things get called as expected
615 :return:
616 """
garciadeblas20fc3b72022-11-14 00:48:32 +0100617 placement_ret_val = [
618 {
619 "vimAccountId": "bbbbbbbb-38f5-438d-b8ee-3f93b3531f87",
620 "member-vnf-index": "VNF0",
621 },
622 {
623 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
624 "member-vnf-index": "VNF1",
625 },
626 {
627 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
628 "member-vnf-index": "VNF2",
629 },
630 ]
magnussonl2b0e2d72020-02-04 10:52:46 +0100631 server = self.serverSetup()
632
633 server.msgBus.aiowrite = _async_mock()
magnussonl31181aa2020-11-25 09:04:51 +0100634 nsd_for_test = copy.deepcopy(nsd_from_db)
635 mock__get_nsd.return_value = nsd_for_test
magnussonl2b0e2d72020-02-04 10:52:46 +0100636 mock__get_vim_accounts.return_value = list_of_vims
637
638 # FIXME need update to match nslcmop, not for test but for consistency
639 mock_do_placement_computation.return_value = placement_ret_val
garciadeblas20fc3b72022-11-14 00:48:32 +0100640 _run(server.get_placement(nslcmop_record_wo_pinning["id"]))
magnussonl2b0e2d72020-02-04 10:52:46 +0100641
garciadeblas20fc3b72022-11-14 00:48:32 +0100642 self.assertTrue(
643 mock_get_projects.called, "_get_projects not called as expected"
644 )
645 self.assertTrue(
646 mock_get_vnf_price_list.called, "_get_vnf_price_list not called as expected"
647 )
648 self.assertTrue(
649 mock_get_pil_info.called, "_get_pil_info not called as expected"
650 )
651 self.assertTrue(mock__get_nslcmop.called, "_get_nslcmop not called as expected")
magnussonl2b0e2d72020-02-04 10:52:46 +0100652 # mock_get_nsd.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100653 self.assertTrue(mock__get_nsd.called, "get_nsd not called as expected")
magnussonl2b0e2d72020-02-04 10:52:46 +0100654 # mock_get_enabled_vims.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100655 self.assertTrue(
656 mock__get_vim_accounts.called, "get_vim_accounts not called as expected"
657 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100658 # mock_create_ns_placement_data.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100659 self.assertTrue(
660 mock_create_ns_placement_data.called,
661 "create_ns_placement_data not called as expected",
662 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100663 # mock_do_placement_computation.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100664 self.assertTrue(
665 mock_do_placement_computation.called,
666 "do_placement_computation not called as expected",
667 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100668 self.assertTrue(server.msgBus.aiowrite.mock.called)
669
670 args, kwargs = server.msgBus.aiowrite.mock.call_args
garciadeblas20fc3b72022-11-14 00:48:32 +0100671 self.assertTrue(len(args) == 3, "invalid format")
672 self.assertEqual("pla", args[0], "topic invalid")
673 self.assertEqual("placement", args[1], "message invalid")
magnussonl2b0e2d72020-02-04 10:52:46 +0100674 # extract placement result and check content
675 rsp_payload = args[2]
676
garciadeblas20fc3b72022-11-14 00:48:32 +0100677 expected_rsp_keys = {"placement"}
678 self.assertEqual(
679 expected_rsp_keys,
680 set(rsp_payload.keys()),
681 "placement response missing keys",
682 )
683 self.assertIs(type(rsp_payload["placement"]), dict, "placement not a dict")
magnussonl2b0e2d72020-02-04 10:52:46 +0100684
garciadeblas20fc3b72022-11-14 00:48:32 +0100685 expected_placement_keys = {"vnf", "nslcmopId"}
686 self.assertEqual(
687 expected_placement_keys,
688 set(rsp_payload["placement"]),
689 "placement keys invalid",
690 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100691
garciadeblas20fc3b72022-11-14 00:48:32 +0100692 vim_account_candidates = [e["vimAccountId"] for e in placement_ret_val]
magnussonl2b0e2d72020-02-04 10:52:46 +0100693
garciadeblas20fc3b72022-11-14 00:48:32 +0100694 self.assertEqual(
695 nslcmop_record_wo_pinning["id"],
696 rsp_payload["placement"]["nslcmopId"],
697 "nslcmopId invalid",
698 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100699
garciadeblas20fc3b72022-11-14 00:48:32 +0100700 self.assertIs(type(rsp_payload["placement"]["vnf"]), list, "vnf not a list")
701 expected_vnf_keys = {"vimAccountId", "member-vnf-index"}
702 self.assertEqual(
703 expected_vnf_keys,
704 set(rsp_payload["placement"]["vnf"][0]),
705 "placement['vnf'] missing keys",
706 )
707 self.assertIn(
708 rsp_payload["placement"]["vnf"][0]["vimAccountId"],
709 vim_account_candidates,
710 "vimAccountId invalid",
711 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100712
garciadeblas20fc3b72022-11-14 00:48:32 +0100713 @mock.patch.object(
714 NsPlacementDataFactory, "__init__", lambda x0, x1, x2, x3, x4, x5, x6: None
715 )
716 @mock.patch.object(MznPlacementConductor, "do_placement_computation")
717 @mock.patch.object(NsPlacementDataFactory, "create_ns_placement_data")
718 @mock.patch.object(Server, "_get_vim_accounts")
719 @mock.patch.object(Server, "_get_nsd")
720 @mock.patch.object(Server, "_get_nslcmop")
721 @mock.patch.object(Server, "_get_vnf_price_list")
722 @mock.patch.object(Server, "_get_pil_info")
723 @mock.patch.object(Server, "_get_projects")
724 def test_get_placement_with_pinning(
725 self,
726 mock_get_projects,
727 mock_get_pil_info,
728 mock_get_vnf_price_list,
729 mock__get_nslcmop,
730 mock__get_nsd,
731 mock__get_vim_accounts,
732 mock_create_ns_placement_data,
733 mock_do_placement_computation,
734 ):
magnussonl2b0e2d72020-02-04 10:52:46 +0100735 """
736 run _get_placement and check that things get called as expected
737 :return:
738 """
garciadeblas20fc3b72022-11-14 00:48:32 +0100739 placement_ret_val = [
740 {
741 "vimAccountId": "bbbbbbbb-38f5-438d-b8ee-3f93b3531f87",
742 "member-vnf-index": "VNF0",
743 },
744 {
745 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
746 "member-vnf-index": "VNF1",
747 },
748 {
749 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
750 "member-vnf-index": "VNF2",
751 },
752 ]
magnussonl2b0e2d72020-02-04 10:52:46 +0100753 server = self.serverSetup()
754
755 server.msgBus.aiowrite = _async_mock()
magnussonl31181aa2020-11-25 09:04:51 +0100756 nsd_for_test = copy.deepcopy(nsd_from_db)
757 mock__get_nsd.return_value = nsd_for_test
magnussonl2b0e2d72020-02-04 10:52:46 +0100758 mock__get_vim_accounts.return_value = list_of_vims
759
760 # FIXME need update to match nslcmop, not for test but for consistency
761 mock_do_placement_computation.return_value = placement_ret_val
garciadeblas20fc3b72022-11-14 00:48:32 +0100762 _run(server.get_placement(nslcmop_record_w_pinning["id"]))
magnussonl2b0e2d72020-02-04 10:52:46 +0100763
garciadeblas20fc3b72022-11-14 00:48:32 +0100764 self.assertTrue(
765 (mock_get_projects.called, "_get_projects not called as expected")
766 )
767 self.assertTrue(
768 mock_get_vnf_price_list.called, "_get_vnf_price_list not called as expected"
769 )
770 self.assertTrue(
771 mock_get_pil_info.called, "_get_pil_info not called as expected"
772 )
773 self.assertTrue(mock__get_nslcmop.called, "_get_nslcmop not called as expected")
magnussonl2b0e2d72020-02-04 10:52:46 +0100774 # mock_get_nsd.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100775 self.assertTrue(mock__get_nsd.called, "get_nsd not called as expected")
magnussonl2b0e2d72020-02-04 10:52:46 +0100776 # mock_get_enabled_vims.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100777 self.assertTrue(
778 mock__get_vim_accounts.called, "get_vim_accounts not called as expected"
779 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100780 # mock_create_ns_placement_data.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100781 self.assertTrue(
782 mock_create_ns_placement_data.called,
783 "create_ns_placement_data not called as expected",
784 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100785 # mock_do_placement_computation.assert_called_once() assert_called_once() for python > 3.5
garciadeblas20fc3b72022-11-14 00:48:32 +0100786 self.assertTrue(
787 mock_do_placement_computation.called,
788 "do_placement_computation not called as expected",
789 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100790 self.assertTrue(server.msgBus.aiowrite.mock.called)
791
792 args, kwargs = server.msgBus.aiowrite.mock.call_args
garciadeblas20fc3b72022-11-14 00:48:32 +0100793 self.assertTrue(len(args) == 3, "invalid format")
794 self.assertEqual("pla", args[0], "topic invalid")
795 self.assertEqual("placement", args[1], "message invalid")
magnussonl2b0e2d72020-02-04 10:52:46 +0100796 # extract placement result and check content
797 rsp_payload = args[2]
798
garciadeblas20fc3b72022-11-14 00:48:32 +0100799 expected_rsp_keys = {"placement"}
800 self.assertEqual(
801 expected_rsp_keys,
802 set(rsp_payload.keys()),
803 "placement response missing keys",
804 )
805 self.assertIs(type(rsp_payload["placement"]), dict, "placement not a dict")
magnussonl2b0e2d72020-02-04 10:52:46 +0100806
garciadeblas20fc3b72022-11-14 00:48:32 +0100807 expected_placement_keys = {"vnf", "nslcmopId"}
808 self.assertEqual(
809 expected_placement_keys,
810 set(rsp_payload["placement"]),
811 "placement keys invalid",
812 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100813
garciadeblas20fc3b72022-11-14 00:48:32 +0100814 vim_account_candidates = [e["vimAccountId"] for e in placement_ret_val]
magnussonl2b0e2d72020-02-04 10:52:46 +0100815
garciadeblas20fc3b72022-11-14 00:48:32 +0100816 self.assertEqual(
817 nslcmop_record_w_pinning["id"],
818 rsp_payload["placement"]["nslcmopId"],
819 "nslcmopId invalid",
820 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100821
garciadeblas20fc3b72022-11-14 00:48:32 +0100822 self.assertIs(type(rsp_payload["placement"]["vnf"]), list, "vnf not a list")
823 expected_vnf_keys = {"vimAccountId", "member-vnf-index"}
824 self.assertEqual(
825 expected_vnf_keys,
826 set(rsp_payload["placement"]["vnf"][0]),
827 "placement['vnf'] missing keys",
828 )
829 self.assertIn(
830 rsp_payload["placement"]["vnf"][0]["vimAccountId"],
831 vim_account_candidates,
832 "vimAccountId invalid",
833 )
magnussonl2b0e2d72020-02-04 10:52:46 +0100834
835 # Note: does not mock reading of price list and pil_info
garciadeblas20fc3b72022-11-14 00:48:32 +0100836 @mock.patch.object(
837 NsPlacementDataFactory, "__init__", lambda x0, x1, x2, x3, x4, x5: None
838 )
839 @mock.patch.object(MznPlacementConductor, "do_placement_computation")
840 @mock.patch.object(NsPlacementDataFactory, "create_ns_placement_data")
841 @mock.patch.object(Server, "_get_vim_accounts")
842 @mock.patch.object(Server, "_get_nsd")
843 @mock.patch.object(Server, "_get_nslcmop")
844 def test_get_placement_w_exception(
845 self,
846 mock__get_nslcmop,
847 mock__get_nsd,
848 mock__get_vim_accounts,
849 mock_create_ns_placement_data,
850 mock_do_placement_computation,
851 ):
magnussonl2b0e2d72020-02-04 10:52:46 +0100852 """
853 check that raised exceptions are handled and response provided accordingly
854 """
855 server = self.serverSetup()
856
857 server.msgBus.aiowrite = _async_mock()
magnussonl31181aa2020-11-25 09:04:51 +0100858 nsd_for_test = copy.deepcopy(nsd_from_db)
859 mock__get_nsd.return_value = nsd_for_test
garciadeblas20fc3b72022-11-14 00:48:32 +0100860 mock__get_nsd.side_effect = RuntimeError("kaboom!")
magnussonl2b0e2d72020-02-04 10:52:46 +0100861 mock__get_vim_accounts.return_value = list_of_vims
garciadeblas20fc3b72022-11-14 00:48:32 +0100862 mock_do_placement_computation.return_value = [
863 {
864 "vimAccountId": "bbbbbbbb-38f5-438d-b8ee-3f93b3531f87",
865 "member-vnf-index": "1",
866 },
867 {
868 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
869 "member-vnf-index": "2",
870 },
871 {
872 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
873 "member-vnf-index": "3",
874 },
875 ]
magnussonl2b0e2d72020-02-04 10:52:46 +0100876
garciadeblas20fc3b72022-11-14 00:48:32 +0100877 _run(server.get_placement(nslcmop_record_w_pinning["id"]))
magnussonl2b0e2d72020-02-04 10:52:46 +0100878 self.assertTrue(server.msgBus.aiowrite.mock.called)
879 args, kwargs = server.msgBus.aiowrite.mock.call_args
880 rsp_payload = args[2]
garciadeblas20fc3b72022-11-14 00:48:32 +0100881 expected_keys = {"placement"}
882 self.assertEqual(
883 expected_keys, set(rsp_payload.keys()), "placement response missing keys"
884 )
885 self.assertIs(type(rsp_payload["placement"]["vnf"]), list, "vnf not a list")
886 self.assertEqual([], rsp_payload["placement"]["vnf"], "vnf list not empty")
887 self.assertEqual(
888 nslcmop_record_w_pinning["id"],
889 rsp_payload["placement"]["nslcmopId"],
890 "nslcmopId invalid",
891 )