heat-translator/translator/hot/tosca/etsi_nfv/scalingaspect/tosca_policies_nfv_scalinga...

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