21d5819efe5e127d6e442cf5856024657119a082
[modeling/etsicatalog.git] / catalog / packages / biz / vnf_package.py
1 # Copyright 2018 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 urllib
22 import uuid
23
24 from catalog.packages.biz.common import parse_file_range, read, save
25 from catalog.pub.config.config import CATALOG_ROOT_PATH
26 from catalog.pub.database.models import VnfPackageModel, NSPackageModel
27 from catalog.pub.exceptions import CatalogException, ResourceNotFoundException
28 from catalog.pub.utils.values import ignore_case_get
29 from catalog.pub.utils import fileutil, toscaparser
30 from catalog.packages import const
31 from catalog.packages.biz.notificationsutil import prepare_vnfpkg_notification, NotificationsUtil
32
33
34 logger = logging.getLogger(__name__)
35
36
37 class VnfPackage(object):
38
39     def __init__(self):
40         pass
41
42     def create_vnf_pkg(self, data):
43         user_defined_data = ignore_case_get(data, "userDefinedData", {})
44         vnf_pkg_id = str(uuid.uuid4())
45         VnfPackageModel.objects.create(
46             vnfPackageId=vnf_pkg_id,
47             onboardingState=const.PKG_STATUS.CREATED,
48             operationalState=const.PKG_STATUS.DISABLED,
49             usageState=const.PKG_STATUS.NOT_IN_USE,
50             userDefinedData=json.dumps(user_defined_data)
51         )
52         data = {
53             "id": vnf_pkg_id,
54             "onboardingState": const.PKG_STATUS.CREATED,
55             "operationalState": const.PKG_STATUS.DISABLED,
56             "usageState": const.PKG_STATUS.NOT_IN_USE,
57             "userDefinedData": user_defined_data,
58             "_links": None
59         }
60         return data
61
62     def query_multiple(self):
63         pkgs_info = []
64         nf_pkgs = VnfPackageModel.objects.filter()
65         for nf_pkg in nf_pkgs:
66             ret = fill_response_data(nf_pkg)
67             pkgs_info.append(ret)
68         return pkgs_info
69
70     def query_single(self, vnf_pkg_id):
71         nf_pkg = VnfPackageModel.objects.filter(vnfPackageId=vnf_pkg_id)
72         if not nf_pkg.exists():
73             logger.error('VNF package(%s) does not exist.' % vnf_pkg_id)
74             raise ResourceNotFoundException('VNF package(%s) does not exist.' % vnf_pkg_id)
75         return fill_response_data(nf_pkg[0])
76
77     def delete_vnf_pkg(self, vnf_pkg_id):
78         vnf_pkg = VnfPackageModel.objects.filter(vnfPackageId=vnf_pkg_id)
79         if not vnf_pkg.exists():
80             logger.debug('VNF package(%s) has been deleted.' % vnf_pkg_id)
81             return
82         '''
83         if vnf_pkg[0].operationalState != PKG_STATUS.DISABLED:
84             raise CatalogException("The VNF package (%s) is not disabled" % vnf_pkg_id)
85         if vnf_pkg[0].usageState != PKG_STATUS.NOT_IN_USE:
86             raise CatalogException("The VNF package (%s) is in use" % vnf_pkg_id)
87         '''
88         del_vnfd_id = vnf_pkg[0].vnfdId
89         ns_pkgs = NSPackageModel.objects.all()
90         for ns_pkg in ns_pkgs:
91             nsd_model = None
92             if ns_pkg.nsdModel:
93                 nsd_model = json.JSONDecoder().decode(ns_pkg.nsdModel)
94             if not nsd_model:
95                 continue
96             for vnf in nsd_model['vnfs']:
97                 if del_vnfd_id == vnf["properties"]["descriptor_id"]:
98                     raise CatalogException('VNFD(%s) is referenced.' % del_vnfd_id)
99         vnf_pkg.delete()
100         send_notification(vnf_pkg_id, const.PKG_NOTIFICATION_TYPE.CHANGE,
101                           const.PKG_CHANGE_TYPE.PKG_DELETE)
102
103         vnf_pkg_path = os.path.join(CATALOG_ROOT_PATH, vnf_pkg_id)
104         fileutil.delete_dirs(vnf_pkg_path)
105         logger.info('VNF package(%s) has been deleted.' % vnf_pkg_id)
106
107     def upload(self, vnf_pkg_id, remote_file):
108         logger.info('Start to upload VNF package(%s)...' % vnf_pkg_id)
109         vnf_pkg = VnfPackageModel.objects.filter(vnfPackageId=vnf_pkg_id)
110         # if vnf_pkg[0].onboardingState != PKG_STATUS.CREATED:
111         #     logger.error("VNF package(%s) is not CREATED" % vnf_pkg_id)
112         #     raise CatalogException("VNF package(%s) is not CREATED" % vnf_pkg_id)
113         vnf_pkg.update(onboardingState=const.PKG_STATUS.UPLOADING)
114         send_notification(vnf_pkg_id, const.PKG_NOTIFICATION_TYPE.ONBOARDING,
115                           const.PKG_CHANGE_TYPE.OP_STATE_CHANGE)
116
117         local_file_name = save(remote_file, vnf_pkg_id)
118         logger.info('VNF package(%s) has been uploaded.' % vnf_pkg_id)
119         return local_file_name
120
121     def download(self, vnf_pkg_id, file_range):
122         logger.info('Start to download VNF package(%s)...' % vnf_pkg_id)
123         nf_pkg = VnfPackageModel.objects.filter(vnfPackageId=vnf_pkg_id)
124         if not nf_pkg.exists():
125             logger.error('VNF package(%s) does not exist.' % vnf_pkg_id)
126             raise ResourceNotFoundException('VNF package(%s) does not exist.' % vnf_pkg_id)
127         if nf_pkg[0].onboardingState != const.PKG_STATUS.ONBOARDED:
128             raise CatalogException("VNF package (%s) is not on-boarded" % vnf_pkg_id)
129
130         local_file_path = nf_pkg[0].localFilePath
131         start, end = parse_file_range(local_file_path, file_range)
132         logger.info('VNF package (%s) has been downloaded.' % vnf_pkg_id)
133         return read(local_file_path, start, end)
134
135
136 class VnfPkgUploadThread(threading.Thread):
137     def __init__(self, data, vnf_pkg_id):
138         threading.Thread.__init__(self)
139         self.vnf_pkg_id = vnf_pkg_id
140         self.data = data
141         self.upload_file_name = None
142
143     def run(self):
144         try:
145             self.upload_vnf_pkg_from_uri()
146             parse_vnfd_and_save(self.vnf_pkg_id, self.upload_file_name)
147         except CatalogException as e:
148             logger.error(e.args[0])
149         except Exception as e:
150             logger.error(e.args[0])
151             logger.error(traceback.format_exc())
152             logger.error(str(sys.exc_info()))
153
154     def upload_vnf_pkg_from_uri(self):
155         logger.info("Start to upload VNF packge(%s) from URI..." % self.vnf_pkg_id)
156         vnf_pkg = VnfPackageModel.objects.filter(vnfPackageId=self.vnf_pkg_id)
157         if vnf_pkg[0].onboardingState != const.PKG_STATUS.CREATED:
158             logger.error("VNF package(%s) is not CREATED" % self.vnf_pkg_id)
159             raise CatalogException("VNF package (%s) is not created" % self.vnf_pkg_id)
160         vnf_pkg.update(onboardingState=const.PKG_STATUS.UPLOADING)
161         send_notification(self.vnf_pkg_id, const.PKG_NOTIFICATION_TYPE.ONBOARDING,
162                           const.PKG_CHANGE_TYPE.OP_STATE_CHANGE)
163
164         uri = ignore_case_get(self.data, "addressInformation")
165         response = urllib.request.urlopen(uri)
166
167         local_file_dir = os.path.join(CATALOG_ROOT_PATH, self.vnf_pkg_id)
168         self.upload_file_name = os.path.join(local_file_dir, os.path.basename(uri))
169         if not os.path.exists(local_file_dir):
170             fileutil.make_dirs(local_file_dir)
171         with open(self.upload_file_name, "wt") as local_file:
172             local_file.write(response.read())
173         response.close()
174         logger.info('VNF packge(%s) has been uploaded.' % self.vnf_pkg_id)
175
176
177 def fill_response_data(nf_pkg):
178     pkg_info = {}
179     pkg_info["id"] = nf_pkg.vnfPackageId
180     pkg_info["vnfdId"] = nf_pkg.vnfdId
181     pkg_info["vnfProductName"] = nf_pkg.vnfdProductName
182     pkg_info["vnfSoftwareVersion"] = nf_pkg.vnfSoftwareVersion
183     pkg_info["vnfdVersion"] = nf_pkg.vnfdVersion
184     if nf_pkg.checksum:
185         pkg_info["checksum"] = json.JSONDecoder().decode(nf_pkg.checksum)
186     pkg_info["softwareImages"] = None  # TODO
187     pkg_info["additionalArtifacts"] = None  # TODO
188     pkg_info["onboardingState"] = nf_pkg.onboardingState
189     pkg_info["operationalState"] = nf_pkg.operationalState
190     pkg_info["usageState"] = nf_pkg.usageState
191     if nf_pkg.userDefinedData:
192         pkg_info["userDefinedData"] = json.JSONDecoder().decode(nf_pkg.userDefinedData)
193     pkg_info["_links"] = None  # TODO
194     return pkg_info
195
196
197 def parse_vnfd_and_save(vnf_pkg_id, vnf_pkg_path):
198     logger.info('Start to process VNF package(%s)...' % vnf_pkg_id)
199     vnf_pkg = VnfPackageModel.objects.filter(vnfPackageId=vnf_pkg_id)
200     vnf_pkg.update(onboardingState=const.PKG_STATUS.PROCESSING)
201     vnfd_json = toscaparser.parse_vnfd(vnf_pkg_path)
202     vnfd = json.JSONDecoder().decode(vnfd_json)
203
204     if vnfd.get("vnf", "") != "":
205         vnfd_id = vnfd["vnf"]["properties"].get("descriptor_id", "")
206         other_pkg = VnfPackageModel.objects.filter(vnfdId=vnfd_id)
207         if other_pkg and other_pkg[0].vnfPackageId != vnf_pkg_id:
208             logger.error("VNF package(%s,%s) already exists.", other_pkg[0].vnfPackageId, vnfd_id)
209             raise CatalogException("VNF package(%s) already exists." % vnfd_id)
210         vnf_provider = vnfd["vnf"]["properties"].get("provider", "")
211         vnfd_ver = vnfd["vnf"]["properties"].get("descriptor_version", "")
212         vnf_software_version = vnfd["vnf"]["properties"].get("software_version", "")
213         vnfd_product_name = vnfd["vnf"]["properties"].get("product_name", "")
214         vnf_pkg.update(
215             vnfPackageId=vnf_pkg_id,
216             vnfdId=vnfd_id,
217             vnfdProductName=vnfd_product_name,
218             vnfVendor=vnf_provider,
219             vnfdVersion=vnfd_ver,
220             vnfSoftwareVersion=vnf_software_version,
221             vnfdModel=vnfd_json,
222             onboardingState=const.PKG_STATUS.ONBOARDED,
223             operationalState=const.PKG_STATUS.ENABLED,
224             usageState=const.PKG_STATUS.NOT_IN_USE,
225             localFilePath=vnf_pkg_path,
226             vnfPackageUri=os.path.split(vnf_pkg_path)[-1]
227         )
228     else:
229         raise CatalogException("VNF propeties and metadata in VNF Package(id=%s) are empty." % vnf_pkg_id)
230     logger.info('VNF package(%s) has been processed(done).' % vnf_pkg_id)
231
232
233 def handle_upload_failed(vnf_pkg_id):
234     vnf_pkg = VnfPackageModel.objects.filter(vnfPackageId=vnf_pkg_id)
235     vnf_pkg.update(onboardingState=const.PKG_STATUS.CREATED)
236
237
238 def send_notification(pkg_id, type, pkg_change_type, operational_state=None):
239     data = prepare_vnfpkg_notification(vnf_pkg_id=pkg_id,
240                                        notification_type=type,
241                                        pkg_change_type=pkg_change_type,
242                                        operational_state=operational_state)
243     filters = {
244         'vnfdId': 'vnfd_id',
245         'vnfPkgId': 'vnf_pkg_id'
246     }
247     logger.debug('Notify request data = %s' % data)
248     logger.debug('Notify request filters = %s' % filters)
249     NotificationsUtil().send_notification(data, filters, True)