tacker/tacker/db/nfvo/vnffg_db.py

1110 lines
49 KiB
Python

# Copyright 2016 Red Hat Inc
# 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 oslo_utils import uuidutils
import random
import sqlalchemy as sa
from oslo_log import log as logging
from six import iteritems
from sqlalchemy import orm
from sqlalchemy.orm import exc as orm_exc
from tacker.db import db_base
from tacker.db import model_base
from tacker.db import models_v1
from tacker.db import types
from tacker.extensions import nfvo
from tacker.extensions.nfvo_plugins import vnffg
from tacker import manager
from tacker.plugins.common import constants
LOG = logging.getLogger(__name__)
_ACTIVE_UPDATE = (constants.ACTIVE, constants.PENDING_UPDATE)
_ACTIVE_UPDATE_ERROR_DEAD = (
constants.PENDING_CREATE, constants.ACTIVE, constants.PENDING_UPDATE,
constants.ERROR, constants.DEAD)
_VALID_VNFFG_UPDATE_ATTRIBUTES = ('vnf_mapping',)
_VALID_SFC_UPDATE_ATTRIBUTES = ('chain', 'symmetrical')
_VALID_NFP_UPDATE_ATTRIBUTES = ('symmetrical',)
_VALID_FC_UPDATE_ATTRIBUTES = ()
MATCH_CRITERIA = (
'eth_type', 'eth_src', 'eth_dst', 'vlan_id', 'vlan_pcp', 'mpls_label',
'mpls_tc', 'ip_dscp', 'ip_ecn', 'ip_src_prefix', 'ip_dst_prefix',
'ip_proto', 'destination_port_range', 'source_port_range',
'network_src_port_id', 'network_dst_port_id', 'network_id', 'network_name',
'tenant_id', 'icmpv4_type', 'icmpv4_code', 'arp_op', 'arp_spa',
'arp_tpa', 'arp_sha', 'arp_tha', 'ipv6_src', 'ipv6_dst', 'ipv6_flabel',
'icmpv6_type', 'icmpv6_code', 'ipv6_nd_target', 'ipv6_nd_sll',
'ipv6_nd_tll')
MATCH_DB_KEY_LIST = (
'eth_type', 'eth_src', 'eth_dst', 'vlan_id', 'vlan_pcp', 'mpls_label',
'mpls_tc', 'ip_dscp', 'ip_ecn', 'ip_src_prefix', 'ip_dst_prefix',
'ip_proto', 'destination_port_min', 'destination_port_max',
'source_port_min', 'source_port_max', 'network_src_port_id',
'network_dst_port_id', 'network_id', 'tenant_id', 'icmpv4_type',
'icmpv4_code', 'arp_op', 'arp_spa', 'arp_tpa', 'arp_sha', 'arp_tha',
'ipv6_src', 'ipv6_dst', 'ipv6_flabel', 'icmpv6_type', 'icmpv6_code',
'ipv6_nd_target', 'ipv6_nd_sll', 'ipv6_nd_tll'
)
CP = 'connection_points'
class VnffgTemplate(model_base.BASE, models_v1.HasId, models_v1.HasTenant):
"""Represents template to create a VNF Forwarding Graph."""
# Descriptive name
name = sa.Column(sa.String(255), nullable=False)
description = sa.Column(sa.Text)
# Vnffg template
template = sa.Column(types.Json)
# Vnffgd template source - onboarded
template_source = sa.Column(sa.String(255), server_default='onboarded')
class Vnffg(model_base.BASE, models_v1.HasTenant, models_v1.HasId):
"""VNF Forwarding Graph Data Model"""
name = sa.Column(sa.String(255), nullable=False)
description = sa.Column(sa.String(255), nullable=True)
# List of associated NFPs
forwarding_paths = orm.relationship("VnffgNfp", backref="vnffg")
vnffgd_id = sa.Column(types.Uuid, sa.ForeignKey('vnffgtemplates.id'))
vnffgd = orm.relationship('VnffgTemplate')
status = sa.Column(sa.String(255), nullable=False)
# Mapping of VNFD to VNF instance names
vnf_mapping = sa.Column(types.Json)
attributes = sa.Column(types.Json)
class VnffgNfp(model_base.BASE, models_v1.HasTenant, models_v1.HasId):
"""Network Forwarding Path Data Model"""
name = sa.Column(sa.String(255), nullable=False)
vnffg_id = sa.Column(types.Uuid, sa.ForeignKey('vnffgs.id'),
nullable=False)
# List of associated classifiers
classifiers = orm.relationship('VnffgClassifier', backref='nfp')
chain = orm.relationship('VnffgChain', backref='nfp',
uselist=False)
status = sa.Column(sa.String(255), nullable=False)
path_id = sa.Column(sa.String(255), nullable=False)
# symmetry of forwarding path
symmetrical = sa.Column(sa.Boolean(), default=False)
class VnffgChain(model_base.BASE, models_v1.HasTenant, models_v1.HasId):
"""Service Function Chain Data Model"""
status = sa.Column(sa.String(255), nullable=False)
instance_id = sa.Column(sa.String(255), nullable=True)
# symmetry of forwarding path
symmetrical = sa.Column(sa.Boolean(), default=False)
# chain
chain = sa.Column(types.Json)
path_id = sa.Column(sa.String(255), nullable=False)
nfp_id = sa.Column(types.Uuid, sa.ForeignKey('vnffgnfps.id'))
class VnffgClassifier(model_base.BASE, models_v1.HasTenant, models_v1.HasId):
"""VNFFG NFP Classifier Data Model"""
name = sa.Column(sa.String(255), nullable=True)
status = sa.Column(sa.String(255), nullable=False)
instance_id = sa.Column(sa.String(255), nullable=True)
chain_id = sa.Column(types.Uuid, sa.ForeignKey('vnffgchains.id'))
chain = orm.relationship('VnffgChain', backref='classifier',
uselist=False, foreign_keys=[chain_id])
nfp_id = sa.Column(types.Uuid, sa.ForeignKey('vnffgnfps.id'))
# match criteria
match = orm.relationship('ACLMatchCriteria')
class ACLMatchCriteria(model_base.BASE, models_v1.HasId):
"""Represents ACL match criteria of a classifier."""
vnffgc_id = sa.Column(types.Uuid, sa.ForeignKey('vnffgclassifiers.id'))
eth_src = sa.Column(sa.String(36), nullable=True)
eth_dst = sa.Column(sa.String(36), nullable=True)
eth_type = sa.Column(sa.String(36), nullable=True)
vlan_id = sa.Column(sa.Integer, nullable=True)
vlan_pcp = sa.Column(sa.Integer, nullable=True)
mpls_label = sa.Column(sa.Integer, nullable=True)
mpls_tc = sa.Column(sa.Integer, nullable=True)
ip_dscp = sa.Column(sa.Integer, nullable=True)
ip_ecn = sa.Column(sa.Integer, nullable=True)
ip_src_prefix = sa.Column(sa.String(36), nullable=True)
ip_dst_prefix = sa.Column(sa.String(36), nullable=True)
source_port_min = sa.Column(sa.Integer, nullable=True)
source_port_max = sa.Column(sa.Integer, nullable=True)
destination_port_min = sa.Column(sa.Integer, nullable=True)
destination_port_max = sa.Column(sa.Integer, nullable=True)
ip_proto = sa.Column(sa.Integer, nullable=True)
network_id = sa.Column(types.Uuid, nullable=True)
network_src_port_id = sa.Column(types.Uuid, nullable=True)
network_dst_port_id = sa.Column(types.Uuid, nullable=True)
tenant_id = sa.Column(sa.String(64), nullable=True)
icmpv4_type = sa.Column(sa.Integer, nullable=True)
icmpv4_code = sa.Column(sa.Integer, nullable=True)
arp_op = sa.Column(sa.Integer, nullable=True)
arp_spa = sa.Column(sa.String(36), nullable=True)
arp_tpa = sa.Column(sa.String(36), nullable=True)
arp_sha = sa.Column(sa.String(36), nullable=True)
arp_tha = sa.Column(sa.String(36), nullable=True)
ipv6_src = sa.Column(sa.String(36), nullable=True)
ipv6_dst = sa.Column(sa.String(36), nullable=True)
ipv6_flabel = sa.Column(sa.Integer, nullable=True)
icmpv6_type = sa.Column(sa.Integer, nullable=True)
icmpv6_code = sa.Column(sa.Integer, nullable=True)
ipv6_nd_target = sa.Column(sa.String(36), nullable=True)
ipv6_nd_sll = sa.Column(sa.String(36), nullable=True)
ipv6_nd_tll = sa.Column(sa.String(36), nullable=True)
class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
def __init__(self):
super(VnffgPluginDbMixin, self).__init__()
def create_vnffg(self, context, vnffg):
vnffg_dict = self._create_vnffg_pre(context, vnffg)
sfc_instance = uuidutils.generate_uuid()
fc_instance = uuidutils.generate_uuid()
self._create_vnffg_post(context, sfc_instance,
fc_instance, vnffg_dict)
self._create_vnffg_status(context, vnffg_dict)
return vnffg_dict
def get_vnffg(self, context, vnffg_id, fields=None):
vnffg_db = self._get_resource(context, Vnffg, vnffg_id)
return self._make_vnffg_dict(vnffg_db, fields)
def get_vnffgs(self, context, filters=None, fields=None):
return self._get_collection(context, Vnffg, self._make_vnffg_dict,
filters=filters, fields=fields)
def update_vnffg(self, context, vnffg_id, vnffg):
vnffg_dict = self._update_vnffg_pre(context, vnffg_id)
self._update_vnffg_post(context, vnffg_id, constants.ACTIVE, vnffg)
return vnffg_dict
def delete_vnffg(self, context, vnffg_id):
self._delete_vnffg_pre(context, vnffg_id)
self._delete_vnffg_post(context, vnffg_id, False)
def create_vnffgd(self, context, vnffgd):
template = vnffgd['vnffgd']
LOG.debug('template %s', template)
tenant_id = self._get_tenant_id_for_create(context, template)
template_source = template.get('template_source')
with context.session.begin(subtransactions=True):
template_id = uuidutils.generate_uuid()
template_db = VnffgTemplate(
id=template_id,
tenant_id=tenant_id,
name=template.get('name'),
description=template.get('description'),
template=template.get('template'),
template_source=template_source)
context.session.add(template_db)
LOG.debug('template_db %(template_db)s',
{'template_db': template_db})
return self._make_template_dict(template_db)
def get_vnffgd(self, context, vnffgd_id, fields=None):
template_db = self._get_resource(context, VnffgTemplate,
vnffgd_id)
return self._make_template_dict(template_db, fields)
def get_vnffgds(self, context, filters=None, fields=None):
if ('template_source' in filters) and \
(filters['template_source'][0] == 'all'):
filters.pop('template_source')
return self._get_collection(context, VnffgTemplate,
self._make_template_dict,
filters=filters, fields=fields)
def delete_vnffgd(self, context, vnffgd_id):
with context.session.begin(subtransactions=True):
vnffg_db = context.session.query(Vnffg).filter_by(
vnffgd_id=vnffgd_id).first()
if vnffg_db is not None:
raise nfvo.VnffgdInUse(vnffgd_id=vnffgd_id)
template_db = self._get_resource(context, VnffgTemplate,
vnffgd_id)
context.session.delete(template_db)
def get_classifier(self, context, classifier_id, fields=None):
classifier_db = self._get_resource(context, VnffgClassifier,
classifier_id)
return self._make_classifier_dict(classifier_db, fields)
def get_classifiers(self, context, filters=None, fields=None):
return self._get_collection(context, VnffgClassifier,
self._make_classifier_dict,
filters=filters, fields=fields)
def create_classifiers_map(self, classifier_ids, instance_ids):
return {classifier_id: instance_ids[i]
for i, classifier_id in enumerate(classifier_ids)}
def get_nfp(self, context, nfp_id, fields=None):
nfp_db = self._get_resource(context, VnffgNfp, nfp_id)
return self._make_nfp_dict(nfp_db, fields)
def get_nfps(self, context, filters=None, fields=None):
return self._get_collection(context, VnffgNfp,
self._make_nfp_dict,
filters=filters, fields=fields)
def get_sfc(self, context, sfc_id, fields=None):
chain_db = self._get_resource(context, VnffgChain, sfc_id)
return self._make_chain_dict(chain_db, fields)
def get_sfcs(self, context, filters=None, fields=None):
return self._get_collection(context, VnffgChain,
self._make_chain_dict,
filters=filters, fields=fields)
def _update_template_params(self, original, paramvalues, param_matched):
if 'get_input' not in str(original):
return
if isinstance(original, dict):
for key_, value in iteritems(original):
if isinstance(value, dict) and 'get_input' in value:
if value['get_input'] in paramvalues:
original[key_] = paramvalues[value['get_input']]
param_matched.setdefault(value['get_input'], 0)
param_matched[value['get_input']] += 1
else:
raise nfvo.VnffgTemplateParamParsingException(
get_input=value['get_input'])
else:
self._update_template_params(value,
paramvalues, param_matched)
elif isinstance(original, list):
for element in original:
self._update_template_params(element,
paramvalues, param_matched)
def _process_parameterized_template(self, dev_attrs, vnffgd_template):
param_vattrs_dict = dev_attrs.pop('param_values', None)
param_matched = {}
if isinstance(param_vattrs_dict, dict):
self._update_template_params(vnffgd_template,
param_vattrs_dict, param_matched)
else:
raise nfvo.VnffgParamValueFormatError(
param_value=param_vattrs_dict)
for param_key in param_vattrs_dict.keys():
if param_matched.get(param_key) is None:
raise nfvo.VnffgParamValueNotUsed(param_key=param_key)
# called internally, not by REST API
def _create_vnffg_pre(self, context, vnffg):
vnffg = vnffg['vnffg']
LOG.debug('vnffg %s', vnffg)
tenant_id = self._get_tenant_id_for_create(context, vnffg)
name = vnffg.get('name')
vnffg_id = vnffg.get('id') or uuidutils.generate_uuid()
template_id = vnffg['vnffgd_id']
symmetrical = vnffg['symmetrical']
with context.session.begin(subtransactions=True):
template_db = self._get_resource(context, VnffgTemplate,
template_id)
LOG.debug('vnffg template %s', template_db)
if vnffg.get('attributes') and \
vnffg['attributes'].get('param_values'):
vnffg_param = vnffg['attributes']
vnffgd_topology_template = \
template_db.template['vnffgd']['topology_template']
self._process_parameterized_template(vnffg_param,
vnffgd_topology_template)
template_db.template['vnffgd']['topology_template'] = \
vnffgd_topology_template
vnf_members = self._get_vnffg_property(template_db.template,
'constituent_vnfs')
LOG.debug('Constituent VNFs: %s', vnf_members)
vnf_mapping = self._get_vnf_mapping(context, vnffg.get(
'vnf_mapping'), vnf_members)
LOG.debug('VNF Mapping: %s', vnf_mapping)
# create NFP dict
nfp_dict = self._create_nfp_pre(template_db)
LOG.debug('NFP: %s', nfp_dict)
vnffg_db = Vnffg(id=vnffg_id,
tenant_id=tenant_id,
name=name,
description=template_db.description,
vnf_mapping=vnf_mapping,
vnffgd_id=template_id,
attributes=template_db.get('template'),
status=constants.PENDING_CREATE)
context.session.add(vnffg_db)
nfp_id = uuidutils.generate_uuid()
sfc_id = uuidutils.generate_uuid()
classifiers = self._policy_to_acl_criteria(context, template_db,
nfp_dict['name'],
vnf_mapping)
LOG.debug('classifiers %s', classifiers)
classifier_ids = [uuidutils.generate_uuid() for i in classifiers]
nfp_db = VnffgNfp(id=nfp_id, vnffg_id=vnffg_id,
tenant_id=tenant_id,
name=nfp_dict['name'],
status=constants.PENDING_CREATE,
path_id=nfp_dict['path_id'],
symmetrical=symmetrical)
context.session.add(nfp_db)
chain = self._create_port_chain(context, vnf_mapping, template_db,
nfp_dict['name'])
LOG.debug('chain: %s', chain)
sfc_db = VnffgChain(id=sfc_id,
tenant_id=tenant_id,
status=constants.PENDING_CREATE,
symmetrical=symmetrical,
chain=chain,
nfp_id=nfp_id,
path_id=nfp_dict['path_id'])
context.session.add(sfc_db)
for i, classifier_id in enumerate(classifier_ids):
sfcc_db = VnffgClassifier(id=classifier_id,
name=classifiers[i]['name'],
tenant_id=tenant_id,
status=constants.PENDING_CREATE,
nfp_id=nfp_id,
chain_id=sfc_id)
context.session.add(sfcc_db)
match_db_table = ACLMatchCriteria(
id=uuidutils.generate_uuid(),
vnffgc_id=classifier_id,
**classifiers[i]['match'])
context.session.add(match_db_table)
return self._make_vnffg_dict(vnffg_db)
@staticmethod
def _create_nfp_pre(template_db):
template = template_db.template['vnffgd']['topology_template']
nfp_dict = dict()
vnffg_name = list(template['groups'].keys())[0]
# we assume only one NFP for initial implementation
nfp_dict['name'] = template['groups'][vnffg_name]['members'][0]
nfp_dict['path_id'] = template['node_templates'][nfp_dict['name']][
'properties']['id']
if not nfp_dict['path_id']:
# TODO(trozet): do we need to check if this path ID is already
# taken by another VNFFG
nfp_dict['path_id'] = random.randint(1, 16777216)
return nfp_dict
def _create_port_chain(self, context, vnf_mapping, template_db, nfp_name):
"""Creates a list of physical port ids to represent an ordered chain
:param context: SQL session context
:param vnf_mapping: dict of VNFD to VNF instance mappings
:param template_db: VNFFG Descriptor
:param nfp_name: name of the forwarding path with chain requirements
:return: list of port chain including vnf name and list of CPs
"""
chain_list = []
prev_forwarder = None
vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
# Build the list of logical chain representation
logical_chain = self._get_nfp_attribute(template_db.template,
nfp_name, 'path')
# Build physical port chain
for element in logical_chain:
if element['forwarder'] not in vnf_mapping.keys():
raise nfvo.NfpForwarderNotFoundException(vnfd=element[
'forwarder'],
mapping=vnf_mapping)
# TODO(trozet): validate CP in VNFD has forwarding capability
# Find VNF resources
vnf = vnfm_plugin.get_vnf_resources(context,
vnf_mapping[element[
'forwarder']]
)
vnf_info = vnfm_plugin.get_vnf(context,
vnf_mapping[element['forwarder']])
vnf_cp = None
for resource in vnf:
if resource['name'] == element['capability']:
vnf_cp = resource['id']
break
if vnf_cp is None:
raise nfvo.VnffgCpNotFoundException(cp_id=element[
'capability'], vnf_id=vnf_mapping[element['forwarder']])
# Check if this is a new VNF entry in the chain
if element['forwarder'] != prev_forwarder:
chain_list.append({'name': vnf_info['name'],
CP: [vnf_cp]})
prev_forwarder = element['forwarder']
# Must be an egress CP
else:
if len(chain_list[-1][CP]) > 1:
raise nfvo.NfpRequirementsException(vnfd=element[
'forwarder'])
else:
chain_list[-1][CP].append(vnf_cp)
return chain_list
@staticmethod
def _get_vnffg_property(template, vnffg_property):
template = template['vnffgd']['topology_template']
vnffg_name = list(template['groups'].keys())[0]
try:
return template['groups'][vnffg_name]['properties'][vnffg_property]
except KeyError:
raise nfvo.VnffgPropertyNotFoundException(
vnffg_property=vnffg_property)
@staticmethod
def _get_nfp_attribute(template, nfp, attribute):
"""Finds any attribute of an NFP described in a template
:param template: VNFFGD template
:param nfp: name of NFP
:param attribute: attribute to find
:return: value of attribute from template
"""
template = template['vnffgd']['topology_template']
try:
attr_val = VnffgPluginDbMixin._search_value(
template['node_templates'][nfp], attribute)
if attr_val is None:
LOG.debug('NFP %(nfp)s, attr %(attr)s',
{'nfp': template['node_templates'][nfp],
'attr': attribute})
raise nfvo.NfpAttributeNotFoundException(attribute=attribute)
else:
return attr_val
except KeyError:
raise nfvo.NfpAttributeNotFoundException(attribute=attribute)
@staticmethod
def _search_value(search_dict, search_key):
for k, v in search_dict.items():
if k == search_key:
return v
elif isinstance(v, dict):
val = VnffgPluginDbMixin._search_value(v, search_key)
if val is not None:
return val
def _validate_vnfd_in_vnf_mapping(self, vnf_mapping, vnf_members):
"""Validate whether or not the vnf_mapping is valid for update.
In the update_vnnfg procedure we need to know whether or not the
the vnf_mapping is valid so we can use it to update the chain.
"""
if not vnf_mapping:
raise nfvo.VnfMappingNotFoundException()
else:
for vnfd, vnf in vnf_mapping.items():
if vnfd not in vnf_members:
raise nfvo.VnfMappingNotValidException(vnfd=vnfd)
def _combine_current_and_new_vnf_mapping(self, context,
new_mapping, old_mapping):
"""Create an updated vnf mapping.
In this function we create an updated vnf mapping which is
a mix of the vnf_mapping which already exists in database
and the new mapping that the user passes.
"""
updated_vnf_mapping = old_mapping.copy()
updated_vnf_mapping.update(new_mapping)
return updated_vnf_mapping
def _get_vnf_mapping(self, context, vnf_mapping, vnf_members):
"""Creates/validates a mapping of VNFD names to VNF IDs for NFP.
:param context: SQL session context
:param vnf_mapping: dict of requested VNFD:VNF_ID mappings
:param vnf_members: list of constituent VNFs from a VNFFG
:return: dict of VNFD:VNF_ID mappings
"""
vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
new_mapping = dict()
for vnfd in vnf_members:
# there should only be one ID returned for a unique name
try:
vnfd_id = vnfm_plugin.get_vnfds(context, {'name': [vnfd]},
fields=['id']).pop()['id']
except Exception:
raise nfvo.VnffgdVnfdNotFoundException(vnfd_name=vnfd)
if vnfd_id is None:
raise nfvo.VnffgdVnfdNotFoundException(vnfd_name=vnfd)
else:
# if no VNF mapping, we need to abstractly look for instances
# that match VNFD
if vnf_mapping is None or vnfd not in vnf_mapping.keys():
# find suitable VNFs from vnfd_id
LOG.debug('Searching VNFS with id %s', vnfd_id)
vnf_list = vnfm_plugin.get_vnfs(context,
{'vnfd_id': [vnfd_id]},
fields=['id'])
if len(vnf_list) == 0:
raise nfvo.VnffgInvalidMappingException(vnfd_name=vnfd)
else:
LOG.debug('Matching VNFs found %s', vnf_list)
vnf_list = [vnf['id'] for vnf in vnf_list]
if len(vnf_list) > 1:
new_mapping[vnfd] = random.choice(vnf_list)
else:
new_mapping[vnfd] = vnf_list[0]
# if VNF mapping, validate instances exist and match the VNFD
else:
vnf_vnfd = vnfm_plugin.get_vnf(context, vnf_mapping[vnfd],
fields=['vnfd_id'])
if vnf_vnfd is not None:
vnf_vnfd_id = vnf_vnfd['vnfd_id']
else:
raise nfvo.VnffgInvalidMappingException(vnfd_name=vnfd)
if vnfd_id != vnf_vnfd_id:
raise nfvo.VnffgInvalidMappingException(vnfd_name=vnfd)
else:
new_mapping[vnfd] = vnf_mapping.pop(vnfd)
self._validate_vim(context, new_mapping.values())
return new_mapping
def _validate_vim(self, context, vnfs):
"""Validates all VNFs are in the same VIM
:param context: SQL Session Context
:param vnfs: List of VNF instance IDs
:return: None
"""
LOG.debug('validating vim for vnfs %s', vnfs)
vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
vim_id = None
for vnf in vnfs:
vnf_dict = vnfm_plugin.get_vnf(context, vnf)
if vim_id is None:
vim_id = vnf_dict['vim_id']
elif vnf_dict['vim_id'] != vim_id:
raise nfvo.VnffgVimMappingException(vnf_id=vnf, vim_id=vim_id)
def _validate_criteria(self, criteria):
"""Validate whether or not the classifiers are unique.
We define a classifier as unique when at least one
key-value pair is different from another classifier.
"""
if not criteria:
raise nfvo.NfpPolicyCriteriaIndexError()
elif len(criteria) == 1:
pass
else:
for index, dict_one in enumerate(criteria):
if index != (len(criteria) - 1):
for dict_two in criteria[(index + 1):]:
if dict_one == dict_two:
raise nfvo. \
NfpDuplicatePolicyCriteria(first_dict=dict_one,
sec_dict=dict_two)
def _policy_to_acl_criteria(self, context, template_db, nfp_name,
vnf_mapping):
template = template_db.template['vnffgd']['topology_template']
nfp = template['node_templates'][nfp_name]
try:
policy = nfp['properties']['policy']
except KeyError:
raise nfvo.NfpPolicyNotFoundException(policy=nfp)
if 'type' in policy:
if policy['type'] != 'ACL':
raise nfvo.NfpPolicyTypeError(type=policy['type'])
if 'criteria' not in policy:
raise nfvo.NfpPolicyCriteriaError(
error="Missing criteria in policy")
validation_list = []
for item in policy['criteria']:
if item.get('name') is None:
LOG.warning('The unnamed classifier approach'
' will be deprecated in subsequent'
' releases')
validation_list.append(item)
else:
validation_list.append(item['classifier'])
self._validate_criteria(validation_list)
classifiers = []
for criteria in policy['criteria']:
match = dict()
if criteria.get('name') is None:
criteria_dict = criteria.copy()
else:
criteria_dict = criteria['classifier'].copy()
for key, val in criteria_dict.items():
if key in MATCH_CRITERIA:
match.update(self._convert_criteria(context, key, val,
vnf_mapping))
else:
raise nfvo.NfpPolicyCriteriaError(error="Unsupported "
"criteria: "
"{}".format(key))
classifiers.append({'name': criteria.get('name'),
'match': match})
return classifiers
def _convert_criteria(self, context, criteria, value, vnf_mapping):
"""Method is used to convert criteria to proper db value from template
:param context: SQL session context
:param criteria: input criteria name
:param value: input value
:param vnf_mapping: mapping of VNFD to VNF instances
:return: converted dictionary
"""
if criteria.endswith('_range'):
prefix = criteria[:-6]
criteria_min = prefix + "_min"
criteria_max = prefix + "_max"
try:
min_val, max_val = value.split('-')
except ValueError:
raise nfvo.NfpPolicyCriteriaError(error="Range missing or "
"incorrect for "
"%s".format(criteria))
return {criteria_min: int(min_val), criteria_max: int(max_val)}
elif criteria.endswith('_name'):
prefix = criteria[:-5]
vnf_id = list(vnf_mapping.values())[0]
new_value = self._vim_resource_name_to_id(context, prefix, value,
vnf_id)
new_name = prefix + "_id"
return {new_name: new_value}
else:
return {criteria: value}
def _vim_resource_name_to_id(self, context, resource, name, vnf_id):
"""Converts a VIM resource name to its ID
:param context: SQL session context
:param resource: resource type to find (network, subnet, etc)
:param name: name of the resource to find its ID
:param vnf_id: A VNF instance ID that is part of the chain to which
the classifier will apply to
:return: ID of the resource name
"""
# this should be overridden with driver call to find ID given name
# for resource
return uuidutils.generate_uuid()
# called internally, not by REST API
# instance_id = None means error on creation
def _create_vnffg_post(self, context, sfc_instance_id,
classifiers_map, vnffg_dict):
LOG.debug('SFC created instance is %s', sfc_instance_id)
LOG.debug('Flow Classifiers created instances are %s',
[classifiers_map[item] for item in classifiers_map])
nfp_dict = self.get_nfp(context, vnffg_dict['forwarding_paths'])
sfc_id = nfp_dict['chain_id']
with context.session.begin(subtransactions=True):
query = (self._model_query(context, VnffgChain).
filter(VnffgChain.id == sfc_id).
filter(VnffgChain.status == constants.PENDING_CREATE).
one())
query.update({'instance_id': sfc_instance_id})
if sfc_instance_id is None:
query.update({'status': constants.ERROR})
else:
query.update({'status': constants.ACTIVE})
for classifier_id, fc_instance_id in classifiers_map.items():
query = (self._model_query(context, VnffgClassifier).
filter(VnffgClassifier.id == classifier_id).
filter(VnffgClassifier.status ==
constants.PENDING_CREATE).
one())
query.update({'instance_id': fc_instance_id})
if fc_instance_id is None:
query.update({'status': constants.ERROR})
else:
query.update({'status': constants.ACTIVE})
def _create_vnffg_status(self, context, vnffg):
nfp = self.get_nfp(context, vnffg['forwarding_paths'])
chain = self.get_sfc(context, nfp['chain_id'])
if chain['status'] == constants.ERROR:
self._update_all_status(context, vnffg['id'], nfp['id'],
constants.ERROR)
elif chain['status'] == constants.ACTIVE:
classifiers_active_state = True
for classifier in [self.get_classifier(context, classifier_id)
for classifier_id in nfp['classifier_ids']]:
if classifier['status'] == constants.ACTIVE:
continue
elif classifier['status'] == constants.ERROR:
classifiers_active_state = False
break
if classifiers_active_state:
self._update_all_status(context, vnffg['id'], nfp['id'],
constants.ACTIVE)
else:
self._update_all_status(context, vnffg['id'], nfp['id'],
constants.ERROR)
def _update_all_status(self, context, vnffg_id, nfp_id, status):
with context.session.begin(subtransactions=True):
query = (self._model_query(context, Vnffg).
filter(Vnffg.id == vnffg_id))
query.update({'status': status})
nfp_query = (self._model_query(context, VnffgNfp).
filter(VnffgNfp.id == nfp_id))
nfp_query.update({'status': status})
def _make_vnffg_dict(self, vnffg_db, fields=None):
LOG.debug('vnffg_db %s', vnffg_db)
LOG.debug('vnffg_db nfp %s', vnffg_db.forwarding_paths)
res = {
'forwarding_paths': vnffg_db.forwarding_paths[0]['id']
}
key_list = ('id', 'tenant_id', 'name', 'description',
'vnf_mapping', 'status', 'vnffgd_id', 'attributes')
res.update((key, vnffg_db[key]) for key in key_list)
return self._fields(res, fields)
def _update_vnffg_pre(self, context, vnffg_id):
vnffg = self.get_vnffg(context, vnffg_id)
nfp = self.get_nfp(context, vnffg['forwarding_paths'])
sfc = self.get_sfc(context, nfp['chain_id'])
classifiers = [self.get_classifier(context, classifier_id) for
classifier_id in nfp['classifier_ids']]
with context.session.begin(subtransactions=True):
vnffg_db = self._get_vnffg_db(context, vnffg['id'], _ACTIVE_UPDATE,
constants.PENDING_UPDATE)
self._get_nfp_db(context, nfp['id'], _ACTIVE_UPDATE,
constants.PENDING_UPDATE)
self._get_sfc_db(context, sfc['id'], _ACTIVE_UPDATE,
constants.PENDING_UPDATE)
for classifier in classifiers:
self._get_classifier_db(context, classifier['id'],
_ACTIVE_UPDATE,
constants.PENDING_UPDATE)
return self._make_vnffg_dict(vnffg_db)
def _update_vnffg_post(self, context, vnffg_id,
updated_items,
n_sfc_chain_id=None):
vnffg = self.get_vnffg(context, vnffg_id)
nfp = self.get_nfp(context, vnffg['forwarding_paths'])
with context.session.begin(subtransactions=True):
query_chain = (self._model_query(context, VnffgChain).
filter(VnffgChain.id == nfp['chain_id']).
filter(VnffgChain.status ==
constants.PENDING_UPDATE))
query_nfp = (self._model_query(context, VnffgNfp).
filter(VnffgNfp.id == nfp['id']).
filter(VnffgNfp.status ==
constants.PENDING_UPDATE))
query_vnffg = (self._model_query(context, Vnffg).
filter(Vnffg.id == vnffg['id']).
filter(Vnffg.status ==
constants.PENDING_UPDATE))
if n_sfc_chain_id is None:
query_chain.update({'status': constants.ERROR})
else:
for key in _VALID_VNFFG_UPDATE_ATTRIBUTES:
if updated_items.get(key) is not None:
query_vnffg.update({key: updated_items[key]})
for key in _VALID_NFP_UPDATE_ATTRIBUTES:
if updated_items.get(key) is not None:
query_nfp.update({key: updated_items[key]})
for key in _VALID_SFC_UPDATE_ATTRIBUTES:
if updated_items.get(key) is not None:
query_chain.update({key: updated_items[key]})
query_chain.update({'status': constants.ACTIVE})
def _update_vnffg_status(self, context, vnffg_id, error=False,
db_state=constants.ERROR):
query_cls = []
vnffg = self.get_vnffg(context, vnffg_id)
nfp = self.get_nfp(context, vnffg['forwarding_paths'])
classifier_ids = nfp['classifier_ids']
chain_dict = self.get_sfc(context, nfp['chain_id'])
with context.session.begin(subtransactions=True):
query_chain = (self._model_query(context, VnffgChain).
filter(VnffgChain.id == nfp['chain_id']))
for classifier_id in classifier_ids:
query_cl = (self._model_query(context, VnffgClassifier).
filter(VnffgClassifier.id == classifier_id))
query_cls.append(query_cl)
query_nfp = (self._model_query(context, VnffgNfp).
filter(VnffgNfp.id == nfp['id']))
query_vnffg = (self._model_query(context, Vnffg).
filter(Vnffg.id == vnffg['id']))
if not error and chain_dict['status'] == constants.ACTIVE:
for query_cl in query_cls:
query_cl.update({'status': constants.ACTIVE})
query_nfp.update({'status': constants.ACTIVE})
query_vnffg.update({'status': constants.ACTIVE})
else:
if db_state == constants.ACTIVE:
query_chain.update({'status': constants.ACTIVE})
for query_cl in query_cls:
query_cl.update({'status': constants.ACTIVE})
query_nfp.update({'status': constants.ACTIVE})
query_vnffg.update({'status': constants.ACTIVE})
else:
query_chain.update({'status': constants.ERROR})
for query_cl in query_cls:
query_cl.update({'status': constants.ERROR})
query_nfp.update({'status': constants.ERROR})
query_vnffg.update({'status': constants.ERROR})
def _get_vnffg_db(self, context, vnffg_id, current_statuses, new_status):
try:
vnffg_db = (
self._model_query(context, Vnffg).
filter(Vnffg.id == vnffg_id).
filter(Vnffg.status.in_(current_statuses)).
with_lockmode('update').one())
except orm_exc.NoResultFound:
raise nfvo.VnffgNotFoundException(vnffg_id=vnffg_id)
if vnffg_db.status == constants.PENDING_UPDATE:
raise nfvo.VnffgInUse(vnffg_id=vnffg_id)
vnffg_db.update({'status': new_status})
return vnffg_db
def _get_nfp_db(self, context, nfp_id, current_statuses, new_status):
try:
nfp_db = (
self._model_query(context, VnffgNfp).
filter(VnffgNfp.id == nfp_id).
filter(VnffgNfp.status.in_(current_statuses)).
with_lockmode('update').one())
except orm_exc.NoResultFound:
raise nfvo.NfpNotFoundException(nfp_id=nfp_id)
if nfp_db.status == constants.PENDING_UPDATE:
raise nfvo.NfpInUse(nfp_id=nfp_id)
nfp_db.update({'status': new_status})
return nfp_db
def _get_sfc_db(self, context, sfc_id, current_statuses, new_status):
try:
sfc_db = (
self._model_query(context, VnffgChain).
filter(VnffgChain.id == sfc_id).
filter(VnffgChain.status.in_(current_statuses)).
with_lockmode('update').one())
except orm_exc.NoResultFound:
raise nfvo.SfcNotFoundException(sfc_id=sfc_id)
if sfc_db.status == constants.PENDING_UPDATE:
raise nfvo.SfcInUse(sfc_id=sfc_id)
sfc_db.update({'status': new_status})
return sfc_db
def _get_classifier_db(self, context, fc_id, current_statuses, new_status):
try:
fc_db = (
self._model_query(context, VnffgClassifier).
filter(VnffgClassifier.id == fc_id).
filter(VnffgClassifier.status.in_(current_statuses)).
with_lockmode('update').one())
except orm_exc.NoResultFound:
raise nfvo.ClassifierNotFoundException(fc_id=fc_id)
if fc_db.status == constants.PENDING_UPDATE:
raise nfvo.ClassifierInUse(fc_id=fc_id)
fc_db.update({'status': new_status})
return fc_db
def _delete_vnffg_pre(self, context, vnffg_id):
vnffg = self.get_vnffg(context, vnffg_id)
nfp = self.get_nfp(context, vnffg['forwarding_paths'])
chain = self.get_sfc(context, nfp['chain_id'])
classifiers = [self.get_classifier(context, classifier_id)
for classifier_id in nfp['classifier_ids']]
with context.session.begin(subtransactions=True):
vnffg_db = self._get_vnffg_db(
context, vnffg['id'], _ACTIVE_UPDATE_ERROR_DEAD,
constants.PENDING_DELETE)
self._get_nfp_db(context, nfp['id'], _ACTIVE_UPDATE_ERROR_DEAD,
constants.PENDING_DELETE)
self._get_sfc_db(context, chain['id'], _ACTIVE_UPDATE_ERROR_DEAD,
constants.PENDING_DELETE)
for classifier in classifiers:
self._get_classifier_db(context, classifier['id'],
_ACTIVE_UPDATE_ERROR_DEAD,
constants.PENDING_DELETE)
return self._make_vnffg_dict(vnffg_db)
def _delete_vnffg_post(self, context, vnffg_id, error):
vnffg = self.get_vnffg(context, vnffg_id)
nfp = self.get_nfp(context, vnffg['forwarding_paths'])
chain = self.get_sfc(context, nfp['chain_id'])
classifiers = [self.get_classifier(context, classifier_id)
for classifier_id in nfp['classifier_ids']]
fc_queries = []
match_queries = []
with context.session.begin(subtransactions=True):
vnffg_query = (
self._model_query(context, Vnffg).
filter(Vnffg.id == vnffg['id']).
filter(Vnffg.status == constants.PENDING_DELETE))
nfp_query = (
self._model_query(context, VnffgNfp).
filter(VnffgNfp.id == nfp['id']).
filter(VnffgNfp.status == constants.PENDING_DELETE))
sfc_query = (
self._model_query(context, VnffgChain).
filter(VnffgChain.id == chain['id']).
filter(VnffgChain.status == constants.PENDING_DELETE))
for classifier in classifiers:
fc_queries.append((
self._model_query(context, VnffgClassifier).
filter(VnffgClassifier.id == classifier['id']).
filter(VnffgClassifier.status ==
constants.PENDING_DELETE)))
match_queries.append((
self._model_query(context, ACLMatchCriteria).
filter(ACLMatchCriteria.vnffgc_id == classifier['id'])))
if error:
vnffg_query.update({'status': constants.ERROR})
nfp_query.update({'status': constants.ERROR})
sfc_query.update({'status': constants.ERROR})
for fc_query in fc_queries:
fc_query.update({'status': constants.ERROR})
else:
for match_query in match_queries:
match_query.delete()
for fc_query in fc_queries:
fc_query.delete()
sfc_query.delete()
nfp_query.delete()
vnffg_query.delete()
vnffgd_id = vnffg.get('vnffgd_id')
template_db = self._get_resource(context, VnffgTemplate,
vnffgd_id)
if template_db.get('template_source') == 'inline':
self.delete_vnffgd(context, vnffgd_id)
def _make_template_dict(self, template, fields=None):
res = {}
key_list = ('id', 'tenant_id', 'name', 'description', 'template',
'template_source')
res.update((key, template[key]) for key in key_list)
return self._fields(res, fields)
def _make_acl_match_dict(self, acl_match_db):
key_list = MATCH_DB_KEY_LIST
return {key: entry[key] for key in key_list for entry in acl_match_db
if entry[key]}
def _make_classifier_dict(self, classifier_db, fields=None):
LOG.debug('classifier_db %s', classifier_db)
LOG.debug('classifier_db match %s', classifier_db.match)
res = {
'match': self._make_acl_match_dict(classifier_db.match)
}
key_list = ('id', 'name', 'tenant_id', 'instance_id', 'status',
'chain_id', 'nfp_id')
res.update((key, classifier_db[key]) for key in key_list)
return self._fields(res, fields)
def _make_nfp_dict(self, nfp_db, fields=None):
LOG.debug('nfp_db %s', nfp_db)
res = {'chain_id': nfp_db.chain['id'],
'classifier_ids': [classifier['id'] for classifier in
nfp_db.classifiers]}
key_list = ('name', 'id', 'tenant_id', 'symmetrical', 'status',
'path_id', 'vnffg_id')
res.update((key, nfp_db[key]) for key in key_list)
return self._fields(res, fields)
def _make_chain_dict(self, chain_db, fields=None):
LOG.debug('chain_db %s', chain_db)
res = {}
key_list = ('id', 'tenant_id', 'symmetrical', 'status', 'chain',
'path_id', 'nfp_id', 'instance_id')
res.update((key, chain_db[key]) for key in key_list)
return self._fields(res, fields)
def _get_resource(self, context, model, res_id):
try:
return self._get_by_id(context, model, res_id)
except orm_exc.NoResultFound:
if issubclass(model, Vnffg):
raise nfvo.VnffgNotFoundException(vnffg_id=res_id)
elif issubclass(model, VnffgClassifier):
raise nfvo.ClassifierNotFoundException(classifier_id=res_id)
if issubclass(model, VnffgTemplate):
raise nfvo.VnffgdNotFoundException(vnffgd_id=res_id)
if issubclass(model, VnffgChain):
raise nfvo.SfcNotFoundException(sfc_id=res_id)
else:
raise