Fix black and cover errors
[osm/PLA.git] / osm_pla / test / test_server.py
1 # 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.
15
16 # pylint: disable=E1120
17
18 import asyncio
19 import copy
20 import os
21 import sys
22 from unittest import TestCase, mock
23 from unittest.mock import Mock
24
25 import yaml
26
27 from osm_pla.placement.mznplacement import NsPlacementDataFactory, MznPlacementConductor
28 from pathlib import Path
29
30 # need to Mock the imports from osm_common made in Server and Config beforehand
31 sys.modules["osm_common"] = Mock()
32 from osm_pla.server.server import Server # noqa: E402
33 from osm_pla.config.config import Config # noqa: E402
34
35 nslcmop_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 }
74
75 nslcmop_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 }
120
121 nslcmop_record_w_pinning_and_order_constraints = {
122 "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 }
178
179 list_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 ]
324
325 # FIXME this is not correct re mgmt-network setting.
326 nsd_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 }
426
427
428 ######################################################
429 # These are helper functions to handle unittest of asyncio.
430 # Inspired by: https://blog.miguelgrinberg.com/post/unit-testing-asyncio-code
431 def _run(co_routine):
432 return asyncio.get_event_loop().run_until_complete(co_routine)
433
434
435 def _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
447
448 class TestServer(TestCase):
449 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 """
454 return {_["name"]: _["_id"] for _ in list_of_vims}
455
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)
460 return {
461 i["vnfd"]: {i1["vim_name"]: i1["price"] for i1 in i["prices"]}
462 for i in next(price_list_data)
463 }
464
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
474 @mock.patch.object(Config, "_read_config_file")
475 @mock.patch.object(
476 Config,
477 "get",
478 side_effect=["doesnotmatter", "memory", "memory", "local", "doesnotmatter"],
479 )
480 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
491 """
492 path_component = "/osm_pla/test/"
493 real_path = os.path.realpath(file)
494 if path_component not in real_path:
495 return (
496 os.path.dirname(real_path)
497 + path_component
498 + os.path.basename(real_path)
499 )
500 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"])
507 server.db.get_one.assert_called_with(
508 "nslcmops", {"_id": nslcmop_record_wo_pinning["id"]}
509 )
510
511 def test__get_nsd(self): # OK
512 server = self.serverSetup()
513 server.db = Mock()
514 _ = 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 )
518
519 def test__create_vnf_id_maps(self):
520 server = self.serverSetup()
521 server.db = Mock()
522 expected_mvi2mzn = {"one": "VNF0", "two": "VNF1", "three": "VNF2"}
523 expected_mzn2mvi = {"VNF0": "one", "VNF1": "two", "VNF2": "three"}
524
525 nsd_for_test = copy.deepcopy(nsd_from_db)
526 mvi2mzn, mzn2mvi = server._create_vnf_id_maps(nsd_for_test)
527
528 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 )
534
535 def test__get_vim_accounts(self): # OK
536 server = self.serverSetup()
537 server.db = Mock()
538 _ = 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 )
545
546 def test__get_vnf_price_list(self):
547 server = self.serverSetup()
548 pl1 = server._get_vnf_price_list(
549 Path(self._adjust_path("./vnf_price_list.yaml"))
550 )
551 self.assertIs(type(pl1), dict, "price list not a dictionary")
552 for k, v in pl1.items():
553 self.assertIs(type(v), dict, "price list values not a dict")
554
555 pl2 = server._get_vnf_price_list(
556 Path(self._adjust_path("./vnf_price_list_keys.yaml")), "hackfest_project_a"
557 )
558 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
563 def test__get_pil_info(self):
564 server = self.serverSetup()
565 ppi = server._get_pil_info(Path(self._adjust_path("./pil_price_list.yaml")))
566 self.assertIs(type(ppi), dict, "pil is not a dict")
567 self.assertIn("pil", ppi.keys(), "pil has no pil key")
568 self.assertIs(type(ppi["pil"]), list, "pil does not contain a list")
569 # check for expected keys
570 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")
578
579 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")
591
592 @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 ):
614 """
615 run _get_placement and check that things get called as expected
616 :return:
617 """
618 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 ]
632 server = self.serverSetup()
633
634 server.msgBus.aiowrite = _async_mock()
635 nsd_for_test = copy.deepcopy(nsd_from_db)
636 mock__get_nsd.return_value = nsd_for_test
637 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
641 _run(server.get_placement(nslcmop_record_wo_pinning["id"]))
642
643 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")
653 # mock_get_nsd.assert_called_once() assert_called_once() for python > 3.5
654 self.assertTrue(mock__get_nsd.called, "get_nsd not called as expected")
655 # mock_get_enabled_vims.assert_called_once() assert_called_once() for python > 3.5
656 self.assertTrue(
657 mock__get_vim_accounts.called, "get_vim_accounts not called as expected"
658 )
659 # mock_create_ns_placement_data.assert_called_once() assert_called_once() for python > 3.5
660 self.assertTrue(
661 mock_create_ns_placement_data.called,
662 "create_ns_placement_data not called as expected",
663 )
664 # mock_do_placement_computation.assert_called_once() assert_called_once() for python > 3.5
665 self.assertTrue(
666 mock_do_placement_computation.called,
667 "do_placement_computation not called as expected",
668 )
669 self.assertTrue(server.msgBus.aiowrite.mock.called)
670
671 args, kwargs = server.msgBus.aiowrite.mock.call_args
672 self.assertTrue(len(args) == 3, "invalid format")
673 self.assertEqual("pla", args[0], "topic invalid")
674 self.assertEqual("placement", args[1], "message invalid")
675 # extract placement result and check content
676 rsp_payload = args[2]
677
678 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")
685
686 expected_placement_keys = {"vnf", "nslcmopId"}
687 self.assertEqual(
688 expected_placement_keys,
689 set(rsp_payload["placement"]),
690 "placement keys invalid",
691 )
692
693 vim_account_candidates = [e["vimAccountId"] for e in placement_ret_val]
694
695 self.assertEqual(
696 nslcmop_record_wo_pinning["id"],
697 rsp_payload["placement"]["nslcmopId"],
698 "nslcmopId invalid",
699 )
700
701 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 )
713
714 @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 ):
736 """
737 run _get_placement and check that things get called as expected
738 :return:
739 """
740 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 ]
754 server = self.serverSetup()
755
756 server.msgBus.aiowrite = _async_mock()
757 nsd_for_test = copy.deepcopy(nsd_from_db)
758 mock__get_nsd.return_value = nsd_for_test
759 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
763 _run(server.get_placement(nslcmop_record_w_pinning["id"]))
764
765 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")
775 # mock_get_nsd.assert_called_once() assert_called_once() for python > 3.5
776 self.assertTrue(mock__get_nsd.called, "get_nsd not called as expected")
777 # mock_get_enabled_vims.assert_called_once() assert_called_once() for python > 3.5
778 self.assertTrue(
779 mock__get_vim_accounts.called, "get_vim_accounts not called as expected"
780 )
781 # mock_create_ns_placement_data.assert_called_once() assert_called_once() for python > 3.5
782 self.assertTrue(
783 mock_create_ns_placement_data.called,
784 "create_ns_placement_data not called as expected",
785 )
786 # mock_do_placement_computation.assert_called_once() assert_called_once() for python > 3.5
787 self.assertTrue(
788 mock_do_placement_computation.called,
789 "do_placement_computation not called as expected",
790 )
791 self.assertTrue(server.msgBus.aiowrite.mock.called)
792
793 args, kwargs = server.msgBus.aiowrite.mock.call_args
794 self.assertTrue(len(args) == 3, "invalid format")
795 self.assertEqual("pla", args[0], "topic invalid")
796 self.assertEqual("placement", args[1], "message invalid")
797 # extract placement result and check content
798 rsp_payload = args[2]
799
800 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")
807
808 expected_placement_keys = {"vnf", "nslcmopId"}
809 self.assertEqual(
810 expected_placement_keys,
811 set(rsp_payload["placement"]),
812 "placement keys invalid",
813 )
814
815 vim_account_candidates = [e["vimAccountId"] for e in placement_ret_val]
816
817 self.assertEqual(
818 nslcmop_record_w_pinning["id"],
819 rsp_payload["placement"]["nslcmopId"],
820 "nslcmopId invalid",
821 )
822
823 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 )
835
836 # Note: does not mock reading of price list and pil_info
837 @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 ):
853 """
854 check that raised exceptions are handled and response provided accordingly
855 """
856 server = self.serverSetup()
857
858 server.msgBus.aiowrite = _async_mock()
859 nsd_for_test = copy.deepcopy(nsd_from_db)
860 mock__get_nsd.return_value = nsd_for_test
861 mock__get_nsd.side_effect = RuntimeError("kaboom!")
862 mock__get_vim_accounts.return_value = list_of_vims
863 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 ]
877
878 _run(server.get_placement(nslcmop_record_w_pinning["id"]))
879 self.assertTrue(server.msgBus.aiowrite.mock.called)
880 args, kwargs = server.msgBus.aiowrite.mock.call_args
881 rsp_payload = args[2]
882 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 )