Code Coverage

Cobertura Coverage Report > osm_pla.test >

test_server.py

Trend

Classes100%
 
Lines96%
   
Conditionals100%
 

File Coverage summary

NameClassesLinesConditionals
test_server.py
100%
1/1
96%
201/210
100%
0/0

Coverage Breakdown by Class

NameLinesConditionals
test_server.py
96%
201/210
N/A

Source

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 1 import asyncio
19 1 import copy
20 1 import os
21 1 import sys
22 1 from unittest import TestCase, mock
23 1 from unittest.mock import Mock
24
25 1 import yaml
26
27 1 from osm_pla.placement.mznplacement import NsPlacementDataFactory, MznPlacementConductor
28 1 from pathlib import Path
29
30 # need to Mock the imports from osm_common made in Server and Config beforehand
31 1 sys.modules["osm_common"] = Mock()
32 1 from osm_pla.server.server import Server  # noqa: E402
33 1 from osm_pla.config.config import Config  # noqa: E402
34
35 1 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 1 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 1 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 1 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 1 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 1 def _run(co_routine):
432 1     return asyncio.get_event_loop().run_until_complete(co_routine)
433
434
435 1 def _async_mock(*args, **kwargs):
436 1     m = mock.MagicMock(*args, **kwargs)
437
438 1     async def mock_coro(*args, **kwargs):
439 1         return m(*args, **kwargs)
440
441 1     mock_coro.mock = m
442 1     return mock_coro
443
444
445 ######################################################
446
447
448 1 class TestServer(TestCase):
449 1     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 0         return {_["name"]: _["_id"] for _ in list_of_vims}
455
456 1     def _produce_ut_vnf_price_list(self):
457 0         price_list_file = "vnf_price_list.yaml"
458 0         with open(str(Path(price_list_file))) as pl_fd:
459 0             price_list_data = yaml.safe_load_all(pl_fd)
460 0             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 1     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 0         with open(str(Path(file))) as pp_fd:
471 0             test_data = yaml.safe_load_all(pp_fd)
472 0             return next(test_data)
473
474 1     @mock.patch.object(Config, "_read_config_file")
475 1     @mock.patch.object(
476         Config,
477         "get",
478         side_effect=["doesnotmatter", "memory", "memory", "local", "doesnotmatter"],
479     )
480 1     def serverSetup(self, mock_get, mock__read_config_file):
481         """
482         Helper that returns a Server object
483         :return:
484         """
485 1         cfg = Config(None)
486 1         return Server(cfg)
487
488 1     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 1         path_component = "/osm_pla/test/"
493 1         real_path = os.path.realpath(file)
494 1         if path_component not in real_path:
495 1             return (
496                 os.path.dirname(real_path)
497                 + path_component
498                 + os.path.basename(real_path)
499             )
500         else:
501 0             return real_path
502
503 1     def test__get_nslcmop(self):
504 1         server = self.serverSetup()
505 1         server.db = Mock()
506 1         _ = server._get_nslcmop(nslcmop_record_wo_pinning["id"])
507 1         server.db.get_one.assert_called_with(
508             "nslcmops", {"_id": nslcmop_record_wo_pinning["id"]}
509         )
510
511 1     def test__get_nsd(self):  # OK
512 1         server = self.serverSetup()
513 1         server.db = Mock()
514 1         _ = server._get_nsd(nslcmop_record_wo_pinning["operationParams"]["nsdId"])
515 1         server.db.get_one.assert_called_with(
516             "nsds", {"_id": nslcmop_record_wo_pinning["operationParams"]["nsdId"]}
517         )
518
519 1     def test__create_vnf_id_maps(self):
520 1         server = self.serverSetup()
521 1         server.db = Mock()
522 1         expected_mvi2mzn = {"one": "VNF0", "two": "VNF1", "three": "VNF2"}
523 1         expected_mzn2mvi = {"VNF0": "one", "VNF1": "two", "VNF2": "three"}
524
525 1         nsd_for_test = copy.deepcopy(nsd_from_db)
526 1         mvi2mzn, mzn2mvi = server._create_vnf_id_maps(nsd_for_test)
527
528 1         self.assertDictEqual(
529             expected_mvi2mzn, mvi2mzn, "Faulty mzn2member-vnf-index mapping"
530         )
531 1         self.assertDictEqual(
532             expected_mzn2mvi, mzn2mvi, "Faulty mzn2member-vnf-index mapping"
533         )
534
535 1     def test__get_vim_accounts(self):  # OK
536 1         server = self.serverSetup()
537 1         server.db = Mock()
538 1         _ = server._get_vim_accounts(
539             nslcmop_record_wo_pinning["operationParams"]["validVimAccounts"]
540         )
541 1         server.db.get_list.assert_called_with(
542             "vim_accounts",
543             {"_id": nslcmop_record_wo_pinning["operationParams"]["validVimAccounts"]},
544         )
545
546 1     def test__get_vnf_price_list(self):
547 1         server = self.serverSetup()
548 1         pl1 = server._get_vnf_price_list(
549             Path(self._adjust_path("./vnf_price_list.yaml"))
550         )
551 1         self.assertIs(type(pl1), dict, "price list not a dictionary")
552 1         for k, v in pl1.items():
553 1             self.assertIs(type(v), dict, "price list values not a dict")
554
555 1         pl2 = server._get_vnf_price_list(
556             Path(self._adjust_path("./vnf_price_list_keys.yaml")), "hackfest_project_a"
557         )
558 1         self.assertIs(type(pl2), dict, "price list not a dictionary")
559 1         for k, v in pl2.items():
560 1             self.assertIs(type(v), dict, "price list values not a dict")
561 1         self.assertEqual(pl1, pl2, "non-project and project price lists differ")
562
563 1     def test__get_pil_info(self):
564 1         server = self.serverSetup()
565 1         ppi = server._get_pil_info(Path(self._adjust_path("./pil_price_list.yaml")))
566 1         self.assertIs(type(ppi), dict, "pil is not a dict")
567 1         self.assertIn("pil", ppi.keys(), "pil has no pil key")
568 1         self.assertIs(type(ppi["pil"]), list, "pil does not contain a list")
569         # check for expected keys
570 1         expected_keys = {
571             "pil_description",
572             "pil_price",
573             "pil_latency",
574             "pil_jitter",
575             "pil_endpoints",
576         }
577 1         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 1     @mock.patch.object(
593         NsPlacementDataFactory, "__init__", lambda x0, x1, x2, x3, x4, x5, x6: None
594     )
595 1     @mock.patch.object(MznPlacementConductor, "do_placement_computation")
596 1     @mock.patch.object(NsPlacementDataFactory, "create_ns_placement_data")
597 1     @mock.patch.object(Server, "_get_vim_accounts")
598 1     @mock.patch.object(Server, "_get_nsd")
599 1     @mock.patch.object(Server, "_get_nslcmop")
600 1     @mock.patch.object(Server, "_get_vnf_price_list")
601 1     @mock.patch.object(Server, "_get_pil_info")
602 1     @mock.patch.object(Server, "_get_projects")
603 1     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 1         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 1         server = self.serverSetup()
633
634 1         server.msgBus.aiowrite = _async_mock()
635 1         nsd_for_test = copy.deepcopy(nsd_from_db)
636 1         mock__get_nsd.return_value = nsd_for_test
637 1         mock__get_vim_accounts.return_value = list_of_vims
638
639         # FIXME need update to match nslcmop, not for test but for consistency
640 1         mock_do_placement_computation.return_value = placement_ret_val
641 1         _run(server.get_placement(nslcmop_record_wo_pinning["id"]))
642
643 1         self.assertTrue(
644             mock_get_projects.called, "_get_projects not called as expected"
645         )
646 1         self.assertTrue(
647             mock_get_vnf_price_list.called, "_get_vnf_price_list not called as expected"
648         )
649 1         self.assertTrue(
650             mock_get_pil_info.called, "_get_pil_info not called as expected"
651         )
652 1         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 1         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 1         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 1         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 1         self.assertTrue(
666             mock_do_placement_computation.called,
667             "do_placement_computation not called as expected",
668         )
669 1         self.assertTrue(server.msgBus.aiowrite.mock.called)
670
671 1         args, kwargs = server.msgBus.aiowrite.mock.call_args
672 1         self.assertTrue(len(args) == 3, "invalid format")
673 1         self.assertEqual("pla", args[0], "topic invalid")
674 1         self.assertEqual("placement", args[1], "message invalid")
675         # extract placement result and check content
676 1         rsp_payload = args[2]
677
678 1         expected_rsp_keys = {"placement"}
679 1         self.assertEqual(
680             expected_rsp_keys,
681             set(rsp_payload.keys()),
682             "placement response missing keys",
683         )
684 1         self.assertIs(type(rsp_payload["placement"]), dict, "placement not a dict")
685
686 1         expected_placement_keys = {"vnf", "nslcmopId"}
687 1         self.assertEqual(
688             expected_placement_keys,
689             set(rsp_payload["placement"]),
690             "placement keys invalid",
691         )
692
693 1         vim_account_candidates = [e["vimAccountId"] for e in placement_ret_val]
694
695 1         self.assertEqual(
696             nslcmop_record_wo_pinning["id"],
697             rsp_payload["placement"]["nslcmopId"],
698             "nslcmopId invalid",
699         )
700
701 1         self.assertIs(type(rsp_payload["placement"]["vnf"]), list, "vnf not a list")
702 1         expected_vnf_keys = {"vimAccountId", "member-vnf-index"}
703 1         self.assertEqual(
704             expected_vnf_keys,
705             set(rsp_payload["placement"]["vnf"][0]),
706             "placement['vnf'] missing keys",
707         )
708 1         self.assertIn(
709             rsp_payload["placement"]["vnf"][0]["vimAccountId"],
710             vim_account_candidates,
711             "vimAccountId invalid",
712         )
713
714 1     @mock.patch.object(
715         NsPlacementDataFactory, "__init__", lambda x0, x1, x2, x3, x4, x5, x6: None
716     )
717 1     @mock.patch.object(MznPlacementConductor, "do_placement_computation")
718 1     @mock.patch.object(NsPlacementDataFactory, "create_ns_placement_data")
719 1     @mock.patch.object(Server, "_get_vim_accounts")
720 1     @mock.patch.object(Server, "_get_nsd")
721 1     @mock.patch.object(Server, "_get_nslcmop")
722 1     @mock.patch.object(Server, "_get_vnf_price_list")
723 1     @mock.patch.object(Server, "_get_pil_info")
724 1     @mock.patch.object(Server, "_get_projects")
725 1     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 1         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 1         server = self.serverSetup()
755
756 1         server.msgBus.aiowrite = _async_mock()
757 1         nsd_for_test = copy.deepcopy(nsd_from_db)
758 1         mock__get_nsd.return_value = nsd_for_test
759 1         mock__get_vim_accounts.return_value = list_of_vims
760
761         # FIXME need update to match nslcmop, not for test but for consistency
762 1         mock_do_placement_computation.return_value = placement_ret_val
763 1         _run(server.get_placement(nslcmop_record_w_pinning["id"]))
764
765 1         self.assertTrue(
766             (mock_get_projects.called, "_get_projects not called as expected")
767         )
768 1         self.assertTrue(
769             mock_get_vnf_price_list.called, "_get_vnf_price_list not called as expected"
770         )
771 1         self.assertTrue(
772             mock_get_pil_info.called, "_get_pil_info not called as expected"
773         )
774 1         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 1         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 1         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 1         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 1         self.assertTrue(
788             mock_do_placement_computation.called,
789             "do_placement_computation not called as expected",
790         )
791 1         self.assertTrue(server.msgBus.aiowrite.mock.called)
792
793 1         args, kwargs = server.msgBus.aiowrite.mock.call_args
794 1         self.assertTrue(len(args) == 3, "invalid format")
795 1         self.assertEqual("pla", args[0], "topic invalid")
796 1         self.assertEqual("placement", args[1], "message invalid")
797         # extract placement result and check content
798 1         rsp_payload = args[2]
799
800 1         expected_rsp_keys = {"placement"}
801 1         self.assertEqual(
802             expected_rsp_keys,
803             set(rsp_payload.keys()),
804             "placement response missing keys",
805         )
806 1         self.assertIs(type(rsp_payload["placement"]), dict, "placement not a dict")
807
808 1         expected_placement_keys = {"vnf", "nslcmopId"}
809 1         self.assertEqual(
810             expected_placement_keys,
811             set(rsp_payload["placement"]),
812             "placement keys invalid",
813         )
814
815 1         vim_account_candidates = [e["vimAccountId"] for e in placement_ret_val]
816
817 1         self.assertEqual(
818             nslcmop_record_w_pinning["id"],
819             rsp_payload["placement"]["nslcmopId"],
820             "nslcmopId invalid",
821         )
822
823 1         self.assertIs(type(rsp_payload["placement"]["vnf"]), list, "vnf not a list")
824 1         expected_vnf_keys = {"vimAccountId", "member-vnf-index"}
825 1         self.assertEqual(
826             expected_vnf_keys,
827             set(rsp_payload["placement"]["vnf"][0]),
828             "placement['vnf'] missing keys",
829         )
830 1         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 1     @mock.patch.object(
838         NsPlacementDataFactory, "__init__", lambda x0, x1, x2, x3, x4, x5: None
839     )
840 1     @mock.patch.object(MznPlacementConductor, "do_placement_computation")
841 1     @mock.patch.object(NsPlacementDataFactory, "create_ns_placement_data")
842 1     @mock.patch.object(Server, "_get_vim_accounts")
843 1     @mock.patch.object(Server, "_get_nsd")
844 1     @mock.patch.object(Server, "_get_nslcmop")
845 1     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 1         server = self.serverSetup()
857
858 1         server.msgBus.aiowrite = _async_mock()
859 1         nsd_for_test = copy.deepcopy(nsd_from_db)
860 1         mock__get_nsd.return_value = nsd_for_test
861 1         mock__get_nsd.side_effect = RuntimeError("kaboom!")
862 1         mock__get_vim_accounts.return_value = list_of_vims
863 1         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 1         _run(server.get_placement(nslcmop_record_w_pinning["id"]))
879 1         self.assertTrue(server.msgBus.aiowrite.mock.called)
880 1         args, kwargs = server.msgBus.aiowrite.mock.call_args
881 1         rsp_payload = args[2]
882 1         expected_keys = {"placement"}
883 1         self.assertEqual(
884             expected_keys, set(rsp_payload.keys()), "placement response missing keys"
885         )
886 1         self.assertIs(type(rsp_payload["placement"]["vnf"]), list, "vnf not a list")
887 1         self.assertEqual([], rsp_payload["placement"]["vnf"], "vnf list not empty")
888 1         self.assertEqual(
889             nslcmop_record_w_pinning["id"],
890             rsp_payload["placement"]["nslcmopId"],
891             "nslcmopId invalid",
892         )