1 # Copyright 2016-2017 ZTE Corporation.
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 implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
16 from rest_framework import status
17 from django.test import TestCase
18 from django.test import Client
20 from lcm.pub.utils import restcall
21 from lcm.pub.utils import fileutil
22 from lcm.pub.nfvi.vim.vimadaptor import VimAdaptor
23 from lcm.pub.database.models import NfPackageModel, VnfPackageFileModel, NfInstModel
24 from lcm.pub.database.models import JobStatusModel, JobModel
25 from lcm.packages.nf_package import NfOnBoardingThread, NfPkgDeletePendingThread
26 from lcm.packages.nf_package import NfPkgDeleteThread
27 from lcm.packages import nf_package
28 from lcm.pub.nfvi.vim.const import VIM_OPENSTACK
31 class TestNfPackage(TestCase):
33 self.client = Client()
34 NfPackageModel.objects.filter().delete()
35 VnfPackageFileModel.objects.filter().delete()
36 NfInstModel.objects.filter().delete()
37 JobModel.objects.filter().delete()
38 JobStatusModel.objects.filter().delete()
39 self.vnfd_raw_data = {
44 "plugin_info":"vbrasplugin_1.0",
46 "request_reclassification":False,
51 "vnfd_version":"1.0.0",
57 "id":"aaa_dnet_cp_0xu2j5sbigxc8h1ega3if0ld1",
58 "type_name":"tosca.nodes.nfv.ext.zte.CP",
59 "template_name":"aaa_dnet_cp",
62 "type_name":"integer",
67 "value":"bidirectional"
78 "type_name":"integer",
85 "source_requirement_index":0,
86 "target_node_id":"AAA_image_d8aseebr120nbm7bo1ohkj194",
87 "target_capability_name":"feature"
92 "id":"LB_Image_oj5l2ay8l2g6vcq6fsswzduha",
93 "type_name":"tosca.nodes.nfv.ext.ImageFile",
94 "template_name":"LB_Image",
101 "type_name":"string",
102 "value":"/SoftwareImages/image-lb"
105 "type_name":"string",
115 "plugin_info":"vbrasplugin_1.0",
117 "request_reclassification":False,
122 "vnfd_version":"1.0.0",
123 "id":"zte_vbras_1.0",
128 "name":"aaa_dnet_cp",
129 "type_name":"tosca.nodes.nfv.ext.zte.CP",
130 "default_instances":1,
134 "type_name":"integer",
138 "requirement_templates":[
140 "name":"virtualbinding",
141 "target_node_template_name":"AAA",
142 "target_capability_name":"virtualbinding"
154 def assert_job_result(self, job_id, job_progress, job_detail):
155 jobs = JobStatusModel.objects.filter(
157 progress=job_progress,
159 self.assertEqual(1, len(jobs))
161 @mock.patch.object(NfOnBoardingThread, 'run')
162 def test_nf_pkg_on_boarding_normal(self, mock_run):
163 resp = self.client.post("/api/nslcm/v0/vnfpackage", {
167 self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED)
169 @mock.patch.object(restcall, 'call_req')
170 def test_nf_pkg_on_boarding_when_on_boarded(self, mock_call_req):
171 mock_call_req.return_value = [0, json.JSONEncoder().encode({"onBoardState": "onBoarded"}), '200']
172 NfOnBoardingThread(csar_id="1",
176 self.assert_job_result("2", 255, "CSAR(1) already onBoarded.")
178 @mock.patch.object(restcall, 'call_req')
179 def test_nf_pkg_on_boarding_when_on_boarding(self, mock_call_req):
180 mock_call_req.return_value = [0, json.JSONEncoder().encode({
181 "onBoardState": "non-onBoarded",
182 "processState": "onBoarding"
184 NfOnBoardingThread(csar_id="2",
188 self.assert_job_result("3", 255, "CSAR(2) is onBoarding now.")
190 @mock.patch.object(restcall, 'call_req')
191 def test_nf_on_boarding_when_nfd_already_exists(self, mock_call_req):
193 "/api/catalog/v1/csars/2":
194 [0, json.JSONEncoder().encode({
195 "onBoardState": "onBoardFailed", "processState": "deleteFailed"}), '200'],
196 "/api/catalog/v1/servicetemplates/queryingrawdata":
197 [0, json.JSONEncoder().encode(self.vnfd_raw_data), '200']}
199 def side_effect(*args):
200 return mock_vals[args[4]]
202 mock_call_req.side_effect = side_effect
203 NfPackageModel(uuid="1", nfpackageid="2", vnfdid="zte_vbras_1.0").save()
204 NfOnBoardingThread(csar_id="2", vim_ids=["1"], lab_vim_id="", job_id="4").run()
205 self.assert_job_result("4", 255, "NFD(zte_vbras_1.0) already exists.")
207 @mock.patch.object(restcall, 'call_req')
208 @mock.patch.object(fileutil, 'download_file_from_http')
209 @mock.patch.object(VimAdaptor, '__init__')
210 @mock.patch.object(VimAdaptor, 'create_image')
211 @mock.patch.object(VimAdaptor, 'get_image')
212 def test_nf_on_boarding_when_successfully(self, mock_get_image, mock_create_image,
213 mock__init__, mock_download_file_from_http, mock_call_req):
214 mock_download_file_from_http.return_value = True, "/root/package"
216 "/api/catalog/v1/csars/2":
217 [0, json.JSONEncoder().encode({
218 "onBoardState": "onBoardFailed", "processState": "deleteFailed"}), '200'],
219 "/api/catalog/v1/servicetemplates/queryingrawdata":
220 [0, json.JSONEncoder().encode(self.vnfd_raw_data), '200'],
221 "/api/catalog/v1/csars/2/files?relativePath=/SoftwareImages/image-lb":
222 [0, json.JSONEncoder().encode({
223 "csar_file_info": [{"downloadUri": "8"}, {"localPath": "9"}]}), '200'],
224 "/api/extsys/v1/vims":
225 [0, json.JSONEncoder().encode([{
226 "vimId": "1", "type": VIM_OPENSTACK,
227 "url": "/root/package", "userName": "tom",
228 "password": "tom", "tenant": "10"}]), '200'],
229 "/api/catalog/v1/csars/2?onBoardState=onBoarded": [0, '{}', 200],
230 "/api/catalog/v1/csars/2?operationalState=Enabled": [0, '{}', 200],
231 "/api/catalog/v1/csars/2?processState=normal": [0, '{}', 200]}
232 mock_create_image.return_value = [0, {"id": "30", "name": "jerry", "res_type": 0}]
233 mock__init__.return_value = None
234 mock_get_image.return_value = [0, {"id": "30", "name": "jerry", "size": "60", "status": "active"}]
236 def side_effect(*args):
237 return mock_vals[args[4]]
238 mock_call_req.side_effect = side_effect
240 NfOnBoardingThread(csar_id="2", vim_ids=["1"], lab_vim_id="", job_id="4").run()
241 self.assert_job_result("4", 100, "CSAR(2) onBoarding successfully.")
243 @mock.patch.object(restcall, 'call_req')
244 @mock.patch.object(fileutil, 'download_file_from_http')
245 @mock.patch.object(VimAdaptor, '__init__')
246 @mock.patch.object(VimAdaptor, 'create_image')
247 @mock.patch.object(VimAdaptor, 'get_image')
248 def test_nf_on_boarding_when_timeout(self, mock_get_image, mock_create_image,
249 mock__init__, mock_download_file_from_http, mock_call_req):
250 nf_package.MAX_RETRY_TIMES = 2
251 nf_package.SLEEP_INTERVAL_SECONDS = 1
252 mock_download_file_from_http.return_value = True, "/root/package"
254 "/api/catalog/v1/csars/3":
255 [0, json.JSONEncoder().encode({"onBoardState": "onBoardFailed",
256 "processState": "deleteFailed"}), '200'],
257 "/api/catalog/v1/servicetemplates/queryingrawdata":
258 [0, json.JSONEncoder().encode(self.vnfd_raw_data), '200'],
259 "/api/catalog/v1/csars/3/files?relativePath=/SoftwareImages/image-lb":
260 [0, json.JSONEncoder().encode({
261 "csar_file_info": [{"downloadUri": "8"}, {"localPath": "9"}]}), '200'],
262 "/api/catalog/v1/csars/3?processState=onBoardFailed": [0, '{}', 200],
263 "/api/extsys/v1/vims":
264 [0, json.JSONEncoder().encode([{
265 "vimId": "1", "type": VIM_OPENSTACK,
266 "url": "/root/package", "userName": "tom",
267 "password": "tom", "tenant": "10"}]), 200]}
268 mock_create_image.return_value = [0, {"id": "30", "name": "jerry", "res_type": 0}]
269 mock__init__.return_value = None
270 mock_get_image.return_value = [0, {"id": "30", "name": "jerry", "size": "60", "status": "0"}]
272 def side_effect(*args):
273 return mock_vals[args[4]]
275 mock_call_req.side_effect = side_effect
276 NfOnBoardingThread(csar_id="3", vim_ids=["1"], lab_vim_id="", job_id="6").run()
277 self.assert_job_result("6", 255, "Failed to create image:timeout(2 seconds.)")
279 @mock.patch.object(restcall, 'call_req')
280 @mock.patch.object(fileutil, 'download_file_from_http')
281 @mock.patch.object(VimAdaptor, '__init__')
282 @mock.patch.object(VimAdaptor, 'create_image')
283 def test_nf_on_boarding_when_failed_to_create_image(self, mock_create_image,
284 mock__init__, mock_download_file_from_http, mock_call_req):
285 mock_download_file_from_http.return_value = True, "/root/package"
287 "/api/catalog/v1/csars/5":
288 [0, json.JSONEncoder().encode({
289 "onBoardState": "onBoardFailed", "processState": "deleteFailed"}), '200'],
290 "/api/catalog/v1/servicetemplates/queryingrawdata":
291 [0, json.JSONEncoder().encode(self.vnfd_raw_data), '200'],
292 "/api/catalog/v1/csars/5/files?relativePath=/SoftwareImages/image-lb":
293 [0, json.JSONEncoder().encode({
294 "csar_file_info": [{"downloadUri": "8"}, {"localPath": "9"}]}), '200'],
295 "/api/catalog/v1/csars/5?processState=onBoardFailed": [0, '{}', 200],
296 "/api/extsys/v1/vims":
297 [0, json.JSONEncoder().encode([{
298 "vimId": "1", "type": VIM_OPENSTACK,
299 "url": "/root/package", "userName": "tom",
300 "password": "tom", "tenant": "10"}]), '200']}
301 mock_create_image.return_value = [1, 'Unsupported image format.']
302 mock__init__.return_value = None
304 def side_effect(*args):
305 return mock_vals[args[4]]
306 mock_call_req.side_effect = side_effect
307 NfOnBoardingThread(csar_id="5", vim_ids=["1"], lab_vim_id="", job_id="8").run()
308 self.assert_job_result("8", 255, "Failed to create image:Unsupported image format.")
310 #########################################################################
311 @mock.patch.object(restcall, 'call_req')
312 def test_get_csar_successfully(self, mock_call_req):
313 mock_call_req.return_value = [0, json.JSONEncoder().encode({
314 "name": "1", "provider": "2", "version": "3", "operationalState": "4",
315 "usageState": "5", "onBoardState": "6", "processState": "7",
316 "deletionPending": "8", "downloadUri": "9", "createTime": "10",
317 "modifyTime": "11", "format": "12", "size": "13"
319 NfPackageModel(uuid="1", vnfdid="001", vendor="vendor",
320 vnfdversion="1.2.0", vnfversion="1.1.0", nfpackageid="13").save()
321 VnfPackageFileModel(id="1", filename="filename", imageid="00001",
322 vimid="1", vimuser="001", tenant="12", status="1", vnfpid="13").save()
323 NfInstModel(nfinstid="1", mnfinstid="001", nf_name="name", package_id="13").save()
324 resp = self.client.get("/api/nslcm/v0/vnfpackage/13")
325 self.assertEqual(resp.status_code, status.HTTP_200_OK)
330 "vnfdProvider": "vendor",
331 "vnfdVersion": "1.2.0",
332 "vnfVersion": "1.1.0",
336 "operationalState": "4",
340 "deletionPending": "8",
348 "fileName": "filename",
354 "vnfInstanceInfo": [{
355 "vnfInstanceId": "1",
356 "vnfInstanceName": "name"}]}
357 self.assertEqual(expect_data, resp.data)
359 #########################################################################
360 @mock.patch.object(restcall, 'call_req')
361 def test_delete_pending_csar_when_successfully(self, mock_call_req):
362 mock_call_req.return_value = [0, json.JSONEncoder().encode({
363 "processState": "deleting"}), "200"]
364 NfPkgDeletePendingThread(csar_id="1", job_id='2').run()
365 self.assert_job_result("2", 100, "Delete pending CSAR(1) successfully.")
367 @mock.patch.object(restcall, 'call_req')
368 def test_delete_pending_csar_when_deleting(self, mock_call_req):
369 NfPackageModel(uuid="01", nfpackageid="1").save()
370 mock_call_req.return_value = [0, json.JSONEncoder().encode({
371 "processState": "deleting"}), "200"]
372 NfPkgDeletePendingThread(csar_id="1", job_id='2').run()
373 self.assert_job_result("2", 100, "CSAR(1) is deleting now.")
375 @mock.patch.object(restcall, 'call_req')
376 def test_delete_pending_csar_when_not_deletion_pending(self, mock_call_req):
377 NfPackageModel(uuid="01", nfpackageid="1").save()
378 mock_call_req.return_value = [0, json.JSONEncoder().encode({
379 "deletionPending": "false"}), "200"]
380 NfPkgDeletePendingThread(csar_id="1", job_id='2').run()
381 self.assert_job_result("2", 100, "CSAR(1) need not to be deleted.")
383 @mock.patch.object(restcall, 'call_req')
384 def test_delete_pending_csar_when_in_using(self, mock_call_req):
385 mock_call_req.return_value = [0, json.JSONEncoder().encode({
386 "processState": "normal"}), "200"]
387 NfPackageModel(uuid="01", nfpackageid="1").save()
388 NfInstModel(nfinstid="01", package_id="1").save()
389 NfPkgDeletePendingThread(csar_id="1", job_id='2').run()
390 self.assert_job_result("2", 100, "CSAR(1) is in using, cannot be deleted.")
392 @mock.patch.object(VimAdaptor, '__init__')
393 @mock.patch.object(VimAdaptor, 'delete_image')
394 @mock.patch.object(restcall, 'call_req')
395 def test_delete_csarr_when_exception(self, mock_call_req, mock_delete_image, mock_init_):
397 ("/api/catalog/v1/csars/1", "DELETE"):
399 ("/api/catalog/v1/csars/1?processState=deleting", "PUT"):
401 ("/api/catalog/v1/csars/1?processState=deleteFailed", "PUT"):
403 ("/api/catalog/v1/csars/1", "GET"):
404 [0, json.JSONEncoder().encode({"processState": "normal"}), "200"],
405 ("/api/extsys/v1/vims", "GET"):
406 [0, json.JSONEncoder().encode([{"vimId": "002",
408 "userName": "test01",
409 "password": "123456",
410 "tenant": "test"}]), "200"]}
411 mock_delete_image.return_value = [0, "", '200']
413 def side_effect(*args):
414 return mock_vals[(args[4], args[5])]
416 mock_call_req.side_effect = side_effect
417 mock_init_.return_value = None
418 VnfPackageFileModel(vnfpid="1", imageid="001", vimid="002").save()
419 NfPackageModel(uuid="01", nfpackageid="1").save()
420 NfPkgDeletePendingThread(csar_id="1", job_id='2').run()
421 self.assert_job_result("2", 255, "Failed to delete CSAR(1) from catalog.")
423 @mock.patch.object(VimAdaptor, '__init__')
424 @mock.patch.object(VimAdaptor, 'delete_image')
425 @mock.patch.object(restcall, 'call_req')
426 def test_delete_csar_when_successfully(self, mock_call_req, mock_delete_image, mock_init_):
428 ("/api/catalog/v1/csars/1", "DELETE"):
429 [0, json.JSONEncoder().encode({"successfully": "successfully"}), "200"],
430 ("/api/catalog/v1/csars/1?processState=deleting", "PUT"):
431 [0, json.JSONEncoder().encode({"successfully": "successfully"}), "200"],
432 ("/api/catalog/v1/csars/1?processState=deleteFailed", "PUT"):
433 [0, json.JSONEncoder().encode({"successfully": "successfully"}), "200"],
434 ("/api/catalog/v1/csars/1", "GET"):
435 [0, json.JSONEncoder().encode({"notProcessState": "notProcessState"}), "200"],
436 ("/api/extsys/v1/vims", "GET"):
437 [0, json.JSONEncoder().encode([{
440 "userName": "test01",
441 "password": "123456",
442 "tenant": "test"}]), "200"]}
443 mock_delete_image.return_value = [0, json.JSONEncoder().encode({"test": "test"}), '200']
445 def side_effect(*args):
446 return mock_vals[(args[4], args[5])]
448 mock_call_req.side_effect = side_effect
449 mock_init_.return_value = None
450 VnfPackageFileModel(vnfpid="1", imageid="001", vimid="002").save()
451 NfPackageModel(uuid="01", nfpackageid="1").save()
452 NfPkgDeletePendingThread(csar_id="1", job_id='2').run()
453 self.assert_job_result("2", 100, "Delete CSAR(1) successfully.")
455 #########################################################################
456 @mock.patch.object(restcall, 'call_req')
457 def test_delete_nf_pkg_when_deleting(self, mock_call_req):
458 mock_call_req.return_value = [0, json.JSONEncoder().encode({"processState": "deleting"}), '200']
459 NfPkgDeleteThread(csar_id="1", job_id="2").run()
460 self.assert_job_result("2", 100, "CSAR(1) is deleting now.")
463 def test_get_nf_csars_normal(self):
464 NfPackageModel(uuid="01", nfpackageid="1", vnfdid="2").save()
465 resp = self.client.get("/api/nslcm/v0/vnfpackage")
466 self.assertEqual(resp.status_code, status.HTTP_200_OK)
467 self.assertEqual(1, len(resp.data["csars"]))
468 self.assertEqual("1", resp.data["csars"][0]["csarId"])
469 self.assertEqual("2", resp.data["csars"][0]["vnfdId"])