import threading
import uuid
import zlib
+import re
import tornado
import tornado.escape
import gi
gi.require_version('RwLaunchpadYang', '1.0')
-gi.require_version('NsdYang', '1.0')
-gi.require_version('VnfdYang', '1.0')
+gi.require_version('ProjectNsdYang', '1.0')
+gi.require_version('ProjectVnfdYang', '1.0')
from gi.repository import (
- NsdYang,
- VnfdYang,
+ ProjectNsdYang as NsdYang,
+ ProjectVnfdYang as VnfdYang,
)
import rift.mano.cloud
-import rift.package.charm
import rift.package.checksums
-import rift.package.config
import rift.package.convert
import rift.package.handler as pkg_handler
import rift.package.icon
from gi.repository import (
RwDts as rwdts,
- RwPkgMgmtYang)
+ RwPkgMgmtYang
+ )
import rift.downloader as downloader
import rift.mano.dts as mano_dts
import rift.tasklets
from .tosca import ExportTosca
+from .onboard import OnboardError as OnboardException
+
MB = 1024 * 1024
GB = 1024 * MB
RPC_PACKAGE_CREATE_ENDPOINT = RwPkgMgmtYang.YangOutput_RwPkgMgmt_PackageCreate
RPC_PACKAGE_UPDATE_ENDPOINT = RwPkgMgmtYang.YangOutput_RwPkgMgmt_PackageUpdate
-
-
class HttpMessageError(Exception):
def __init__(self, code, msg):
self.code = code
class UploadRpcHandler(mano_dts.AbstractRpcHandler):
- def __init__(self, log, dts, loop, application):
+ def __init__(self, application):
"""
Args:
application: UploaderApplication
"""
- super().__init__(log, dts, loop)
+ super().__init__(application.log, application.dts, application.loop)
self.application = application
@property
log = self.application.get_logger(transaction_id)
log.message(OnboardStart())
+ self.log.debug("Package create RPC: {}".format(msg))
auth = None
if msg.username is not None:
auth = (msg.username, msg.password)
+ try:
+ project = msg.project_name
+ except AttributeError as e:
+ self._log.warning("Did not get project name in RPC: {}".
+ format(msg.as_dict()))
+ project = rift.mano.utils.project.DEFAULT_PROJECT
+
self.application.onboard(
msg.external_url,
transaction_id,
- auth=auth
+ auth=auth,
+ project=project,
)
rpc_op = RPC_PACKAGE_CREATE_ENDPOINT.from_dict({
- "transaction_id": transaction_id})
+ "transaction_id": transaction_id,
+ "project_name": project,
+ })
return rpc_op
class UpdateRpcHandler(mano_dts.AbstractRpcHandler):
- def __init__(self, log, dts, loop, application):
+ def __init__(self, application):
"""
Args:
application: UploaderApplication
"""
- super().__init__(log, dts, loop)
+ super().__init__(application.log, application.dts, application.loop)
self.application = application
@property
self.application.update(
msg.external_url,
transaction_id,
- auth=auth
+ auth=auth,
+ project=msg.project_name,
)
rpc_op = RPC_PACKAGE_UPDATE_ENDPOINT.from_dict({
- "transaction_id": transaction_id})
+ "transaction_id": transaction_id,
+ "project_name": msg.project_name,
+ })
return rpc_op
class UpdatePackage(downloader.DownloaderProtocol):
- def __init__(self, log, loop, url, auth,
- onboarder, uploader, package_store_map):
+ def __init__(self, log, loop, project, url, auth,
+ onboarder, uploader, package_store_map, transaction_id):
super().__init__()
self.log = log
self.loop = loop
+ self.project = project
self.url = url
self.auth = auth
self.onboarder = onboarder
self.uploader = uploader
self.package_store_map = package_store_map
+ self.transaction_id = transaction_id
def _update_package(self, packages):
with pkg as temp_package:
package_checksums = self.validate_package(temp_package)
stored_package = self.update_package(temp_package)
+ self.validate_descriptor_fields(temp_package)
try:
- self.extract_charms(temp_package)
- self.extract_scripts(temp_package)
- self.extract_configs(temp_package)
self.extract_icons(temp_package)
-
self.update_descriptors(temp_package)
except Exception:
except MessageException as e:
self.log.message(e.msg)
self.log.message(UpdateFailure())
+ raise UpdateFailure(str(e))
except Exception as e:
self.log.exception(e)
file_backed_packages = extractor.create_packages_from_upload(
job.filename, job.filepath
)
+ try:
+ self.extract(file_backed_packages)
+ except Exception as e:
+ raise Exception("Error in Package Update")
- self.extract(file_backed_packages)
+ def on_download_finished(self, job):
+ self.log.debug("*** Download completed")
+ if hasattr(self.project, 'update_status_handler'):
+ self.project.update_status_handler.update_status(job, self.transaction_id)
+
+ def on_download_progress(self, job):
+ self.log.debug("*** Download in progress")
+ if hasattr(self.project, 'update_status_handler'):
+ self.project.update_status_handler.update_status(job, self.transaction_id)
def on_download_failed(self, job):
self.log.error(job.detail)
)
try:
self.uploader.upload_image(image_name, image_checksum, image_hdl)
- self.uploader.upload_image_to_cloud_accounts(image_name, image_checksum)
+ self.uploader.upload_image_to_cloud_accounts(image_name, image_checksum, self.project)
except image.ImageUploadError as e:
self.log.exception("Failed to upload image: %s", image_name)
finally:
_ = [image_hdl.close() for image_hdl in name_hdl_map.values()]
- def extract_charms(self, package):
- try:
- charm_extractor = rift.package.charm.PackageCharmExtractor(self.log)
- charm_extractor.extract_charms(package)
- except rift.package.charm.CharmExtractionError as e:
- raise MessageException(UpdateExtractionError()) from e
-
- def extract_scripts(self, package):
- try:
- script_extractor = rift.package.script.PackageScriptExtractor(self.log)
- script_extractor.extract_scripts(package)
- except rift.package.script.ScriptExtractionError as e:
- raise MessageException(UpdateExtractionError()) from e
-
- def extract_configs(self, package):
- try:
- config_extractor = rift.package.config.PackageConfigExtractor(self.log)
- config_extractor.extract_configs(package)
- except rift.package.config.ConfigExtractionError as e:
- raise MessageException(UpdateExtractionError()) from e
-
def extract_icons(self, package):
try:
icon_extractor = rift.package.icon.PackageIconExtractor(self.log)
except rift.package.icon.IconExtractionError as e:
raise MessageException(UpdateExtractionError()) from e
+ def validate_descriptor_fields(self, package):
+ # We can add more VNFD validations here. Currently we are validating only cloud-init
+ if package.descriptor_msg is not None:
+ self.validate_cloud_init_file(package)
+
+ def validate_cloud_init_file(self, package):
+ """ This validation is for VNFDs with associated VDUs. """
+ if 'vdu' in package.descriptor_msg.as_dict():
+ for vdu in package.descriptor_msg.as_dict()['vdu']:
+ if 'cloud_init_file' in vdu:
+ cloud_init_file = vdu['cloud_init_file']
+ for file in package.files:
+ if file.endswith('/' + cloud_init_file) is True:
+ return
+ raise MessageException(
+ OnboardError("Cloud-Init file reference in VNFD does not match with cloud-init file"))
+
def validate_package(self, package):
checksum_validator = rift.package.package.PackageChecksumValidator(self.log)
try:
- file_checksums = checksum_validator.validate(package)
+ checksum_validator.validate(package)
except rift.package.package.PackageFileChecksumError as e:
raise MessageException(UpdateChecksumMismatch(e.filename)) from e
except rift.package.package.PackageValidationError as e:
raise MessageException(UpdateUnreadablePackage()) from e
- return file_checksums
+ return checksum_validator.checksums
def update_descriptors(self, package):
descriptor_msg = package.descriptor_msg
self.log.message(UpdateDescriptorUpdate())
try:
- self.onboarder.update(descriptor_msg)
+ self.onboarder.update(descriptor_msg, project=self.project.name)
except onboard.UpdateError as e:
raise MessageException(UpdateDescriptorError(package.descriptor_file)) from e
class OnboardPackage(downloader.DownloaderProtocol):
- def __init__(self, log, loop, url, auth,
- onboarder, uploader, package_store_map):
+ def __init__(self, log, loop, project, url, auth,
+ onboarder, uploader, package_store_map, transaction_id):
self.log = log
self.loop = loop
+ self.project = project
self.url = url
self.auth = auth
self.onboarder = onboarder
self.uploader = uploader
self.package_store_map = package_store_map
+ self.transaction_id = transaction_id
def _onboard_package(self, packages):
# Extract package could return multiple packages if
with pkg as temp_package:
package_checksums = self.validate_package(temp_package)
stored_package = self.store_package(temp_package)
-
try:
- self.extract_charms(temp_package)
- self.extract_scripts(temp_package)
- self.extract_configs(temp_package)
+ self.validate_descriptor_fields(temp_package)
+ except Exception as e:
+ self.log.exception("Descriptor validation Failed")
+ raise
+ try:
self.extract_icons(temp_package)
-
self.onboard_descriptors(temp_package)
- except Exception:
- self.delete_stored_package(stored_package)
+ except Exception as e:
+ if "data-exists" not in e.msg.text:
+ self.delete_stored_package(stored_package)
raise
-
else:
self.upload_images(temp_package, package_checksums)
except MessageException as e:
self.log.message(e.msg)
self.log.message(OnboardFailure())
+ raise OnboardException(OnboardFailure())
+
except Exception as e:
self.log.exception(e)
file_backed_packages = extractor.create_packages_from_upload(
job.filename, job.filepath
)
+ try:
+ self.extract(file_backed_packages)
+ except Exception as e:
+ raise Exception("Error in Onboarding Package")
+
+ def on_download_finished(self, job):
+ self.log.debug("*** Download completed")
+ if hasattr(self.project, 'upload_status_handler'):
+ self.project.upload_status_handler.upload_status(job, self.transaction_id)
- self.extract(file_backed_packages)
+ def on_download_progress(self, job):
+ self.log.debug("*** Download in progress")
+ if hasattr(self.project, 'upload_status_handler'):
+ self.project.upload_status_handler.upload_status(job, self.transaction_id)
def on_download_failed(self, job):
self.log.error(job.detail)
def download_package(self):
+ self.log.debug("Before pkg download, project = {}".format(self.project.name))
_, filename = tempfile.mkstemp()
url_downloader = downloader.UrlDownloader(
self.url,
package.open(image_file_map[image_name])
)
try:
- self.uploader.upload_image(image_name, image_checksum, image_hdl)
- self.uploader.upload_image_to_cloud_accounts(image_name, image_checksum)
+ set_image_property = {}
+ self.uploader.upload_image(image_name, image_checksum, image_hdl, set_image_property)
+ self.uploader.upload_image_to_cloud_accounts(image_name, image_checksum, self.project.name)
except image.ImageUploadError as e:
- raise MessageException(OnboardImageUploadError()) from e
+ raise MessageException(OnboardImageUploadError(str(e))) from e
finally:
_ = [image_hdl.close() for image_hdl in name_hdl_map.values()]
- def extract_charms(self, package):
- try:
- charm_extractor = rift.package.charm.PackageCharmExtractor(self.log)
- charm_extractor.extract_charms(package)
- except rift.package.charm.CharmExtractionError as e:
- raise MessageException(OnboardExtractionError()) from e
-
- def extract_scripts(self, package):
- try:
- script_extractor = rift.package.script.PackageScriptExtractor(self.log)
- script_extractor.extract_scripts(package)
- except rift.package.script.ScriptExtractionError as e:
- raise MessageException(OnboardExtractionError()) from e
-
- def extract_configs(self, package):
- try:
- config_extractor = rift.package.config.PackageConfigExtractor(self.log)
- config_extractor.extract_configs(package)
- except rift.package.config.ConfigExtractionError as e:
- raise MessageException(OnboardExtractionError()) from e
-
def extract_icons(self, package):
try:
icon_extractor = rift.package.icon.PackageIconExtractor(self.log)
except rift.package.icon.IconExtractionError as e:
raise MessageException(OnboardExtractionError()) from e
+ def validate_descriptor_fields(self, package):
+ # We can add more VNFD/NSD validations here.
+ if package.descriptor_msg is not None:
+ try:
+ self.validate_cloud_init_file(package)
+ self.validate_vld_mgmt_network(package)
+ except Exception as e:
+ raise
+
+ def validate_vld_mgmt_network(self, package):
+ """ This is validation at onboarding of NSD for atleast one of the VL's to have mgmt network true
+ and have minimum one connection point"""
+ if package.descriptor_type == 'nsd':
+ for vld in package.descriptor_msg.as_dict().get('vld',[]):
+ if vld.get('mgmt_network', False) is True and \
+ len(vld.get('vnfd_connection_point_ref',[])) > 0 :
+ break
+ else:
+ self.log.error(("AtLeast One of the VL's should have Management Network as True "
+ "and have minimum one connection point"))
+ raise Exception("Management Network not defined.")
+
+ def validate_cloud_init_file(self, package):
+ """ This validation is for VNFDs with associated VDUs. """
+ if 'vdu' in package.descriptor_msg.as_dict():
+ for vdu in package.descriptor_msg.as_dict()['vdu']:
+ if 'cloud_init_file' in vdu:
+ cloud_init_file = vdu['cloud_init_file']
+ for file in package.files:
+ if file.endswith('/' + cloud_init_file) is True:
+ return
+ raise MessageException(
+ OnboardError("Cloud-Init file reference in VNFD does not match with cloud-init file"))
+
def validate_package(self, package):
- checksum_validator = rift.package.package.PackageChecksumValidator(self.log)
+ validators = (
+ rift.package.package.PackageChecksumValidator(self.log),
+ rift.package.package.PackageConstructValidator(self.log),
+ )
- try:
- file_checksums = checksum_validator.validate(package)
- except rift.package.package.PackageFileChecksumError as e:
- raise MessageException(OnboardChecksumMismatch(e.filename)) from e
- except rift.package.package.PackageValidationError as e:
- raise MessageException(OnboardUnreadablePackage()) from e
+ # Run the validators for checksum and package construction for imported pkgs
+ for validator in validators:
+ try:
+ validator.validate(package)
+
+ except rift.package.package.PackageFileChecksumError as e:
+ raise MessageException(OnboardChecksumMismatch(e.filename)) from e
+ except rift.package.package.PackageValidationError as e:
+ raise MessageException(OnboardUnreadablePackage()) from e
- return file_checksums
+ return validators[0].checksums
def onboard_descriptors(self, package):
- descriptor_msg = package.descriptor_msg
+ def process_error_messsage(exception, package):
+ """
+ This method captures error reason. This needs to be enhanced with time.
+ """
+ exception_msg = str(exception)
+ match_duplicate = re.findall('<error-tag>(.*?)</error-tag>', exception_msg, re.DOTALL)
+
+ if len(match_duplicate) > 0:
+ error_message = str(match_duplicate[0])
+ return error_message
+
+ match = re.findall('<tailf:missing-element>(.*?)</tailf:missing-element>', exception_msg, re.DOTALL)
+ error_message = ""
+ if len(match) > 0:
+ for element in match:
+ element_message = "Missing element : {}".format(element)
+ error_message += element_message
+ else:
+ error_message = package.descriptor_file
+ return error_message
+
+ def process_exception(exception, package):
+ return OnboardDescriptorError(process_error_messsage(exception, package))
+ descriptor_msg = package.descriptor_msg
self.log.message(OnboardDescriptorOnboard())
try:
- self.onboarder.onboard(descriptor_msg)
+ self.onboarder.onboard(descriptor_msg, project=self.project.name)
except onboard.OnboardError as e:
- raise MessageException(OnboardDescriptorError(package.descriptor_file)) from e
+ raise MessageException(process_exception(e, package)) from e
class UploaderApplication(tornado.web.Application):
ssl_cert = manifest.bootstrap_phase.rwsecurity.cert
ssl_key = manifest.bootstrap_phase.rwsecurity.key
return cls(
- tasklet.log,
- tasklet.dts,
- tasklet.loop,
- ssl=(ssl_cert, ssl_key),
- vnfd_store=tasklet.vnfd_package_store,
- nsd_store=tasklet.nsd_package_store,
- vnfd_catalog=tasklet.vnfd_catalog,
- nsd_catalog=tasklet.nsd_catalog)
+ tasklet,
+ ssl=(ssl_cert, ssl_key))
def __init__(
self,
- log,
- dts,
- loop,
+ tasklet,
ssl=None,
vnfd_store=None,
- nsd_store=None,
- vnfd_catalog=None,
- nsd_catalog=None):
+ nsd_store=None):
- self.log = log
- self.loop = loop
- self.dts = dts
+ self.log = tasklet.log
+ self.loop = tasklet.loop
+ self.dts = tasklet.dts
+
+ self.accounts = {}
+ self.ro_accounts = {}
self.use_ssl = False
self.ssl_cert, self.ssl_key = None, None
self.use_ssl = True
self.ssl_cert, self.ssl_key = ssl
- if not vnfd_store:
- vnfd_store = rift.package.store.VnfdPackageFilesystemStore(self.log)
-
- if not nsd_store:
- nsd_store = rift.package.store.NsdPackageFilesystemStore(self.log)
-
- self.accounts = []
self.messages = collections.defaultdict(list)
- self.export_dir = os.path.join(os.environ['RIFT_ARTIFACTS'], 'launchpad/exports')
+ self.export_dir = os.path.join(os.environ['RIFT_VAR_ROOT'], 'launchpad/exports')
self.uploader = image.ImageUploader(self.log, self.loop, self.dts)
self.onboarder = onboard.DescriptorOnboarder(
self.log, "127.0.0.1", 8008, self.use_ssl, self.ssl_cert, self.ssl_key
)
- self.package_store_map = {
- "vnfd": vnfd_store,
- "nsd": nsd_store
- }
self.exporter = export.DescriptorPackageArchiveExporter(self.log)
self.loop.create_task(export.periodic_export_cleanup(self.log, self.loop, self.export_dir))
- self.vnfd_catalog = vnfd_catalog
- self.nsd_catalog = nsd_catalog
+ self.tasklet = tasklet
+ self.get_vnfd_catalog = tasklet.get_vnfd_catalog
+ self.get_nsd_catalog = tasklet.get_nsd_catalog
catalog_map = {
- "vnfd": self.vnfd_catalog,
- "nsd": self.nsd_catalog
+ "vnfd": self.get_vnfd_catalog,
+ "nsd": self.get_nsd_catalog
}
- self.upload_handler = UploadRpcHandler(self.log, self.dts, self.loop, self)
- self.update_handler = UpdateRpcHandler(self.log, self.dts, self.loop, self)
- self.export_handler = export.ExportRpcHandler(
- self.log,
- self.dts,
- self.loop,
- self,
- store_map=self.package_store_map,
- exporter=self.exporter,
- catalog_map=catalog_map
- )
+ self.upload_handler = UploadRpcHandler(self)
+ self.update_handler = UpdateRpcHandler(self)
+ self.export_handler = export.ExportRpcHandler(self, catalog_map)
attrs = dict(log=self.log, loop=self.loop)
super(UploaderApplication, self).__init__([
(r"/api/package/vnfd/(.*)", pkg_handler.FileRestApiHandler, {
- 'path': vnfd_store.root_dir}),
+ 'path': rift.package.store.VnfdPackageFilesystemStore.DEFAULT_ROOT_DIR}),
(r"/api/package/nsd/(.*)", pkg_handler.FileRestApiHandler, {
- 'path': nsd_store.root_dir}),
+ 'path': rift.package.store.NsdPackageFilesystemStore.DEFAULT_ROOT_DIR}),
(r"/api/upload/([^/]+)/state", UploadStateHandler, attrs),
(r"/api/update/([^/]+)/state", UpdateStateHandler, attrs),
def get_logger(self, transaction_id):
return message.Logger(self.log, self.messages[transaction_id])
- def onboard(self, url, transaction_id, auth=None):
+ def build_store_map(self, project=None):
+ ''' Use project information to build vnfd/nsd filesystem stores with appropriate
+ package directory root.
+ '''
+ vnfd_store = rift.package.store.VnfdPackageFilesystemStore(self.log) if not \
+ project else rift.package.store.VnfdPackageFilesystemStore(self.log, project=project)
+ nsd_store = rift.package.store.NsdPackageFilesystemStore(self.log) if not \
+ project else rift.package.store.NsdPackageFilesystemStore(self.log, project=project)
+
+ return dict(vnfd = vnfd_store, nsd = nsd_store)
+
+ def onboard(self, url, transaction_id, auth=None, project=None):
log = message.Logger(self.log, self.messages[transaction_id])
+ try:
+ self.project = self.tasklet._get_project(project)
+ except Exception as e:
+ self.log.error("Exception raised ...%s" % (str(e)))
+ self.log.exception(e)
+
+ self.package_store_map = self.build_store_map(project)
onboard_package = OnboardPackage(
log,
self.loop,
+ self.project,
url,
auth,
self.onboarder,
self.uploader,
self.package_store_map,
+ transaction_id
)
self.loop.run_in_executor(None, onboard_package.download_package)
- def update(self, url, transaction_id, auth=None):
+ def update(self, url, transaction_id, auth=None, project=None):
log = message.Logger(self.log, self.messages[transaction_id])
+ try:
+ self.project = self.tasklet._get_project(project)
+ except Exception as e:
+ self.log.error("Exception raised ...%s" % (str(e)))
+ self.log.exception(e)
+
+ self.package_store_map = self.build_store_map(project)
update_package = UpdatePackage(
log,
self.loop,
+ self.project,
url,
auth,
self.onboarder,
self.uploader,
self.package_store_map,
+ transaction_id
)
self.loop.run_in_executor(None, update_package.download_package)
-