7c9d547fe6d30445ace24e4d800de2a547378881
[modeling/etsicatalog.git] / catalog / packages / biz / sdc_vnf_package.py
1 # Copyright 2017 ZTE Corporation.
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 implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import json
16 import logging
17 import os
18 import sys
19 import threading
20 import traceback
21 import zipfile
22
23 from catalog.packages.biz.vnf_package import VnfPackage
24 from catalog.packages import const
25 from catalog.pub.config.config import CATALOG_ROOT_PATH, CATALOG_URL_PATH
26 from catalog.pub.config.config import REG_TO_MSB_REG_PARAM
27 from catalog.pub.database.models import VnfPackageModel
28 from catalog.pub.exceptions import CatalogException, PackageHasExistsException
29 from catalog.pub.msapi import sdc
30 from catalog.pub.utils import fileutil
31 from catalog.pub.utils import toscaparser
32 from catalog.pub.utils.jobutil import JobUtil, JOB_ERROR_CODE
33 from catalog.packages.biz.notificationsutil import PkgNotifications
34
35 logger = logging.getLogger(__name__)
36
37 JOB_ERROR = 255
38
39
40 def nf_get_csars():
41     ret = None
42     try:
43         ret = NfPackage().get_csars()
44     except CatalogException as e:
45         return [1, e.args[0]]
46     except Exception as e:
47         logger.error(e.args[0])
48         logger.error(traceback.format_exc())
49         return [1, str(sys.exc_info())]
50     return ret
51
52
53 def nf_get_csar(csar_id):
54     ret = None
55     try:
56         ret = NfPackage().get_csar(csar_id)
57     except CatalogException as e:
58         return [1, e.args[0]]
59     except Exception as e:
60         logger.error(e.args[0])
61         logger.error(traceback.format_exc())
62         return [1, str(sys.exc_info())]
63     return ret
64
65
66 def parse_vnfd(csar_id, inputs):
67     ret = None
68     try:
69         nf_pkg = VnfPackageModel.objects.filter(vnfPackageId=csar_id)
70         if not nf_pkg:
71             raise CatalogException("VNF CSAR(%s) does not exist." % csar_id)
72         csar_path = nf_pkg[0].localFilePath
73         ret = {"model": toscaparser.parse_vnfd(csar_path, inputs)}
74     except CatalogException as e:
75         return [1, e.args[0]]
76     except Exception as e:
77         logger.error(e.args[0])
78         logger.error(traceback.format_exc())
79         return [1, str(sys.exc_info())]
80     return [0, ret]
81
82
83 class NfDistributeThread(threading.Thread):
84     """
85     Sdc NF Package Distribute
86     """
87
88     def __init__(self, csar_id, vim_ids, lab_vim_id, job_id):
89         threading.Thread.__init__(self)
90         self.csar_id = csar_id
91         self.vim_ids = vim_ids
92         self.lab_vim_id = lab_vim_id
93         self.job_id = job_id
94
95         self.csar_save_path = os.path.join(CATALOG_ROOT_PATH, csar_id)
96
97     def run(self):
98         try:
99             self.on_distribute()
100         except PackageHasExistsException as e:
101             self.rollback_distribute()
102             JobUtil.add_job_status(self.job_id, JOB_ERROR, e.args[0], error_code=JOB_ERROR_CODE.PACKAGE_EXIST)
103         except CatalogException as e:
104             self.rollback_distribute()
105             JobUtil.add_job_status(self.job_id, JOB_ERROR, e.args[0], error_code=JOB_ERROR_CODE.CATALOG_EXCEPTION)
106         except Exception as e:
107             logger.error(e.args[0])
108             logger.error(traceback.format_exc())
109             logger.error(str(sys.exc_info()))
110             self.rollback_distribute()
111             JobUtil.add_job_status(self.job_id, JOB_ERROR, "Failed to distribute CSAR(%s)" % self.csar_id,
112                                    error_code=JOB_ERROR_CODE.SYSTEM_ERROR)
113
114     def on_distribute(self):
115         JobUtil.create_job(
116             inst_type='nf',
117             jobaction='on_distribute',
118             inst_id=self.csar_id,
119             job_id=self.job_id)
120         JobUtil.add_job_status(self.job_id, 5, "Start CSAR(%s) distribute." % self.csar_id)
121
122         if VnfPackageModel.objects.filter(vnfPackageId=self.csar_id):
123             err_msg = "NF CSAR(%s) already exists." % self.csar_id
124             JobUtil.add_job_status(self.job_id, JOB_ERROR, err_msg, error_code=JOB_ERROR_CODE.PACKAGE_EXIST)
125             return
126
127         artifact = sdc.get_artifact(sdc.ASSETTYPE_RESOURCES, self.csar_id)
128         local_path = os.path.join(CATALOG_ROOT_PATH, self.csar_id)
129         csar_name = "%s.csar" % artifact.get("name", self.csar_id)
130         local_file_name = sdc.download_artifacts(artifact["toscaModelURL"], local_path, csar_name)
131         if local_file_name.endswith(".csar") or local_file_name.endswith(".zip"):
132             fileutil.unzip_csar(local_file_name, local_path)
133             vendor_vnf_file = ''
134             # find original vendor ETSI package under the ETSI_PACKAGE directory
135             etsi_package_dir = os.path.join(local_path, "Artifacts/Deployment/ETSI_PACKAGE")
136             if os.path.exists(etsi_package_dir):
137                 files = os.listdir(etsi_package_dir)
138                 for file_name in files:
139                     a_file = os.path.join(etsi_package_dir, file_name)
140                     if os.path.isfile(a_file) & file_name.endswith(".csar"):
141                         vendor_vnf_file = a_file
142                         break
143
144             # find original vendor ETSI package under Artifacts/Deployment/OTHER directory
145             if vendor_vnf_file.strip() == '':
146                 vendor_vnf_file = os.path.join(local_path, "Artifacts/Deployment/OTHER/vnf.csar")
147                 if os.path.exists(vendor_vnf_file):
148                     local_file_name = vendor_vnf_file
149             else:
150                 local_file_name = vendor_vnf_file
151
152         # create VNFD zip file
153         self.create_vnfd_zip(self.csar_id, vendor_vnf_file)
154
155         vnfd_json = toscaparser.parse_vnfd(local_file_name)
156         vnfd = json.JSONDecoder().decode(vnfd_json)
157
158         if not vnfd.get("vnf"):
159             raise CatalogException("VNF properties and metadata in VNF Package(id=%s) are empty." % self.csar_id)
160
161         vnfd_id = vnfd["vnf"]["properties"].get("descriptor_id", "")
162         if VnfPackageModel.objects.filter(vnfdId=vnfd_id):
163             logger.error("VNF package(%s) already exists.", vnfd_id)
164             raise PackageHasExistsException("VNF package(%s) already exists." % vnfd_id)
165         JobUtil.add_job_status(self.job_id, 30, "Save CSAR(%s) to database." % self.csar_id)
166         vnfd_ver = vnfd["vnf"]["properties"].get("descriptor_version", "")
167         vnf_provider = vnfd["vnf"]["properties"].get("provider", "")
168         vnf_software_version = vnfd["vnf"]["properties"].get("software_version", "")
169         vnfd_product_name = vnfd["vnf"]["properties"].get("product_name", "")
170         VnfPackageModel(
171             vnfPackageId=self.csar_id,
172             vnfdId=vnfd_id,
173             vnfVendor=vnf_provider,
174             vnfdProductName=vnfd_product_name,
175             vnfdVersion=vnfd_ver,
176             vnfSoftwareVersion=vnf_software_version,
177             vnfdModel=vnfd_json,
178             localFilePath=local_file_name,
179             vnfPackageUri=csar_name,
180             onboardingState=const.PKG_STATUS.ONBOARDED,
181             operationalState=const.PKG_STATUS.ENABLED,
182             usageState=const.PKG_STATUS.NOT_IN_USE
183         ).save()
184         JobUtil.add_job_status(self.job_id, 100, "CSAR(%s) distribute successfully." % self.csar_id)
185         send_notification(self.csar_id, const.PKG_NOTIFICATION_TYPE.ONBOARDING,
186                           const.PKG_CHANGE_TYPE.OP_STATE_CHANGE)
187
188     def create_vnfd_zip(self, csar_id, vendor_vnf_file):
189         """
190         Create VNFD zip file.
191         :param csar_id: CSAR Id
192         :param vendor_vnf_file: the vendor original package(csar)
193         :return:
194         """
195         if os.path.exists(vendor_vnf_file):
196             # create VNFD from vendor original package
197             VnfPackage().creat_vnfd(csar_id, vendor_vnf_file)
198         else:
199             try:
200                 vnf_package_path = os.path.join(CATALOG_ROOT_PATH, self.csar_id)
201                 vnfd_zip_file = os.path.join(vnf_package_path, 'VNFD.zip')
202                 with zipfile.ZipFile(vnfd_zip_file, 'w', zipfile.ZIP_DEFLATED) as vnfd_zip:
203                     def_path = os.path.join(vnf_package_path, "Definitions")
204                     if os.path.exists(def_path):
205                         def_files = os.listdir(def_path)
206                         for def_file in def_files:
207                             full_path = os.path.join(def_path, def_file)
208                             vnfd_zip.write(full_path, def_file)
209             except Exception as e:
210                 logger.error(e)
211                 if os.path.exists(vnfd_zip_file):
212                     os.remove(vnfd_zip_file)
213
214     def rollback_distribute(self):
215         try:
216             VnfPackageModel.objects.filter(vnfPackageId=self.csar_id).delete()
217             fileutil.delete_dirs(self.csar_save_path)
218         except Exception as e:
219             logger.error(e.args[0])
220             logger.error(traceback.format_exc())
221             logger.error(str(sys.exc_info()))
222
223
224 class NfPkgDeleteThread(threading.Thread):
225     """
226     Sdc NF Package Deleting
227     """
228
229     def __init__(self, csar_id, job_id):
230         threading.Thread.__init__(self)
231         self.csar_id = csar_id
232         self.job_id = job_id
233
234     def run(self):
235         try:
236             self.delete_csar()
237         except CatalogException as e:
238             JobUtil.add_job_status(self.job_id, JOB_ERROR, e.args[0])
239         except Exception as e:
240             logger.error(e.args[0])
241             logger.error(traceback.format_exc())
242             logger.error(str(sys.exc_info()))
243             JobUtil.add_job_status(self.job_id, JOB_ERROR, "Failed to delete CSAR(%s)" % self.csar_id)
244
245     def delete_csar(self):
246         JobUtil.create_job(
247             inst_type='nf',
248             jobaction='delete',
249             inst_id=self.csar_id,
250             job_id=self.job_id)
251         JobUtil.add_job_status(self.job_id, 5, "Start to delete CSAR(%s)." % self.csar_id)
252         send_notification(self.csar_id, const.PKG_NOTIFICATION_TYPE.CHANGE,
253                           const.PKG_CHANGE_TYPE.PKG_DELETE)
254         VnfPackageModel.objects.filter(vnfPackageId=self.csar_id).delete()
255
256         JobUtil.add_job_status(self.job_id, 50, "Delete local CSAR(%s) file." % self.csar_id)
257
258         csar_save_path = os.path.join(CATALOG_ROOT_PATH, self.csar_id)
259         fileutil.delete_dirs(csar_save_path)
260
261         JobUtil.add_job_status(self.job_id, 100, "Delete CSAR(%s) successfully." % self.csar_id)
262
263
264 class NfPackage(object):
265     """
266     Actions for sdc nf package.
267     """
268
269     def __init__(self):
270         pass
271
272     def get_csars(self):
273         csars = []
274         nf_pkgs = VnfPackageModel.objects.filter()
275         for nf_pkg in nf_pkgs:
276             ret = self.get_csar(nf_pkg.vnfPackageId)
277             csars.append(ret[1])
278         return [0, csars]
279
280     def get_csar(self, csar_id):
281         pkg_info = {}
282         nf_pkg = VnfPackageModel.objects.filter(vnfPackageId=csar_id)
283         if not nf_pkg:
284             nf_pkg = VnfPackageModel.objects.filter(vnfdId=csar_id)
285
286         if nf_pkg:
287             db_csar_id = nf_pkg[0].vnfPackageId
288             pkg_info["vnfdId"] = nf_pkg[0].vnfdId
289             pkg_info["vnfPackageId"] = nf_pkg[0].vnfPackageId
290             pkg_info["vnfdProvider"] = nf_pkg[0].vnfVendor
291             pkg_info["vnfdVersion"] = nf_pkg[0].vnfdVersion
292             pkg_info["vnfVersion"] = nf_pkg[0].vnfSoftwareVersion
293             pkg_info["csarName"] = nf_pkg[0].vnfPackageUri
294             pkg_info["vnfdModel"] = nf_pkg[0].vnfdModel
295             pkg_info["downloadUrl"] = "http://%s:%s/%s/%s/%s" % (
296                 REG_TO_MSB_REG_PARAM[0]["nodes"][0]["ip"],
297                 REG_TO_MSB_REG_PARAM[0]["nodes"][0]["port"],
298                 CATALOG_URL_PATH,
299                 db_csar_id,
300                 nf_pkg[0].vnfPackageUri)
301         else:
302             raise CatalogException("Vnf package[%s] not Found." % csar_id)
303
304         csar_info = {
305             "csarId": db_csar_id,
306             "packageInfo": pkg_info,
307             "imageInfo": []
308         }
309         return [0, csar_info]
310
311
312 def send_notification(pkg_id, type, pkg_change_type, operational_state=None):
313     notify = PkgNotifications(type, pkg_id, change_type=pkg_change_type,
314                               operational_state=operational_state)
315     notify.send_notification()