Coverage for osm_pla/test/test_server.py: 96%

200 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2024-06-22 10:12 +0000

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 

18import asyncio 

19import copy 

20import os 

21import sys 

22from unittest import TestCase, mock 

23from unittest.mock import Mock 

24 

25import yaml 

26 

27from osm_pla.placement.mznplacement import NsPlacementDataFactory, MznPlacementConductor 

28from pathlib import Path 

29 

30# need to Mock the imports from osm_common made in Server and Config beforehand 

31sys.modules["osm_common"] = Mock() 

32from osm_pla.server.server import Server # noqa: E402 

33from osm_pla.config.config import Config # noqa: E402 

34 

35nslcmop_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 

75nslcmop_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 

121nslcmop_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 

179list_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. 

326nsd_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 

431def _run(co_routine): 

432 return asyncio.get_event_loop().run_until_complete(co_routine) 

433 

434 

435def _async_mock(*args, **kwargs): 

436 m = mock.MagicMock(*args, **kwargs) 

437 

438 async def mock_coro(*args, **kwargs): 

439 return m(*args, **kwargs) 

440 

441 mock_coro.mock = m 

442 return mock_coro 

443 

444 

445###################################################### 

446 

447 

448class TestServer(TestCase): 

449 def _produce_ut_vim_accounts_info(self, list_of_vims): 

450 """ 

451 FIXME temporary, we will need more control over vim_urls and _id for test purpose - make a generator 

452 :return: vim_url and _id as dict, i.e. extract these from vim_accounts data 

453 """ 

454 return {_["name"]: _["_id"] for _ in list_of_vims} 

455 

456 def _produce_ut_vnf_price_list(self): 

457 price_list_file = "vnf_price_list.yaml" 

458 with open(str(Path(price_list_file))) as pl_fd: 

459 price_list_data = yaml.safe_load_all(pl_fd) 

460 return { 

461 i["vnfd"]: {i1["vim_name"]: i1["price"] for i1 in i["prices"]} 

462 for i in next(price_list_data) 

463 } 

464 

465 def _populate_pil_info(self, file): 

466 """ 

467 FIXME we need more control over content in pil information - more files or generator and data 

468 Note str(Path()) is a 3.5 thing 

469 """ 

470 with open(str(Path(file))) as pp_fd: 

471 test_data = yaml.safe_load_all(pp_fd) 

472 return next(test_data) 

473 

474 @mock.patch.object(Config, "_read_config_file") 

475 @mock.patch.object( 

476 Config, 

477 "get", 

478 side_effect=["doesnotmatter", "memory", "memory", "local", "doesnotmatter"], 

479 ) 

480 def serverSetup(self, mock_get, mock__read_config_file): 

481 """ 

482 Helper that returns a Server object 

483 :return: 

484 """ 

485 cfg = Config(None) 

486 return Server(cfg) 

487 

488 def _adjust_path(self, file): 

489 """In case we are not running from test directory, 

490 then assume we are in top level directory (e.g. running from tox) and adjust file path accordingly 

491 """ 

492 path_component = "/osm_pla/test/" 

493 real_path = os.path.realpath(file) 

494 if path_component not in real_path: 

495 return ( 

496 os.path.dirname(real_path) 

497 + path_component 

498 + os.path.basename(real_path) 

499 ) 

500 else: 

501 return real_path 

502 

503 def test__get_nslcmop(self): 

504 server = self.serverSetup() 

505 server.db = Mock() 

506 _ = server._get_nslcmop(nslcmop_record_wo_pinning["id"]) 

507 server.db.get_one.assert_called_with( 

508 "nslcmops", {"_id": nslcmop_record_wo_pinning["id"]} 

509 ) 

510 

511 def test__get_nsd(self): # OK 

512 server = self.serverSetup() 

513 server.db = Mock() 

514 _ = server._get_nsd(nslcmop_record_wo_pinning["operationParams"]["nsdId"]) 

515 server.db.get_one.assert_called_with( 

516 "nsds", {"_id": nslcmop_record_wo_pinning["operationParams"]["nsdId"]} 

517 ) 

518 

519 def test__create_vnf_id_maps(self): 

520 server = self.serverSetup() 

521 server.db = Mock() 

