2 # Licensed under the Apache License, Version 2.0 (the "License"); you may
3 # not use this file except in compliance with the License. You may obtain
4 # a copy of the License at
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11 # License for the specific language governing permissions and limitations
14 # For those usages not covered by the Apache License, Version 2.0 please
15 # contact: esousa@whitestack.com or alfonso.tiernosepulveda@telefonica.com
18 from contextlib
import contextmanager
21 from unittest
.mock
import Mock
, mock_open
22 from osm_common
.dbbase
import DbException
23 from osm_nbi
.engine
import EngineException
24 from osm_common
.dbmemory
import DbMemory
25 from osm_common
.fsbase
import FsBase
26 from osm_common
.msgbase
import MsgBase
27 from osm_common
import dbbase
28 from http
import HTTPStatus
29 from osm_nbi
.instance_topics
import NsLcmOpTopic
, NsrTopic
30 from osm_nbi
.tests
.test_db_descriptors
import (
32 db_paas_accounts_text
,
38 from copy
import deepcopy
42 class TestNsLcmOpTopic(unittest
.TestCase
):
45 self
.fs
= Mock(FsBase())
46 self
.fs
.get_params
.return_value
= {"./fake/folder"}
47 self
.fs
.file_open
= mock_open()
48 self
.msg
= Mock(MsgBase())
50 self
.nslcmop_topic
= NsLcmOpTopic(self
.db
, self
.fs
, self
.msg
, None)
51 self
.nslcmop_topic
.check_quota
= Mock(return_value
=None) # skip quota
54 "vim_accounts", yaml
.load(db_vim_accounts_text
, Loader
=yaml
.Loader
)
56 self
.db
.create_list("nsds", yaml
.load(db_nsds_text
, Loader
=yaml
.Loader
))
57 self
.db
.create_list("vnfds", yaml
.load(db_vnfds_text
, Loader
=yaml
.Loader
))
58 self
.db
.create_list("vnfrs", yaml
.load(db_vnfrs_text
, Loader
=yaml
.Loader
))
59 self
.db
.create_list("nsrs", yaml
.load(db_nsrs_text
, Loader
=yaml
.Loader
))
60 self
.db
.create
= Mock(return_value
="created_id")
61 self
.nsd
= self
.db
.get_list("nsds")[0]
62 self
.nsd_id
= self
.nsd
["_id"]
63 self
.nsr
= self
.db
.get_list("nsrs")[0]
64 self
.nsr_id
= self
.nsr
["_id"]
65 self
.nsr_project
= self
.nsr
["_admin"]["projects_read"][0]
67 self
.vim
= self
.db
.get_list("vim_accounts")[0]
68 self
.vim_id
= self
.vim
["_id"]
70 "paas", yaml
.load(db_paas_accounts_text
, Loader
=yaml
.Loader
)
73 paas_element
["_id"] for paas_element
in self
.db
.get_list("paas")
75 self
.paas_id
= self
.paas_ids
[0]
77 vnfr
["_id"]: vnfr
["member-vnf-index-ref"]
78 for vnfr
in self
.db
.get_list("vnfrs")
80 self
.number_of_vnfr
= len(self
.vnfrs
)
85 "project_id": [self
.nsr_project
],
89 def check_operation_params(self
, operation_params
):
90 self
.assertEqual(self
.nsd_id
, operation_params
["nsdId"])
91 self
.assertEqual(self
.nsr_id
, operation_params
["nsInstanceId"])
92 self
.assertEqual("name", operation_params
["nsName"])
94 def test_create_instantiate_with_vim_account(self
):
95 self
.db
.set_one
= Mock(return_value
={"updated": 1})
98 "nsInstanceId": self
.nsr_id
,
100 "vimAccountId": self
.vim_id
,
101 "additionalParamsForVnf": [
103 "member-vnf-index": "1",
104 "additionalParams": {"touch_filename": "file"},
107 "member-vnf-index": "2",
108 "additionalParams": {"touch_filename": "file"},
113 "member-vnf-index": "1",
119 "name": "dataVM-eth0",
120 "ip-address": "10.11.12.13",
121 "floating-ip-required": True,
127 {"name": "internal", "vim-network-id": "vim-net-id"}
131 "lcmOperationType": "instantiate",
136 nslcmop_id
, _
= self
.nslcmop_topic
.new(
139 indata
=deepcopy(indata
),
144 # check nslcmop is created at database
146 self
.db
.create
.call_count
,
148 "database create not called, or called more than once",
150 _call
= self
.db
.create
.call_args_list
[0]
152 _call
[0][0], "nslcmops", "must be create a nslcmops entry at database"
155 created_nslcmop
= _call
[0][1]
158 created_nslcmop
["_id"],
159 "mismatch between return id and database '_id'",
163 created_nslcmop
["nsInstanceId"],
164 "bad reference id from nslcmop to nsr",
167 created_nslcmop
["_admin"].get("projects_read"),
168 "Database record must contain '_amdin.projects_read'",
172 created_nslcmop
["_admin"],
173 "Database record must contain '_admin.created'",
176 created_nslcmop
["lcmOperationType"] == "instantiate",
177 "Database record must contain 'lcmOperationType=instantiate'",
182 len(self
.db
.set_one
.call_args_list
) + 1,
183 "rollback mismatch with created/set items at database",
186 operation_params
= created_nslcmop
["operationParams"]
187 self
.check_operation_params(operation_params
)
188 self
.assertEqual(self
.vim_id
, operation_params
["vimAccountId"])
189 self
.assertNotIn("paasAccountId", operation_params
)
191 self
.assertEqual(len(self
.db
.set_one
.call_args_list
), self
.number_of_vnfr
)
193 for call
in self
.db
.set_one
.call_args_list
:
194 topic
, vnfr_id
, update
= call
[0]
195 self
.assertEqual(topic
, "vnfrs")
196 self
.assertIn(vnfr_id
["_id"], self
.vnfrs
)
197 self
.assertEqual(self
.vim_id
, update
["vim-account-id"])
198 self
.assertNotIn("paas-account-id", update
)
200 # test parameters with error
201 bad_id
= "88d90b0c-faff-4b9f-bccd-aaaaaaaaaaaa"
205 {"nsInstanceId": bad_id
},
207 HTTPStatus
.NOT_FOUND
,
208 ("not found", bad_id
),
211 # ({"vimAccountId": bad_id}, DbException, HTTPStatus.NOT_FOUND, ("not found", bad_id)), # TODO add "vim"
213 "bad member-vnf-index",
214 {"vnf.0.member-vnf-index": "k"},
216 HTTPStatus
.BAD_REQUEST
,
220 for message
, kwargs_
, expect_exc
, expect_code
, expect_text_list
in test_set
:
221 with self
.assertRaises(expect_exc
, msg
=message
) as e
:
222 self
.nslcmop_topic
.new(
225 indata
=deepcopy(indata
),
230 self
.assertTrue(e
.exception
.http_code
== expect_code
)
232 for expect_text
in expect_text_list
:
235 str(e
.exception
).lower(),
236 "Expected '{}' at exception text".format(expect_text
),
239 def test_create_instantiate_with_paas_account(self
):
240 self
.db
.set_one
= Mock(return_value
={"updated": 1})
242 "nsdId": self
.nsd_id
,
243 "nsInstanceId": self
.nsr_id
,
245 "paasAccountId": self
.paas_id
,
246 "lcmOperationType": "instantiate",
249 nslcmop_id
, _
= self
.nslcmop_topic
.new(
250 [], self
.new_session
, indata
=deepcopy(indata
), kwargs
=None, headers
={}
253 # check nslcmop is created at database
255 self
.db
.create
.call_count
,
257 "database create not called, or called more than once",
259 _call
= self
.db
.create
.call_args_list
[0]
261 _call
[0][0], "nslcmops", "must be create a nslcmops entry at database"
264 created_nslcmop
= _call
[0][1]
267 created_nslcmop
["_id"],
268 "mismatch between return id and database '_id'",
271 operation_params
= created_nslcmop
["operationParams"]
272 self
.check_operation_params(operation_params
)
273 self
.assertEqual(self
.paas_id
, operation_params
["paasAccountId"])
274 self
.assertNotIn("vimAccountId", operation_params
)
276 self
.assertEqual(len(self
.db
.set_one
.call_args_list
), self
.number_of_vnfr
)
278 for call
in self
.db
.set_one
.call_args_list
:
279 topic
, vnfr_id
, update
= call
[0]
280 self
.assertEqual(topic
, "vnfrs")
281 self
.assertIn(vnfr_id
["_id"], self
.vnfrs
)
282 self
.assertEqual(self
.paas_id
, update
["paas-account-id"])
283 self
.assertNotIn("vim-account-id", update
)
285 def test_create_instantiate_invalid_paas_account_raises_exception(self
):
286 self
.db
.set_one
= Mock(return_value
={"updated": 1})
287 invalid_paas_id
= "88d90b0c-faff-4b9f-bccd-017f33985984"
289 "nsdId": self
.nsd_id
,
290 "nsInstanceId": self
.nsr_id
,
292 "paasAccountId": invalid_paas_id
,
293 "lcmOperationType": "instantiate",
296 with self
.assertRaises(EngineException
):
297 nslcmop_id
, _
= self
.nslcmop_topic
.new(
298 [], self
.new_session
, indata
=deepcopy(indata
), kwargs
=None, headers
={}
300 self
.db
.set_one
.assert_not_called()
302 def test_create_instantiate_with_paas_account_in_vnf(self
):
303 self
.db
.set_one
= Mock(return_value
={"updated": 1})
305 "nsdId": self
.nsd_id
,
306 "nsInstanceId": self
.nsr_id
,
308 "paasAccountId": self
.paas_ids
[0],
309 "lcmOperationType": "instantiate",
310 "vnf": [{"member-vnf-index": "1", "paasAccountId": self
.paas_ids
[1]}],
313 nslcmop_id
, _
= self
.nslcmop_topic
.new(
314 [], self
.new_session
, indata
=deepcopy(indata
), kwargs
=None, headers
={}
317 # check nslcmop is created at database
319 self
.db
.create
.call_count
,
321 "database create not called, or called more than once",
323 _call
= self
.db
.create
.call_args_list
[0]
325 _call
[0][0], "nslcmops", "must be create a nslcmops entry at database"
328 created_nslcmop
= _call
[0][1]
331 created_nslcmop
["_id"],
332 "mismatch between return id and database '_id'",
335 operation_params
= created_nslcmop
["operationParams"]
336 self
.check_operation_params(operation_params
)
337 self
.assertEqual(self
.paas_id
, operation_params
["paasAccountId"])
338 self
.assertNotIn("vimAccountId", operation_params
)
339 expected_paas_id
= ""
341 self
.assertEqual(len(self
.db
.set_one
.call_args_list
), self
.number_of_vnfr
)
342 for call
in self
.db
.set_one
.call_args_list
:
343 topic
, vnfr_id
, update
= call
[0]
344 self
.assertEqual(topic
, "vnfrs")
345 self
.assertIn(vnfr_id
["_id"], self
.vnfrs
)
346 vnf_index_ref
= self
.vnfrs
[vnfr_id
["_id"]]
347 if vnf_index_ref
== "1":
348 expected_paas_id
= self
.paas_ids
[1]
349 elif vnf_index_ref
== "2":
350 expected_paas_id
= self
.paas_ids
[0]
353 self
.assertEqual(expected_paas_id
, update
["paas-account-id"])
354 self
.assertNotIn("vim-account-id", update
)
356 def test_create_instantiate_invalid_paas_account_in_vnf_raises_exception(self
):
357 self
.db
.set_one
= Mock(return_value
={"updated": 1})
358 invalid_paas_id
= "88d90b0c-faff-4b9f-bccd-017f33985984"
360 "nsdId": self
.nsd_id
,
361 "nsInstanceId": self
.nsr_id
,
363 "paasAccountId": self
.paas_ids
[0],
364 "lcmOperationType": "instantiate",
365 "vnf": [{"member-vnf-index": "1", "paasAccountId": invalid_paas_id
}],
368 with self
.assertRaises(EngineException
):
369 self
.nslcmop_topic
.new(
370 [], self
.new_session
, indata
=deepcopy(indata
), kwargs
=None, headers
={}
372 self
.db
.set_one
.assert_not_called()
374 def test_check_ns_operation_action(self
):
375 nsrs
= self
.db
.get_list("nsrs")[0]
379 "member_vnf_index": "1",
381 "primitive": "touch",
382 "primitive_params": {"filename": "file"},
385 self
.nslcmop_topic
._check
_ns
_operation
(session
, nsrs
, "action", indata
)
387 indata_copy
= indata
.copy()
388 if k
== "primitive_params":
390 indata_copy
[k
] = "non_existing"
391 with self
.assertRaises(EngineException
) as exc_manager
:
392 self
.nslcmop_topic
._check
_ns
_operation
(
393 session
, nsrs
, "action", indata_copy
395 exc
= exc_manager
.exception
398 HTTPStatus
.BAD_REQUEST
,
399 "Engine exception bad http_code with {}".format(indata_copy
),
402 def test_update_remove_vnf(self
):
403 vnfr_id
= self
.db
.get_list("vnfrs")[0]["_id"]
406 "nsrs", {"_id": self
.nsr_id
}, {"_admin.nsState": "INSTANTIATED"}
409 "lcmOperationType": "update",
410 "updateType": "REMOVE_VNF",
411 "nsInstanceId": self
.nsr_id
,
412 "removeVnfInstanceId": vnfr_id
,
419 "project_id": [self
.nsr_project
],
425 nslcmop_id
, _
= self
.nslcmop_topic
.new(
426 rollback
, session
, indata
, kwargs
=None, headers
=headers
430 self
.db
.create
.call_count
,
432 "database create not called, or called more than once",
434 _call
= self
.db
.create
.call_args_list
[0]
436 _call
[0][0], "nslcmops", "nslcmops entry must be created at database"
438 created_nslcmop
= _call
[0][1]
441 created_nslcmop
["nsInstanceId"],
442 "mismatch between nsId '_id' in created nslcmop and database nsr",
445 created_nslcmop
["lcmOperationType"] == "update",
446 "Database record must contain 'lcmOperationType=update'",
449 created_nslcmop
["operationParams"]["updateType"] == "REMOVE_VNF",
450 "Database record must contain 'updateType=REMOVE_VNF'",
453 def test_migrate(self
):
456 "nsrs", {"_id": self
.nsr_id
}, {"_admin.nsState": "INSTANTIATED"}
462 "project_id": [self
.nsr_project
],
468 with self
.subTest(i
=1, t
="Migration for Specific VM"):
470 "lcmOperationType": "migrate",
471 "nsInstanceId": self
.nsr_id
,
472 "migrateToHost": "sample02",
473 "vdu": {"vduCountIndex": 0, "vduId": "mgmtVM"},
474 "vnfInstanceId": "9e8006df-cdfa-4f63-bf6a-fce860d71c1f",
476 nslcmop_id
, _
= self
.nslcmop_topic
.new(
477 rollback
, session
, indata
, kwargs
=None, headers
=headers
481 self
.db
.create
.call_count
,
483 "database create not called, or called more than once",
485 _call
= self
.db
.create
.call_args_list
[0]
487 _call
[0][0], "nslcmops", "nslcmops entry must be created at database"
489 created_nslcmop
= _call
[0][1]
492 created_nslcmop
["nsInstanceId"],
493 "mismatch between nsId '_id' in created nslcmop and database nsr",
496 created_nslcmop
["lcmOperationType"] == "migrate",
497 "Database record must contain 'lcmOperationType=migrate'",
499 with self
.subTest(i
=2, t
="Migration of all VDUs in a VNF"):
501 "lcmOperationType": "migrate",
502 "nsInstanceId": self
.nsr_id
,
503 "vnfInstanceId": "9e8006df-cdfa-4f63-bf6a-fce860d71c1f",
505 nslcmop_id
, _
= self
.nslcmop_topic
.new(
506 rollback
, session
, indata
, kwargs
=None, headers
=headers
510 self
.db
.create
.call_count
,
512 "database create not called, or called more than once",
514 _call
= self
.db
.create
.call_args_list
[0]
516 _call
[0][0], "nslcmops", "nslcmops entry must be created at database"
518 created_nslcmop
= _call
[0][1]
521 created_nslcmop
["nsInstanceId"],
522 "mismatch between nsId '_id' in created nslcmop and database nsr",
525 created_nslcmop
["lcmOperationType"] == "migrate",
526 "Database record must contain 'lcmOperationType=migrate'",
528 with self
.subTest(i
=3, t
="Migration failure - vduId not provided in vdu "):
530 "lcmOperationType": "migrate",
531 "nsInstanceId": self
.nsr_id
,
532 "migrateToHost": "sample02",
533 "vdu": {"vduCountIndex": 0},
534 "vnfInstanceId": "9e8006df-cdfa-4f63-bf6a-fce860d71c1f",
537 with self
.assertRaises(Exception) as e
:
538 nslcmop_id
, _
= self
.nslcmop_topic
.new(
539 rollback
, session
, indata
, kwargs
=None, headers
=headers
542 "Format error at 'vdu' ''vduId' is a required property'"
547 class TestNsLcmOpTopicWithMock(unittest
.TestCase
):
549 self
.db
= Mock(dbbase
.DbBase())
550 self
.fs
= Mock(FsBase())
551 self
.fs
.get_params
.return_value
= {"./fake/folder"}
552 self
.fs
.file_open
= mock_open()
553 self
.msg
= Mock(MsgBase())
555 self
.nslcmop_topic
= NsLcmOpTopic(self
.db
, self
.fs
, self
.msg
, None)
557 def test_get_vnfd_from_vnf_member_revision(self
):
558 test_vnfr
= yaml
.load(db_vnfrs_text
, Loader
=yaml
.Loader
)[0]
559 test_vnfd
= yaml
.load(db_vnfds_text
, Loader
=yaml
.Loader
)
560 self
.db
.get_one
.side_effect
= [test_vnfr
, test_vnfd
]
561 self
.nslcmop_topic
._get
_vnfd
_from
_vnf
_member
_index
("1", test_vnfr
["_id"])
563 self
.db
.get_one
.call_args_list
[0][0][0],
565 "Incorrect first DB lookup",
568 self
.db
.get_one
.call_args_list
[1][0][0],
570 "Incorrect second DB lookup",
573 def test_get_vnfd_from_vnf_member_no_revision(self
):
574 test_vnfr
= yaml
.load(db_vnfrs_text
, Loader
=yaml
.Loader
)[0]
575 test_vnfr
["revision"] = 3
576 test_vnfd
= yaml
.load(db_vnfds_text
, Loader
=yaml
.Loader
)
577 self
.db
.get_one
.side_effect
= [test_vnfr
, test_vnfd
]
578 self
.nslcmop_topic
._get
_vnfd
_from
_vnf
_member
_index
("1", test_vnfr
["_id"])
580 self
.db
.get_one
.call_args_list
[0][0][0],
582 "Incorrect first DB lookup",
585 self
.db
.get_one
.call_args_list
[1][0][0],
587 "Incorrect second DB lookup",
591 def assertNotRaises(self
, exception_type
):
594 except exception_type
:
595 raise self
.failureException("{} raised".format(exception_type
.__name
__))
597 def test_check_ns_update_operation(self
):
599 self
.nslcmop_topic
= NsLcmOpTopic(self
.db
, self
.fs
, self
.msg
, None)
602 with self
.subTest(i
=1, t
="VNF instance does not belong to NS"):
603 test_vnfr
= yaml
.load(db_vnfrs_text
, Loader
=yaml
.Loader
)
604 test_vnfr
[0]["revision"] = 2
605 test_nsr
= yaml
.load(db_nsrs_text
, Loader
=yaml
.Loader
)
606 test_nsr
[0]["constituent-vnfr-ref"][
608 ] = "99d90b0c-faff-4b9f-bccd-017f33985984"
609 self
.db
.create_list("vnfrs", test_vnfr
)
610 self
.db
.create_list("nsrs", test_nsr
)
611 nsrs
= self
.db
.get_list("nsrs")[0]
613 "updateType": "CHANGE_VNFPKG",
614 "changeVnfPackageData": {
615 "vnfInstanceId": "88d90b0c-faff-4b9f-bccd-017f33985984",
616 "vnfdId": "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77",
618 "nsInstanceId": "f48163a6-c807-47bc-9682-f72caef5af85",
620 with self
.assertRaises(EngineException
) as expected_exception
:
621 self
.nslcmop_topic
._check
_ns
_operation
(session
, nsrs
, "update", indata
)
623 str(expected_exception
.exception
),
624 "Error in validating ns-update request: vnf 88d90b0c-faff-4b9f-bccd-017f33985984"
625 " does not belong to NS f48163a6-c807-47bc-9682-f72caef5af85",
628 with self
.subTest(i
=2, t
="Ns update request validated with no exception"):
629 test_vnfr
= yaml
.load(db_vnfrs_text
, Loader
=yaml
.Loader
)
630 test_vnfr
[0]["revision"] = 2
631 test_nsr
= yaml
.load(db_nsrs_text
, Loader
=yaml
.Loader
)
632 self
.db
.create_list("vnfrs", test_vnfr
)
633 self
.db
.create_list("nsrs", test_nsr
)
634 nsrs
= self
.db
.get_list("nsrs")[1]
636 "updateType": "CHANGE_VNFPKG",
637 "changeVnfPackageData": {
638 "vnfInstanceId": "88d90b0c-faff-4b9f-bccd-017f33985984",
639 "vnfdId": "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77",
641 "nsInstanceId": "f48163a6-c807-47bc-9682-f72caef5af85",
643 with self
.assertNotRaises(EngineException
):
644 self
.nslcmop_topic
._check
_ns
_operation
(session
, nsrs
, "update", indata
)
647 i
=3, t
="Ns update request rejected because of too small timeout"
650 "updateType": "CHANGE_VNFPKG",
651 "changeVnfPackageData": {
652 "vnfInstanceId": "88d90b0c-faff-4b9f-bccd-017f33985984",
653 "vnfdId": "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77",
655 "nsInstanceId": "f48163a6-c807-47bc-9682-f72caef5af85",
656 "timeout_ns_update": 50,
658 with self
.assertRaises(EngineException
) as expected_exception
:
659 self
.nslcmop_topic
._check
_ns
_operation
(session
, nsrs
, "update", indata
)
661 str(expected_exception
.exception
),
662 "Error in validating ns-update request: 50 second is not enough "
663 "to upgrade the VNF instance: 88d90b0c-faff-4b9f-bccd-017f33985984",
666 with self
.subTest(i
=4, t
="wrong vnfdid is given as an update parameter"):
667 test_vnfr
= yaml
.load(db_vnfrs_text
, Loader
=yaml
.Loader
)
668 test_vnfr
[0]["revision"] = 2
669 test_nsr
= yaml
.load(db_nsrs_text
, Loader
=yaml
.Loader
)
670 self
.db
.create_list("vnfrs", test_vnfr
)
671 self
.db
.create_list("nsrs", test_nsr
)
672 nsrs
= self
.db
.get_list("nsrs")[2]
674 "updateType": "CHANGE_VNFPKG",
675 "changeVnfPackageData": {
676 "vnfInstanceId": "88d90b0c-faff-4b9f-bccd-017f33985984",
677 "vnfdId": "9637bcf8-cf14-42dc-ad70-c66fcf1e6e77",
679 "nsInstanceId": "f48163a6-c807-47bc-9682-f72caef5af85",
681 with self
.assertRaises(EngineException
) as expected_exception
:
682 self
.nslcmop_topic
._check
_ns
_operation
(session
, nsrs
, "update", indata
)
684 str(expected_exception
.exception
),
685 "Error in validating ns-update request: vnfd-id 9637bcf8-cf14-42dc-ad70-c66fcf1e6e77 does not "
686 "match with the vnfd-id: 7637bcf8-cf14-42dc-ad70-c66fcf1e6e77 of "
687 "VNF instance: 88d90b0c-faff-4b9f-bccd-017f33985984",
691 i
=5, t
="Ns update REMOVE_VNF request validated with no exception"
693 test_vnfr
= yaml
.load(db_vnfrs_text
, Loader
=yaml
.Loader
)
694 test_vnfr
[0]["revision"] = 2
695 test_nsr
= yaml
.load(db_nsrs_text
, Loader
=yaml
.Loader
)
696 self
.db
.create_list("vnfrs", test_vnfr
)
697 self
.db
.create_list("nsrs", test_nsr
)
698 nsrs
= self
.db
.get_list("nsrs")[1]
700 "updateType": "REMOVE_VNF",
701 "removeVnfInstanceId": "88d90b0c-faff-4b9f-bccd-017f33985984",
702 "nsInstanceId": "f48163a6-c807-47bc-9682-f72caef5af85",
704 with self
.assertNotRaises(EngineException
):
705 self
.nslcmop_topic
._check
_ns
_operation
(session
, nsrs
, "update", indata
)
708 class TestNsrTopic(unittest
.TestCase
):
711 self
.fs
= Mock(FsBase())
712 self
.fs
.get_params
.return_value
= {"./fake/folder"}
713 self
.fs
.file_open
= mock_open()
714 self
.msg
= Mock(MsgBase())
716 self
.nsr_topic
= NsrTopic(self
.db
, self
.fs
, self
.msg
, None)
717 self
.nsr_topic
.check_quota
= Mock(return_value
=None) # skip quota
720 "vim_accounts", yaml
.load(db_vim_accounts_text
, Loader
=yaml
.Loader
)
722 self
.db
.create_list("nsds", yaml
.load(db_nsds_text
, Loader
=yaml
.Loader
))
723 self
.db
.create_list("vnfds", yaml
.load(db_vnfds_text
, Loader
=yaml
.Loader
))
724 self
.db
.create
= Mock(return_value
="created_id")
725 self
.nsd
= self
.db
.get_list("nsds")[0]
726 self
.nsd_id
= self
.nsd
["_id"]
727 self
.nsd_project
= self
.nsd
["_admin"]["projects_read"][0]
729 self
.vim
= self
.db
.get_list("vim_accounts")[0]
730 self
.vim_id
= self
.vim
["_id"]
732 self
.paas_id
= "3d93ca4e-b007-4d94-bbdc-61911078b864"
734 def stock_db_creations(self
, created_vnfrs
, created_nsrs
):
735 for _call
in self
.db
.create
.call_args_list
:
736 assert len(_call
[0]) >= 2, "called db.create with few parameters"
737 created_item
= _call
[0][1]
738 if _call
[0][0] == "vnfrs":
739 created_vnfrs
.append(created_item
)
740 elif _call
[0][0] == "nsrs":
741 created_nsrs
.append(created_item
)
743 assert False, "created an unknown record {} at database".format(
746 self
.check_admin_field_in_db_call(created_item
)
748 def check_admin_field_in_db_call(self
, created_item
):
750 created_item
["_admin"].get("projects_read"),
751 "Database record must contain '_amdin.projects_read'",
755 created_item
["_admin"],
756 "Database record must contain '_admin.created'",
759 created_item
["_admin"]["nsState"] == "NOT_INSTANTIATED",
760 "Database record must contain '_admin.nstate=NOT INSTANTIATE'",
763 def check_vnfr_content(self
, vnfr
, nsr_id
):
764 self
.assertEqual(vnfr
["vim-account-id"], None)
765 self
.assertEqual(vnfr
["paas-account-id"], None)
767 "member-vnf-index-ref",
769 "Created item must contain member-vnf-index-ref section",
772 nsr_id
, vnfr
["nsr-id-ref"], "bad reference id from vnfr to nsr"
774 self
.check_vdur_interfaces(vnfr
)
776 def check_vdur_interfaces(self
, vnfr
):
778 vnfr
["vdur"][0]["interfaces"][0]["position"],
780 "vdur first interface position does not match",
783 vnfr
["vdur"][0]["interfaces"][1]["position"],
785 "vdur second interface position does not match",
788 def check_vnfrs(self
, created_vnfrs
, nsrs_id
):
790 len(created_vnfrs
), 2, "created a mismatch number of vnfr at database"
792 for vnfr
in created_vnfrs
:
793 self
.check_vnfr_content(vnfr
, nsrs_id
)
795 def check_vnfrs_ref_in_nsr(self
, nsrs
, created_vnfrs
):
796 self
.assertEqual(len(nsrs
["constituent-vnfr-ref"]), len(created_vnfrs
))
797 self
.assertNotEqual(created_vnfrs
[0]["_id"], created_vnfrs
[1]["_id"])
798 for vnfr
in created_vnfrs
:
799 vnfr_id
= vnfr
["_id"]
800 self
.assertIn(vnfr_id
, nsrs
["constituent-vnfr-ref"])
802 def check_nsrs(self
, created_nsrs
, created_vnfrs
, vim_id
, paas_id
):
804 len(created_nsrs
), 1, "Only one nsrs must be created at database"
806 nsrs
= created_nsrs
[0]
807 self
.assertEqual(nsrs
["vimdatacenter"], vim_id
)
808 self
.assertEqual(nsrs
["paasdatacenter"], paas_id
)
809 self
.check_vnfrs_ref_in_nsr(nsrs
, created_vnfrs
)
811 def check_created_nsrs_and_vnfrs(
812 self
, created_nsrs
, created_vnfrs
, rollback
, vim_id
, paas_id
815 nsrs_id
= created_nsrs
[0]["_id"]
819 len(created_vnfrs
) + 1,
820 "rollback mismatch with created items at database",
823 self
.check_nsrs(created_nsrs
, created_vnfrs
, vim_id
, paas_id
)
824 self
.check_vnfrs(created_vnfrs
, nsrs_id
)
826 def test_create_with_vim_account(self
):
831 "project_id": [self
.nsd_project
],
835 "nsdId": self
.nsd_id
,
837 "vimAccountId": self
.vim_id
,
838 "additionalParamsForVnf": [
840 "member-vnf-index": "hackfest_vnf1",
841 "additionalParams": {"touch_filename": "file"},
844 "member-vnf-index": "hackfest_vnf2",
845 "additionalParams": {"touch_filename": "file"},
853 rollback
, session
, indata
=indata
, kwargs
=None, headers
=headers
856 # check vnfrs and nsrs created in whatever order
859 self
.stock_db_creations(created_vnfrs
, created_nsrs
)
860 self
.check_created_nsrs_and_vnfrs(
861 created_nsrs
, created_vnfrs
, rollback
, self
.vim_id
, None
864 def test_create_with_vim_account_raise_exception(self
):
865 # test parameters with error
866 bad_id
= "88d90b0c-faff-4b9f-bccd-aaaaaaaaaaaa"
871 "project_id": [self
.nsd_project
],
875 "nsdId": self
.nsd_id
,
877 "vimAccountId": self
.vim_id
,
878 "additionalParamsForVnf": [
880 "member-vnf-index": "hackfest_vnf1",
881 "additionalParams": {"touch_filename": "file"},
884 "member-vnf-index": "hackfest_vnf2",
885 "additionalParams": {"touch_filename": "file"},
897 HTTPStatus
.NOT_FOUND
,
898 ("not found", bad_id
),
900 # ({"vimAccountId": bad_id}, DbException, HTTPStatus.NOT_FOUND, ("not found", bad_id)), # TODO add "vim"
902 "additional params not supply",
903 {"additionalParamsForVnf.0.member-vnf-index": "k"},
905 HTTPStatus
.BAD_REQUEST
,
909 for message
, kwargs_
, expect_exc
, expect_code
, expect_text_list
in test_set
:
910 with self
.assertRaises(expect_exc
, msg
=message
) as e
:
914 indata
=deepcopy(indata
),
919 self
.assertTrue(e
.exception
.http_code
== expect_code
)
921 for expect_text
in expect_text_list
:
924 str(e
.exception
).lower(),
925 "Expected '{}' at exception text".format(expect_text
),
928 def test_create_with_paas_account(self
):
933 "project_id": [self
.nsd_project
],
937 "nsdId": self
.nsd_id
,
939 "paasAccountId": self
.paas_id
,
940 "additionalParamsForVnf": [
942 "member-vnf-index": "hackfest_vnf1",
943 "additionalParams": {"touch_filename": "file"},
946 "member-vnf-index": "hackfest_vnf2",
947 "additionalParams": {"touch_filename": "file"},
955 rollback
, session
, indata
=indata
, kwargs
=None, headers
=headers
960 self
.stock_db_creations(created_vnfrs
, created_nsrs
)
961 self
.check_created_nsrs_and_vnfrs(
962 created_nsrs
, created_vnfrs
, rollback
, None, self
.paas_id
965 def test_show_instance(self
):
970 "project_id": [self
.nsd_project
],
974 for refresh_status
in ("true", "false"):
975 self
.db
.create_list("nsrs", yaml
.load(db_nsrs_text
, Loader
=yaml
.Loader
))
976 actual_nsr
= self
.db
.get_list("nsrs")[0]
977 nsr_id
= actual_nsr
["_id"]
978 filter_q
["vcaStatus-refresh"] = refresh_status
979 expected_nsr
= self
.nsr_topic
.show(session
, nsr_id
, filter_q
=filter_q
)
980 self
.nsr_topic
.delete(session
, nsr_id
)
981 actual_nsr
.pop("_admin")
982 expected_nsr
.pop("_admin")
984 expected_nsr
, actual_nsr
, "Database nsr and show() nsr do not match."
987 def test_vca_status_refresh(self
):
992 "project_id": [self
.nsd_project
],
995 filter_q
= {"vcaStatus-refresh": "true"}
997 self
.db
.create_list("nsrs", yaml
.load(db_nsrs_text
, Loader
=yaml
.Loader
))
998 nsr
= self
.db
.get_list("nsrs")[0]
1000 # When vcaStatus-refresh is true
1001 filter_q
["vcaStatus-refresh"] = "true"
1002 self
.nsr_topic
.vca_status_refresh(session
, nsr
, filter_q
)
1003 msg_args
= self
.msg
.write
.call_args
[0]
1004 self
.assertEqual(msg_args
[1], "vca_status_refresh", "Wrong message action")
1005 self
.assertGreater(nsr
["_admin"]["modified"], time() - time_delta
)
1007 # When vcaStatus-refresh is false but modified time is within threshold
1008 filter_q
["vcaStatus-refresh"] = "false"
1010 nsr
["_admin"]["modified"] = time_now
1011 self
.nsr_topic
.vca_status_refresh(session
, nsr
, filter_q
)
1012 msg_args
= self
.msg
.write
.call_args
[1]
1013 self
.assertEqual(msg_args
, {}, "Message should not be sent.")
1015 nsr
["_admin"]["modified"], time_now
, "Modified time should not be changed."
1018 # When vcaStatus-refresh is false but modified time is less than threshold
1019 filter_q
["vcaStatus-refresh"] = "false"
1020 nsr
["_admin"]["modified"] = time() - (2 * time_delta
)
1021 self
.nsr_topic
.vca_status_refresh(session
, nsr
, filter_q
)
1022 msg_args
= self
.msg
.write
.call_args
[0]
1023 self
.assertEqual(msg_args
[1], "vca_status_refresh", "Wrong message action")
1024 self
.nsr_topic
.delete(session
, nsr
["_id"])
1026 nsr
["_admin"]["modified"],
1027 time() - time_delta
,
1028 "Modified time is not changed.",
1031 def test_delete_ns(self
):
1032 self
.db
.create_list("nsrs", yaml
.load(db_nsrs_text
, Loader
=yaml
.Loader
))
1033 self
.nsr
= self
.db
.get_list("nsrs")[0]
1034 self
.nsr_id
= self
.nsr
["_id"]
1035 self
.db_set_one
= self
.db
.set_one
1036 p_id
= self
.nsd_project
1043 "project_id": [p_id
],
1050 "project_id": [p_other
],
1060 with self
.subTest(i
=1, t
="Normal Deletion"):
1061 self
.db
.del_one
= Mock()
1062 self
.db
.set_one
= Mock()
1063 self
.nsr_topic
.delete(session
, self
.nsr_id
)
1065 db_args_ro_nsrs
= self
.db
.del_one
.call_args_list
[1][0]
1066 db_args
= self
.db
.del_one
.call_args_list
[0][0]
1067 msg_args
= self
.msg
.write
.call_args
[0]
1069 msg_args
[0], self
.nsr_topic
.topic_msg
, "Wrong message topic"
1071 self
.assertEqual(msg_args
[1], "deleted", "Wrong message action")
1072 self
.assertEqual(msg_args
[2], {"_id": self
.nsr_id
}, "Wrong message content")
1073 self
.assertEqual(db_args_ro_nsrs
[0], "ro_nsrs", "Wrong DB topic")
1074 self
.assertEqual(db_args
[0], self
.nsr_topic
.topic
, "Wrong DB topic")
1075 self
.assertEqual(db_args
[1]["_id"], self
.nsr_id
, "Wrong DB ID")
1077 db_args
[1]["_admin.projects_read.cont"], [p_id
], "Wrong DB filter"
1079 self
.db
.set_one
.assert_not_called()
1080 fs_del_calls
= self
.fs
.file_delete
.call_args_list
1081 self
.assertEqual(fs_del_calls
[0][0][0], self
.nsr_id
, "Wrong FS file id")
1082 with self
.subTest(i
=2, t
="No delete because referenced by other project"):
1085 {"_id": self
.nsr_id
},
1088 "_admin.projects_read": p_other
,
1089 "_admin.projects_write": p_other
,
1092 self
.db
.del_one
.reset_mock()
1093 self
.db
.set_one
.reset_mock()
1094 self
.msg
.write
.reset_mock()
1095 self
.fs
.file_delete
.reset_mock()
1097 self
.nsr_topic
.delete(session2
, self
.nsr_id
)
1098 self
.db
.del_one
.assert_not_called()
1099 self
.msg
.write
.assert_not_called()
1100 db_s1_args
= self
.db
.set_one
.call_args
1101 self
.assertEqual(db_s1_args
[0][0], self
.nsr_topic
.topic
, "Wrong DB topic")
1102 self
.assertEqual(db_s1_args
[0][1]["_id"], self
.nsr_id
, "Wrong DB ID")
1104 db_s1_args
[1]["update_dict"], "Wrong DB update dictionary"
1107 db_s1_args
[1]["pull_list"],
1108 {"_admin.projects_read": [p_other
], "_admin.projects_write": [p_other
]},
1109 "Wrong DB pull_list dictionary",
1111 self
.fs
.file_delete
.assert_not_called()
1112 with self
.subTest(i
=4, t
="Delete with force and admin"):
1113 self
.db
.del_one
.reset_mock()
1114 self
.db
.set_one
.reset_mock()
1115 self
.msg
.write
.reset_mock()
1116 self
.fs
.file_delete
.reset_mock()
1117 self
.nsr_topic
.delete(session_force
, self
.nsr_id
)
1119 db_args_ro_nsrs
= self
.db
.del_one
.call_args_list
[1][0]
1120 db_args
= self
.db
.del_one
.call_args_list
[0][0]
1121 msg_args
= self
.msg
.write
.call_args
[0]
1123 msg_args
[0], self
.nsr_topic
.topic_msg
, "Wrong message topic"
1125 self
.assertEqual(msg_args
[1], "deleted", "Wrong message action")
1126 self
.assertEqual(msg_args
[2], {"_id": self
.nsr_id
}, "Wrong message content")
1127 self
.assertEqual(db_args_ro_nsrs
[0], "ro_nsrs", "Wrong DB topic")
1128 self
.assertEqual(db_args
[0], self
.nsr_topic
.topic
, "Wrong DB topic")
1129 self
.assertEqual(db_args
[1]["_id"], self
.nsr_id
, "Wrong DB ID")
1130 self
.db
.set_one
.assert_not_called()
1131 fs_del_calls
= self
.fs
.file_delete
.call_args_list
1132 self
.assertEqual(fs_del_calls
[0][0][0], self
.nsr_id
, "Wrong FS file id")
1133 with self
.subTest(i
=3, t
="Conflict on Delete - NS in INSTANTIATED state"):
1136 {"_id": self
.nsr_id
},
1137 {"_admin.nsState": "INSTANTIATED"},
1139 "_admin.projects_read": p_other
,
1140 "_admin.projects_write": p_other
,
1143 self
.db
.del_one
.reset_mock()
1144 self
.db
.set_one
.reset_mock()
1145 self
.msg
.write
.reset_mock()
1146 self
.fs
.file_delete
.reset_mock()
1148 with self
.assertRaises(
1149 EngineException
, msg
="Accepted NSR with nsState INSTANTIATED"
1151 self
.nsr_topic
.delete(session
, self
.nsr_id
)
1153 e
.exception
.http_code
, HTTPStatus
.CONFLICT
, "Wrong HTTP status code"
1155 self
.assertIn("INSTANTIATED", str(e
.exception
), "Wrong exception text")
1156 # TODOD with self.subTest(i=3, t='Conflict on Delete - NS in use by NSI'):
1158 with self
.subTest(i
=4, t
="Non-existent NS"):
1159 self
.db
.del_one
.reset_mock()
1160 self
.db
.set_one
.reset_mock()
1161 self
.msg
.write
.reset_mock()
1162 self
.fs
.file_delete
.reset_mock()
1163 excp_msg
= "Not found"
1164 with self
.assertRaises(
1165 DbException
, msg
="Accepted non-existent NSD ID"
1167 self
.nsr_topic
.delete(session2
, "other_id")
1169 e
.exception
.http_code
, HTTPStatus
.NOT_FOUND
, "Wrong HTTP status code"
1171 self
.assertIn(excp_msg
, str(e
.exception
), "Wrong exception text")
1172 self
.assertIn("other_id", str(e
.exception
), "Wrong exception text")