228 lines
9.1 KiB
Python
228 lines
9.1 KiB
Python
#
|
|
# 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 itertools import chain
|
|
import logging
|
|
|
|
from translator.hot.syntax.hot_resource import HotResource
|
|
log = logging.getLogger('heat-translator')
|
|
|
|
# Name used to dynamically load appropriate map class.
|
|
TARGET_CLASS_NAME = 'ToscaNfvScalingAspect'
|
|
HEAT_NON_SCALING_RESOURCES = ['OS::Heat::ScalingPolicy',
|
|
'OS::Heat::AutoScalingGroup']
|
|
HOT_FLAVOR = 'OS::Nova::Flavor'
|
|
SCALING_ASPECT_DELTA = 'tosca.policies.nfv.VduScalingAspectDeltas'
|
|
VDU_CP = 'tosca.nodes.nfv.VduCp'
|
|
VDU_INITIAL_DELTA = 'tosca.policies.nfv.VduInitialDelta'
|
|
|
|
|
|
class ToscaNfvScalingAspect(HotResource):
|
|
"""Translate TOSCA policy type tosca.policies.nfv.ScalingAspects."""
|
|
|
|
toscatype = 'tosca.policies.nfv.ScalingAspects'
|
|
DESIRED_CAPACITY = 'DESIRED_CAPACITY'
|
|
|
|
def __init__(self, policy, aspect_name, csar_dir=None,
|
|
hot_template_parameters=None):
|
|
hot_type = "OS::Heat::ScalingPolicy"
|
|
self.aspect_name = aspect_name
|
|
super(ToscaNfvScalingAspect, self).__init__(
|
|
policy,
|
|
name=self.aspect_name,
|
|
type=hot_type,
|
|
csar_dir=csar_dir)
|
|
self.policy = policy
|
|
self.hot_template_parameters = hot_template_parameters
|
|
|
|
# default
|
|
self.scaling_adjustment = None
|
|
self.vdu_name = None
|
|
self.delta_name = None
|
|
|
|
def handle_properties(self, resources):
|
|
# Resources of non HotResource
|
|
# (Exclude derived)
|
|
non_hot_rsrcs = [r for r in resources if type(r) is not HotResource]
|
|
|
|
# Extract resource name from VduScalingAspectDeltas
|
|
vsad_rsrcs = [
|
|
r for r in non_hot_rsrcs if r.toscatype == SCALING_ASPECT_DELTA]
|
|
vsad_rsrcs = [
|
|
r for r in vsad_rsrcs if r.aspect == self.aspect_name]
|
|
vsad_rsrc = vsad_rsrcs[0]
|
|
|
|
# The names of the resource associated with the VDU.
|
|
# Supporting resources below.
|
|
# - tosca.nodes.nfv.Vdu.Compute
|
|
# - tosca.nodes.nfv.VduCp
|
|
# - tosca.nodes.nfv.Vdu.VirtualBlockStorage
|
|
vdu_infos = [
|
|
r for r in resources
|
|
if vsad_rsrc.targets is not None and r.name in vsad_rsrc.targets]
|
|
if vdu_infos == []:
|
|
log.warning('Can not create %s node '
|
|
'because target vdu does not defined.'
|
|
% self.aspect_name)
|
|
|
|
related_rsrc_names = []
|
|
related_vl_names = []
|
|
for vdu_info in vdu_infos:
|
|
vdu_name = vdu_info.name
|
|
port_rsrc_names = [p.name for p in vdu_info.assoc_port_resources]
|
|
strg_rsrc_names = [s for s in vdu_info.virtual_storages]
|
|
related_rsrc_names.append(vdu_name)
|
|
related_rsrc_names.extend(port_rsrc_names)
|
|
related_rsrc_names.extend(strg_rsrc_names)
|
|
|
|
# Extract virtual_link mapping to vdu_name
|
|
cp_rsrcs = [
|
|
r for r in non_hot_rsrcs
|
|
if r.toscatype == VDU_CP and r.virtual_binding == vdu_name]
|
|
related_vl_names.extend([
|
|
cp.virtual_link for cp in cp_rsrcs
|
|
if cp.virtual_link is not None])
|
|
|
|
# Parameters defined in referenced YAML
|
|
parameters = {}
|
|
|
|
# Resources of scaling/non-scaling
|
|
scl_rsrcs = []
|
|
non_scl_rsrcs = []
|
|
|
|
# Properties of :AutoScalingGroup
|
|
asg_props = {}
|
|
|
|
# Resources which are contain related_rsrc_names are not
|
|
# call handle_expansion(), so call here and add resources.
|
|
related_rsrcs = [
|
|
r for r in resources if r.name in related_rsrc_names]
|
|
exp_rsrcs = list(
|
|
chain.from_iterable([
|
|
r.handle_expansion()
|
|
for r in related_rsrcs
|
|
if r.handle_expansion() is not None]))
|
|
|
|
# Allocate resources generated by handle_expansion()
|
|
# to scaling or non-scaling resource.
|
|
# Flavor is non-scaling resource.
|
|
scl_rsrcs.extend([
|
|
r for r in exp_rsrcs
|
|
if r.type != HOT_FLAVOR])
|
|
non_scl_rsrcs.extend([
|
|
r for r in exp_rsrcs
|
|
if r.type == HOT_FLAVOR])
|
|
|
|
for resource in resources:
|
|
# Allocate resources to scaling or non-scaling resource.
|
|
if resource.type not in HEAT_NON_SCALING_RESOURCES and \
|
|
resource.name in related_rsrc_names:
|
|
scl_rsrcs.append(resource)
|
|
else:
|
|
non_scl_rsrcs.append(resource)
|
|
|
|
# Processing for VDU
|
|
if resource.name in related_rsrc_names and \
|
|
resource.type == 'OS::Nova::Server':
|
|
self.vdu_name = resource.name
|
|
|
|
# Target aspect
|
|
target_aspect = \
|
|
self.policy.properties['aspects'][self.aspect_name]
|
|
|
|
# Extract scaling_adjustment from VduScalingAspectDeltas.
|
|
# VduScalingAspectDeltas can specify a delta for each scaling
|
|
# step but only the first one is used and others are ignored.
|
|
delta_names = target_aspect['step_deltas']
|
|
self.delta_name = delta_names[0]
|
|
self.scaling_adjustment = vsad_rsrc.deltas.get(self.delta_name)
|
|
|
|
# Extract min_size from VduInitialDelta
|
|
vid_rsrcs = [
|
|
r for r in non_hot_rsrcs
|
|
if r.toscatype == VDU_INITIAL_DELTA]
|
|
initial_deltas = [
|
|
r for r in vid_rsrcs
|
|
if r.targets is not None and self.vdu_name in r.targets]
|
|
min_size = None \
|
|
if initial_deltas == [] else initial_deltas[0].num
|
|
|
|
res = {}
|
|
if min_size is not None and \
|
|
self.scaling_adjustment is not None:
|
|
# Calculate max_size
|
|
max_scale_level = target_aspect['max_scale_level']
|
|
max_size = \
|
|
min_size + max_scale_level * self.scaling_adjustment
|
|
res["min_size"] = min_size
|
|
res["max_size"] = max_size
|
|
else:
|
|
log.warning('No min_size or(and) max_size is found for '
|
|
'aspect_name:%s, VDU:%s' % (
|
|
self.aspect_name, self.vdu_name))
|
|
|
|
# desired_capacity needs to be replaced by users because it is
|
|
# calculated using scaling aspect and instantiation level given
|
|
# in instantiation VNF request.
|
|
res["desired_capacity"] = self.DESIRED_CAPACITY
|
|
res['resource'] = {'type': self.aspect_name + '.hot.yaml'}
|
|
|
|
props = {}
|
|
props['type'] = resource.type
|
|
props['properties'] = resource.properties
|
|
|
|
for vl_name in related_vl_names:
|
|
vl_id = '%s_id' % (vl_name.lower())
|
|
asg_props.update({
|
|
vl_id: '{ get_resource: %s }' % (vl_name)})
|
|
|
|
# Replace flavor id
|
|
flvr_name = resource.flavor_resource_name
|
|
flvr_id = '%s_id' % (flvr_name.lower())
|
|
asg_props.update({
|
|
flvr_id: '{ get_resource: %s }' % (flvr_name)})
|
|
resource.properties['flavor'] = \
|
|
'{ get_param: %s }' % (flvr_id)
|
|
parameters[flvr_id] = {'type': 'string'}
|
|
|
|
res['resource'].update({
|
|
'properties': asg_props
|
|
})
|
|
non_scl_rsrcs.append(
|
|
HotResource(resource,
|
|
type='OS::Heat::AutoScalingGroup',
|
|
name=self.aspect_name,
|
|
properties=res))
|
|
|
|
# Processing for CP related to target VDU
|
|
elif resource.name in related_rsrc_names and \
|
|
resource.type == 'OS::Neutron::Port':
|
|
for vl_name in related_vl_names:
|
|
if vl_name == resource.virtual_link:
|
|
# Replace network id
|
|
vl_id = '%s_id' % (vl_name.lower())
|
|
resource.properties['network'] = \
|
|
'{ get_param: %s }' % (vl_id)
|
|
parameters[vl_id] = {'type': 'string'}
|
|
|
|
# Create referenced YAML which is defined scaling resources
|
|
yaml_name = self.aspect_name + '.hot.yaml'
|
|
parameters = None if parameters == {} else parameters
|
|
nested_template = self._handle_nested_template(
|
|
scl_rsrcs,
|
|
yaml_name,
|
|
self.hot_template_parameters,
|
|
parameters=parameters)
|
|
|
|
return non_scl_rsrcs, nested_template
|