522 expected_mvi2mzn = {"one": "VNF0", "two": "VNF1", "three": "VNF2"} 

523 expected_mzn2mvi = {"VNF0": "one", "VNF1": "two", "VNF2": "three"} 

524 

525 nsd_for_test = copy.deepcopy(nsd_from_db) 

526 mvi2mzn, mzn2mvi = server._create_vnf_id_maps(nsd_for_test) 

527 

528 self.assertDictEqual( 

529 expected_mvi2mzn, mvi2mzn, "Faulty mzn2member-vnf-index mapping" 

530 ) 

531 self.assertDictEqual( 

532 expected_mzn2mvi, mzn2mvi, "Faulty mzn2member-vnf-index mapping" 

533 ) 

534 

535 def test__get_vim_accounts(self): # OK 

536 server = self.serverSetup() 

537 server.db = Mock() 

538 _ = server._get_vim_accounts( 

539 nslcmop_record_wo_pinning["operationParams"]["validVimAccounts"] 

540 ) 

541 server.db.get_list.assert_called_with( 

542 "vim_accounts", 

543 {"_id": nslcmop_record_wo_pinning["operationParams"]["validVimAccounts"]}, 

544 ) 

545 

546 def test__get_vnf_price_list(self): 

547 server = self.serverSetup() 

548 pl1 = server._get_vnf_price_list( 

549 Path(self._adjust_path("./vnf_price_list.yaml")) 

550 ) 

551 self.assertIs(type(pl1), dict, "price list not a dictionary") 

552 for k, v in pl1.items(): 

553 self.assertIs(type(v), dict, "price list values not a dict") 

554 

555 pl2 = server._get_vnf_price_list( 

556 Path(self._adjust_path("./vnf_price_list_keys.yaml")), "hackfest_project_a" 

557 ) 

558 self.assertIs(type(pl2), dict, "price list not a dictionary") 

559 for k, v in pl2.items(): 

560 self.assertIs(type(v), dict, "price list values not a dict") 

561 self.assertEqual(pl1, pl2, "non-project and project price lists differ") 

562 

563 def test__get_pil_info(self): 

564 server = self.serverSetup() 

565 ppi = server._get_pil_info(Path(self._adjust_path("./pil_price_list.yaml"))) 

566 self.assertIs(type(ppi), dict, "pil is not a dict") 

567 self.assertIn("pil", ppi.keys(), "pil has no pil key") 

568 self.assertIs(type(ppi["pil"]), list, "pil does not contain a list") 

569 # check for expected keys 

570 expected_keys = { 

571 "pil_description", 

572 "pil_price", 

573 "pil_latency", 

574 "pil_jitter", 

575 "pil_endpoints", 

576 } 

577 self.assertEqual(expected_keys, ppi["pil"][0].keys(), "expected keys not found") 

578 

579 # def test_handle_kafka_command(self): # OK 

580 # server = self.serverSetup() 

581 # server.loop.create_task = Mock() 

582 # server.handle_kafka_command("pli", "get_placement", {}) 

583 # server.loop.create_task.assert_not_called() 

584 # server.loop.create_task.reset_mock() 

585 # server.handle_kafka_command( 

586 # "pla", "get_placement", {"nslcmopId": nslcmop_record_wo_pinning["id"]} 

587 # ) 

588 # self.assertTrue(server.loop.create_task.called, "create_task not called") 

589 # args, kwargs = server.loop.create_task.call_args 

590 # self.assertIn("Server.get_placement", str(args[0]), "get_placement not called") 

591 

592 @mock.patch.object( 

593 NsPlacementDataFactory, "__init__", lambda x0, x1, x2, x3, x4, x5, x6: None 

594 ) 

595 @mock.patch.object(MznPlacementConductor, "do_placement_computation") 

596 @mock.patch.object(NsPlacementDataFactory, "create_ns_placement_data") 

597 @mock.patch.object(Server, "_get_vim_accounts") 

598 @mock.patch.object(Server, "_get_nsd") 

599 @mock.patch.object(Server, "_get_nslcmop") 

600 @mock.patch.object(Server, "_get_vnf_price_list") 

601 @mock.patch.object(Server, "_get_pil_info") 

