Enable black in tox.ini
[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 path_component = "/osm_pla/test/"
492 real_path = os.path.realpath(file)
493 if path_component not in real_path:
494 return (
495 os.path.dirname(real_path)
496 + path_component
497 + os.path.basename(real_path)
498 )
499 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"])
506 server.db.get_one.assert_called_with(
507 "nslcmops", {"_id": nslcmop_record_wo_pinning["id"]}
508 )
509
510 def test__get_nsd(self): # OK
511 server = self.serverSetup()
512 server.db = Mock()
513 _ = 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 )
517
518 def test__create_vnf_id_maps(self):
519 server = self.serverSetup()
520 server.db = Mock()
521 expected_mvi2mzn = {"one": "VNF0", "two": "VNF1", "three": "VNF2"}
522 expected_mzn2mvi = {"VNF0": "one", "VNF1": "two", "VNF2": "three"}
523
524 nsd_for_test = copy.deepcopy(nsd_from_db)
525 mvi2mzn, mzn2mvi = server._create_vnf_id_maps(nsd_for_test)
526
527 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 )
533
534 def test__get_vim_accounts(self): # OK
535 server = self.serverSetup()
536 server.db = Mock()
537 _ = 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 )
544
545 def test__get_vnf_price_list(self):
546 server = self.serverSetup()
547 pl1 = server._get_vnf_price_list(
548 Path(self._adjust_path("./vnf_price_list.yaml"))
549 )
550 self.assertIs(type(pl1), dict, "price list not a dictionary")
551 for k, v in pl1.items():
552 self.assertIs(type(v), dict, "price list values not a dict")
553
554 pl2 = server._get_vnf_price_list(
555 Path(self._adjust_path("./vnf_price_list_keys.yaml")), "hackfest_project_a"
556 )
557 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
562 def test__get_pil_info(self):
563 server = self.serverSetup()
564 ppi = server._get_pil_info(Path(self._adjust_path("./pil_price_list.yaml")))
565 self.assertIs(type(ppi), dict, "pil is not a dict")
566 self.assertIn("pil", ppi.keys(), "pil has no pil key")
567 self.assertIs(type(ppi["pil"]), list, "pil does not contain a list")
568 # check for expected keys
569 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")
577
578 def test_handle_kafka_command(self): # OK
579 server = self.serverSetup()
580 server.loop.create_task = Mock()
581 server.handle_kafka_command("pli", "get_placement", {})
582 server.loop.create_task.assert_not_called()
583 server.loop.create_task.reset_mock()
584 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")
588 args, kwargs = server.loop.create_task.call_args
589 self.assertIn("Server.get_placement", str(args[0]), "get_placement not called")
590
591 @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 ):
613 """
614 run _get_placement and check that things get called as expected
615 :return:
616 """
617 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 ]
631 server = self.serverSetup()
632
633 server.msgBus.aiowrite = _async_mock()
634 nsd_for_test = copy.deepcopy(nsd_from_db)
635 mock__get_nsd.return_value = nsd_for_test
636 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
640 _run(server.get_placement(nslcmop_record_wo_pinning["id"]))
641
642 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")
652 # mock_get_nsd.assert_called_once() assert_called_once() for python > 3.5
653 self.assertTrue(mock__get_nsd.called, "get_nsd not called as expected")
654 # mock_get_enabled_vims.assert_called_once() assert_called_once() for python > 3.5
655 self.assertTrue(
656 mock__get_vim_accounts.called, "get_vim_accounts not called as expected"
657 )
658 # mock_create_ns_placement_data.assert_called_once() assert_called_once() for python > 3.5
659 self.assertTrue(
660 mock_create_ns_placement_data.called,
661 "create_ns_placement_data not called as expected",
662 )
663 # mock_do_placement_computation.assert_called_once() assert_called_once() for python > 3.5
664 self.assertTrue(
665 mock_do_placement_computation.called,
666 "do_placement_computation not called as expected",
667 )
668 self.assertTrue(server.msgBus.aiowrite.mock.called)
669
670 args, kwargs = server.msgBus.aiowrite.mock.call_args
671 self.assertTrue(len(args) == 3, "invalid format")
672 self.assertEqual("pla", args[0], "topic invalid")
673 self.assertEqual("placement", args[1], "message invalid")
674 # extract placement result and check content
675 rsp_payload = args[2]
676
677 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")
684
685 expected_placement_keys = {"vnf", "nslcmopId"}
686 self.assertEqual(
687 expected_placement_keys,
688 set(rsp_payload["placement"]),
689 "placement keys invalid",
690 )
691
692 vim_account_candidates = [e["vimAccountId"] for e in placement_ret_val]
693
694 self.assertEqual(
695 nslcmop_record_wo_pinning["id"],
696 rsp_payload["placement"]["nslcmopId"],
697 "nslcmopId invalid",
698 )
699
700 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 )
712
713 @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 ):
735 """
736 run _get_placement and check that things get called as expected
737 :return:
738 """
739 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 ]
753 server = self.serverSetup()
754
755 server.msgBus.aiowrite = _async_mock()
756 nsd_for_test = copy.deepcopy(nsd_from_db)
757 mock__get_nsd.return_value = nsd_for_test
758 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
762 _run(server.get_placement(nslcmop_record_w_pinning["id"]))
763
764 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")
774 # mock_get_nsd.assert_called_once() assert_called_once() for python > 3.5
775 self.assertTrue(mock__get_nsd.called, "get_nsd not called as expected")
776 # mock_get_enabled_vims.assert_called_once() assert_called_once() for python > 3.5
777 self.assertTrue(
778 mock__get_vim_accounts.called, "get_vim_accounts not called as expected"
779 )
780 # mock_create_ns_placement_data.assert_called_once() assert_called_once() for python > 3.5
781 self.assertTrue(
782 mock_create_ns_placement_data.called,
783 "create_ns_placement_data not called as expected",
784 )
785 # mock_do_placement_computation.assert_called_once() assert_called_once() for python > 3.5
786 self.assertTrue(
787 mock_do_placement_computation.called,
788 "do_placement_computation not called as expected",
789 )
790 self.assertTrue(server.msgBus.aiowrite.mock.called)
791
792 args, kwargs = server.msgBus.aiowrite.mock.call_args
793 self.assertTrue(len(args) == 3, "invalid format")
794 self.assertEqual("pla", args[0], "topic invalid")
795 self.assertEqual("placement", args[1], "message invalid")
796 # extract placement result and check content
797 rsp_payload = args[2]
798
799 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")
806
807 expected_placement_keys = {"vnf", "nslcmopId"}
808 self.assertEqual(
809 expected_placement_keys,
810 set(rsp_payload["placement"]),
811 "placement keys invalid",
812 )
813
814 vim_account_candidates = [e["vimAccountId"] for e in placement_ret_val]
815
816 self.assertEqual(
817 nslcmop_record_w_pinning["id"],
818 rsp_payload["placement"]["nslcmopId"],
819 "nslcmopId invalid",
820 )
821
822 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 )
834
835 # Note: does not mock reading of price list and pil_info
836 @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 ):
852 """
853 check that raised exceptions are handled and response provided accordingly
854 """
855 server = self.serverSetup()
856
857 server.msgBus.aiowrite = _async_mock()
858 nsd_for_test = copy.deepcopy(nsd_from_db)
859 mock__get_nsd.return_value = nsd_for_test
860 mock__get_nsd.side_effect = RuntimeError("kaboom!")
861 mock__get_vim_accounts.return_value = list_of_vims
862 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 ]
876
877 _run(server.get_placement(nslcmop_record_w_pinning["id"]))
878 self.assertTrue(server.msgBus.aiowrite.mock.called)
879 args, kwargs = server.msgBus.aiowrite.mock.call_args
880 rsp_payload = args[2]
881 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 )