Multi version API support
This patch provides a base to support multi version API. The existing code of functions for SOL specification was hard to understand and enhance since it is based on the code of legacy tacker API and they are connected with each other complicatedly. Therefore the code for SOL specification is newly created which is independent to the legacy tacker API so that it will be easy to maintain and enhance. This patch supports vnflcm v2 API (api_version 2.0.0) as a starting point. It supports less functions than the exsisting v1 API at the moment(Xena) but it will catch up with by the next release (Y). This patch makes supporting another API version easy when it will be supported in the future. Possibly it may thought to add v1 API to this code base. TODO: enhance UT/FT UT/FT is not sufficient at the moment. Additional UTs and FTs will be provided with another patches. Implements: blueprint multi-version-api Implements: blueprint support-nfv-solv3-start-and-terminate-vnf Implements: blueprint support-nfv-solv3-query-vnf-instances Implements: blueprint support-nfv-solv3-query-operation-occurrences Implements: blueprint support-nfv-solv3-subscriptions Change-Id: If76f315d8b3856e0eef9b8808b90f0b15d80d488
This commit is contained in:
parent
49ab5f9a15
commit
5f35b695bf
10
.zuul.yaml
10
.zuul.yaml
@ -258,6 +258,15 @@
|
||||
controller-tacker:
|
||||
tox_envlist: dsvm-functional-sol
|
||||
|
||||
- job:
|
||||
name: tacker-functional-devstack-multinode-sol-v2
|
||||
parent: tacker-functional-devstack-multinode-sol
|
||||
description: |
|
||||
Multinodes job for SOL V2 devstack-based functional tests
|
||||
host-vars:
|
||||
controller-tacker:
|
||||
tox_envlist: dsvm-functional-sol-v2
|
||||
|
||||
- job:
|
||||
name: tacker-functional-devstack-multinode-sol-separated-nfvo
|
||||
parent: tacker-functional-devstack-multinode-sol
|
||||
@ -507,3 +516,4 @@
|
||||
- tacker-functional-devstack-multinode-sol-separated-nfvo
|
||||
- tacker-functional-devstack-multinode-sol-kubernetes
|
||||
- tacker-functional-devstack-multinode-libs-master
|
||||
- tacker-functional-devstack-multinode-sol-v2
|
||||
|
@ -3,7 +3,9 @@ use = egg:Paste#urlmap
|
||||
/: tackerversions
|
||||
/v1.0: tackerapi_v1_0
|
||||
/vnfpkgm/v1: vnfpkgmapi_v1
|
||||
/vnflcm: vnflcm_versions
|
||||
/vnflcm/v1: vnflcm_v1
|
||||
/vnflcm/v2: vnflcm_v2
|
||||
|
||||
[composite:tackerapi_v1_0]
|
||||
use = call:tacker.auth:pipeline_factory
|
||||
@ -20,6 +22,16 @@ use = call:tacker.auth:pipeline_factory
|
||||
noauth = request_id catch_errors vnflcmaapp_v1
|
||||
keystone = request_id catch_errors authtoken keystonecontext vnflcmaapp_v1
|
||||
|
||||
[composite:vnflcm_v2]
|
||||
use = call:tacker.auth:pipeline_factory
|
||||
noauth = request_id catch_errors vnflcmaapp_v2
|
||||
keystone = request_id catch_errors authtoken keystonecontext vnflcmaapp_v2
|
||||
|
||||
[composite:vnflcm_versions]
|
||||
use = call:tacker.auth:pipeline_factory
|
||||
noauth = request_id catch_errors vnflcm_api_versions
|
||||
keystone = request_id catch_errors authtoken keystonecontext vnflcm_api_versions
|
||||
|
||||
[filter:request_id]
|
||||
paste.filter_factory = oslo_middleware:RequestId.factory
|
||||
|
||||
@ -49,3 +61,9 @@ paste.app_factory = tacker.api.vnfpkgm.v1.router:VnfpkgmAPIRouter.factory
|
||||
|
||||
[app:vnflcmaapp_v1]
|
||||
paste.app_factory = tacker.api.vnflcm.v1.router:VnflcmAPIRouter.factory
|
||||
|
||||
[app:vnflcmaapp_v2]
|
||||
paste.app_factory = tacker.sol_refactored.api.router:VnflcmAPIRouterV2.factory
|
||||
|
||||
[app:vnflcm_api_versions]
|
||||
paste.app_factory = tacker.sol_refactored.api.router:VnflcmVersions.factory
|
||||
|
@ -0,0 +1,24 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Support multi-version of RESTfulAPI. The client can use
|
||||
both VNF LCM API "1.3.0" and "2.0.0" defined by ETSI NFV.
|
||||
|
||||
- |
|
||||
Add new RESTful APIs of List VNF LCM API versions
|
||||
and Show VNF LCM API versions based on ETSI NFV specifications.
|
||||
They enable the client to retrieve supported versions of VNF LCM API.
|
||||
|
||||
- |
|
||||
Add the following new version of RESTful APIs
|
||||
based on ETSI NFV specifications.
|
||||
Version "2.0.0" API of Create VNF, Delete VNF,
|
||||
Instantiate VNF, Terminate VNF, List VNF, Show VNF,
|
||||
List VNF LCM operation occurrence, Show VNF LCM operation occurrence,
|
||||
Create subscription, List subscription, and Show subscription are added.
|
||||
|
||||
- |
|
||||
VNF LCM API "2.0.0" provides a new type of userdata script
|
||||
and utility functions to describe it.
|
||||
They enable the user to freely operate HEAT to meet the unique
|
||||
requirements of VNF.
|
@ -62,6 +62,18 @@ def validate_mac_address_or_none(instance):
|
||||
return True
|
||||
|
||||
|
||||
@jsonschema.FormatChecker.cls_checks('mac_address',
|
||||
webob.exc.HTTPBadRequest)
|
||||
def validate_mac_address(instance):
|
||||
"""Validate instance is a MAC address"""
|
||||
|
||||
if not netaddr.valid_mac(instance):
|
||||
msg = _("'%s' is not a valid mac address")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg % instance)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _validate_query_parameter_without_value(parameter_name, instance):
|
||||
"""The query parameter is a flag without a value."""
|
||||
if not (isinstance(instance, str) and len(instance)):
|
||||
|
@ -562,6 +562,11 @@ class VnfLcmController(wsgi.Controller):
|
||||
|
||||
return self._view_builder.show(vnf_instance)
|
||||
|
||||
@wsgi.response(http_client.OK)
|
||||
def api_versions(self, request):
|
||||
return {'uriPrefix': '/vnflcm/v1',
|
||||
'apiVersions': [{'version': '1.3.0', 'isDeprecated': False}]}
|
||||
|
||||
@wsgi.response(http_client.OK)
|
||||
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.BAD_REQUEST))
|
||||
@api_common.validate_supported_params({'filter'})
|
||||
|
@ -60,6 +60,10 @@ class VnflcmAPIRouter(wsgi.Router):
|
||||
|
||||
controller = vnf_lcm_controller.create_resource()
|
||||
|
||||
methods = {"GET": "api_versions"}
|
||||
self._setup_route(mapper, "/api_versions",
|
||||
methods, controller, default_resource)
|
||||
|
||||
# Allowed methods on /vnflcm/v1/vnf_instances resource
|
||||
methods = {"GET": "index", "POST": "create"}
|
||||
self._setup_route(mapper, "/vnf_instances",
|
||||
|
@ -26,6 +26,7 @@ from tacker._i18n import _
|
||||
from tacker.common import config
|
||||
from tacker import objects
|
||||
from tacker import service
|
||||
from tacker.sol_refactored import objects as sol_objects
|
||||
|
||||
|
||||
oslo_i18n.install("tacker")
|
||||
@ -35,6 +36,7 @@ def main():
|
||||
# the configuration will be read into the cfg.CONF global data structure
|
||||
config.init(sys.argv[1:])
|
||||
objects.register_all()
|
||||
sol_objects.register_all()
|
||||
if not cfg.CONF.config_file:
|
||||
sys.exit(_("ERROR: Unable to find configuration file via the default"
|
||||
" search paths (~/.tacker/, ~/, /etc/tacker/, /etc/) and"
|
||||
|
@ -296,11 +296,12 @@ class Connection(object):
|
||||
self.servers = []
|
||||
|
||||
def create_consumer(self, topic, endpoints, fanout=False,
|
||||
exchange='tacker', host=None):
|
||||
exchange='tacker', host=None, serializer=None):
|
||||
target = oslo_messaging.Target(
|
||||
topic=topic, server=host or cfg.CONF.host, fanout=fanout,
|
||||
exchange=exchange)
|
||||
serializer = objects_base.TackerObjectSerializer()
|
||||
if not serializer:
|
||||
serializer = objects_base.TackerObjectSerializer()
|
||||
server = get_server(target, endpoints, serializer)
|
||||
self.servers.append(server)
|
||||
|
||||
|
@ -65,6 +65,8 @@ from tacker.objects import vnfd as vnfd_db
|
||||
from tacker.objects import vnfd_attribute as vnfd_attribute_db
|
||||
from tacker.plugins.common import constants
|
||||
from tacker import service as tacker_service
|
||||
from tacker.sol_refactored.conductor import v2_hook
|
||||
from tacker.sol_refactored import objects as sol_objects
|
||||
from tacker import version
|
||||
from tacker.vnflcm import utils as vnflcm_utils
|
||||
from tacker.vnflcm import vnflcm_driver
|
||||
@ -296,7 +298,7 @@ def grant_error_common(function):
|
||||
return decorated_function
|
||||
|
||||
|
||||
class Conductor(manager.Manager):
|
||||
class Conductor(manager.Manager, v2_hook.ConductorV2Hook):
|
||||
def __init__(self, host, conf=None):
|
||||
if conf:
|
||||
self.conf = conf
|
||||
@ -2334,6 +2336,7 @@ def init(args, **kwargs):
|
||||
def main(manager='tacker.conductor.conductor_server.Conductor'):
|
||||
init(sys.argv[1:])
|
||||
objects.register_all()
|
||||
sol_objects.register_all()
|
||||
logging.setup(CONF, "tacker")
|
||||
oslo_messaging.set_transport_defaults(control_exchange='tacker')
|
||||
logging.setup(CONF, "tacker")
|
||||
|
@ -169,6 +169,7 @@ class Context(ContextBaseWithSession):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Context, self).__init__(*args, **kwargs)
|
||||
self._session = None
|
||||
self._api_version = None
|
||||
|
||||
@property
|
||||
def session(self):
|
||||
@ -180,6 +181,14 @@ class Context(ContextBaseWithSession):
|
||||
self._session = db_api.get_session()
|
||||
return self._session
|
||||
|
||||
@property
|
||||
def api_version(self):
|
||||
return self._api_version
|
||||
|
||||
@api_version.setter
|
||||
def api_version(self, api_version):
|
||||
self._api_version = api_version
|
||||
|
||||
|
||||
def get_admin_context():
|
||||
return Context(user_id=None,
|
||||
|
@ -1 +1 @@
|
||||
6dc60a5760e5
|
||||
a23ebee909a8
|
||||
|
@ -0,0 +1,97 @@
|
||||
# Copyright 2021 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""introduce_sol_refactored_models
|
||||
|
||||
Revision ID: a23ebee909a8
|
||||
Revises: 6dc60a5760e5
|
||||
Create Date: 2021-04-20 15:33:42.686284
|
||||
|
||||
"""
|
||||
|
||||
# flake8: noqa: E402
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'a23ebee909a8'
|
||||
down_revision = '6dc60a5760e5'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade(active_plugins=None, options=None):
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('LccnSubscriptionV2',
|
||||
sa.Column('id', sa.String(length=255), nullable=False),
|
||||
sa.Column('filter', sa.JSON(), nullable=True),
|
||||
sa.Column('callbackUri', sa.String(length=255), nullable=False),
|
||||
sa.Column('authentication', sa.JSON(), nullable=True),
|
||||
sa.Column('verbosity', sa.Enum('FULL', 'SHORT'), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
mysql_engine='InnoDB'
|
||||
)
|
||||
|
||||
op.create_table('VnfInstanceV2',
|
||||
sa.Column('id', sa.String(length=255), nullable=False),
|
||||
sa.Column('vnfInstanceName', sa.String(length=255), nullable=True),
|
||||
sa.Column('vnfInstanceDescription', sa.Text(), nullable=True),
|
||||
sa.Column('vnfdId', sa.String(length=255), nullable=False),
|
||||
sa.Column('vnfProvider', sa.String(length=255), nullable=False),
|
||||
sa.Column('vnfProductName', sa.String(length=255), nullable=False),
|
||||
sa.Column('vnfSoftwareVersion', sa.String(length=255), nullable=False),
|
||||
sa.Column('vnfdVersion', sa.String(length=255), nullable=False),
|
||||
sa.Column('vnfConfigurableProperties', sa.JSON(), nullable=True),
|
||||
sa.Column('vimConnectionInfo', sa.JSON(), nullable=True),
|
||||
sa.Column('instantiationState',
|
||||
sa.Enum('NOT_INSTANTIATED', 'INSTANTIATED'),
|
||||
nullable=False),
|
||||
sa.Column('instantiatedVnfInfo', sa.JSON(), nullable=True),
|
||||
sa.Column('metadata', sa.JSON(), nullable=True),
|
||||
sa.Column('extensions', sa.JSON(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
mysql_engine='InnoDB'
|
||||
)
|
||||
|
||||
op.create_table('VnfLcmOpOccV2',
|
||||
sa.Column('id', sa.String(length=255), nullable=False),
|
||||
sa.Column('operationState',
|
||||
sa.Enum('STARTING', 'PROCESSING', 'COMPLETED', 'FAILED_TEMP',
|
||||
'FAILED', 'ROLLING_BACK', 'ROLLED_BACK'),
|
||||
nullable=False),
|
||||
sa.Column('stateEnteredTime', sa.DateTime(), nullable=False),
|
||||
sa.Column('startTime', sa.DateTime(), nullable=False),
|
||||
sa.Column('vnfInstanceId', sa.String(length=255), nullable=False),
|
||||
sa.Column('grantId', sa.String(length=255), nullable=True),
|
||||
sa.Column('operation',
|
||||
sa.Enum('INSTANTIATE', 'SCALE', 'SCALE_TO_LEVEL',
|
||||
'CHANGE_FLAVOUR', 'TERMINATE', 'HEAL', 'OPERATE',
|
||||
'CHANGE_EXT_CONN', 'MODIFY_INFO', 'CREATE_SNAPSHOT',
|
||||
'REVERT_TO_SNAPSHOT', 'CHANGE_VNFPKG'),
|
||||
nullable=False),
|
||||
sa.Column('isAutomaticInvocation', sa.Boolean(), nullable=False),
|
||||
sa.Column('operationParams', sa.JSON(), nullable=True),
|
||||
sa.Column('isCancelPending', sa.Boolean(), nullable=False),
|
||||
sa.Column('cancelMode', sa.Enum('GRACEFUL', 'FORCEFUL'), nullable=True),
|
||||
sa.Column('error', sa.JSON(), nullable=True),
|
||||
sa.Column('resourceChanges', sa.JSON(), nullable=True),
|
||||
sa.Column('changedInfo', sa.JSON(), nullable=True),
|
||||
sa.Column('changedExtConnectivity', sa.JSON(), nullable=True),
|
||||
sa.Column('modificationsTriggeredByVnfPkgChange', sa.JSON(),
|
||||
nullable=True),
|
||||
sa.Column('vnfSnapshotInfoId', sa.String(length=255), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
mysql_engine='InnoDB'
|
||||
)
|
||||
# ### end Alembic commands ###
|
@ -21,12 +21,9 @@ Based on this comparison database can be healed with healing migration.
|
||||
|
||||
"""
|
||||
|
||||
from tacker.db import model_base
|
||||
from tacker.db import model_base # noqa
|
||||
from tacker.db.nfvo import nfvo_db # noqa
|
||||
from tacker.db.nfvo import ns_db # noqa
|
||||
from tacker.db.nfvo import vnffg_db # noqa
|
||||
from tacker.db.vnfm import vnfm_db # noqa
|
||||
|
||||
|
||||
def get_metadata():
|
||||
return model_base.BASE.metadata
|
||||
from tacker.sol_refactored.db.sqlalchemy import models # noqa
|
||||
|
@ -19,6 +19,7 @@ import itertools
|
||||
from tacker.policies import base
|
||||
from tacker.policies import vnf_lcm
|
||||
from tacker.policies import vnf_package
|
||||
from tacker.sol_refactored.api.policies import vnflcm_v2
|
||||
|
||||
|
||||
def list_rules():
|
||||
@ -26,4 +27,5 @@ def list_rules():
|
||||
base.list_rules(),
|
||||
vnf_package.list_rules(),
|
||||
vnf_lcm.list_rules(),
|
||||
vnflcm_v2.list_rules(),
|
||||
)
|
||||
|
@ -22,6 +22,17 @@ from tacker.policies import base
|
||||
VNFLCM = 'os_nfv_orchestration_api:vnf_instances:%s'
|
||||
|
||||
rules = [
|
||||
policy.DocumentedRuleDefault(
|
||||
name=VNFLCM % 'api_versions',
|
||||
check_str=base.RULE_ANY,
|
||||
description="Get API Versions.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': '/vnflcm/v1/api_versions'
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=VNFLCM % 'create',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
|
115
tacker/sol_refactored/api/api_version.py
Normal file
115
tacker/sol_refactored/api/api_version.py
Normal file
@ -0,0 +1,115 @@
|
||||
# Copyright (C) 2021 Nippon Telegraph and Telephone 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 re
|
||||
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
|
||||
|
||||
supported_versions_v1 = {
|
||||
'uriPrefix': '/vnflcm/v1',
|
||||
'apiVersions': [
|
||||
{'version': '1.3.0', 'isDeprecated': False}
|
||||
]
|
||||
}
|
||||
|
||||
supported_versions_v2 = {
|
||||
'uriPrefix': '/vnflcm/v2',
|
||||
'apiVersions': [
|
||||
{'version': '2.0.0', 'isDeprecated': False}
|
||||
]
|
||||
}
|
||||
|
||||
CURRENT_VERSION = '2.0.0'
|
||||
|
||||
supported_versions = [
|
||||
item['version'] for item in supported_versions_v2['apiVersions']
|
||||
]
|
||||
|
||||
|
||||
class APIVersion(object):
|
||||
|
||||
def __init__(self, version_string=None):
|
||||
self.ver_major = 0
|
||||
self.ver_minor = 0
|
||||
self.ver_patch = 0
|
||||
|
||||
if version_string is None:
|
||||
return
|
||||
|
||||
version_string = self._get_version_id(version_string)
|
||||
match = re.match(r"^([1-9]\d*)\.([1-9]\d*|0)\.([1-9]\d*|0)$",
|
||||
version_string)
|
||||
if match:
|
||||
self.ver_major = int(match.group(1))
|
||||
self.ver_minor = int(match.group(2))
|
||||
self.ver_patch = int(match.group(3))
|
||||
else:
|
||||
raise sol_ex.InvalidAPIVersionString(version=version_string)
|
||||
|
||||
if version_string not in supported_versions:
|
||||
raise sol_ex.APIVersionNotSupported(version=version_string)
|
||||
|
||||
def _get_version_id(self, version_string):
|
||||
# version example (see. SOL013 Table 4.2.2-1)
|
||||
# `1.2.0` or `1.2.0-impl:example.com:myProduct:4`
|
||||
# This method checks the later case and return the part of
|
||||
# version identifier. check is loose.
|
||||
if '-' not in version_string:
|
||||
return version_string
|
||||
items = version_string.split('-')
|
||||
if len(items) == 2 and items[1].startswith("impl:"):
|
||||
return items[0]
|
||||
raise sol_ex.InvalidAPIVersionString(version=version_string)
|
||||
|
||||
def is_null(self):
|
||||
return (self.ver_major, self.ver_minor, self.ver_patch) == (0, 0, 0)
|
||||
|
||||
def __str__(self):
|
||||
return "%d.%d.%d" % (self.ver_major, self.ver_minor, self.ver_patch)
|
||||
|
||||
def __lt__(self, other):
|
||||
return ((self.ver_major, self.ver_minor, self.ver_patch) <
|
||||
(other.ver_major, other.ver_minor, other.ver_patch))
|
||||
|
||||
def __eq__(self, other):
|
||||
return ((self.ver_major, self.ver_minor, self.ver_patch) ==
|
||||
(other.ver_major, other.ver_minor, other.ver_patch))
|
||||
|
||||
def __gt__(self, other):
|
||||
return ((self.ver_major, self.ver_minor, self.ver_patch) >
|
||||
(other.ver_major, other.ver_minor, other.ver_patch))
|
||||
|
||||
def __le__(self, other):
|
||||
return self < other or self == other
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __ge__(self, other):
|
||||
return self > other or self == other
|
||||
|
||||
def matches(self, min_version, max_version):
|
||||
if self.is_null():
|
||||
return False
|
||||
if max_version.is_null() and min_version.is_null():
|
||||
return True
|
||||
elif max_version.is_null():
|
||||
return min_version <= self
|
||||
elif min_version.is_null():
|
||||
return self <= max_version
|
||||
else:
|
||||
return min_version <= self <= max_version
|
170
tacker/sol_refactored/api/policies/vnflcm_v2.py
Normal file
170
tacker/sol_refactored/api/policies/vnflcm_v2.py
Normal file
@ -0,0 +1,170 @@
|
||||
# Copyright (C) 2021 Nippon Telegraph and Telephone 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.
|
||||
|
||||
|
||||
from oslo_policy import policy
|
||||
|
||||
|
||||
POLICY_NAME = 'os_nfv_orchestration_api_v2:vnf_instances:{}'
|
||||
RULE_ANY = '@'
|
||||
|
||||
V2_PATH = '/vnflcm/v2'
|
||||
API_VERSIONS_PATH = V2_PATH + '/api_versions'
|
||||
VNF_INSTANCES_PATH = V2_PATH + '/vnf_instances'
|
||||
VNF_INSTANCES_ID_PATH = VNF_INSTANCES_PATH + '/{vnfInstanceId}'
|
||||
SUBSCRIPTIONS_PATH = V2_PATH + '/subscriptions'
|
||||
SUBSCRIPTIONS_ID_PATH = VNF_INSTANCES_PATH + '/{subscriptionId}'
|
||||
VNF_LCM_OP_OCCS_PATH = V2_PATH + '/vnf_lcm_op_occs'
|
||||
VNF_LCM_OP_OCCS_ID_PATH = VNF_LCM_OP_OCCS_PATH + '/{vnfLcmOpOccId}'
|
||||
|
||||
rules = [
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('api_versions'),
|
||||
check_str=RULE_ANY,
|
||||
description="Get API Versions.",
|
||||
operations=[
|
||||
{'method': 'GET',
|
||||
'path': API_VERSIONS_PATH}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('create'),
|
||||
check_str=RULE_ANY,
|
||||
description="Creates vnf instance.",
|
||||
operations=[
|
||||
{'method': 'POST',
|
||||
'path': VNF_INSTANCES_PATH}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('index'),
|
||||
check_str=RULE_ANY,
|
||||
description="Query VNF instances.",
|
||||
operations=[
|
||||
{'method': 'GET',
|
||||
'path': VNF_INSTANCES_PATH}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('show'),
|
||||
check_str=RULE_ANY,
|
||||
description="Query an Individual VNF instance.",
|
||||
operations=[
|
||||
{'method': 'GET',
|
||||
'path': VNF_INSTANCES_ID_PATH}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('delete'),
|
||||
check_str=RULE_ANY,
|
||||
description="Delete an Individual VNF instance.",
|
||||
operations=[
|
||||
{'method': 'DELETE',
|
||||
'path': VNF_INSTANCES_ID_PATH}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('instantiate'),
|
||||
check_str=RULE_ANY,
|
||||
description="Instantiate vnf instance.",
|
||||
operations=[
|
||||
{'method': 'POST',
|
||||
'path': VNF_INSTANCES_ID_PATH + '/instantiate'}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('terminate'),
|
||||
check_str=RULE_ANY,
|
||||
description="Terminate vnf instance.",
|
||||
operations=[
|
||||
{'method': 'POST',
|
||||
'path': VNF_INSTANCES_ID_PATH + '/terminate'}
|
||||
]
|
||||
),
|
||||
|
||||
# TODO(oda-g): add more lcm operations etc when implemented.
|
||||
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('subscription_create'),
|
||||
check_str=RULE_ANY,
|
||||
description="Create subscription.",
|
||||
operations=[
|
||||
{'method': 'POST',
|
||||
'path': SUBSCRIPTIONS_PATH}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('subscription_list'),
|
||||
check_str=RULE_ANY,
|
||||
description="List subscription.",
|
||||
operations=[
|
||||
{'method': 'GET',
|
||||
'path': SUBSCRIPTIONS_PATH}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('subscription_show'),
|
||||
check_str=RULE_ANY,
|
||||
description="Show subscription.",
|
||||
operations=[
|
||||
{'method': 'GET',
|
||||
'path': SUBSCRIPTIONS_ID_PATH}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('subscription_delete'),
|
||||
check_str=RULE_ANY,
|
||||
description="Delete subscription.",
|
||||
operations=[
|
||||
{'method': 'DELETE',
|
||||
'path': SUBSCRIPTIONS_ID_PATH}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('lcm_op_occ_list'),
|
||||
check_str=RULE_ANY,
|
||||
description="List VnfLcmOpOcc.",
|
||||
operations=[
|
||||
{'method': 'GET',
|
||||
'path': VNF_LCM_OP_OCCS_PATH}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('lcm_op_occ_show'),
|
||||
check_str=RULE_ANY,
|
||||
description="Show VnfLcmOpOcc.",
|
||||
operations=[
|
||||
{'method': 'GET',
|
||||
'path': VNF_LCM_OP_OCCS_ID_PATH}
|
||||
]
|
||||
),
|
||||
# NOTE: 'DELETE' is not defined in the specification. It is for test
|
||||
# use since it is convenient to be able to delete under development.
|
||||
# It is available when config parameter
|
||||
# v2_vnfm.test_enable_lcm_op_occ_delete set to True.
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('lcm_op_occ_delete'),
|
||||
check_str=RULE_ANY,
|
||||
description="Delete VnfLcmOpOcc.",
|
||||
operations=[
|
||||
{'method': 'DELETE',
|
||||
'path': VNF_LCM_OP_OCCS_ID_PATH}
|
||||
]
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def list_rules():
|
||||
return rules
|
54
tacker/sol_refactored/api/router.py
Normal file
54
tacker/sol_refactored/api/router.py
Normal file
@ -0,0 +1,54 @@
|
||||
# Copyright (C) 2021 Nippon Telegraph and Telephone 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.
|
||||
|
||||
|
||||
from tacker.sol_refactored.api.policies import vnflcm_v2 as vnflcm_policy_v2
|
||||
from tacker.sol_refactored.api import wsgi as sol_wsgi
|
||||
from tacker.sol_refactored.controller import vnflcm_v2
|
||||
from tacker.sol_refactored.controller import vnflcm_versions
|
||||
|
||||
|
||||
class VnflcmVersions(sol_wsgi.SolAPIRouter):
|
||||
|
||||
controller = sol_wsgi.SolResource(
|
||||
vnflcm_versions.VnfLcmVersionsController())
|
||||
route_list = [("/api_versions", {"GET": "index"})]
|
||||
|
||||
|
||||
class VnflcmAPIRouterV2(sol_wsgi.SolAPIRouter):
|
||||
|
||||
controller = sol_wsgi.SolResource(vnflcm_v2.VnfLcmControllerV2(),
|
||||
policy_name=vnflcm_policy_v2.POLICY_NAME)
|
||||
route_list = [
|
||||
("/vnf_instances", {"GET": "index", "POST": "create"}),
|
||||
("/vnf_instances/{id}",
|
||||
{"DELETE": "delete", "GET": "show", "PATCH": "update"}),
|
||||
("/vnf_instances/{id}/instantiate", {"POST": "instantiate"}),
|
||||
("/vnf_instances/{id}/heal", {"POST": "heal"}),
|
||||
("/vnf_instances/{id}/terminate", {"POST": "terminate"}),
|
||||
("/vnf_instances/{id}/scale", {"POST": "scale"}),
|
||||
("/api_versions", {"GET": "api_versions"}),
|
||||
("/subscriptions", {"GET": "subscription_list",
|
||||
"POST": "subscription_create"}),
|
||||
("/subscriptions/{id}", {"GET": "subscription_show",
|
||||
"DELETE": "subscription_delete"}),
|
||||
("/vnf_lcm_op_occs", {"GET": "lcm_op_occ_list"}),
|
||||
# NOTE: 'DELETE' is not defined in the specification. It is for test
|
||||
# use since it is convenient to be able to delete under development.
|
||||
# It is available when config parameter
|
||||
# v2_vnfm.test_enable_lcm_op_occ_delete set to True.
|
||||
("/vnf_lcm_op_occs/{id}", {"GET": "lcm_op_occ_show",
|
||||
"DELETE": "lcm_op_occ_delete"})
|
||||
]
|
0
tacker/sol_refactored/api/schemas/__init__.py
Normal file
0
tacker/sol_refactored/api/schemas/__init__.py
Normal file
244
tacker/sol_refactored/api/schemas/common_types.py
Normal file
244
tacker/sol_refactored/api/schemas/common_types.py
Normal file
@ -0,0 +1,244 @@
|
||||
# Copyright (C) 2021 Nippon Telegraph and Telephone 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.
|
||||
|
||||
|
||||
from tacker.api.validation import parameter_types
|
||||
|
||||
|
||||
# SOL013 7.2.2
|
||||
Identifier = {
|
||||
'type': 'string', 'minLength': 1, 'maxLength': 255
|
||||
}
|
||||
|
||||
# SOL003 4.4.2.2
|
||||
IdentifierInVnfd = {
|
||||
'type': 'string', 'minLength': 1, 'maxLength': 255
|
||||
}
|
||||
|
||||
# SOL003 4.4.2.2
|
||||
IdentifierInVim = {
|
||||
'type': 'string', 'minLength': 1, 'maxLength': 255
|
||||
}
|
||||
|
||||
# SOL003 4.4.2.2
|
||||
IdentifierInVnf = {
|
||||
'type': 'string', 'minLength': 1, 'maxLength': 255
|
||||
}
|
||||
|
||||
# SOL003 4.4.2.2
|
||||
IdentifierLocal = {
|
||||
'type': 'string', 'minLength': 1, 'maxLength': 255
|
||||
}
|
||||
|
||||
# SOL003 4.4.1.7
|
||||
ResourceHandle = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'vimConnectionId': Identifier,
|
||||
'resourceProviderId': Identifier,
|
||||
'resourceId': IdentifierInVim,
|
||||
'vimLevelResourceType': {'type': 'string', 'maxLength': 255},
|
||||
},
|
||||
'required': ['resourceId'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL003 4.4.1.6
|
||||
VimConnectionInfo = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'vimId': Identifier,
|
||||
'vimType': {'type': 'string', 'minLength': 1, 'maxLength': 255},
|
||||
'interfaceInfo': parameter_types.keyvalue_pairs,
|
||||
'accessInfo': parameter_types.keyvalue_pairs,
|
||||
'extra': parameter_types.keyvalue_pairs,
|
||||
},
|
||||
'required': ['vimType'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL003 4.4.1.10c (inner)
|
||||
_IpAddresses = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'type': {'enum': ('IPV4', 'IPV6')},
|
||||
'fixedAddresses': {'type': 'array'},
|
||||
'numDynamicAddresses': parameter_types.positive_integer,
|
||||
'addressRange': {'type': 'object'},
|
||||
'subnetId': IdentifierInVim
|
||||
},
|
||||
'if': {'properties': {'type': {'const': 'IPV4'}}},
|
||||
'then': {
|
||||
'properties': {
|
||||
'fixedAddresses': {
|
||||
'type': 'array',
|
||||
'items': {'type': 'string', 'format': 'ipv4'}
|
||||
},
|
||||
'addressRange': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'minAddress': {'type': 'string', 'format': 'ipv4'},
|
||||
'maxAddress': {'type': 'string', 'format': 'ipv4'}
|
||||
},
|
||||
'required': ['minAddress', 'maxAddress'],
|
||||
'additionalProperties': True
|
||||
},
|
||||
}
|
||||
},
|
||||
'else': {
|
||||
'properties': {
|
||||
'fixedAddresses': {
|
||||
'type': 'array',
|
||||
'items': {'type': 'string', 'format': 'ipv6'}
|
||||
},
|
||||
'addressRange': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'minAddress': {'type': 'string', 'format': 'ipv6'},
|
||||
'maxAddress': {'type': 'string', 'format': 'ipv6'}
|
||||
},
|
||||
'required': ['minAddress', 'maxAddress'],
|
||||
'additionalProperties': True
|
||||
},
|
||||
}
|
||||
},
|
||||
'required': ['type'],
|
||||
'oneOf': [
|
||||
{'required': ['numDynamicAddresses']},
|
||||
{'required': ['fixedAddresses']},
|
||||
{'required': ['addressRange']},
|
||||
],
|
||||
'additionalProperties': True
|
||||
}
|
||||
|
||||
# SOL003 4.4.1.10c
|
||||
IpOverEthernetAddressData = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'macAddress': {'type': 'string', 'format': 'mac_address'},
|
||||
'segmentationId': {'type': 'string'},
|
||||
'ipAddresses': {
|
||||
'type': 'array',
|
||||
'items': _IpAddresses}
|
||||
},
|
||||
'anyOf': [
|
||||
{'required': ['macAddress']},
|
||||
{'required': ['ipAddresses']}
|
||||
],
|
||||
'additionalProperties': True
|
||||
}
|
||||
|
||||
# SOL003 4.4.1.10b
|
||||
CpProtocolData = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'layerProtocol': {
|
||||
'type': 'string',
|
||||
'enum': 'IP_OVER_ETHERNET'},
|
||||
'ipOverEthernet': IpOverEthernetAddressData,
|
||||
},
|
||||
'required': ['layerProtocol'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL003 4.4.1.10a
|
||||
VnfExtCpConfig = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'parentCpConfigId': IdentifierInVnf,
|
||||
'linkPortId': Identifier,
|
||||
'cpProtocolData': {
|
||||
'type': 'array',
|
||||
'items': CpProtocolData}
|
||||
},
|
||||
'additionalProperties': True
|
||||
}
|
||||
|
||||
# SOL003 4.4.1.10
|
||||
VnfExtCpData = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'cpdId': IdentifierInVnfd,
|
||||
'cpConfig': {
|
||||
'type': 'object',
|
||||
'minProperties': 1,
|
||||
'patternProperties': {
|
||||
'^.*$': VnfExtCpConfig
|
||||
}
|
||||
}
|
||||
},
|
||||
'required': ['cpdId', 'cpConfig'],
|
||||
'additionalProperties': True
|
||||
}
|
||||
|
||||
# SOL003 4.4.1.14
|
||||
ExtLinkPortData = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': Identifier,
|
||||
'resourceHandle': ResourceHandle,
|
||||
},
|
||||
'required': ['id', 'resourceHandle'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL003 4.4.1.11
|
||||
ExtVirtualLinkData = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': Identifier,
|
||||
'vimConnectionId': Identifier,
|
||||
'resourceProviderId': Identifier,
|
||||
'resourceId': IdentifierInVim,
|
||||
'extCps': {
|
||||
'type': 'array',
|
||||
'minItems': 1,
|
||||
'items': VnfExtCpData},
|
||||
'extLinkPorts': {
|
||||
'type': 'array',
|
||||
'items': ExtLinkPortData}
|
||||
},
|
||||
'required': ['id', 'resourceId', 'extCps'],
|
||||
'additionalProperties': True
|
||||
}
|
||||
|
||||
# SOL003 5.5.3.18
|
||||
VnfLinkPortData = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'vnfLinkPortId': Identifier,
|
||||
'resourceHandle': ResourceHandle
|
||||
},
|
||||
'required': ['vnfLinkPortId', 'resourceHandle'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL003 4.4.1.12
|
||||
ExtManagedVirtualLinkData = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': Identifier,
|
||||
'vnfVirtualLinkDescId': IdentifierInVnfd,
|
||||
'vimConnectionId': Identifier,
|
||||
'resourceProviderId': Identifier,
|
||||
'resourceId': IdentifierInVim,
|
||||
'vnfLinkPort': {
|
||||
'type': 'array',
|
||||
'items': VnfLinkPortData},
|
||||
'extManagedMultisiteVirtualLinkId': Identifier
|
||||
},
|
||||
'required': ['id', 'vnfVirtualLinkDescId', 'resourceId'],
|
||||
'additionalProperties': True,
|
||||
}
|
252
tacker/sol_refactored/api/schemas/vnflcm_v2.py
Normal file
252
tacker/sol_refactored/api/schemas/vnflcm_v2.py
Normal file
@ -0,0 +1,252 @@
|
||||
# Copyright (C) 2021 Nippon Telegraph and Telephone 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.
|
||||
|
||||
from tacker.api.validation import parameter_types
|
||||
|
||||
from tacker.sol_refactored.api.schemas import common_types
|
||||
|
||||
|
||||
# SOL003 5.5.2.3
|
||||
CreateVnfRequest_V200 = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'vnfdId': common_types.Identifier,
|
||||
'vnfInstanceName': {'type': 'string', 'maxLength': 255},
|
||||
'vnfInstanceDescription': {'type': 'string', 'maxLength': 1024},
|
||||
'metadata': parameter_types.keyvalue_pairs,
|
||||
},
|
||||
'required': ['vnfdId'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL003 5.5.2.4
|
||||
InstantiateVnfRequest_V200 = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'flavourId': common_types.IdentifierInVnfd,
|
||||
'instantiationLevelId': common_types.IdentifierInVnfd,
|
||||
'extVirtualLinks': {
|
||||
'type': 'array',
|
||||
'items': common_types.ExtVirtualLinkData},
|
||||
'extManagedVirtualLinks': {
|
||||
'type': 'array',
|
||||
'items': common_types.ExtManagedVirtualLinkData},
|
||||
'vimConnectionInfo': {
|
||||
'type': 'object',
|
||||
'patternProperties': {
|
||||
'^.*$': common_types.VimConnectionInfo
|
||||
},
|
||||
},
|
||||
'localizationLanguage': {'type': 'string', 'maxLength': 255},
|
||||
'additionalParams': parameter_types.keyvalue_pairs,
|
||||
'extensions': parameter_types.keyvalue_pairs,
|
||||
'vnfConfigurableProperties': parameter_types.keyvalue_pairs
|
||||
},
|
||||
'required': ['flavourId'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL003 5.5.2.8
|
||||
TerminateVnfRequest_V200 = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'terminationType': {
|
||||
'type': 'string',
|
||||
'enum': [
|
||||
'FORCEFUL',
|
||||
'GRACEFUL']
|
||||
},
|
||||
'gracefulTerminationTimeout': {
|
||||
'type': 'integer', 'minimum': 1
|
||||
},
|
||||
'additionalParams': parameter_types.keyvalue_pairs,
|
||||
},
|
||||
'required': ['terminationType'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL013 8.3.4
|
||||
_SubscriptionAuthentication = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'authType': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'string',
|
||||
'enum': [
|
||||
'BASIC',
|
||||
'OAUTH2_CLIENT_CREDENTIALS',
|
||||
'TLS_CERT']
|
||||
}
|
||||
},
|
||||
'paramsBasic': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'userName': {'type': 'string'},
|
||||
'password': {'type': 'string'}
|
||||
},
|
||||
# NOTE: must be specified since the way to specify them out of
|
||||
# band is not supported.
|
||||
'required': ['userName', 'password']
|
||||
},
|
||||
'paramsOauth2ClientCredentials': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'clientId': {'type': 'string'},
|
||||
'clientPassword': {'type': 'string'},
|
||||
'tokenEndpoint': {'type': 'string'}
|
||||
},
|
||||
# NOTE: must be specified since the way to specify them out of
|
||||
# band is not supported.
|
||||
'required': ['clientId', 'clientPassword', 'tokenEndpoint']
|
||||
}
|
||||
},
|
||||
'required': ['authType'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL003 4.4.1.5 inner
|
||||
_VnfProductVersions = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'objects',
|
||||
'properties': {
|
||||
'vnfSoftwareVersion': {'type': 'string'},
|
||||
'vnfdVersions': {
|
||||
'type': 'array',
|
||||
'items': {'type': 'string'}
|
||||
}
|
||||
},
|
||||
'required': ['vnfSoftwareVersion'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
}
|
||||
|
||||
# SOL003 4.4.1.5 inner
|
||||
_VnfProducts = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'vnfProductName': {'type': 'string'},
|
||||
'versions': _VnfProductVersions
|
||||
},
|
||||
'required': ['vnfProductName'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
}
|
||||
|
||||
# SOL003 4.4.1.5 inner
|
||||
_VnfProductsFromProviders = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'vnfProvider': {'type': 'string'},
|
||||
'vnfProducts': _VnfProducts
|
||||
}
|
||||
},
|
||||
'required': ['vnfProvider'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
}
|
||||
|
||||
# SOL003 4.4.1.5
|
||||
_VnfInstanceSubscriptionFilter = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'vnfdIds': {
|
||||
'type': 'array',
|
||||
'items': common_types.Identifier
|
||||
},
|
||||
'vnfProductsFromProviders': _VnfProductsFromProviders,
|
||||
'vnfInstanceIds': {
|
||||
'type': 'array',
|
||||
'items': common_types.Identifier
|
||||
},
|
||||
'vnfInstanceNames': {
|
||||
'type': 'array',
|
||||
'items': {'type': 'string'}
|
||||
}
|
||||
},
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL003 5.5.3.12
|
||||
_LifecycleChangeNotificationsFilter = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'vnfInstanceSubscriptionFilter': _VnfInstanceSubscriptionFilter,
|
||||
'notificationTypes': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'string',
|
||||
'enum': [
|
||||
'VnfLcmOperationOccurrenceNotification',
|
||||
'VnfIdentifierCreationNotification',
|
||||
'VnfIdentifierDeletionNotification']
|
||||
}
|
||||
},
|
||||
'operationTypes': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'string',
|
||||
'enum': [
|
||||
'INSTANTIATE',
|
||||
'SCALE',
|
||||
'SCALE_TO_LEVEL',
|
||||
'CHANGE_FLAVOUR',
|
||||
'TERMINATE',
|
||||
'HEAL',
|
||||
'OPERATE',
|
||||
'CHANGE_EXT_CONN',
|
||||
'MODIFY_INFO']
|
||||
}
|
||||
},
|
||||
'operationStates': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'string',
|
||||
'enum': [
|
||||
'STARTING',
|
||||
'PROCESSING',
|
||||
'COMPLETED',
|
||||
'FAILED_TEMP',
|
||||
'FAILED',
|
||||
'ROLLING_BACK',
|
||||
'ROLLED_BACK']
|
||||
}
|
||||
}
|
||||
},
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL003 5.5.2.15
|
||||
LccnSubscriptionRequest_V200 = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'filter': _LifecycleChangeNotificationsFilter,
|
||||
'callbackUri': {'type': 'string', 'maxLength': 255},
|
||||
'authentication': _SubscriptionAuthentication,
|
||||
'verbosity': {
|
||||
'type': 'string',
|
||||
'enum': ['FULL', 'SHORT']
|
||||
}
|
||||
},
|
||||
'required': ['callbackUri'],
|
||||
'additionalProperties': True,
|
||||
}
|
49
tacker/sol_refactored/api/validator.py
Normal file
49
tacker/sol_refactored/api/validator.py
Normal file
@ -0,0 +1,49 @@
|
||||
# Copyright (C) 2021 Nippon Telegraph and Telephone 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 functools
|
||||
|
||||
from tacker.api.validation import validators
|
||||
from tacker.common import exceptions as tacker_ex
|
||||
|
||||
from tacker.sol_refactored.api import api_version
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
|
||||
|
||||
class SolSchemaValidator(validators._SchemaValidator):
|
||||
def validate(self, *args, **kwargs):
|
||||
try:
|
||||
super(SolSchemaValidator, self).validate(*args, **kwargs)
|
||||
except tacker_ex.ValidationError as ex:
|
||||
raise sol_ex.SolValidationError(detail=str(ex))
|
||||
|
||||
|
||||
def schema(request_body_schema, min_version, max_version=None):
|
||||
|
||||
def add_validator(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
ver = kwargs['request'].context.api_version
|
||||
min_ver = api_version.APIVersion(min_version)
|
||||
max_ver = api_version.APIVersion(max_version)
|
||||
if ver.matches(min_ver, max_ver):
|
||||
schema_validator = SolSchemaValidator(request_body_schema)
|
||||
schema_validator.validate(kwargs['body'])
|
||||
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
return add_validator
|
180
tacker/sol_refactored/api/wsgi.py
Normal file
180
tacker/sol_refactored/api/wsgi.py
Normal file
@ -0,0 +1,180 @@
|
||||
# Copyright (C) 2021 Nippon Telegraph and Telephone 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 routes
|
||||
import webob
|
||||
|
||||
import oslo_i18n as i18n
|
||||
from oslo_log import log as logging
|
||||
|
||||
from tacker.common import exceptions as common_ex
|
||||
from tacker import wsgi
|
||||
|
||||
from tacker.sol_refactored.api import api_version
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SolResponse(object):
|
||||
|
||||
# SOL013 4.2.3 Response header field
|
||||
allowed_headers = ['version', 'location', 'content_type',
|
||||
'www_authenticate', 'accept_ranges', 'content_range',
|
||||
'retry_after', 'link']
|
||||
|
||||
def __init__(self, status, body, **kwargs):
|
||||
self.status = status
|
||||
self.body = body
|
||||
self.headers = {}
|
||||
for hdr in self.allowed_headers:
|
||||
if hdr in kwargs:
|
||||
self.headers[hdr] = kwargs[hdr]
|
||||
self.headers.setdefault('version', api_version.CURRENT_VERSION)
|
||||
self.headers.setdefault('accept-ranges', 'none')
|
||||
|
||||
def serialize(self, request, content_type):
|
||||
self.headers.setdefault('content_type', content_type)
|
||||
content_type = self.headers['content_type']
|
||||
if self.body is None:
|
||||
body = None
|
||||
elif content_type == 'text/plain':
|
||||
body = self.body
|
||||
elif content_type == 'application/zip':
|
||||
body = self.body
|
||||
else: # 'application/json'
|
||||
serializer = wsgi.JSONDictSerializer()
|
||||
body = serializer.serialize(self.body)
|
||||
if len(body) > config.CONF.v2_vnfm.max_content_length:
|
||||
raise sol_ex.ResponseTooBig(
|
||||
size=config.CONF.v2_vnfm.max_content_length)
|
||||
response = w |