602 @mock.patch.object(Server, "_get_projects") 

603 def test_get_placement( 

604 self, 

605 mock_get_projects, 

606 mock_get_pil_info, 

607 mock_get_vnf_price_list, 

608 mock__get_nslcmop, 

609 mock__get_nsd, 

610 mock__get_vim_accounts, 

611 mock_create_ns_placement_data, 

612 mock_do_placement_computation, 

613 ): 

614 """ 

615 run _get_placement and check that things get called as expected 

616 :return: 

617 """ 

618 placement_ret_val = [ 

619 { 

620 "vimAccountId": "bbbbbbbb-38f5-438d-b8ee-3f93b3531f87", 

621 "member-vnf-index": "VNF0", 

622 }, 

623 { 

624 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87", 

625 "member-vnf-index": "VNF1", 

626 }, 

627 { 

628 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87", 

629 "member-vnf-index": "VNF2", 

630 }, 

631 ] 

632 server = self.serverSetup() 

633 

634 server.msgBus.aiowrite = _async_mock() 

635 nsd_for_test = copy.deepcopy(nsd_from_db) 

636 mock__get_nsd.return_value = nsd_for_test 

637 mock__get_vim_accounts.return_value = list_of_vims 

638 

639 # FIXME need update to match nslcmop, not for test but for consistency 

640 mock_do_placement_computation.return_value = placement_ret_val 

641 _run(server.get_placement(nslcmop_record_wo_pinning["id"])) 

642 

643 self.assertTrue( 

644 mock_get_projects.called, "_get_projects not called as expected" 

645 ) 

646 self.assertTrue( 

647 mock_get_vnf_price_list.called, "_get_vnf_price_list not called as expected" 

648 ) 

649 self.assertTrue( 

650 mock_get_pil_info.called, "_get_pil_info not called as expected" 

651 ) 

652 self.assertTrue(mock__get_nslcmop.called, "_get_nslcmop not called as expected") 

653 # mock_get_nsd.assert_called_once() assert_called_once() for python > 3.5 

654 self.assertTrue(mock__get_nsd.called, "get_nsd not called as expected") 

655 # mock_get_enabled_vims.assert_called_once() assert_called_once() for python > 3.5 

656 self.assertTrue( 

657 mock__get_vim_accounts.called, "get_vim_accounts not called as expected" 

658 ) 

659 # mock_create_ns_placement_data.assert_called_once() assert_called_once() for python > 3.5 

660 self.assertTrue( 

661 mock_create_ns_placement_data.called, 

662 "create_ns_placement_data not called as expected", 

663 ) 

664 # mock_do_placement_computation.assert_called_once() assert_called_once() for python > 3.5 

665 self.assertTrue( 

666 mock_do_placement_computation.called, 

667 "do_placement_computation not called as expected", 

668 ) 

669 self.assertTrue(server.msgBus.aiowrite.mock.called) 

670 

671 args, kwargs = server.msgBus.aiowrite.mock.call_args 

672 self.assertTrue(len(args) == 3, "invalid format") 

673 self.assertEqual("pla", args[0], "topic invalid") 

674 self.assertEqual("placement", args[1], "message invalid") 

675 # extract placement result and check content 

676 rsp_payload = args[2] 

677 

678 expected_rsp_keys = {"placement"} 

679 self.assertEqual( 

680 expected_rsp_keys, 

681 set(rsp_payload.keys()), 

682 "placement response missing keys", 

683 ) 

684 self.assertIs(type(rsp_payload["placement"]), dict, "placement not a dict") 

685 

686 expected_placement_keys = {"vnf", "nslcmopId"} 

687 self.assertEqual( 

688 expected_placement_keys, 

689 set(rsp_payload["placement"]), 

690 "placement keys invalid", 

691 ) 

692 

693 vim_account_candidates = [e["vimAccountId"] for e in placement_ret_val] 

694 

695 self.assertEqual( 

696 nslcmop_record_wo_pinning["id"], 

697 rsp_payload["placement"]["nslcmopId"], 

698 "nslcmopId invalid", 

699 ) 

700 

701 self.assertIs(type(rsp_payload["placement"]["vnf"]), list, "vnf not a list") 

702 expected_vnf_keys = {"vimAccountId", "member-vnf-index"} 

