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
19 from unittest
.mock
import Mock
, mock_open
# patch, MagicMock
20 from osm_common
.dbbase
import DbException
21 from osm_nbi
.engine
import EngineException
22 from osm_common
.dbmemory
import DbMemory
23 from osm_common
.fsbase
import FsBase
24 from osm_common
.msgbase
import MsgBase
25 from http
import HTTPStatus
26 from osm_nbi
.instance_topics
import NsLcmOpTopic
, NsrTopic
27 from osm_nbi
.tests
.test_db_descriptors
import (
34 from copy
import deepcopy
38 class TestNsLcmOpTopic(unittest
.TestCase
):
41 self
.fs
= Mock(FsBase())
42 self
.fs
.get_params
.return_value
= {"./fake/folder"}
43 self
.fs
.file_open
= mock_open()
44 self
.msg
= Mock(MsgBase())
46 self
.nslcmop_topic
= NsLcmOpTopic(self
.db
, self
.fs
, self
.msg
, None)
47 self
.nslcmop_topic
.check_quota
= Mock(return_value
=None) # skip quota
50 "vim_accounts", yaml
.load(db_vim_accounts_text
, Loader
=yaml
.Loader
)
52 self
.db
.create_list("nsds", yaml
.load(db_nsds_text
, Loader
=yaml
.Loader
))
53 self
.db
.create_list("vnfds", yaml
.load(db_vnfds_text
, Loader
=yaml
.Loader
))
54 self
.db
.create_list("vnfrs", yaml
.load(db_vnfrs_text
, Loader
=yaml
.Loader
))
55 self
.db
.create_list("nsrs", yaml
.load(db_nsrs_text
, Loader
=yaml
.Loader
))
56 self
.db
.create
= Mock(return_value
="created_id")
57 self
.db
.set_one
= Mock(return_value
={"updated": 1})
58 self
.nsd
= self
.db
.get_list("nsds")[0]
59 self
.nsd_id
= self
.nsd
["_id"]
60 self
.nsr
= self
.db
.get_list("nsrs")[0]
61 self
.nsr_id
= self
.nsr
["_id"]
62 self
.nsr_project
= self
.nsr
["_admin"]["projects_read"][0]
64 self
.vim
= self
.db
.get_list("vim_accounts")[0]
65 self
.vim_id
= self
.vim
["_id"]
67 def test_create_instantiate(self
):
72 "project_id": [self
.nsr_project
],
77 "nsInstanceId": self
.nsr_id
,
79 "vimAccountId": self
.vim_id
,
80 "additionalParamsForVnf": [
82 "member-vnf-index": "1",
83 "additionalParams": {"touch_filename": "file"},
86 "member-vnf-index": "2",
87 "additionalParams": {"touch_filename": "file"},
92 "member-vnf-index": "1",
98 "name": "dataVM-eth0",
99 "ip-address": "10.11.12.13",
100 "floating-ip-required": True,
106 {"name": "internal", "vim-network-id": "vim-net-id"}
110 "lcmOperationType": "instantiate",
115 nslcmop_id
, _
= self
.nslcmop_topic
.new(
116 rollback
, session
, indata
=deepcopy(indata
), kwargs
=None, headers
=headers
119 # check nslcmop is created at database
121 self
.db
.create
.call_count
,
123 "database create not called, or called more than once",
125 _call
= self
.db
.create
.call_args_list
[0]
127 _call
[0][0], "nslcmops", "must be create a nslcmops entry at database"
130 created_nslcmop
= _call
[0][1]
133 created_nslcmop
["_id"],
134 "mismatch between return id and database '_id'",
138 created_nslcmop
["nsInstanceId"],
139 "bad reference id from nslcmop to nsr",
142 created_nslcmop
["_admin"].get("projects_read"),
143 "Database record must contain '_amdin.projects_read'",
147 created_nslcmop
["_admin"],
148 "Database record must contain '_admin.created'",
151 created_nslcmop
["lcmOperationType"] == "instantiate",
152 "Database record must contain 'lcmOperationType=instantiate'",
157 len(self
.db
.set_one
.call_args_list
) + 1,
158 "rollback mismatch with created/set items at database",
161 # test parameters with error
162 bad_id
= "88d90b0c-faff-4b9f-bccd-aaaaaaaaaaaa"
166 {"nsInstanceId": bad_id
},
168 HTTPStatus
.NOT_FOUND
,
169 ("not found", bad_id
),
172 # ({"vimAccountId": bad_id}, DbException, HTTPStatus.NOT_FOUND, ("not found", bad_id)), # TODO add "vim"
174 "bad member-vnf-index",
175 {"vnf.0.member-vnf-index": "k"},
177 HTTPStatus
.BAD_REQUEST
,
181 for message
, kwargs_
, expect_exc
, expect_code
, expect_text_list
in test_set
:
182 with self
.assertRaises(expect_exc
, msg
=message
) as e
:
183 self
.nslcmop_topic
.new(
186 indata
=deepcopy(indata
),
191 self
.assertTrue(e
.exception
.http_code
== expect_code
)
193 for expect_text
in expect_text_list
:
196 str(e
.exception
).lower(),
197 "Expected '{}' at exception text".format(expect_text
),
200 def test_check_ns_operation_action(self
):
201 nsrs
= self
.db
.get_list("nsrs")[0]
205 "member_vnf_index": "1",
207 "primitive": "touch",
208 "primitive_params": {"filename": "file"},
211 self
.nslcmop_topic
._check
_ns
_operation
(session
, nsrs
, "action", indata
)
213 indata_copy
= indata
.copy()
214 if k
== "primitive_params":
216 indata_copy
[k
] = "non_existing"
217 with self
.assertRaises(EngineException
) as exc_manager
:
218 self
.nslcmop_topic
._check
_ns
_operation
(
219 session
, nsrs
, "action", indata_copy
221 exc
= exc_manager
.exception
224 HTTPStatus
.BAD_REQUEST
,
225 "Engine exception bad http_code with {}".format(indata_copy
),
229 class TestNsrTopic(unittest
.TestCase
):
232 self
.fs
= Mock(FsBase())
233 self
.fs
.get_params
.return_value
= {"./fake/folder"}
234 self
.fs
.file_open
= mock_open()
235 self
.msg
= Mock(MsgBase())
237 self
.nsr_topic
= NsrTopic(self
.db
, self
.fs
, self
.msg
, None)
238 self
.nsr_topic
.check_quota
= Mock(return_value
=None) # skip quota
241 "vim_accounts", yaml
.load(db_vim_accounts_text
, Loader
=yaml
.Loader
)
243 self
.db
.create_list("nsds", yaml
.load(db_nsds_text
, Loader
=yaml
.Loader
))
244 self
.db
.create_list("vnfds", yaml
.load(db_vnfds_text
, Loader
=yaml
.Loader
))
245 self
.db
.create
= Mock(return_value
="created_id")
246 self
.nsd
= self
.db
.get_list("nsds")[0]
247 self
.nsd_id
= self
.nsd
["_id"]
248 self
.nsd_project
= self
.nsd
["_admin"]["projects_read"][0]
250 self
.vim
= self
.db
.get_list("vim_accounts")[0]
251 self
.vim_id
= self
.vim
["_id"]
253 def test_create(self
):
258 "project_id": [self
.nsd_project
],
262 "nsdId": self
.nsd_id
,
264 "vimAccountId": self
.vim_id
,
265 "additionalParamsForVnf": [
267 "member-vnf-index": "hackfest_vnf1",
268 "additionalParams": {"touch_filename": "file"},
271 "member-vnf-index": "hackfest_vnf2",
272 "additionalParams": {"touch_filename": "file"},
280 rollback
, session
, indata
=indata
, kwargs
=None, headers
=headers
283 # check vnfrs and nsrs created in whatever order
287 for _call
in self
.db
.create
.call_args_list
:
288 assert len(_call
[0]) >= 2, "called db.create with few parameters"
289 created_item
= _call
[0][1]
290 if _call
[0][0] == "vnfrs":
291 created_vnfrs
.append(created_item
)
293 "member-vnf-index-ref",
295 "Created item must contain member-vnf-index-ref section",
300 created_item
["nsr-id-ref"],
301 "bad reference id from vnfr to nsr",
304 nsr_id
= created_item
["nsr-id-ref"]
306 elif _call
[0][0] == "nsrs":
307 created_nsrs
.append(created_item
)
310 nsr_id
, created_item
["_id"], "bad reference id from vnfr to nsr"
313 nsr_id
= created_item
["_id"]
315 assert True, "created an unknown record {} at database".format(
320 created_item
["_admin"].get("projects_read"),
321 "Database record must contain '_amdin.projects_read'",
325 created_item
["_admin"],
326 "Database record must contain '_admin.created'",
329 created_item
["_admin"]["nsState"] == "NOT_INSTANTIATED",
330 "Database record must contain '_admin.nstate=NOT INSTANTIATE'",
334 len(created_vnfrs
), 2, "created a mismatch number of vnfr at database"
337 len(created_nsrs
), 1, "Only one nsrs must be created at database"
341 len(created_vnfrs
) + 1,
342 "rollback mismatch with created items at database",
345 # test parameters with error
346 bad_id
= "88d90b0c-faff-4b9f-bccd-aaaaaaaaaaaa"
353 HTTPStatus
.NOT_FOUND
,
354 ("not found", bad_id
),
356 # ({"vimAccountId": bad_id}, DbException, HTTPStatus.NOT_FOUND, ("not found", bad_id)), # TODO add "vim"
358 "additional params not supply",
359 {"additionalParamsForVnf.0.member-vnf-index": "k"},
361 HTTPStatus
.BAD_REQUEST
,
365 for message
, kwargs_
, expect_exc
, expect_code
, expect_text_list
in test_set
:
366 with self
.assertRaises(expect_exc
, msg
=message
) as e
:
370 indata
=deepcopy(indata
),
375 self
.assertTrue(e
.exception
.http_code
== expect_code
)
377 for expect_text
in expect_text_list
:
380 str(e
.exception
).lower(),
381 "Expected '{}' at exception text".format(expect_text
),
384 def test_delete_ns(self
):
385 self
.db
.create_list("nsrs", yaml
.load(db_nsrs_text
, Loader
=yaml
.Loader
))
386 self
.nsr
= self
.db
.get_list("nsrs")[0]
387 self
.nsr_id
= self
.nsr
["_id"]
388 self
.db_set_one
= self
.db
.set_one
389 p_id
= self
.nsd_project
396 "project_id": [p_id
],
403 "project_id": [p_other
],
413 with self
.subTest(i
=1, t
="Normal Deletion"):
414 self
.db
.del_one
= Mock()
415 self
.db
.set_one
= Mock()
416 self
.nsr_topic
.delete(session
, self
.nsr_id
)
418 db_args_ro_nsrs
= self
.db
.del_one
.call_args_list
[1][0]
419 db_args
= self
.db
.del_one
.call_args_list
[0][0]
420 msg_args
= self
.msg
.write
.call_args
[0]
422 msg_args
[0], self
.nsr_topic
.topic_msg
, "Wrong message topic"
424 self
.assertEqual(msg_args
[1], "deleted", "Wrong message action")
425 self
.assertEqual(msg_args
[2], {"_id": self
.nsr_id
}, "Wrong message content")
426 self
.assertEqual(db_args_ro_nsrs
[0], "ro_nsrs", "Wrong DB topic")
427 self
.assertEqual(db_args
[0], self
.nsr_topic
.topic
, "Wrong DB topic")
428 self
.assertEqual(db_args
[1]["_id"], self
.nsr_id
, "Wrong DB ID")
430 db_args
[1]["_admin.projects_read.cont"], [p_id
], "Wrong DB filter"
432 self
.db
.set_one
.assert_not_called()
433 fs_del_calls
= self
.fs
.file_delete
.call_args_list
434 self
.assertEqual(fs_del_calls
[0][0][0], self
.nsr_id
, "Wrong FS file id")
435 with self
.subTest(i
=2, t
="No delete because referenced by other project"):
438 {"_id": self
.nsr_id
},
441 "_admin.projects_read": p_other
,
442 "_admin.projects_write": p_other
,
445 self
.db
.del_one
.reset_mock()
446 self
.db
.set_one
.reset_mock()
447 self
.msg
.write
.reset_mock()
448 self
.fs
.file_delete
.reset_mock()
450 self
.nsr_topic
.delete(session2
, self
.nsr_id
)
451 self
.db
.del_one
.assert_not_called()
452 self
.msg
.write
.assert_not_called()
453 db_s1_args
= self
.db
.set_one
.call_args
454 self
.assertEqual(db_s1_args
[0][0], self
.nsr_topic
.topic
, "Wrong DB topic")
455 self
.assertEqual(db_s1_args
[0][1]["_id"], self
.nsr_id
, "Wrong DB ID")
457 db_s1_args
[1]["update_dict"], "Wrong DB update dictionary"
460 db_s1_args
[1]["pull_list"],
461 {"_admin.projects_read": [p_other
], "_admin.projects_write": [p_other
]},
462 "Wrong DB pull_list dictionary",
464 self
.fs
.file_delete
.assert_not_called()
465 with self
.subTest(i
=4, t
="Delete with force and admin"):
466 self
.db
.del_one
.reset_mock()
467 self
.db
.set_one
.reset_mock()
468 self
.msg
.write
.reset_mock()
469 self
.fs
.file_delete
.reset_mock()
470 self
.nsr_topic
.delete(session_force
, self
.nsr_id
)
472 db_args_ro_nsrs
= self
.db
.del_one
.call_args_list
[1][0]
473 db_args
= self
.db
.del_one
.call_args_list
[0][0]
474 msg_args
= self
.msg
.write
.call_args
[0]
476 msg_args
[0], self
.nsr_topic
.topic_msg
, "Wrong message topic"
478 self
.assertEqual(msg_args
[1], "deleted", "Wrong message action")
479 self
.assertEqual(msg_args
[2], {"_id": self
.nsr_id
}, "Wrong message content")
480 self
.assertEqual(db_args_ro_nsrs
[0], "ro_nsrs", "Wrong DB topic")
481 self
.assertEqual(db_args
[0], self
.nsr_topic
.topic
, "Wrong DB topic")
482 self
.assertEqual(db_args
[1]["_id"], self
.nsr_id
, "Wrong DB ID")
483 self
.db
.set_one
.assert_not_called()
484 fs_del_calls
= self
.fs
.file_delete
.call_args_list
485 self
.assertEqual(fs_del_calls
[0][0][0], self
.nsr_id
, "Wrong FS file id")
486 with self
.subTest(i
=3, t
="Conflict on Delete - NS in INSTANTIATED state"):
489 {"_id": self
.nsr_id
},
490 {"_admin.nsState": "INSTANTIATED"},
492 "_admin.projects_read": p_other
,
493 "_admin.projects_write": p_other
,
496 self
.db
.del_one
.reset_mock()
497 self
.db
.set_one
.reset_mock()
498 self
.msg
.write
.reset_mock()
499 self
.fs
.file_delete
.reset_mock()
501 with self
.assertRaises(
502 EngineException
, msg
="Accepted NSR with nsState INSTANTIATED"
504 self
.nsr_topic
.delete(session
, self
.nsr_id
)
506 e
.exception
.http_code
, HTTPStatus
.CONFLICT
, "Wrong HTTP status code"
508 self
.assertIn("INSTANTIATED", str(e
.exception
), "Wrong exception text")
509 # TODOD with self.subTest(i=3, t='Conflict on Delete - NS in use by NSI'):
511 with self
.subTest(i
=4, t
="Non-existent NS"):
512 self
.db
.del_one
.reset_mock()
513 self
.db
.set_one
.reset_mock()
514 self
.msg
.write
.reset_mock()
515 self
.fs
.file_delete
.reset_mock()
516 excp_msg
= "Not found"
517 with self
.assertRaises(
518 DbException
, msg
="Accepted non-existent NSD ID"
520 self
.nsr_topic
.delete(session2
, "other_id")
522 e
.exception
.http_code
, HTTPStatus
.NOT_FOUND
, "Wrong HTTP status code"
524 self
.assertIn(excp_msg
, str(e
.exception
), "Wrong exception text")
525 self
.assertIn("other_id", str(e
.exception
), "Wrong exception text")