1 # Copyright 2020 ArctosLabs Scandinavia AB
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 # pylint: disable=E1120
22 from unittest
import TestCase
, mock
23 from unittest
.mock
import Mock
27 from osm_pla
.placement
.mznplacement
import NsPlacementDataFactory
, MznPlacementConductor
28 from pathlib
import Path
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
35 nslcmop_record_wo_pinning
= {
36 "statusEnteredTime": 1574625718.8280587,
37 "startTime": 1574625718.8280587,
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"],
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,
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",
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",
64 "eb553051-5b6c-4ad6-939b-2ad23bd82e57",
65 "576bbe0a-b95d-4ced-a63e-f387f8e6e2ce",
66 "3d1ffc5d-b36d-4f69-8356-7f59c740ca2f",
67 "db54dcd4-9fc4-441c-8820-17bce0aef2c3",
69 "nsr_id": "45f588bd-5bf4-4181-b13b-f16a55a23be4",
70 "placement-engine": "PLA",
71 "nsInstanceId": "45f588bd-5bf4-4181-b13b-f16a55a23be4",
75 nslcmop_record_w_pinning
= {
76 "statusEnteredTime": 1574627411.420499,
77 "startTime": 1574627411.420499,
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"],
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,
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",
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",
107 "nsName": "ThreeVnfTest2",
108 "wimAccountId": False,
111 "vimAccountId": "3d1ffc5d-b36d-4f69-8356-7f59c740ca2f",
112 "member-vnf-index": "1",
115 "placementEngine": "PLA",
116 "nsInstanceId": "61587478-ea25-44eb-9f13-7005046ddb08",
117 "lcmOperationType": "instantiate",
121 nslcmop_record_w_pinning_and_order_constraints
= {
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",
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",
136 "placementEngine": "PLA",
137 "placement-constraints": {
140 "id": "three_vnf_constrained_vld_1",
141 "link-constraints": {"latency": 120, "jitter": 20},
144 "link_constraints": {"latency": 120, "jitter": 20},
145 "id": "three_vnf_constrained_nsd_vld_2",
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",
159 "wimAccountId": False,
162 "member-vnf-index": "3",
163 "vimAccountId": "3d1ffc5d-b36d-4f69-8356-7f59c740ca2f",
166 "nsInstanceId": "7c4c3d94-ebb2-44e8-b236-d876b118434e",
167 "lcmOperationType": "instantiate",
168 "vimAccountId": "576bbe0a-b95d-4ced-a63e-f387f8e6e2ce",
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",
181 "_id": "73cd1a1b-333e-4e29-8db2-00d23bd9b644",
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",
190 "modified": 1565597984.3155663,
192 "RO": "f0c1b516-bcd9-11e9-bb73-02420aff0030",
193 "RO-account": "f0d45496-bcd9-11e9-bb73-02420aff0030",
195 "projects_write": ["admin"],
196 "operationalState": "ENABLED",
197 "detailed-status": "Done",
198 "created": 1565597984.3155663,
199 "projects_read": ["admin"],
204 "_id": "684165ea-2cf9-4fbd-ac22-8464ca07d1d8",
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",
213 "modified": 1566474766.7288046,
215 "RO": "5bc59656-c4d3-11e9-b1e5-02420aff0006",
216 "RO-account": "5bd772e0-c4d3-11e9-b1e5-02420aff0006",
218 "projects_write": ["admin"],
219 "operationalState": "ENABLED",
220 "detailed-status": "Done",
221 "created": 1566474766.7288046,
222 "projects_read": ["admin"],
225 "schema_version": "1.1",
228 "_id": "8460b670-31cf-4fae-9f3e-d0dd6c57b61e",
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",
237 "modified": 1566992449.5942867,
239 "RO": "aed94f86-c988-11e9-bb38-02420aff0088",
240 "RO-account": "aee72fac-c988-11e9-bb38-02420aff0088",
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"],
249 "schema_version": "1.1",
252 "_id": "9b8b5268-acb7-4893-b494-a77656b418f2",
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",
261 "modified": 1566992484.9190753,
263 "RO": "c3d61158-c988-11e9-bb38-02420aff0088",
264 "RO-account": "c3ec973e-c988-11e9-bb38-02420aff0088",
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"],
273 "schema_version": "1.1",
276 "_id": "3645f215-f32d-4355-b5ab-df0a2e2233c3",
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",
285 "modified": 1567421247.7016313,
287 "RO": "0e80f6a2-cd6f-11e9-bb50-02420aff00b6",
288 "RO-account": "0e974524-cd6f-11e9-bb50-02420aff00b6",
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"],
296 "schema_version": "1.1",
300 "_id": "53f8f2bb-88b5-4bf9-babf-556698b5261f",
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",
309 "modified": 1567421296.1576457,
311 "RO": "2b43c756-cd6f-11e9-bb50-02420aff00b6",
312 "RO-account": "2b535aea-cd6f-11e9-bb50-02420aff00b6",
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"],
320 "schema_version": "1.1",
325 # FIXME this is not correct re mgmt-network setting.
327 "_id": "15fc1941-f095-4cd8-af2d-1000bd6d9eaa",
329 "modified": 1567672251.7531693,
331 "pkg-dir": "ns_constrained_nsd",
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/",
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"],
346 "id": "three_vnf_constrained_nsd_low",
347 "name": "three_vnf_constrained_nsd_low",
348 "description": "Placement constraints NSD",
349 "designer": "ArctosLabs",
351 "vnfd-id": ["cirros_vnfd_v2"],
358 "vnfd-id": "cirros_vnfd_v2",
359 "virtual-link-connectivity": [
361 "virtual-link-profile-id": "three_vnf_constrained_nsd_low_vld1",
362 "constituent-cpd-id": [
364 "constituent-base-element-id": "one",
365 "constituent-cpd-id": "vnf-cp0-ext",
373 "vnfd-id": "cirros_vnfd_v2",
374 "virtual-link-connectivity": [
376 "virtual-link-profile-id": "three_vnf_constrained_nsd_low_vld1",
377 "constituent-cpd-id": [
379 "constituent-base-element-id": "two",
380 "constituent-cpd-id": "vnf-cp0-ext",
385 "virtual-link-profile-id": "three_vnf_constrained_nsd_low_vld2",
386 "constituent-cpd-id": [
388 "constituent-base-element-id": "two",
389 "constituent-cpd-id": "vnf-cp0-ext",
397 "vnfd-id": "cirros_vnfd_v2",
398 "virtual-link-connectivity": [
400 "virtual-link-profile-id": "three_vnf_constrained_nsd_low_vld2",
401 "constituent-cpd-id": [
403 "constituent-base-element-id": "three",
404 "constituent-cpd-id": "vnf-cp0-ext",
413 "virtual-link-desc": [
415 "id": "three_vnf_constrained_nsd_low_vld1",
416 "mgmt-network": True,
417 "vim-network-name": "external",
420 "id": "three_vnf_constrained_nsd_low_vld2",
421 "mgmt-network": True,
422 "vim-network-name": "lanretxe",
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
)
435 def _async_mock(*args
, **kwargs
):
436 m
= mock
.MagicMock(*args
, **kwargs
)
438 async def mock_coro(*args
, **kwargs
):
439 return m(*args
, **kwargs
)
445 ######################################################
448 class TestServer(TestCase
):
449 def _produce_ut_vim_accounts_info(self
, list_of_vims
):
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
454 return {_
["name"]: _
["_id"] for _
in list_of_vims
}
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
)
461 i
["vnfd"]: {i1
["vim_name"]: i1
["price"] for i1
in i
["prices"]}
462 for i
in next(price_list_data
)
465 def _populate_pil_info(self
, file):
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
470 with
open(str(Path(file))) as pp_fd
:
471 test_data
= yaml
.safe_load_all(pp_fd
)
472 return next(test_data
)
474 @mock.patch
.object(Config
, "_read_config_file")
478 side_effect
=["doesnotmatter", "memory", "memory", "local", "doesnotmatter"],
480 def serverSetup(self
, mock_get
, mock__read_config_file
):
482 Helper that returns a Server object
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
:
495 os
.path
.dirname(real_path
)
497 + os
.path
.basename(real_path
)
502 def test__get_nslcmop(self
):
503 server
= self
.serverSetup()
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"]}
510 def test__get_nsd(self
): # OK
511 server
= self
.serverSetup()
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"]}
518 def test__create_vnf_id_maps(self
):
519 server
= self
.serverSetup()
521 expected_mvi2mzn
= {"one": "VNF0", "two": "VNF1", "three": "VNF2"}
522 expected_mzn2mvi
= {"VNF0": "one", "VNF1": "two", "VNF2": "three"}
524 nsd_for_test
= copy
.deepcopy(nsd_from_db
)
525 mvi2mzn
, mzn2mvi
= server
._create
_vnf
_id
_maps
(nsd_for_test
)
527 self
.assertDictEqual(
528 expected_mvi2mzn
, mvi2mzn
, "Faulty mzn2member-vnf-index mapping"
530 self
.assertDictEqual(
531 expected_mzn2mvi
, mzn2mvi
, "Faulty mzn2member-vnf-index mapping"
534 def test__get_vim_accounts(self
): # OK
535 server
= self
.serverSetup()
537 _
= server
._get
_vim
_accounts
(
538 nslcmop_record_wo_pinning
["operationParams"]["validVimAccounts"]
540 server
.db
.get_list
.assert_called_with(
542 {"_id": nslcmop_record_wo_pinning
["operationParams"]["validVimAccounts"]},
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"))
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")
554 pl2
= server
._get
_vnf
_price
_list
(
555 Path(self
._adjust
_path
("./vnf_price_list_keys.yaml")), "hackfest_project_a"
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")
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
576 self
.assertEqual(expected_keys
, ppi
["pil"][0].keys(), "expected keys not found")
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"]}
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")
592 NsPlacementDataFactory
, "__init__", lambda x0
, x1
, x2
, x3
, x4
, x5
, x6
: None
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(
606 mock_get_vnf_price_list
,
609 mock__get_vim_accounts
,
610 mock_create_ns_placement_data
,
611 mock_do_placement_computation
,
614 run _get_placement and check that things get called as expected
617 placement_ret_val
= [
619 "vimAccountId": "bbbbbbbb-38f5-438d-b8ee-3f93b3531f87",
620 "member-vnf-index": "VNF0",
623 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
624 "member-vnf-index": "VNF1",
627 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
628 "member-vnf-index": "VNF2",
631 server
= self
.serverSetup()
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
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"]))
643 mock_get_projects
.called
, "_get_projects not called as expected"
646 mock_get_vnf_price_list
.called
, "_get_vnf_price_list not called as expected"
649 mock_get_pil_info
.called
, "_get_pil_info not called as expected"
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
656 mock__get_vim_accounts
.called
, "get_vim_accounts not called as expected"
658 # mock_create_ns_placement_data.assert_called_once() assert_called_once() for python > 3.5
660 mock_create_ns_placement_data
.called
,
661 "create_ns_placement_data not called as expected",
663 # mock_do_placement_computation.assert_called_once() assert_called_once() for python > 3.5
665 mock_do_placement_computation
.called
,
666 "do_placement_computation not called as expected",
668 self
.assertTrue(server
.msgBus
.aiowrite
.mock
.called
)
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]
677 expected_rsp_keys
= {"placement"}
680 set(rsp_payload
.keys()),
681 "placement response missing keys",
683 self
.assertIs(type(rsp_payload
["placement"]), dict, "placement not a dict")
685 expected_placement_keys
= {"vnf", "nslcmopId"}
687 expected_placement_keys
,
688 set(rsp_payload
["placement"]),
689 "placement keys invalid",
692 vim_account_candidates
= [e
["vimAccountId"] for e
in placement_ret_val
]
695 nslcmop_record_wo_pinning
["id"],
696 rsp_payload
["placement"]["nslcmopId"],
700 self
.assertIs(type(rsp_payload
["placement"]["vnf"]), list, "vnf not a list")
701 expected_vnf_keys
= {"vimAccountId", "member-vnf-index"}
704 set(rsp_payload
["placement"]["vnf"][0]),
705 "placement['vnf'] missing keys",
708 rsp_payload
["placement"]["vnf"][0]["vimAccountId"],
709 vim_account_candidates
,
710 "vimAccountId invalid",
714 NsPlacementDataFactory
, "__init__", lambda x0
, x1
, x2
, x3
, x4
, x5
, x6
: None
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(
728 mock_get_vnf_price_list
,
731 mock__get_vim_accounts
,
732 mock_create_ns_placement_data
,
733 mock_do_placement_computation
,
736 run _get_placement and check that things get called as expected
739 placement_ret_val
= [
741 "vimAccountId": "bbbbbbbb-38f5-438d-b8ee-3f93b3531f87",
742 "member-vnf-index": "VNF0",
745 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
746 "member-vnf-index": "VNF1",
749 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
750 "member-vnf-index": "VNF2",
753 server
= self
.serverSetup()
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
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"]))
765 (mock_get_projects
.called
, "_get_projects not called as expected")
768 mock_get_vnf_price_list
.called
, "_get_vnf_price_list not called as expected"
771 mock_get_pil_info
.called
, "_get_pil_info not called as expected"
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
778 mock__get_vim_accounts
.called
, "get_vim_accounts not called as expected"
780 # mock_create_ns_placement_data.assert_called_once() assert_called_once() for python > 3.5
782 mock_create_ns_placement_data
.called
,
783 "create_ns_placement_data not called as expected",
785 # mock_do_placement_computation.assert_called_once() assert_called_once() for python > 3.5
787 mock_do_placement_computation
.called
,
788 "do_placement_computation not called as expected",
790 self
.assertTrue(server
.msgBus
.aiowrite
.mock
.called
)
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]
799 expected_rsp_keys
= {"placement"}
802 set(rsp_payload
.keys()),
803 "placement response missing keys",
805 self
.assertIs(type(rsp_payload
["placement"]), dict, "placement not a dict")
807 expected_placement_keys
= {"vnf", "nslcmopId"}
809 expected_placement_keys
,
810 set(rsp_payload
["placement"]),
811 "placement keys invalid",
814 vim_account_candidates
= [e
["vimAccountId"] for e
in placement_ret_val
]
817 nslcmop_record_w_pinning
["id"],
818 rsp_payload
["placement"]["nslcmopId"],
822 self
.assertIs(type(rsp_payload
["placement"]["vnf"]), list, "vnf not a list")
823 expected_vnf_keys
= {"vimAccountId", "member-vnf-index"}
826 set(rsp_payload
["placement"]["vnf"][0]),
827 "placement['vnf'] missing keys",
830 rsp_payload
["placement"]["vnf"][0]["vimAccountId"],
831 vim_account_candidates
,
832 "vimAccountId invalid",
835 # Note: does not mock reading of price list and pil_info
837 NsPlacementDataFactory
, "__init__", lambda x0
, x1
, x2
, x3
, x4
, x5
: None
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(
848 mock__get_vim_accounts
,
849 mock_create_ns_placement_data
,
850 mock_do_placement_computation
,
853 check that raised exceptions are handled and response provided accordingly
855 server
= self
.serverSetup()
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
= [
864 "vimAccountId": "bbbbbbbb-38f5-438d-b8ee-3f93b3531f87",
865 "member-vnf-index": "1",
868 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
869 "member-vnf-index": "2",
872 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87",
873 "member-vnf-index": "3",
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"}
883 expected_keys
, set(rsp_payload
.keys()), "placement response missing keys"
885 self
.assertIs(type(rsp_payload
["placement"]["vnf"]), list, "vnf not a list")
886 self
.assertEqual([], rsp_payload
["placement"]["vnf"], "vnf list not empty")
888 nslcmop_record_w_pinning
["id"],
889 rsp_payload
["placement"]["nslcmopId"],