703 self.assertEqual( 

704 expected_vnf_keys, 

705 set(rsp_payload["placement"]["vnf"][0]), 

706 "placement['vnf'] missing keys", 

707 ) 

708 self.assertIn( 

709 rsp_payload["placement"]["vnf"][0]["vimAccountId"], 

710 vim_account_candidates, 

711 "vimAccountId invalid", 

712 ) 

713 

714 @mock.patch.object( 

715 NsPlacementDataFactory, "__init__", lambda x0, x1, x2, x3, x4, x5, x6: None 

716 ) 

717 @mock.patch.object(MznPlacementConductor, "do_placement_computation") 

718 @mock.patch.object(NsPlacementDataFactory, "create_ns_placement_data") 

719 @mock.patch.object(Server, "_get_vim_accounts") 

720 @mock.patch.object(Server, "_get_nsd") 

721 @mock.patch.object(Server, "_get_nslcmop") 

722 @mock.patch.object(Server, "_get_vnf_price_list") 

723 @mock.patch.object(Server, "_get_pil_info") 

724 @mock.patch.object(Server, "_get_projects") 

725 def test_get_placement_with_pinning( 

726 self, 

727 mock_get_projects, 

728 mock_get_pil_info, 

729 mock_get_vnf_price_list, 

730 mock__get_nslcmop, 

731 mock__get_nsd, 

732 mock__get_vim_accounts, 

733 mock_create_ns_placement_data, 

734 mock_do_placement_computation, 

735 ): 

736 """ 

737 run _get_placement and check that things get called as expected 

738 :return: 

739 """ 

740 placement_ret_val = [ 

741 { 

742 "vimAccountId": "bbbbbbbb-38f5-438d-b8ee-3f93b3531f87", 

743 "member-vnf-index": "VNF0", 

744 }, 

745 { 

746 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87", 

747 "member-vnf-index": "VNF1", 

748 }, 

749 { 

750 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87", 

751 "member-vnf-index": "VNF2", 

752 }, 

753 ] 

754 server = self.serverSetup() 

755 

756 server.msgBus.aiowrite = _async_mock() 

757 nsd_for_test = copy.deepcopy(nsd_from_db) 

758 mock__get_nsd.return_value = nsd_for_test 

759 mock__get_vim_accounts.return_value = list_of_vims 

760 

761 # FIXME need update to match nslcmop, not for test but for consistency 

762 mock_do_placement_computation.return_value = placement_ret_val 

763 _run(server.get_placement(nslcmop_record_w_pinning["id"])) 

764 

765 self.assertTrue( 

766 (mock_get_projects.called, "_get_projects not called as expected") 

767 ) 

768 self.assertTrue( 

769 mock_get_vnf_price_list.called, "_get_vnf_price_list not called as expected" 

770 ) 

771 self.assertTrue( 

772 mock_get_pil_info.called, "_get_pil_info not called as expected" 

773 ) 

774 self.assertTrue(mock__get_nslcmop.called, "_get_nslcmop not called as expected") 

775 # mock_get_nsd.assert_called_once() assert_called_once() for python > 3.5 

776 self.assertTrue(mock__get_nsd.called, "get_nsd not called as expected") 

777 # mock_get_enabled_vims.assert_called_once() assert_called_once() for python > 3.5 

778 self.assertTrue( 

779 mock__get_vim_accounts.called, "get_vim_accounts not called as expected" 

780 ) 

781 # mock_create_ns_placement_data.assert_called_once() assert_called_once() for python > 3.5 

782 self.assertTrue( 

783 mock_create_ns_placement_data.called, 

784 "create_ns_placement_data not called as expected", 

785 ) 

786 # mock_do_placement_computation.assert_called_once() assert_called_once() for python > 3.5 

787 self.assertTrue( 

788 mock_do_placement_computation.called, 

789 "do_placement_computation not called as expected", 

790 ) 

791 self.assertTrue(server.msgBus.aiowrite.mock.called) 

792 

793 args, kwargs = server.msgBus.aiowrite.mock.call_args 

794 self.assertTrue(len(args) == 3, "invalid format") 

795 self.assertEqual("pla", args[0], "topic invalid") 

