Files
tacker/tacker/extensions/vnfm.py
Qibin Yao 57902730d6 Add OpenID Connect Token Auth for k8s
This patch adds openid token auth support when calling k8s APIs.

Openid token auth of k8s relies on an external openid provider,
and Keycloak acts as the openid provider in this implementation.

Implements: blueprint support-openid-k8s-vim
Change-Id: Ie5e080a20cba3ba0ed514ede7955eb16729d797c
2022-09-12 01:26:53 +00:00

714 lines
21 KiB
Python

# Copyright 2015 Intel Corporation..
# 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.
import abc
from oslo_log import log as logging
from tacker._i18n import _
from tacker.api import extensions
from tacker.api.v1 import attributes as attr
from tacker.api.v1 import base
from tacker.api.v1 import resource_helper
from tacker.common import exceptions
from tacker import manager
from tacker.plugins.common import constants
from tacker.services import service_base
LOG = logging.getLogger(__name__)
class MultipleMGMTDriversSpecified(exceptions.InvalidInput):
message = _('More than one MGMT Driver per vnfd is not supported')
class ServiceTypesNotSpecified(exceptions.InvalidInput):
message = _('service types are not specified')
class VNFDInUse(exceptions.InUse):
message = _('VNFD %(vnfd_id)s is still in use')
class VNFInUse(exceptions.InUse):
message = _('VNF %(vnf_id)s is still in use')
class InvalidMgmtDriver(exceptions.InvalidInput):
message = _('Invalid Mgmt driver %(mgmt_driver_name)s.')
class InvalidInfraDriver(exceptions.InvalidInput):
message = _('VIM type %(vim_name)s is not supported as an infra driver')
class InvalidAPIAttributeType(exceptions.InvalidInput):
message = _('Expecting dict type for API attribute instead of %(atype)s ')
class VNFCreateFailed(exceptions.TackerException):
message = _('creating VNF based on %(vnfd_id)s failed')
class VNFUpdateInvalidInput(exceptions.TackerException):
message = _('VNF Update Invalid Input %(reason)s')
class VNFUpdateWaitFailed(exceptions.TackerException):
message = _('VNF Update %(reason)s')
class VNFCreateWaitFailed(exceptions.TackerException):
message = _('VNF Create %(reason)s')
class VNFScaleWaitFailed(exceptions.TackerException):
message = _('%(reason)s')
class VNFDeleteWaitFailed(exceptions.TackerException):
message = _('VNF Delete %(reason)s')
class VNFHealWaitFailed(exceptions.TackerException):
message = _('VNF Heal %(reason)s')
class VNFChangeExtConnWaitFailed(exceptions.TackerException):
message = _('VNF ChangeExtConn %(reason)s')
class VNFDeleteFailed(exceptions.TackerException):
message = _('%(reason)s')
class VNFHealFailed(exceptions.TackerException):
message = _('VNF %(vnf_id)s failed to heal')
class VNFDNotFound(exceptions.NotFound):
message = _('VNFD %(vnfd_id)s could not be found')
class CnfDefinitionNotFound(exceptions.NotFound):
message = _(
"CNF definition file with path %(path)s "
"is not found in vnf_artifacts.")
class CNFCreateWaitFailed(exceptions.TackerException):
message = _('CNF Create Failed with reason: %(reason)s')
class CNFScaleFailed(exceptions.TackerException):
message = _('CNF Scale Failed with reason: %(reason)s')
class CNFScaleWaitFailed(exceptions.TackerException):
message = _('CNF Scale Wait Failed with reason: %(reason)s')
class CNFHealFailed(exceptions.TackerException):
message = _('%(reason)s')
class CNFHealWaitFailed(exceptions.TackerException):
message = _('%(reason)s')
class InvalidVimConnectionInfo(exceptions.TackerException):
message = _('Invalid vim_connection_info: %(reason)s')
class HelmClientRemoteCommandError(exceptions.TackerException):
message = _('Failed to execute remote command.')
class HelmClientMissingParamsError(exceptions.TackerException):
message = _('The specified value %(value)s was not found.')
class HelmClientOtherError(exceptions.TackerException):
message = _('An error occurred in HelmClient: %(error_message)s.')
class ServiceTypeNotFound(exceptions.NotFound):
message = _('service type %(service_type_id)s could not be found')
class VNFNotFound(exceptions.NotFound):
message = _('VNF %(vnf_id)s could not be found')
class LCMUserDataFailed(exceptions.TackerException):
message = _('LCM user data %(reason)s')
class ParamYAMLNotWellFormed(exceptions.InvalidInput):
message = _("Parameter YAML not well formed - %(error_msg_details)s")
class ToscaParserFailed(exceptions.InvalidInput):
message = _("tosca-parser failed: - %(error_msg_details)s")
class HeatTranslatorFailed(exceptions.InvalidInput):
message = _("heat-translator failed: - %(error_msg_details)s")
class HeatClientException(exceptions.TackerException):
message = _("%(msg)s")
class IPAddrInvalidInput(exceptions.InvalidInput):
message = _("IP Address input values should be in a list format")
class HugePageSizeInvalidInput(exceptions.InvalidInput):
message = _("Value specified for mem_page_size is invalid: "
"%(error_msg_details)s. The valid values are 'small', 'large',"
" 'any' or an integer value in MB")
class CpuAllocationInvalidKeys(exceptions.InvalidInput):
message = _("Invalid keys specified in VNFD - %(error_msg_details)s."
"Supported keys are: %(valid_keys)s")
class CpuAllocationInvalidValues(exceptions.InvalidInput):
message = _("Invalid values specified in VNFD - %(error_msg_details)s."
"Supported Values are: %(valid_values)s")
class NumaNodesInvalidKeys(exceptions.InvalidInput):
message = _("Invalid keys specified in VNFD - %(error_msg_details)s."
"Supported keys are: %(valid_keys)s")
class FilePathMissing(exceptions.InvalidInput):
message = _("'file' attribute is missing for "
"tosca.artifacts.Deployment.Image.VM artifact type")
class InfraDriverUnreachable(exceptions.ServiceUnavailable):
message = _("Could not retrieve VNF resource IDs and"
" types. Please check %(service)s status.")
class VNFInactive(exceptions.InvalidInput):
message = _("VNF %(vnf_id)s is not in Active state: %(message)s")
class MetadataNotMatched(exceptions.InvalidInput):
message = _("Metadata for alarm policy is not matched")
class InvalidResourceType(exceptions.InvalidInput):
message = _("Resource type %(resource_type)s for alarm policy "
"is not supported")
class InvalidSubstitutionMapping(exceptions.InvalidInput):
message = _("Input for substitution mapping requirements are not"
" valid for %(requirement)s. They must be in the form"
" of list with two entries")
class SMRequirementMissing(exceptions.InvalidInput):
message = _("All the requirements for substitution_mappings are not"
" provided. Missing requirement for %(requirement)s")
class InvalidParamsForSM(exceptions.InvalidInput):
message = _("Please provide parameters for substitution mappings")
class InvalidKubernetesScalingPolicyNumber(exceptions.InvalidInput):
message = _("Please provide only one Scaling policy")
class InvalidKubernetesNetworkNumber(exceptions.InvalidInput):
message = _("Please provide one network for all vdus")
class InvalidKubernetesInputParameter(exceptions.InvalidInput):
message = _("Found unsupported keys for %(found_keys)s ")
class InvalidInstReqInfoForScaling(exceptions.InvalidInput):
message = _("Scaling resource cannot be set to "
"fixed ip_address or mac_address.")
class InvalidMaintenanceParameter(exceptions.InvalidInput):
message = _("Could not find the required params for maintenance")
class OIDCAuthFailed(exceptions.InvalidInput):
message = _("OIDC authentication and authorization failed."
" Detail: %(detail)s")
def _validate_service_type_list(data, valid_values=None):
if not isinstance(data, list):
msg = _("Invalid data format for service list: '%s'") % data
LOG.debug(msg)
return msg
if not data:
msg = _("Empty list is not allowed for service list. '%s'") % data
LOG.debug(msg)
return msg
key_specs = {
'service_type': {
'type:string': None,
}
}
for service in data:
msg = attr._validate_dict(service, key_specs)
if msg:
LOG.debug(msg)
return msg
attr.validators['type:service_type_list'] = _validate_service_type_list
NAME_MAX_LEN = 255
RESOURCE_ATTRIBUTE_MAP = {
'vnfds': {
'id': {
'allow_post': False,
'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True,
},
'tenant_id': {
'allow_post': True,
'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True,
},
'name': {
'allow_post': True,
'allow_put': True,
'validate': {'type:string': NAME_MAX_LEN},
'is_visible': True,
},
'description': {
'allow_post': True,
'allow_put': True,
'validate': {'type:string': None},
'is_visible': True,
'default': '',
},
'service_types': {
'allow_post': True,
'allow_put': False,
'convert_to': attr.convert_to_list,
'validate': {'type:service_type_list': None},
'is_visible': True,
'default': attr.ATTR_NOT_SPECIFIED,
},
'attributes': {
'allow_post': True,
'allow_put': False,
'convert_to': attr.convert_none_to_empty_dict,
'validate': {'type:dict_or_nodata': None},
'is_visible': True,
'default': None,
},
'created_at': {
'allow_post': False,
'allow_put': False,
'is_visible': True,
},
'updated_at': {
'allow_post': False,
'allow_put': False,
'is_visible': True,
},
'template_source': {
'allow_post': False,
'allow_put': False,
'is_visible': True,
'default': 'onboarded'
},
},
'vnfs': {
'id': {
'allow_post': False,
'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True
},
'tenant_id': {
'allow_post': True,
'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True
},
'vnfd_id': {
'allow_post': True,
'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'default': None
},
'vim_id': {
'allow_post': True,
'allow_put': False,
'validate': {'type:string': None},
'is_visible': True,
'default': '',
},
'name': {
'allow_post': True,
'allow_put': True,
'validate': {'type:string': NAME_MAX_LEN},
'is_visible': True,
},
'description': {
'allow_post': True,
'allow_put': True,
'validate': {'type:string': None},
'is_visible': True,
'default': '',
},
'instance_id': {
'allow_post': False,
'allow_put': False,
'validate': {'type:string': None},
'is_visible': True,
},
'mgmt_ip_address': {
'allow_post': False,
'allow_put': False,
'validate': {'type:string': None},
'is_visible': True,
},
'attributes': {
'allow_post': True,
'allow_put': True,
'validate': {'type:dict_or_none': None},
'is_visible': True,
'default': {},
},
'placement_attr': {
'allow_post': True,
'allow_put': False,
'validate': {'type:dict_or_none': None},
'is_visible': True,
'default': {},
},
'status': {
'allow_post': False,
'allow_put': False,
'is_visible': True,
},
'error_reason': {
'allow_post': False,
'allow_put': False,
'is_visible': True,
},
'created_at': {
'allow_post': False,
'allow_put': False,
'is_visible': True,
},
'updated_at': {
'allow_post': False,
'allow_put': False,
'is_visible': True,
},
'vnfd_template': {
'allow_post': True,
'allow_put': False,
'validate': {'type:dict_or_none': None},
'is_visible': True,
'default': None,
},
},
}
SUB_RESOURCE_ATTRIBUTE_MAP = {
'actions': {
'parent': {
'collection_name': 'vnfs',
'member_name': 'vnf'
},
'members': {
'scale': {
'parameters': {
'policy': {
'allow_post': True,
'allow_put': False,
'is_visible': True,
'validate': {'type:string': None}
},
'type': {
'allow_post': True,
'allow_put': False,
'is_visible': True,
'validate': {'type:string': None}
},
'tenant_id': {
'allow_post': True,
'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': False,
'is_visible': False
},
}
},
}
},
'triggers': {
'parent': {
'collection_name': 'vnfs',
'member_name': 'vnf'
},
'members': {
'trigger': {
'parameters': {
'policy_name': {
'allow_post': True,
'allow_put': False,
'is_visible': True,
'validate': {'type:string': None}
},
'action_name': {
'allow_post': True,
'allow_put': False,
'is_visible': True,
'validate': {'type:string': None}
},
'params': {
'allow_post': True,
'allow_put': False,
'is_visible': True,
'validate': {'type:dict_or_none': None}
},
'tenant_id': {
'allow_post': True,
'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': False,
'is_visible': False
}
}
},
}
},
'resources': {
'parent': {
'collection_name': 'vnfs',
'member_name': 'vnf'
},
'members': {
'resource': {
'parameters': {
'name': {
'allow_post': False,
'allow_put': False,
'is_visible': True,
},
'type': {
'allow_post': False,
'allow_put': False,
'is_visible': True,
},
'id': {
'allow_post': False,
'allow_put': False,
'is_visible': True,
},
}
}
}
},
'maintenances': {
'parent': {
'collection_name': 'vnfs',
'member_name': 'vnf'
},
'members': {
'maintenance': {
'parameters': {
'params': {
'allow_post': True,
'allow_put': False,
'is_visible': True,
'validate': {'type:dict_or_none': None}
},
'tenant_id': {
'allow_post': True,
'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': False,
'is_visible': False
},
'response': {
'allow_post': False,
'allow_put': False,
'validate': {'type:dict_or_none': None},
'is_visible': True
}
}
}
}
}
}
class Vnfm(extensions.ExtensionDescriptor):
@classmethod
def get_name(cls):
return 'VNF Manager'
@classmethod
def get_alias(cls):
return 'VNFM'
@classmethod
def get_description(cls):
return "Extension for VNF Manager"
@classmethod
def get_namespace(cls):
return 'https://wiki.openstack.org/Tacker'
@classmethod
def get_updated(cls):
return "2013-11-19T10:00:00-00:00"
@classmethod
def get_resources(cls):
special_mappings = {}
plural_mappings = resource_helper.build_plural_mappings(
special_mappings, RESOURCE_ATTRIBUTE_MAP)
plural_mappings['service_types'] = 'service_type'
attr.PLURALS.update(plural_mappings)
resources = resource_helper.build_resource_info(
plural_mappings, RESOURCE_ATTRIBUTE_MAP, constants.VNFM,
translate_name=True)
plugin = manager.TackerManager.get_service_plugins()[
constants.VNFM]
for collection_name in SUB_RESOURCE_ATTRIBUTE_MAP:
parent = SUB_RESOURCE_ATTRIBUTE_MAP[collection_name]['parent']
for resource_name in SUB_RESOURCE_ATTRIBUTE_MAP[
collection_name]['members']:
params = SUB_RESOURCE_ATTRIBUTE_MAP[
collection_name]['members'][resource_name]['parameters']
controller = base.create_resource(collection_name,
resource_name,
plugin, params,
allow_bulk=True,
parent=parent)
resource = extensions.ResourceExtension(
collection_name,
controller, parent,
attr_map=params)
resources.append(resource)
return resources
@classmethod
def get_plugin_interface(cls):
return VNFMPluginBase
def update_attributes_map(self, attributes):
super(Vnfm, self).update_attributes_map(
attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP)
def get_extended_resources(self, version):
version_map = {'1.0': RESOURCE_ATTRIBUTE_MAP}
return version_map.get(version, {})
class VNFMPluginBase(service_base.NFVPluginBase, metaclass=abc.ABCMeta):
def get_plugin_name(self):
return constants.VNFM
def get_plugin_type(self):
return constants.VNFM
def get_plugin_description(self):
return 'Tacker VNF Manager plugin'
@abc.abstractmethod
def create_vnfd(self, context, vnfd):
pass
@abc.abstractmethod
def delete_vnfd(self, context, vnfd_id):
pass
@abc.abstractmethod
def get_vnfd(self, context, vnfd_id, fields=None):
pass
@abc.abstractmethod
def get_vnfds(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def get_vnfs(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def get_vnf(self, context, vnf_id, fields=None):
pass
@abc.abstractmethod
def get_vnf_resources(self, context, vnf_id, fields=None, filters=None):
pass
@abc.abstractmethod
def create_vnf(self, context, vnf):
pass
@abc.abstractmethod
def update_vnf(
self, context, vnf_id, vnf):
pass
@abc.abstractmethod
def delete_vnf(self, context, vnf_id):
pass
@abc.abstractmethod
def create_vnf_scale(
self, context, vnf_id, scale):
pass
@abc.abstractmethod
def create_vnf_trigger(
self, context, vnf_id, trigger):
pass
@abc.abstractmethod
def create_vnf_maintenance(self, context, vnf_id, maintenance):
pass