9e9741b132
This patch will include `groups` in `topology_template` of `vnf_dict`. Closes-Bug: #1929811 Change-Id: Ic05b58a2c4a605459822936b21a87a97b1899ce5
600 lines
24 KiB
Python
600 lines
24 KiB
Python
# Copyright (C) 2019 NTT DATA
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from copy import deepcopy
|
|
import hashlib
|
|
import os
|
|
import re
|
|
import shutil
|
|
from urllib.parse import urlparse
|
|
from urllib import request as urllib2
|
|
import yaml
|
|
import zipfile
|
|
|
|
from oslo_log import log as logging
|
|
from oslo_utils import encodeutils
|
|
from oslo_utils import excutils
|
|
from tacker.common import utils
|
|
from toscaparser.prereq.csar import CSAR
|
|
from toscaparser.tosca_template import ToscaTemplate
|
|
|
|
from tacker.common import exceptions
|
|
import tacker.conf
|
|
from tacker.extensions import vnfm
|
|
|
|
|
|
HASH_DICT = {
|
|
'sha-224': hashlib.sha224,
|
|
'sha-256': hashlib.sha256,
|
|
'sha-384': hashlib.sha384,
|
|
'sha-512': hashlib.sha512
|
|
}
|
|
CONF = tacker.conf.CONF
|
|
LOG = logging.getLogger(__name__)
|
|
TOSCA_META = 'TOSCA-Metadata/TOSCA.meta'
|
|
ARTIFACT_KEYS = ['Source', 'Algorithm', 'Hash']
|
|
IMAGE_FORMAT_LIST = ['raw', 'vhd', 'vhdx', 'vmdk', 'vdi', 'iso', 'ploop',
|
|
'qcow2', 'aki', 'ari', 'ami', 'img']
|
|
|
|
|
|
def _check_type(custom_def, node_type, type_list):
|
|
for node_data_type, node_data_type_value in custom_def.items():
|
|
if node_data_type == node_type and node_type in type_list:
|
|
return True, node_data_type_value
|
|
for k, v in node_data_type_value.items():
|
|
if k == 'derived_from':
|
|
if v in type_list and node_type == node_data_type:
|
|
return True, node_data_type_value
|
|
return False, None
|
|
|
|
|
|
def _get_sw_image_artifact(artifacts):
|
|
if not artifacts:
|
|
return
|
|
|
|
for artifact_value in artifacts.values():
|
|
if isinstance(artifact_value, dict):
|
|
if artifact_value.get('type') == 'tosca.artifacts.nfv.SwImage':
|
|
return artifact_value
|
|
elif isinstance(artifact_value, str):
|
|
return {'file': artifact_value}
|
|
|
|
|
|
def _update_default_vnfd_data(node_value, node_type_value):
|
|
vnf_properties = node_value['properties']
|
|
type_properties = node_type_value['properties']
|
|
for property_key, property_value in type_properties.items():
|
|
if property_key == 'descriptor_id':
|
|
# if descriptor_id is parameterized, then get the value from the
|
|
# default property and set it in the vnf_properties.
|
|
if vnf_properties and isinstance(
|
|
vnf_properties.get('descriptor_id'), dict):
|
|
vnf_properties['descriptor_id'] = property_value.get("default")
|
|
return vnf_properties
|
|
|
|
|
|
def _get_vnf_data(nodetemplates):
|
|
type_list = ['tosca.nodes.nfv.VNF']
|
|
for nt in nodetemplates:
|
|
for node_name, node_value in nt.templates.items():
|
|
type_status, node_type_value = _check_type(nt.custom_def,
|
|
node_value['type'], type_list)
|
|
if type_status and node_type_value:
|
|
return _update_default_vnfd_data(node_value, node_type_value)
|
|
|
|
|
|
def _get_instantiation_levels(policies):
|
|
if policies:
|
|
for policy in policies:
|
|
if policy.type_definition.type == \
|
|
'tosca.policies.nfv.InstantiationLevels':
|
|
return policy.properties
|
|
|
|
|
|
def _update_flavour_data_from_vnf(custom_defs, node_tpl, flavour):
|
|
type_list = ['tosca.nodes.nfv.VNF']
|
|
|
|
type_status, _ = _check_type(custom_defs, node_tpl['type'], type_list)
|
|
if type_status and node_tpl['properties']:
|
|
vnf_properties = node_tpl['properties']
|
|
if 'flavour_description' in vnf_properties:
|
|
flavour.update(
|
|
{'flavour_description': vnf_properties[
|
|
'flavour_description']})
|
|
|
|
if 'flavour_id' in vnf_properties and isinstance(
|
|
vnf_properties['flavour_id'], str):
|
|
flavour.update({'flavour_id': vnf_properties['flavour_id']})
|
|
|
|
|
|
def _get_software_image(custom_defs, nodetemplate_name, node_tpl):
|
|
type_list = ['tosca.nodes.nfv.Vdu.Compute',
|
|
'tosca.nodes.nfv.Vdu.VirtualBlockStorage']
|
|
type_status, _ = _check_type(custom_defs, node_tpl['type'], type_list)
|
|
if type_status:
|
|
properties = node_tpl['properties']
|
|
sw_image_artifact = _get_sw_image_artifact(node_tpl.get('artifacts'))
|
|
if sw_image_artifact:
|
|
image_path = sw_image_artifact['file'].lstrip("./")
|
|
properties['sw_image_data'].update(
|
|
{'software_image_id': nodetemplate_name,
|
|
'image_path': image_path})
|
|
sw_image_data = properties['sw_image_data']
|
|
_convert_software_images_prop_to_fixed_unit(sw_image_data)
|
|
|
|
if 'metadata' in sw_image_artifact:
|
|
sw_image_data.update({'metadata':
|
|
sw_image_artifact['metadata']})
|
|
return sw_image_data
|
|
|
|
|
|
def _convert_software_images_prop_to_fixed_unit(sw_image_data):
|
|
"""Update values of 'min_disk', 'min_ram' and 'size' to Bytes.
|
|
|
|
Since, the units like MB/MiB/GB/GiB is not stored in the database, we need
|
|
to convert 'min_disk', 'min_ram' and 'size' values to fixed unit before
|
|
saving it in database. Here its converting 'min_disk', 'min_ram', and
|
|
'size' values to Bytes.
|
|
"""
|
|
for attribute in ['min_disk', 'min_ram', 'size']:
|
|
if sw_image_data.get(attribute):
|
|
updated_value = utils.MemoryUnit.convert_unit_size_to_num(
|
|
sw_image_data.get(attribute),
|
|
unit='B')
|
|
sw_image_data[attribute] = updated_value
|
|
|
|
|
|
def _populate_flavour_data(tosca):
|
|
flavours = []
|
|
if tosca.nested_tosca_templates_with_topology:
|
|
for tp in tosca.nested_tosca_templates_with_topology:
|
|
sw_image_list = []
|
|
|
|
# Setting up flavour data
|
|
flavour_id = tp.substitution_mappings.properties.get('flavour_id')
|
|
if flavour_id:
|
|
flavour = {'flavour_id': flavour_id}
|
|
tpl_dict = dict()
|
|
|
|
# get from top-vnfd data
|
|
for key, value in tosca.tpl.items():
|
|
if key in CONF.vnf_package.get_top_list:
|
|
tpl_dict[key] = value
|
|
|
|
# get from lower-vnfd data
|
|
tpl_dict['topology_template'] = dict()
|
|
tpl_dict['topology_template']['groups'] = \
|
|
tp.tpl.get('groups')
|
|
tpl_dict['topology_template']['policies'] = \
|
|
tp.tpl.get('policies')
|
|
tpl_dict['topology_template']['node_templates'] = \
|
|
deepcopy(tp.tpl.get('node_templates'))
|
|
for e_node in CONF.vnf_package.exclude_node:
|
|
if tpl_dict['topology_template']['node_templates'].\
|
|
get(e_node):
|
|
del (tpl_dict['topology_template']
|
|
['node_templates'][e_node])
|
|
tpl_dict['topology_template']['inputs'] = \
|
|
deepcopy(tp.tpl.get('inputs'))
|
|
for del_input in CONF.vnf_package.del_input_list:
|
|
if tpl_dict['topology_template']['inputs'].get(del_input):
|
|
del tpl_dict['topology_template']['inputs'][del_input]
|
|
if len(tpl_dict['topology_template']['inputs']) < 1:
|
|
del tpl_dict['topology_template']['inputs']
|
|
|
|
flavour.update({'tpl_dict': tpl_dict})
|
|
|
|
instantiation_levels = _get_instantiation_levels(tp.policies)
|
|
if instantiation_levels:
|
|
flavour.update(
|
|
{'instantiation_levels': instantiation_levels})
|
|
|
|
mgmt_driver = None
|
|
for template_name, node_tpl in \
|
|
tp.tpl.get('node_templates').items():
|
|
# check the flavour property in vnf data
|
|
_update_flavour_data_from_vnf(
|
|
tp.custom_defs, node_tpl, flavour)
|
|
if node_tpl['type'] in CONF.vnf_package.get_lower_list:
|
|
if node_tpl['type'] == "tosca.nodes.nfv.VDU.Tacker":
|
|
# get mgmt_driver
|
|
mgmt_driver_flavour = \
|
|
node_tpl['properties'].get('mgmt_driver')
|
|
if mgmt_driver_flavour:
|
|
if mgmt_driver and \
|
|
mgmt_driver_flavour != mgmt_driver:
|
|
raise vnfm.MultipleMGMTDriversSpecified()
|
|
mgmt_driver = mgmt_driver_flavour
|
|
flavour.update({'mgmt_driver': mgmt_driver})
|
|
|
|
for template_name, node_tpl in \
|
|
tp.tpl.get('node_templates').items():
|
|
# Update the software image data
|
|
sw_image = _get_software_image(tp.custom_defs,
|
|
template_name,
|
|
node_tpl)
|
|
if sw_image:
|
|
sw_image_list.append(sw_image)
|
|
|
|
# Add software images for flavour
|
|
if sw_image_list:
|
|
flavour.update({'sw_images': sw_image_list})
|
|
|
|
if flavour:
|
|
flavours.append(flavour)
|
|
|
|
else:
|
|
_get_flavour_data(tosca.topology_template, flavours)
|
|
|
|
return flavours
|
|
|
|
|
|
def _get_flavour_data(tp, flavours):
|
|
sw_image_list = []
|
|
|
|
# Setting up flavour data
|
|
flavour_id = tp.substitution_mappings.properties.get('flavour_id')
|
|
if flavour_id:
|
|
if isinstance(flavour_id, dict):
|
|
error_msg = "flavour_id should be string and given" \
|
|
" {}".format(flavour_id)
|
|
raise exceptions.InvalidCSAR(error_msg)
|
|
flavour = {'flavour_id': flavour_id}
|
|
else:
|
|
flavour = {}
|
|
instantiation_levels = _get_instantiation_levels(tp.policies)
|
|
if instantiation_levels:
|
|
flavour.update({'instantiation_levels': instantiation_levels})
|
|
for template_name, node_tpl in tp.tpl.get('node_templates').items():
|
|
# check the flavour property in vnf data
|
|
_update_flavour_data_from_vnf(tp.custom_defs, node_tpl, flavour)
|
|
|
|
# Update the software image data
|
|
sw_image = _get_software_image(tp.custom_defs, template_name,
|
|
node_tpl)
|
|
if sw_image:
|
|
sw_image_list.append(sw_image)
|
|
|
|
# Add software images for flavour
|
|
if sw_image_list:
|
|
flavour.update({'sw_images': sw_image_list})
|
|
|
|
if flavour:
|
|
flavours.append(flavour)
|
|
|
|
|
|
def _get_instantiation_levels_from_policy(tpl_policies):
|
|
"""Get defined instantiation levels
|
|
|
|
Getting instantiation levels defined under policy type
|
|
'tosca.policies.nfv.InstantiationLevels'.
|
|
"""
|
|
|
|
levels = []
|
|
for policy in tpl_policies:
|
|
for key, value in policy.items():
|
|
if value.get('type') == 'tosca.policies.nfv.InstantiationLevels'\
|
|
and value.get('properties', {}).get('levels', {}):
|
|
levels = value.get('properties').get('levels').keys()
|
|
default_level = value.get(
|
|
'properties').get('default_level')
|
|
|
|
if default_level and default_level not in levels:
|
|
error_msg = "Level {} not found in defined levels" \
|
|
" {}".format(default_level,
|
|
",".join(sorted(levels)))
|
|
raise exceptions.InvalidCSAR(error_msg)
|
|
return levels
|
|
|
|
|
|
def _validate_instantiation_levels(policy, instantiation_levels):
|
|
expected_policy_type = ['tosca.policies.nfv.VduInstantiationLevels',
|
|
'tosca.policies.nfv.'
|
|
'VirtualLinkInstantiationLevels']
|
|
for policy_name, policy_tpl in policy.items():
|
|
if policy_tpl.get('type') not in expected_policy_type:
|
|
return
|
|
|
|
if not instantiation_levels:
|
|
msg = ('Policy of type'
|
|
' "tosca.policies.nfv.InstantiationLevels is not defined.')
|
|
raise exceptions.InvalidCSAR(msg)
|
|
if policy_tpl.get('properties'):
|
|
levels_in_policy = policy_tpl.get(
|
|
'properties').get('levels')
|
|
|
|
if levels_in_policy:
|
|
invalid_levels = set(levels_in_policy.keys()) - set(
|
|
instantiation_levels)
|
|
else:
|
|
invalid_levels = set()
|
|
|
|
if invalid_levels:
|
|
error_msg = "Level(s) {} not found in defined levels" \
|
|
" {}".format(",".join(sorted(invalid_levels)),
|
|
",".join(sorted(instantiation_levels)
|
|
))
|
|
raise exceptions.InvalidCSAR(error_msg)
|
|
|
|
|
|
def _validate_sw_image_data_for_artifact(node_tpl, template_name):
|
|
artifact_names = []
|
|
artifacts = node_tpl.get('artifacts')
|
|
if not artifacts:
|
|
return
|
|
|
|
for key, value in artifacts.items():
|
|
if isinstance(value, dict):
|
|
if value.get('type') == 'tosca.artifacts.nfv.SwImage':
|
|
artifact_names.append(key)
|
|
elif isinstance(value, str):
|
|
artifact_names.append(key)
|
|
|
|
if len(artifact_names) > 1:
|
|
error_msg = ('artifacts of type "tosca.artifacts.nfv.SwImage"'
|
|
' is added more than one time for'
|
|
' node %(node)s.') % {'node': template_name}
|
|
raise exceptions.InvalidCSAR(error_msg)
|
|
|
|
if artifact_names and node_tpl.get('properties'):
|
|
if not node_tpl.get('properties').get('sw_image_data'):
|
|
error_msg = ('Node property "sw_image_data" is missing for'
|
|
' artifact %(artifact_name)s for node %(node)s.') % {
|
|
'artifact_name': artifact_names[0], 'node': template_name}
|
|
raise exceptions.InvalidCSAR(error_msg)
|
|
|
|
|
|
def _validate_sw_image_data_for_artifacts(tosca):
|
|
for tp in tosca.nested_tosca_templates_with_topology:
|
|
for template_name, node_tpl in tp.tpl.get('node_templates').items():
|
|
_validate_sw_image_data_for_artifact(node_tpl, template_name)
|
|
|
|
for template in tosca.nodetemplates:
|
|
_validate_sw_image_data_for_artifact(
|
|
template.entity_tpl, template.name)
|
|
|
|
|
|
def _get_data_from_csar(tosca, context, id):
|
|
for tp in tosca.nested_tosca_templates_with_topology:
|
|
policies = tp.tpl.get("policies")
|
|
if policies:
|
|
levels = _get_instantiation_levels_from_policy(policies)
|
|
for policy_tpl in policies:
|
|
_validate_instantiation_levels(policy_tpl, levels)
|
|
|
|
_validate_sw_image_data_for_artifacts(tosca)
|
|
vnf_data = _get_vnf_data(tosca.nodetemplates)
|
|
if not vnf_data:
|
|
error_msg = "VNF properties are mandatory"
|
|
raise exceptions.InvalidCSAR(error_msg)
|
|
|
|
flavours = _populate_flavour_data(tosca)
|
|
if not flavours:
|
|
error_msg = "No VNF flavours are available"
|
|
raise exceptions.InvalidCSAR(error_msg)
|
|
|
|
csar = CSAR(tosca.input_path, tosca.a_file)
|
|
vnf_artifacts = []
|
|
if csar.validate():
|
|
vnf_artifacts = _get_vnf_artifacts(csar)
|
|
|
|
return vnf_data, flavours, vnf_artifacts
|
|
|
|
|
|
def _get_vnf_artifacts(csar):
|
|
vnf_artifacts = []
|
|
if csar.is_tosca_metadata:
|
|
if csar._get_metadata("ETSI-Entry-Manifest"):
|
|
manifest_path = csar._get_metadata("ETSI-Entry-Manifest")
|
|
if manifest_path.lower().endswith(".mf"):
|
|
manifest_data = csar.zfile.read(manifest_path)
|
|
vnf_artifacts = _convert_artifacts(
|
|
vnf_artifacts, manifest_data, csar)
|
|
else:
|
|
invalid_manifest_err_msg = (
|
|
('The file "%(manifest)s" in the CSAR "%(csar)s" does not '
|
|
'contain valid manifest.') %
|
|
{'manifest': manifest_path, 'csar': csar.path})
|
|
raise exceptions.InvalidCSAR(invalid_manifest_err_msg)
|
|
tosca_data = csar.zfile.read(TOSCA_META)
|
|
vnf_artifacts = _convert_artifacts(vnf_artifacts, tosca_data, csar)
|
|
|
|
else:
|
|
filelist = csar.zfile.namelist()
|
|
main_template_file_name = os.path.splitext(
|
|
csar.main_template_file_name)[0]
|
|
for path in filelist:
|
|
if path.lower().endswith(".mf"):
|
|
manifest_file_name = os.path.splitext(path)[0]
|
|
if manifest_file_name == main_template_file_name:
|
|
manifest_data = csar.zfile.read(path)
|
|
vnf_artifacts = _convert_artifacts(
|
|
vnf_artifacts, manifest_data, csar)
|
|
else:
|
|
invalid_manifest_err_msg = \
|
|
(('The filename "%(manifest)s" is an invalid name.'
|
|
'The name must be the same as the main template '
|
|
'file name.') %
|
|
{'manifest': path})
|
|
raise exceptions.InvalidCSAR(invalid_manifest_err_msg)
|
|
# Deduplication
|
|
vnf_artifacts = [dict(t) for t in set([tuple(d.items())
|
|
for d in vnf_artifacts])]
|
|
return vnf_artifacts
|
|
|
|
|
|
def _convert_artifacts(vnf_artifacts, artifacts_data, csar):
|
|
artifacts_data_split = re.split(b'\n\n+', artifacts_data)
|
|
|
|
for data in artifacts_data_split:
|
|
if re.findall(b'.?Name:.?|.?Source:.?|', data):
|
|
# validate key's existence
|
|
if re.findall(b'.?Algorithm:.?|.?Hash:.?', data):
|
|
artifact_data_dict = yaml.safe_load(data)
|
|
if 'Name' in artifact_data_dict.keys():
|
|
artifact_data_dict.update(
|
|
{"Source": artifact_data_dict.pop("Name")})
|
|
if 'Content-Type' in artifact_data_dict.keys():
|
|
del artifact_data_dict['Content-Type']
|
|
if sorted(ARTIFACT_KEYS) != sorted(artifact_data_dict.keys()):
|
|
missing_key = list(set(ARTIFACT_KEYS) ^
|
|
set(artifact_data_dict.keys()))
|
|
missing_key = sorted(missing_key)
|
|
invalid_artifact_err_msg = \
|
|
(('One of the artifact information '
|
|
'may not have the key("%(key)s")') %
|
|
{'key': missing_key})
|
|
raise exceptions.InvalidCSAR(invalid_artifact_err_msg)
|
|
# validate value's existence
|
|
for key, value in artifact_data_dict.items():
|
|
if not value:
|
|
invalid_artifact_err_msg = \
|
|
(('One of the artifact information may not have '
|
|
'the key value("%(key)s")') % {'key': key})
|
|
raise exceptions.InvalidCSAR(invalid_artifact_err_msg)
|
|
artifact_path = artifact_data_dict.get('Source')
|
|
if os.path.splitext(artifact_path)[-1][1:] \
|
|
in IMAGE_FORMAT_LIST:
|
|
continue
|
|
else:
|
|
algorithm = artifact_data_dict.get('Algorithm')
|
|
hash_code = artifact_data_dict.get('Hash')
|
|
result = _validate_hash(algorithm, hash_code,
|
|
csar, artifact_path)
|
|
if result:
|
|
vnf_artifacts.append(artifact_data_dict)
|
|
else:
|
|
invalid_artifact_err_msg = \
|
|
(('The hash "%(hash)s" of artifact file '
|
|
'"%(artifact)s" is an invalid value.') %
|
|
{'hash': hash_code, 'artifact': artifact_path})
|
|
raise exceptions.InvalidCSAR(invalid_artifact_err_msg)
|
|
|
|
return vnf_artifacts
|
|
|
|
|
|
def _validate_hash(algorithm, hash_code, csar, artifact_path):
|
|
z = zipfile.ZipFile(csar.path)
|
|
algorithm = algorithm.lower()
|
|
|
|
# validate Algorithm's value
|
|
if algorithm in HASH_DICT.keys():
|
|
hash_obj = HASH_DICT[algorithm]()
|
|
else:
|
|
invalid_artifact_err_msg = (('The algorithm("%(algorithm)s") of '
|
|
'artifact("%(artifact_path)s") is '
|
|
'an invalid value.') %
|
|
{'algorithm': algorithm,
|
|
'artifact_path': artifact_path})
|
|
raise exceptions.InvalidCSAR(invalid_artifact_err_msg)
|
|
filelist = csar.zfile.namelist()
|
|
|
|
# validate Source's value
|
|
if artifact_path in filelist:
|
|
hash_obj.update(z.read(artifact_path))
|
|
elif ((urlparse(artifact_path).scheme == 'file') or
|
|
(bool(urlparse(artifact_path).scheme) and
|
|
bool(urlparse(artifact_path).netloc))):
|
|
hash_obj.update(urllib2.urlopen(artifact_path).read())
|
|
else:
|
|
invalid_artifact_err_msg = (('The path("%(artifact_path)s") of '
|
|
'artifact Source is an invalid value.') %
|
|
{'artifact_path': artifact_path})
|
|
raise exceptions.InvalidCSAR(invalid_artifact_err_msg)
|
|
|
|
# validate Hash's value
|
|
if hash_code == hash_obj.hexdigest():
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def extract_csar_zip_file(file_path, extract_path):
|
|
try:
|
|
with PreserveZipFilePermissions(file_path, 'r') as zf:
|
|
zf.extractall(extract_path)
|
|
except (RuntimeError, zipfile.BadZipfile) as exp:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.error("Error encountered while extracting "
|
|
"csar zip file %(path)s. Error: %(error)s.",
|
|
{'path': file_path,
|
|
'error': encodeutils.exception_to_unicode(exp)})
|
|
exp.reraise = False
|
|
raise exceptions.InvalidZipFile(path=file_path)
|
|
|
|
|
|
def load_csar_data(context, package_uuid, zip_path):
|
|
|
|
extract_zip_path = os.path.join(CONF.vnf_package.vnf_package_csar_path,
|
|
package_uuid)
|
|
extract_csar_zip_file(zip_path, extract_zip_path)
|
|
|
|
try:
|
|
tosca = ToscaTemplate(zip_path, None, True)
|
|
return _get_data_from_csar(tosca, context, package_uuid)
|
|
except exceptions.InvalidCSAR as exp:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.error("Error processing CSAR file %(path)s for vnf package"
|
|
" %(uuid)s: Error: %(error)s. ",
|
|
{'path': zip_path, 'uuid': package_uuid,
|
|
'error': encodeutils.exception_to_unicode(exp)})
|
|
except Exception as exp:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.error("Tosca parser failed for vnf package %(uuid)s: "
|
|
"Error: %(error)s. ", {'uuid': package_uuid,
|
|
'error': encodeutils.exception_to_unicode(exp)})
|
|
exp.reraise = False
|
|
raise exceptions.InvalidCSAR(encodeutils.exception_to_unicode
|
|
(exp))
|
|
|
|
|
|
def delete_csar_data(package_uuid):
|
|
# Remove zip and folder from the vnf_package_csar_path
|
|
csar_zip_temp_path = os.path.join(CONF.vnf_package.vnf_package_csar_path,
|
|
package_uuid)
|
|
csar_path = os.path.join(CONF.vnf_package.vnf_package_csar_path,
|
|
package_uuid + ".zip")
|
|
|
|
try:
|
|
shutil.rmtree(csar_zip_temp_path)
|
|
os.remove(csar_path)
|
|
except OSError as exc:
|
|
exc_message = encodeutils.exception_to_unicode(exc)
|
|
msg = _('Failed to delete csar folder: '
|
|
'%(csar_path)s, Error: %(exc)s')
|
|
LOG.warning(msg, {'csar_path': csar_path, 'exc': exc_message})
|
|
|
|
|
|
class PreserveZipFilePermissions(zipfile.ZipFile):
|
|
"""Patched _extract_member function of zipFile.
|
|
|
|
zipfile.ZipFile.extractall function internally calls
|
|
_extract_member function.
|
|
Here _extract_member function is patched to retain the
|
|
file permissions using member.external_attr >> 16.
|
|
|
|
Note: First 16 bits of external_attr store permission details.
|
|
"""
|
|
def _extract_member(self, member, targetpath, pwd):
|
|
if not isinstance(member, zipfile.ZipInfo):
|
|
member = self.getinfo(member)
|
|
|
|
targetpath = super()._extract_member(member, targetpath, pwd)
|
|
|
|
attr = member.external_attr >> 16
|
|
if attr != 0:
|
|
os.chmod(targetpath, attr)
|
|
return targetpath
|