796 self.assertEqual("placement", args[1], "message invalid") 

797 # extract placement result and check content 

798 rsp_payload = args[2] 

799 

800 expected_rsp_keys = {"placement"} 

801 self.assertEqual( 

802 expected_rsp_keys, 

803 set(rsp_payload.keys()), 

804 "placement response missing keys", 

805 ) 

806 self.assertIs(type(rsp_payload["placement"]), dict, "placement not a dict") 

807 

808 expected_placement_keys = {"vnf", "nslcmopId"} 

809 self.assertEqual( 

810 expected_placement_keys, 

811 set(rsp_payload["placement"]), 

812 "placement keys invalid", 

813 ) 

814 

815 vim_account_candidates = [e["vimAccountId"] for e in placement_ret_val] 

816 

817 self.assertEqual( 

818 nslcmop_record_w_pinning["id"], 

819 rsp_payload["placement"]["nslcmopId"], 

820 "nslcmopId invalid", 

821 ) 

822 

823 self.assertIs(type(rsp_payload["placement"]["vnf"]), list, "vnf not a list") 

824 expected_vnf_keys = {"vimAccountId", "member-vnf-index"} 

825 self.assertEqual( 

826 expected_vnf_keys, 

827 set(rsp_payload["placement"]["vnf"][0]), 

828 "placement['vnf'] missing keys", 

829 ) 

830 self.assertIn( 

831 rsp_payload["placement"]["vnf"][0]["vimAccountId"], 

832 vim_account_candidates, 

833 "vimAccountId invalid", 

834 ) 

835 

836 # Note: does not mock reading of price list and pil_info 

837 @mock.patch.object( 

838 NsPlacementDataFactory, "__init__", lambda x0, x1, x2, x3, x4, x5: None 

839 ) 

840 @mock.patch.object(MznPlacementConductor, "do_placement_computation") 

841 @mock.patch.object(NsPlacementDataFactory, "create_ns_placement_data") 

842 @mock.patch.object(Server, "_get_vim_accounts") 

843 @mock.patch.object(Server, "_get_nsd") 

844 @mock.patch.object(Server, "_get_nslcmop") 

845 def test_get_placement_w_exception( 

846 self, 

847 mock__get_nslcmop, 

848 mock__get_nsd, 

849 mock__get_vim_accounts, 

850 mock_create_ns_placement_data, 

851 mock_do_placement_computation, 

852 ): 

853 """ 

854 check that raised exceptions are handled and response provided accordingly 

855 """ 

856 server = self.serverSetup() 

857 

858 server.msgBus.aiowrite = _async_mock() 

859 nsd_for_test = copy.deepcopy(nsd_from_db) 

860 mock__get_nsd.return_value = nsd_for_test 

861 mock__get_nsd.side_effect = RuntimeError("kaboom!") 

862 mock__get_vim_accounts.return_value = list_of_vims 

863 mock_do_placement_computation.return_value = [ 

864 { 

865 "vimAccountId": "bbbbbbbb-38f5-438d-b8ee-3f93b3531f87", 

866 "member-vnf-index": "1", 

867 }, 

868 { 

869 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87", 

870 "member-vnf-index": "2", 

871 }, 

872 { 

873 "vimAccountId": "aaaaaaaa-38f5-438d-b8ee-3f93b3531f87", 

874 "member-vnf-index": "3", 

875 }, 

876 ] 

877 

878 _run(server.get_placement(nslcmop_record_w_pinning["id"])) 

879 self.assertTrue(server.msgBus.aiowrite.mock.called) 

880 args, kwargs = server.msgBus.aiowrite.mock.call_args 

881 rsp_payload = args[2] 

882 expected_keys = {"placement"} 

883 self.assertEqual( 

884 expected_keys, set(rsp_payload.keys()), "placement response missing keys" 

885 ) 

886 self.assertIs(type(rsp_payload["placement"]["vnf"]), list, "vnf not a list") 

887 self.assertEqual([], rsp_payload["placement"]["vnf"], "vnf list not empty") 

888 self.assertEqual( 

889 nslcmop_record_w_pinning["id"], 

890 rsp_payload["placement"]["nslcmopId"], 

891 "nslcmopId invalid", 

892 )