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