Add instantiate vnf instance API
Implemented instantiate vnf instance API. * GET /vnflcm/v1/vnf_instances/{vnf_instance_id}/instantiate Co-authored-By: Nitin Uikey <nitin.uikey@nttdata.com> Co-authored-By: tpatil <tushar.vitthal.patil@gmail.com> Co-authored-By: Ajay Parja <ajay.parja@nttdata.com> Co-authored-By: Shubham Potale <shubham.potale@nttdata.com> Co-authored-By: Sameer Thakur <sameer.thakur@nttdata.com> Updated lower-constraints openstacksdk version to 0.44.0 as it includes fix[1] which is required to create images from filename which further requires to upgrade os-service-types, keystoneauth1 and decorato to higher versions. [1] : https://review.opendev.org/#/c/710368 Change-Id: Ic30f8d730d54e3af1345816ffa1bfb702cd00694 Blueprint: support-etsi-nfv-specs
This commit is contained in:
parent
7fe80b0e8b
commit
a14590620b
@ -19,7 +19,7 @@ coverage==4.0
|
||||
cryptography==2.1
|
||||
ddt===1.0.1
|
||||
debtcollector==1.19.0
|
||||
decorator==4.2.1
|
||||
decorator==4.4.1
|
||||
deprecation==2.0
|
||||
doc8==0.6.0
|
||||
docutils==0.14
|
||||
@ -46,8 +46,8 @@ Jinja2==2.10
|
||||
jmespath==0.9.3
|
||||
jsonpatch==1.21
|
||||
jsonpointer==2.0
|
||||
jsonschema==2.6.0
|
||||
keystoneauth1==3.11.0
|
||||
jsonschema==3.0.0
|
||||
keystoneauth1==3.15.0
|
||||
keystonemiddleware==4.17.0
|
||||
kombu==4.0.0
|
||||
kubernetes==5.0.0
|
||||
@ -64,10 +64,10 @@ netaddr==0.7.18
|
||||
netifaces==0.10.6
|
||||
oauthlib==2.0.7
|
||||
openstackdocstheme==1.20.0
|
||||
openstacksdk==0.12.0
|
||||
openstacksdk==0.44.0
|
||||
os-api-ref==1.5.0
|
||||
os-client-config==1.29.0
|
||||
os-service-types==1.2.0
|
||||
os-service-types==1.7.0
|
||||
osc-lib==1.10.0
|
||||
oslo.cache==1.29.0
|
||||
oslo.concurrency==3.26.0
|
||||
|
@ -10,7 +10,7 @@ anyjson>=0.3.3 # BSD
|
||||
Babel!=2.4.0,>=2.3.4 # BSD
|
||||
eventlet!=0.23.0,!=0.25.0,>=0.22.0 # MIT
|
||||
requests>=2.14.2 # Apache-2.0
|
||||
jsonschema>=2.6.0 # MIT
|
||||
jsonschema>=3.0.0 # MIT
|
||||
keystonemiddleware>=4.17.0 # Apache-2.0
|
||||
kombu!=4.0.2,>=4.0.0 # BSD
|
||||
netaddr>=0.7.18 # BSD
|
||||
@ -37,6 +37,7 @@ oslo.upgradecheck>=0.1.0 # Apache-2.0
|
||||
oslo.utils>=3.33.0 # Apache-2.0
|
||||
oslo.versionedobjects>=1.33.3 # Apache-2.0
|
||||
openstackdocstheme>=1.20.0 # Apache-2.0
|
||||
openstacksdk>=0.44.0 # Apache-2.0
|
||||
python-neutronclient>=6.7.0 # Apache-2.0
|
||||
python-novaclient>=9.1.0 # Apache-2.0
|
||||
tosca-parser>=1.6.0 # Apache-2.0
|
||||
|
@ -19,7 +19,161 @@ Schema for vnf lcm APIs.
|
||||
"""
|
||||
|
||||
from tacker.api.validation import parameter_types
|
||||
from tacker.objects import fields
|
||||
|
||||
_extManagedVirtualLinkData = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': parameter_types.identifier,
|
||||
'vnfVirtualLinkDescId': parameter_types.identifier_in_vnfd,
|
||||
'resourceId': parameter_types.identifier_in_vim
|
||||
},
|
||||
'required': ['id', 'vnfVirtualLinkDescId', 'resourceId'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
}
|
||||
|
||||
_ipaddresses = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'type': {'enum': fields.IpAddressType.ALL},
|
||||
'subnetId': parameter_types.identifier_in_vim,
|
||||
'fixedAddresses': {'type': 'array'}
|
||||
},
|
||||
'if': {'properties': {'type': {'const': fields.IpAddressType.IPV4}}},
|
||||
'then': {
|
||||
'properties': {
|
||||
'fixedAddresses': {
|
||||
'type': 'array',
|
||||
'items': {'type': 'string', 'format': 'ipv4'}
|
||||
}
|
||||
}
|
||||
},
|
||||
'else': {
|
||||
'properties': {
|
||||
'fixedAddresses': {
|
||||
'type': 'array',
|
||||
'items': {'type': 'string', 'format': 'ipv6'}
|
||||
}
|
||||
}
|
||||
},
|
||||
'required': ['type', 'fixedAddresses'],
|
||||
'additionalProperties': False
|
||||
}
|
||||
}
|
||||
|
||||
_ipOverEthernetAddressData = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'macAddress': parameter_types.mac_address_or_none,
|
||||
'ipAddresses': _ipaddresses,
|
||||
},
|
||||
'anyOf': [
|
||||
{'required': ['macAddress']},
|
||||
{'required': ['ipAddresses']}
|
||||
],
|
||||
'additionalProperties': False
|
||||
}
|
||||
|
||||
_cpProtocolData = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'layerProtocol': {'type': 'string',
|
||||
'enum': 'IP_OVER_ETHERNET'
|
||||
},
|
||||
'ipOverEthernet': _ipOverEthernetAddressData,
|
||||
},
|
||||
'required': ['layerProtocol'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
}
|
||||
|
||||
_vnfExtCpConfig = {
|
||||
'type': 'array', 'minItems': 1, 'maxItems': 1,
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'cpInstanceId': parameter_types.identifier_in_vnf,
|
||||
'linkPortId': parameter_types.identifier,
|
||||
'cpProtocolData': _cpProtocolData,
|
||||
},
|
||||
'additionalProperties': False,
|
||||
}
|
||||
}
|
||||
|
||||
_vnfExtCpData = {
|
||||
'type': 'array', 'minItems': 1,
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'cpdId': parameter_types.identifier_in_vnfd,
|
||||
'cpConfig': _vnfExtCpConfig,
|
||||
},
|
||||
'required': ['cpdId', 'cpConfig'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
}
|
||||
|
||||
_resourceHandle = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'resourceId': parameter_types.identifier_in_vim,
|
||||
'vimLevelResourceType': {'type': 'string', 'maxLength': 255},
|
||||
},
|
||||
'required': ['resourceId'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
_extLinkPortData = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': parameter_types.identifier,
|
||||
'resourceHandle': _resourceHandle,
|
||||
},
|
||||
'required': ['id', 'resourceHandle'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
}
|
||||
|
||||
_extVirtualLinkData = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': parameter_types.identifier,
|
||||
'resourceId': parameter_types.identifier_in_vim,
|
||||
'extCps': _vnfExtCpData,
|
||||
'extLinkPorts': _extLinkPortData,
|
||||
|
||||
},
|
||||
'required': ['id', 'resourceId', 'extCps'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
}
|
||||
|
||||
_vimConnectionInfo = {
|
||||
'type': 'array',
|
||||
'maxItems': 1,
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': parameter_types.identifier,
|
||||
'vimId': parameter_types.identifier,
|
||||
'vimType': {'type': 'string', 'minLength': 1, 'maxLength': 255},
|
||||
'accessInfo': parameter_types.keyvalue_pairs,
|
||||
},
|
||||
'required': ['id', 'vimType'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
}
|
||||
|
||||
create = {
|
||||
'type': 'object',
|
||||
@ -31,3 +185,17 @@ create = {
|
||||
'required': ['vnfdId'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
instantiate = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'flavourId': {'type': 'string', 'maxLength': 255},
|
||||
'instantiationLevelId': {'type': 'string', 'maxLength': 255},
|
||||
'extVirtualLinks': _extVirtualLinkData,
|
||||
'extManagedVirtualLinks': _extManagedVirtualLinkData,
|
||||
'vimConnectionInfo': _vimConnectionInfo,
|
||||
'additionalParams': parameter_types.keyvalue_pairs,
|
||||
},
|
||||
'required': ['flavourId'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
@ -138,3 +138,36 @@ uuid = {
|
||||
name_allow_zero_min_length = {
|
||||
'type': 'string', 'minLength': 0, 'maxLength': 255
|
||||
}
|
||||
|
||||
ip_address = {
|
||||
'type': 'string',
|
||||
'oneOf': [
|
||||
{'format': 'ipv4'},
|
||||
{'format': 'ipv6'}
|
||||
]
|
||||
}
|
||||
|
||||
positive_integer = {
|
||||
'type': ['integer', 'string'],
|
||||
'pattern': '^[0-9]*$', 'minimum': 1, 'minLength': 1
|
||||
}
|
||||
|
||||
identifier = {
|
||||
'type': 'string', 'minLength': 1, 'maxLength': 255
|
||||
}
|
||||
|
||||
identifier_in_vim = {
|
||||
'type': 'string', 'minLength': 1, 'maxLength': 255
|
||||
}
|
||||
|
||||
identifier_in_vnfd = {
|
||||
'type': 'string', 'minLength': 1, 'maxLength': 255
|
||||
}
|
||||
|
||||
identifier_in_vnf = {
|
||||
'type': 'string', 'minLength': 1, 'maxLength': 255
|
||||
}
|
||||
|
||||
mac_address_or_none = {
|
||||
'type': 'string', 'format': 'mac_address_or_none'
|
||||
}
|
||||
|
@ -20,9 +20,11 @@ Internal implementation of request Body validating middleware.
|
||||
|
||||
import jsonschema
|
||||
from jsonschema import exceptions as jsonschema_exc
|
||||
import netaddr
|
||||
from oslo_utils import uuidutils
|
||||
import rfc3986
|
||||
import six
|
||||
import webob
|
||||
|
||||
from tacker.common import exceptions as exception
|
||||
|
||||
@ -38,6 +40,21 @@ def _validate_uuid_format(instance):
|
||||
return uuidutils.is_uuid_like(instance)
|
||||
|
||||
|
||||
@jsonschema.FormatChecker.cls_checks('mac_address_or_none',
|
||||
webob.exc.HTTPBadRequest)
|
||||
def validate_mac_address_or_none(instance):
|
||||
"""Validate instance is a MAC address"""
|
||||
|
||||
if instance is None:
|
||||
return
|
||||
|
||||
if not netaddr.valid_mac(instance):
|
||||
msg = _("'%s' is not a valid mac address")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg % instance)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class FormatChecker(jsonschema.FormatChecker):
|
||||
"""A FormatChecker can output the message from cause exception
|
||||
|
||||
@ -76,14 +93,14 @@ class FormatChecker(jsonschema.FormatChecker):
|
||||
class _SchemaValidator(object):
|
||||
"""A validator class
|
||||
|
||||
This class is changed from Draft4Validator to validate minimum/maximum
|
||||
This class is changed from Draft7Validator to validate minimum/maximum
|
||||
value of a string number(e.g. '10'). This changes can be removed when
|
||||
we tighten up the API definition and the XML conversion.
|
||||
Also FormatCheckers are added for checking data formats which would be
|
||||
passed through cinder api commonly.
|
||||
|
||||
"""
|
||||
validator_org = jsonschema.Draft4Validator
|
||||
validator_org = jsonschema.Draft7Validator
|
||||
|
||||
def __init__(self, schema):
|
||||
validator_cls = jsonschema.validators.extend(self.validator_org,
|
||||
@ -95,7 +112,9 @@ class _SchemaValidator(object):
|
||||
try:
|
||||
self.validator.validate(*args, **kwargs)
|
||||
except jsonschema.ValidationError as ex:
|
||||
if len(ex.path) > 0:
|
||||
if isinstance(ex.cause, webob.exc.HTTPBadRequest):
|
||||
detail = str(ex.cause)
|
||||
elif len(ex.path) > 0:
|
||||
detail = _("Invalid input for field/attribute %(path)s."
|
||||
" Value: %(value)s. %(message)s") % {
|
||||
'path': ex.path.pop(), 'value': ex.instance,
|
||||
|
@ -13,6 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
import six
|
||||
from six.moves import http_client
|
||||
import webob
|
||||
@ -22,19 +24,141 @@ from tacker.api import validation
|
||||
from tacker.api.views import vnf_lcm as vnf_lcm_view
|
||||
from tacker.common import exceptions
|
||||
from tacker.common import utils
|
||||
from tacker.conductor.conductorrpc import vnf_lcm_rpc
|
||||
from tacker.extensions import nfvo
|
||||
from tacker import objects
|
||||
from tacker.objects import fields
|
||||
from tacker.policies import vnf_lcm as vnf_lcm_policies
|
||||
from tacker.vnfm import vim_client
|
||||
from tacker import wsgi
|
||||
|
||||
|
||||
def check_vnf_state(action, instantiation_state=None, task_state=(None,)):
|
||||
"""Decorator to check vnf states are valid for particular action.
|
||||
|
||||
If the vnf is in the wrong state, it will raise conflict exception.
|
||||
"""
|
||||
|
||||
if instantiation_state is not None and not \
|
||||
isinstance(instantiation_state, set):
|
||||
instantiation_state = set(instantiation_state)
|
||||
if task_state is not None and not isinstance(task_state, set):
|
||||
task_state = set(task_state)
|
||||
|
||||
def outer(f):
|
||||
@six.wraps(f)
|
||||
def inner(self, context, vnf_instance, *args, **kw):
|
||||
if instantiation_state is not None and \
|
||||
vnf_instance.instantiation_state not in \
|
||||
instantiation_state:
|
||||
raise exceptions.VnfInstanceConflictState(
|
||||
attr='instantiation_state',
|
||||
uuid=vnf_instance.id,
|
||||
state=vnf_instance.instantiation_state,
|
||||
action=action)
|
||||
if (task_state is not None and
|
||||
vnf_instance.task_state not in task_state):
|
||||
raise exceptions.VnfInstanceConflictState(
|
||||
attr='task_state',
|
||||
uuid=vnf_instance.id,
|
||||
state=vnf_instance.task_state,
|
||||
action=action)
|
||||
return f(self, context, vnf_instance, *args, **kw)
|
||||
return inner
|
||||
return outer
|
||||
|
||||
|
||||
class VnfLcmController(wsgi.Controller):
|
||||
|
||||
_view_builder_class = vnf_lcm_view.ViewBuilder
|
||||
|
||||
def __init__(self):
|
||||
super(VnfLcmController, self).__init__()
|
||||
self.rpc_api = vnf_lcm_rpc.VNFLcmRPCAPI()
|
||||
|
||||
def _get_vnf_instance_href(self, vnf_instance):
|
||||
return '/vnflcm/v1/vnf_instances/%s' % vnf_instance.id
|
||||
|
||||
def _get_vnf_instance(self, context, id):
|
||||
# check if id is of type uuid format
|
||||
if not uuidutils.is_uuid_like(id):
|
||||
msg = _("Can not find requested vnf instance: %s") % id
|
||||
raise webob.exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
try:
|
||||
vnf_instance = objects.VnfInstance.get_by_id(context, id)
|
||||
except exceptions.VnfInstanceNotFound:
|
||||
msg = _("Can not find requested vnf instance: %s") % id
|
||||
raise webob.exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
return vnf_instance
|
||||
|
||||
def _validate_flavour_and_inst_level(self, context, req_body,
|
||||
vnf_instance):
|
||||
inst_levels = {}
|
||||
flavour_list = []
|
||||
vnf_package_vnfd = objects.VnfPackageVnfd.get_by_id(
|
||||
context, vnf_instance.vnfd_id)
|
||||
vnf_package = objects.VnfPackage.get_by_id(
|
||||
context, vnf_package_vnfd.package_uuid)
|
||||
deployment_flavour = vnf_package.vnf_deployment_flavours
|
||||
for dep_flavour in deployment_flavour.objects:
|
||||
flavour_list.append(dep_flavour.flavour_id)
|
||||
if dep_flavour.instantiation_levels:
|
||||
inst_levels.update({
|
||||
dep_flavour.flavour_id: dep_flavour.instantiation_levels})
|
||||
|
||||
if req_body['flavour_id'] not in flavour_list:
|
||||
raise exceptions.FlavourNotFound(flavour_id=req_body['flavour_id'])
|
||||
|
||||
req_inst_level_id = req_body.get('instantiation_level_id')
|
||||
if req_inst_level_id is None:
|
||||
return
|
||||
|
||||
if not inst_levels:
|
||||
raise exceptions.InstantiationLevelNotFound(
|
||||
inst_level_id=req_inst_level_id)
|
||||
|
||||
for flavour, inst_level in inst_levels.items():
|
||||
if flavour != req_body['flavour_id']:
|
||||
continue
|
||||
|
||||
if req_inst_level_id in inst_level.get('levels').keys():
|
||||
# Found instantiation level
|
||||
return
|
||||
|
||||
raise exceptions.InstantiationLevelNotFound(
|
||||
inst_level_id=req_body['instantiation_level_id'])
|
||||
|
||||
def _validate_vim_connection(self, context, instantiate_vnf_request):
|
||||
if instantiate_vnf_request.vim_connection_info:
|
||||
vim_id = instantiate_vnf_request.vim_connection_info[0].vim_id
|
||||
access_info = \
|
||||
instantiate_vnf_request.vim_connection_info[0].access_info
|
||||
if access_info:
|
||||
region_name = access_info.get('region')
|
||||
else:
|
||||
region_name = None
|
||||
else:
|
||||
vim_id = None
|
||||
region_name = None
|
||||
|
||||
vim_client_obj = vim_client.VimClient()
|
||||
|
||||
try:
|
||||
vim_client_obj.get_vim(
|
||||
context, vim_id, region_name=region_name)
|
||||
except nfvo.VimDefaultNotDefined as exp:
|
||||
raise webob.exc.HTTPBadRequest(explanation=exp.message)
|
||||
except nfvo.VimNotFoundException:
|
||||
msg = _("VimConnection id is not found: %s")\
|
||||
% vim_id
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
except nfvo.VimRegionNotFoundException:
|
||||
msg = _("Region not found for the VimConnection: %s")\
|
||||
% vim_id
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
@wsgi.response(http_client.CREATED)
|
||||
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN))
|
||||
@validation.schema(vnf_lcm.create)
|
||||
@ -77,8 +201,42 @@ class VnfLcmController(wsgi.Controller):
|
||||
def delete(self, request, id):
|
||||
raise webob.exc.HTTPNotImplemented()
|
||||
|
||||
@check_vnf_state(action="instantiate",
|
||||
instantiation_state=[fields.VnfInstanceState.NOT_INSTANTIATED],
|
||||
task_state=[None])
|
||||
def _instantiate(self, context, vnf_instance, request_body):
|
||||
req_body = utils.convert_camelcase_to_snakecase(request_body)
|
||||
|
||||
try:
|
||||
self._validate_flavour_and_inst_level(context, req_body,
|
||||
vnf_instance)
|
||||
except exceptions.NotFound as ex:
|
||||
raise webob.exc.HTTPBadRequest(explanation=six.text_type(ex))
|
||||
|
||||
instantiate_vnf_request = \
|
||||
objects.InstantiateVnfRequest.obj_from_primitive(
|
||||
req_body, context=context)
|
||||
|
||||
# validate the vim connection id passed through request is exist or not
|
||||
self._validate_vim_connection(context, instantiate_vnf_request)
|
||||
|
||||
vnf_instance.task_state = fields.VnfInstanceTaskState.INSTANTIATING
|
||||
vnf_instance.save()
|
||||
|
||||
self.rpc_api.instantiate(context, vnf_instance,
|
||||
instantiate_vnf_request)
|
||||
|
||||
@wsgi.response(http_client.ACCEPTED)
|
||||
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND,
|
||||
http_client.CONFLICT, http_client.BAD_REQUEST))
|
||||
@validation.schema(vnf_lcm.instantiate)
|
||||
def instantiate(self, request, id, body):
|
||||
raise webob.exc.HTTPNotImplemented()
|
||||
context = request.environ['tacker.context']
|
||||
context.can(vnf_lcm_policies.VNFLCM % 'instantiate')
|
||||
|
||||
vnf_instance = self._get_vnf_instance(context, id)
|
||||
|
||||
self._instantiate(context, vnf_instance, body)
|
||||
|
||||
def terminate(self, request, id, body):
|
||||
raise webob.exc.HTTPNotImplemented()
|
||||
|
@ -10,7 +10,11 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
|
||||
from heatclient import client as heatclient
|
||||
from openstack import connection
|
||||
|
||||
from tacker.vnfm import keystone
|
||||
|
||||
|
||||
@ -23,6 +27,13 @@ class OpenstackClients(object):
|
||||
self.mistral_client = None
|
||||
self.keystone_client = None
|
||||
self.region_name = region_name
|
||||
|
||||
if auth_attr:
|
||||
# Note(tpatil): In vnflcm, auth_attr contains region information
|
||||
# which should be popped before creating the keystoneclient.
|
||||
auth_attr = copy.deepcopy(auth_attr)
|
||||
auth_attr.pop('region', None)
|
||||
|
||||
self.auth_attr = auth_attr
|
||||
|
||||
def _keystone_client(self):
|
||||
@ -49,3 +60,31 @@ class OpenstackClients(object):
|
||||
if not self.heat_client:
|
||||
self.heat_client = self._heat_client()
|
||||
return self.heat_client
|
||||
|
||||
|
||||
class OpenstackSdkConnection(object):
|
||||
|
||||
def __init__(self, vim_connection_info, version=None):
|
||||
super(OpenstackSdkConnection, self).__init__()
|
||||
self.keystone_plugin = keystone.Keystone()
|
||||
self.connection = self.openstack_connection(vim_connection_info,
|
||||
version)
|
||||
|
||||
def openstack_connection(self, vim_connection_info, version):
|
||||
access_info = vim_connection_info.access_info
|
||||
auth = dict(auth_url=access_info['auth_url'],
|
||||
username=access_info['username'],
|
||||
password=access_info['password'],
|
||||
project_name=access_info['project_name'],
|
||||
user_domain_name=access_info['user_domain_name'],
|
||||
project_domain_name=access_info['project_domain_name'])
|
||||
|
||||
session = self.keystone_plugin.initialize_client(**auth).session
|
||||
|
||||
conn = connection.Connection(
|
||||
region_name=access_info.get('region'),
|
||||
session=session,
|
||||
identity_interface='internal',
|
||||
image_api_version=version)
|
||||
|
||||
return conn
|
||||
|
@ -43,7 +43,6 @@ class DriverManager(object):
|
||||
LOG.error(msg)
|
||||
raise SystemExit(msg)
|
||||
drivers[type_] = ext
|
||||
|
||||
self._drivers = dict((type_, ext.obj)
|
||||
for (type_, ext) in drivers.items())
|
||||
LOG.info("Registered drivers from %(namespace)s: %(keys)s",
|
||||
|
@ -215,6 +215,23 @@ class VnfInstanceNotFound(NotFound):
|
||||
message = _("No vnf instance with id %(id)s.")
|
||||
|
||||
|
||||
class VnfInstanceConflictState(Conflict):
|
||||
message = _("Vnf instance %(uuid)s in %(attr)s %(state)s. Cannot "
|
||||
"%(action)s while the vnf instance is in this state.")
|
||||
|
||||
|
||||
class FlavourNotFound(NotFound):
|
||||
message = _("No flavour with id '%(flavour_id)s'.")
|
||||
|
||||
|
||||
class InstantiationLevelNotFound(NotFound):
|
||||
message = _("No instantiation level with id '%(inst_level_id)s'.")
|
||||
|
||||
|
||||
class VimConnectionNotFound(NotFound):
|
||||
message = _("No vim found with id '%(vim_id)s'.")
|
||||
|
||||
|
||||
class VnfResourceNotFound(NotFound):
|
||||
message = _("No vnf resource with id %(id)s.")
|
||||
|
||||
@ -235,6 +252,20 @@ class VnfInstantiatedInfoNotFound(NotFound):
|
||||
message = _("No vnf instantiated info for vnf id %(vnf_instance_id)s.")
|
||||
|
||||
|
||||
class VnfInstantiationFailed(TackerException):
|
||||
message = _("Vnf instantiation failed for vnf %(id)s, error: %(error)s")
|
||||
|
||||
|
||||
class VnfInstantiationWaitFailed(TackerException):
|
||||
message = _("Vnf instantiation wait failed for vnf %(id)s, "
|
||||
"error: %(error)s")
|
||||
|
||||
|
||||
class VnfPreInstantiationFailed(TackerException):
|
||||
message = _("Vnf '%(id)s' failed during pre-instantiation due to error: "
|
||||
"%(error)s")
|
||||
|
||||
|
||||
class OrphanedObjectError(TackerException):
|
||||
msg_fmt = _('Cannot call %(method)s on orphaned %(objtype)s object')
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
import functools
|
||||
import inspect
|
||||
import logging as std_logging
|
||||
import math
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
@ -36,6 +37,7 @@ from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import importutils
|
||||
from six.moves import urllib
|
||||
from stevedore import driver
|
||||
try:
|
||||
from eventlet import sleep
|
||||
@ -392,6 +394,14 @@ def convert_snakecase_to_camelcase(request_data):
|
||||
return request_data
|
||||
|
||||
|
||||
def is_url(url):
|
||||
try:
|
||||
urllib.request.urlopen(url)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
class CooperativeReader(object):
|
||||
"""An eventlet thread friendly class for reading in image data.
|
||||
|
||||
@ -516,3 +526,71 @@ class LimitingReader(object):
|
||||
if self.bytes_read > self.limit:
|
||||
raise self.exception_class()
|
||||
return result
|
||||
|
||||
|
||||
class MemoryUnit(object):
|
||||
|
||||
UNIT_SIZE_DEFAULT = 'B'
|
||||
UNIT_SIZE_DICT = {'B': 1, 'kB': 1000, 'KiB': 1024, 'MB': 1000000,
|
||||
'MiB': 1048576, 'GB': 1000000000,
|
||||
'GiB': 1073741824, 'TB': 1000000000000,
|
||||
'TiB': 1099511627776}
|
||||
|
||||
@staticmethod
|
||||
def convert_unit_size_to_num(size, unit=None):
|
||||
"""Convert given size to a number representing given unit.
|
||||
|
||||
If unit is None, convert to a number representing UNIT_SIZE_DEFAULT
|
||||
:param size: unit size e.g. 1 TB
|
||||
:param unit: unit to be converted to e.g GB
|
||||
:return: converted number e.g. 1000 for 1 TB size and unit GB
|
||||
"""
|
||||
if unit:
|
||||
unit = MemoryUnit.validate_unit(unit)
|
||||
else:
|
||||
unit = MemoryUnit.UNIT_SIZE_DEFAULT
|
||||
LOG.info(_('A memory unit is not provided for size; using the '
|
||||
'default unit %(default)s.') % {'default': 'B'})
|
||||
regex = re.compile('(\d*)\s*(\w*)')
|
||||
result = regex.match(str(size)).groups()
|
||||
if result[1]:
|
||||
unit_size = MemoryUnit.validate_unit(result[1])
|
||||
converted = int(str_to_num(result[0]) *
|
||||
MemoryUnit.UNIT_SIZE_DICT[unit_size] *
|
||||
math.pow(MemoryUnit.UNIT_SIZE_DICT
|
||||
[unit], -1))
|
||||
LOG.info(_('Given size %(size)s is converted to %(num)s '
|
||||
'%(unit)s.') % {'size': size,
|
||||
'num': converted, 'unit': unit})
|
||||
else:
|
||||
converted = (str_to_num(result[0]))
|
||||
return converted
|
||||
|
||||
@staticmethod
|
||||
def validate_unit(unit):
|
||||
if unit in MemoryUnit.UNIT_SIZE_DICT.keys():
|
||||
return unit
|
||||
else:
|
||||
for key in MemoryUnit.UNIT_SIZE_DICT.keys():
|
||||
if key.upper() == unit.upper():
|
||||
return key
|
||||
|
||||
msg = _('Provided unit "{0}" is not valid. The valid units are '
|
||||
'{1}').format(unit, MemoryUnit.UNIT_SIZE_DICT.keys())
|
||||
LOG.error(msg)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
def str_to_num(value):
|
||||
"""Convert a string representation of a number into a numeric type."""
|
||||
if (isinstance(value, int) or
|
||||
isinstance(value, float)):
|
||||
return value
|
||||
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
return None
|
||||
|
@ -22,6 +22,7 @@ import shutil
|
||||
import sys
|
||||
|
||||
from glance_store import exceptions as store_exceptions
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging
|
||||
from oslo_service import periodic_task
|
||||
@ -37,7 +38,6 @@ from tacker.common import exceptions
|
||||
from tacker.common import safe_utils
|
||||
from tacker.common import topics
|
||||
from tacker.common import utils
|
||||
import tacker.conf
|
||||
from tacker import context as t_context
|
||||
from tacker.db.common_services import common_services_db
|
||||
from tacker.db.nfvo import nfvo_db
|
||||
@ -50,9 +50,41 @@ from tacker.objects.vnf_package import VnfPackagesList
|
||||
from tacker.plugins.common import constants
|
||||
from tacker import service as tacker_service
|
||||
from tacker import version
|
||||
from tacker.vnflcm import vnflcm_driver
|
||||
from tacker.vnfm import plugin
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
# NOTE(tpatil): keystone_authtoken opts registered explicitly as conductor
|
||||
# service doesn't use the keystonemiddleware.authtoken middleware as it's
|
||||
# used by the tacker.service in the api-paste.ini
|
||||
OPTS = [cfg.StrOpt('user_domain_id',
|
||||
default='default',
|
||||
help='User Domain Id'),
|
||||
cfg.StrOpt('project_domain_id',
|
||||
default='default',
|
||||
help='Project Domain Id'),
|
||||
cfg.StrOpt('password',
|
||||
default='default',
|
||||
help='User Password'),
|
||||
cfg.StrOpt('username',
|
||||
default='default',
|
||||
help='User Name'),
|
||||
cfg.StrOpt('user_domain_name',
|
||||
default='default',
|
||||
help='Use Domain Name'),
|
||||
cfg.StrOpt('project_name',
|
||||
default='default',
|
||||
help='Project Name'),
|
||||
cfg.StrOpt('project_domain_name',
|
||||
default='default',
|
||||
help='Project Domain Name'),
|
||||
cfg.StrOpt('auth_url',
|
||||
default='http://localhost/identity/v3',
|
||||
help='Keystone endpoint')]
|
||||
|
||||
cfg.CONF.register_opts(OPTS, 'keystone_authtoken')
|
||||
|
||||
CONF = tacker.conf.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -109,6 +141,8 @@ class Conductor(manager.Manager):
|
||||
else:
|
||||
self.conf = CONF
|
||||
super(Conductor, self).__init__(host=self.conf.host)
|
||||
self.vnfm_plugin = plugin.VNFMPlugin()
|
||||
self.vnflcm_driver = vnflcm_driver.VnfLcmDriver()
|
||||
|
||||
def init_host(self):
|
||||
glance_store.initialize_glance_store()
|
||||
@ -361,6 +395,35 @@ class Conductor(manager.Manager):
|
||||
{'zip': csar_path, 'folder': csar_zip_temp_path,
|
||||
'uuid': vnf_pack.id})
|
||||
|
||||
def instantiate(self, context, vnf_instance, instantiate_vnf):
|
||||
self.vnflcm_driver.instantiate_vnf(context, vnf_instance,
|
||||
instantiate_vnf)
|
||||
|
||||
vnf_package_vnfd = objects.VnfPackageVnfd.get_by_id(context,
|
||||
vnf_instance.vnfd_id)
|
||||
vnf_package = objects.VnfPackage.get_by_id(context,
|
||||
vnf_package_vnfd.package_uuid, expected_attrs=['vnfd'])
|
||||
try:
|
||||
self._update_package_usage_state(context, vnf_package)
|
||||
except Exception:
|
||||
LOG.error("Failed to update usage_state of vnf package %s",
|
||||
vnf_package.id)
|
||||
|
||||
def _update_package_usage_state(self, context, vnf_package):
|
||||
"""Update vnf package usage state to IN_USE/NOT_IN_USE
|
||||
|
||||
If vnf package is not used by any of the vnf instances, it's usage
|
||||
state should be set to NOT_IN_USE otherwise it should be set to
|
||||
IN_USE.
|
||||
"""
|
||||
result = vnf_package.is_package_in_use(context)
|
||||
if result:
|
||||
vnf_package.usage_state = fields.PackageUsageStateType.IN_USE
|
||||
else:
|
||||
vnf_package.usage_state = fields.PackageUsageStateType.NOT_IN_USE
|
||||
|
||||
vnf_package.save()
|
||||
|
||||
|
||||
def init(args, **kwargs):
|
||||
CONF(args=args, project='tacker',
|
||||
|
40
tacker/conductor/conductorrpc/vnf_lcm_rpc.py
Normal file
40
tacker/conductor/conductorrpc/vnf_lcm_rpc.py
Normal file
@ -0,0 +1,40 @@
|
||||
# Copyright (C) 2020 NTT DATA
|
||||
# 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 oslo_messaging
|
||||
|
||||
from tacker.common import rpc
|
||||
from tacker.common import topics
|
||||
from tacker.objects import base as objects_base
|
||||
|
||||
|
||||
class VNFLcmRPCAPI(object):
|
||||
|
||||
target = oslo_messaging.Target(
|
||||
exchange='tacker',
|
||||
topic=topics.TOPIC_CONDUCTOR,
|
||||
fanout=False,
|
||||
version='1.0')
|
||||
|
||||
def instantiate(self, context, vnf_instance, instantiate_vnf, cast=True):
|
||||
serializer = objects_base.TackerObjectSerializer()
|
||||
|
||||
client = rpc.get_client(self.target, version_cap=None,
|
||||
serializer=serializer)
|
||||
cctxt = client.prepare()
|
||||
rpc_method = cctxt.cast if cast else cctxt.call
|
||||
return rpc_method(context, 'instantiate',
|
||||
vnf_instance=vnf_instance,
|
||||
instantiate_vnf=instantiate_vnf)
|
@ -19,8 +19,8 @@ from oslo_config import cfg
|
||||
from tacker.conf import conductor
|
||||
from tacker.conf import vnf_package
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_group('keystone_authtoken', 'keystonemiddleware.auth_token')
|
||||
|
||||
vnf_package.register_opts(CONF)
|
||||
conductor.register_opts(CONF)
|
||||
|
31
tacker/extensions/vnflcm.py
Normal file
31
tacker/extensions/vnflcm.py
Normal file
@ -0,0 +1,31 @@
|
||||
# Copyright (C) 2020 NTT DATA
|
||||
# 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_log import log as logging
|
||||
|
||||
from tacker._i18n import _
|
||||
from tacker.common import exceptions
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GlanceClientException(exceptions.TackerException):
|
||||
message = _("%(msg)s")
|
||||
|
||||
|
||||
class ImageCreateWaitFailed(exceptions.TackerException):
|
||||
message = _('Create image failed %(reason)s')
|
@ -199,6 +199,11 @@ 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.")
|
||||
|
||||
|
||||
def _validate_service_type_list(data, valid_values=None):
|
||||
if not isinstance(data, list):
|
||||
msg = _("Invalid data format for service list: '%s'") % data
|
||||
|
@ -32,4 +32,5 @@ def register_all():
|
||||
__import__('tacker.objects.vnf_instance')
|
||||
__import__('tacker.objects.vnf_instantiated_info')
|
||||
__import__('tacker.objects.vim_connection')
|
||||
__import__('tacker.objects.instantiate_vnf_req')
|
||||
__import__('tacker.objects.vnf_resources')
|
||||
|
368
tacker/objects/instantiate_vnf_req.py
Normal file
368
tacker/objects/instantiate_vnf_req.py
Normal file
@ -0,0 +1,368 @@
|
||||
# Copyright (C) 2020 NTT DATA
|
||||
# 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_log import log as logging
|
||||
|
||||
from tacker import objects
|
||||
from tacker.objects import base
|
||||
from tacker.objects import fields
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class InstantiateVnfRequest(base.TackerObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'flavour_id': fields.StringField(nullable=False),
|
||||
'instantiation_level_id': fields.StringField(nullable=True,
|
||||
default=None),
|
||||
'ext_managed_virtual_links': fields.ListOfObjectsField(
|
||||
'ExtManagedVirtualLinkData', nullable=True, default=[]),
|
||||
'vim_connection_info': fields.ListOfObjectsField(
|
||||
'VimConnectionInfo', nullable=True, default=[]),
|
||||
'ext_virtual_links': fields.ListOfObjectsField(
|
||||
'ExtVirtualLinkData', nullable=True, default=[]),
|
||||
'additional_params': fields.DictOfStringsField(nullable=True,
|
||||
default={}),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def obj_from_primitive(cls, primitive, context):
|
||||
if 'tacker_object.name' in primitive:
|
||||
obj_instantiate_vnf_req = super(
|
||||
InstantiateVnfRequest, cls).obj_from_primitive(
|
||||
primitive, context)
|
||||
else:
|
||||
if 'ext_managed_virtual_links' in primitive.keys():
|
||||
obj_data = [ExtManagedVirtualLinkData._from_dict(
|
||||
ext_manage) for ext_manage in primitive.get(
|
||||
'ext_managed_virtual_links', [])]
|
||||
primitive.update({'ext_managed_virtual_links': obj_data})
|
||||
|
||||
if 'vim_connection_info' in primitive.keys():
|
||||
obj_data = [objects.VimConnectionInfo._from_dict(
|
||||
vim_conn) for vim_conn in primitive.get(
|
||||
'vim_connection_info', [])]
|
||||
primitive.update({'vim_connection_info': obj_data})
|
||||
|
||||
if 'ext_virtual_links' in primitive.keys():
|
||||
obj_data = [ExtVirtualLinkData.obj_from_primitive(
|
||||
ext_vir_link, context) for ext_vir_link in primitive.get(
|
||||
'ext_virtual_links', [])]
|
||||
primitive.update({'ext_virtual_links': obj_data})
|
||||
obj_instantiate_vnf_req = InstantiateVnfRequest._from_dict(
|
||||
primitive)
|
||||
|
||||
return obj_instantiate_vnf_req
|
||||
|
||||
@classmethod
|
||||
def _from_dict(cls, data_dict):
|
||||
flavour_id = data_dict.get('flavour_id')
|
||||
instantiation_level_id = data_dict.get('instantiation_level_id')
|
||||
ext_managed_virtual_links = data_dict.get('ext_managed_virtual_links',
|
||||
[])
|
||||
vim_connection_info = data_dict.get('vim_connection_info', [])
|
||||
ext_virtual_links = data_dict.get('ext_virtual_links', [])
|
||||
additional_params = data_dict.get('additional_params', {})
|
||||
|
||||
return cls(flavour_id=flavour_id,
|
||||
instantiation_level_id=instantiation_level_id,
|
||||
ext_managed_virtual_links=ext_managed_virtual_links,
|
||||
vim_connection_info=vim_connection_info,
|
||||
ext_virtual_links=ext_virtual_links,
|
||||
additional_params=additional_params)
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class ExtManagedVirtualLinkData(base.TackerObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'id': fields.StringField(nullable=False),
|
||||
'vnf_virtual_link_desc_id': fields.StringField(nullable=False),
|
||||
'resource_id': fields.StringField(nullable=False),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _from_dict(cls, data_dict):
|
||||
id = data_dict.get('id')
|
||||
vnf_virtual_link_desc_id = data_dict.get(
|
||||
'vnf_virtual_link_desc_id')
|
||||
resource_id = data_dict.get('resource_id')
|
||||
obj = cls(id=id, vnf_virtual_link_desc_id=vnf_virtual_link_desc_id,
|
||||
resource_id=resource_id)
|
||||
return obj
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class ExtVirtualLinkData(base.TackerObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'id': fields.StringField(nullable=False),
|
||||
'resource_id': fields.StringField(nullable=False),
|
||||
'ext_cps': fields.ListOfObjectsField(
|
||||
'VnfExtCpData', nullable=True, default=[]),
|
||||
'ext_link_ports': fields.ListOfObjectsField(
|
||||
'ExtLinkPortData', nullable=True, default=[]),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def obj_from_primitive(cls, primitive, context):
|
||||
if 'tacker_object.name' in primitive:
|
||||
obj_ext_virt_link = super(
|
||||
ExtVirtualLinkData, cls).obj_from_primitive(
|
||||
primitive, context)
|
||||
else:
|
||||
if 'ext_cps' in primitive.keys():
|
||||
obj_data = [VnfExtCpData.obj_from_primitive(
|
||||
ext_cp, context) for ext_cp in primitive.get(
|
||||
'ext_cps', [])]
|
||||
primitive.update({'ext_cps': obj_data})
|
||||
|
||||
if 'ext_link_ports' in primitive.keys():
|
||||
obj_data = [ExtLinkPortData.obj_from_primitive(
|
||||
ext_link_port_data, context)
|
||||
for ext_link_port_data in primitive.get(
|
||||
'ext_link_ports', [])]
|
||||
primitive.update({'ext_link_ports': obj_data})
|
||||
|
||||
obj_ext_virt_link = ExtVirtualLinkData._from_dict(primitive)
|
||||
|
||||
return obj_ext_virt_link
|
||||
|
||||
@classmethod
|
||||
def _from_dict(cls, data_dict):
|
||||
id = data_dict.get('id')
|
||||
resource_id = data_dict.get('resource_id')
|
||||
ext_cps = data_dict.get('ext_cps', [])
|
||||
ext_link_ports = data_dict.get('ext_link_ports', [])
|
||||
|
||||
obj = cls(id=id, resource_id=resource_id, ext_cps=ext_cps,
|
||||
ext_link_ports=ext_link_ports)
|
||||
return obj
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class VnfExtCpData(base.TackerObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'cpd_id': fields.StringField(nullable=False),
|
||||
'cp_config': fields.ListOfObjectsField(
|
||||
'VnfExtCpConfig', nullable=True, default=[]),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def obj_from_primitive(cls, primitive, context):
|
||||
if 'tacker_object.name' in primitive:
|
||||
obj_vnf_ext_cp_data = super(VnfExtCpData, cls).obj_from_primitive(
|
||||
primitive, context)
|
||||
else:
|
||||
if 'cp_config' in primitive.keys():
|
||||
obj_data = [VnfExtCpConfig.obj_from_primitive(
|
||||
vnf_ext_cp_conf, context)
|
||||
for vnf_ext_cp_conf in primitive.get('cp_config', [])]
|
||||
primitive.update({'cp_config': obj_data})
|
||||
|
||||
obj_vnf_ext_cp_data = VnfExtCpData._from_dict(primitive)
|
||||
|
||||
return obj_vnf_ext_cp_data
|
||||
|
||||
@classmethod
|
||||
def _from_dict(cls, data_dict):
|
||||
cpd_id = data_dict.get('cpd_id')
|
||||
cp_config = data_dict.get('cp_config', [])
|
||||
|
||||
obj = cls(cpd_id=cpd_id, cp_config=cp_config)
|
||||
return obj
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class VnfExtCpConfig(base.TackerObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'cp_instance_id': fields.StringField(nullable=True, default=None),
|
||||
'link_port_id': fields.StringField(nullable=True, default=None),
|
||||
'cp_protocol_data': fields.ListOfObjectsField(
|
||||
'CpProtocolData', nullable=True, default=[]),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def obj_from_primitive(cls, primitive, context):
|
||||
if 'tacker_object.name' in primitive:
|
||||
obj_ext_cp_config = super(VnfExtCpConfig, cls).obj_from_primitive(
|
||||
primitive, context)
|
||||
else:
|
||||
if 'cp_protocol_data' in primitive.keys():
|
||||
obj_data = [CpProtocolData.obj_from_primitive(
|
||||
cp_protocol, context) for cp_protocol in primitive.get(
|
||||
'cp_protocol_data', [])]
|
||||
primitive.update({'cp_protocol_data': obj_data})
|
||||
|
||||
obj_ext_cp_config = VnfExtCpConfig._from_dict(primitive)
|
||||
|
||||
return obj_ext_cp_config
|
||||
|
||||
@classmethod
|
||||
def _from_dict(cls, data_dict):
|
||||
cp_instance_id = data_dict.get('cp_instance_id')
|
||||
link_port_id = data_dict.get('link_port_id')
|
||||
cp_protocol_data = data_dict.get('cp_protocol_data', [])
|
||||
|
||||
obj = cls(cp_instance_id=cp_instance_id,
|
||||
link_port_id=link_port_id, cp_protocol_data=cp_protocol_data)
|
||||
return obj
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class CpProtocolData(base.TackerObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'layer_protocol': fields.StringField(nullable=False),
|
||||
'ip_over_ethernet': fields.ObjectField(
|
||||
'IpOverEthernetAddressData', nullable=True, default=None),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def obj_from_primitive(cls, primitive, context):
|
||||
if 'tacker_object.name' in primitive:
|
||||
obj_cp_protocal = super(CpProtocolData, cls).obj_from_primitive(
|
||||
primitive, context)
|
||||
else:
|
||||
if 'ip_over_ethernet' in primitive.keys():
|
||||
obj_data = IpOverEthernetAddressData.obj_from_primitive(
|
||||
primitive.get('ip_over_ethernet', {}), context)
|
||||
primitive.update({'ip_over_ethernet': obj_data})
|
||||
obj_cp_protocal = CpProtocolData._from_dict(primitive)
|
||||
|
||||
return obj_cp_protocal
|
||||
|
||||
@classmethod
|
||||
def _from_dict(cls, data_dict):
|
||||
layer_protocol = data_dict.get('layer_protocol')
|
||||
ip_over_ethernet = data_dict.get('ip_over_ethernet')
|
||||
|
||||
obj = cls(layer_protocol=layer_protocol,
|
||||
ip_over_ethernet=ip_over_ethernet)
|
||||
return obj
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class IpOverEthernetAddressData(base.TackerObject):
|
||||
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'mac_address': fields.StringField(nullable=True, default=None),
|
||||
'ip_addresses': fields.ListOfObjectsField('IpAddress', nullable=True,
|
||||
default=[]),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def obj_from_primitive(cls, primitive, context):
|
||||
if 'tacker_object.name' in primitive:
|
||||
ip_over_ethernet = super(
|
||||
IpOverEthernetAddressData, cls).obj_from_primitive(
|
||||
primitive, context)
|
||||
else:
|
||||
if 'ip_addresses' in primitive.keys():
|
||||
obj_data = [IpAddress._from_dict(
|
||||
ip_address) for ip_address in primitive.get(
|
||||
'ip_addresses', [])]
|
||||
primitive.update({'ip_addresses': obj_data})
|
||||
|
||||
ip_over_ethernet = IpOverEthernetAddressData._from_dict(primitive)
|
||||
|
||||
return ip_over_ethernet
|
||||
|
||||
@classmethod
|
||||
def _from_dict(cls, data_dict):
|
||||
mac_address = data_dict.get('mac_address')
|
||||
ip_addresses = data_dict.get('ip_addresses', [])
|
||||
obj = cls(mac_address=mac_address, ip_addresses=ip_addresses)
|
||||
return obj
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class IpAddress(base.TackerObject):
|
||||
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'type': fields.IpAddressTypeField(nullable=False),
|
||||
'subnet_id': fields.StringField(nullable=True, default=None),
|
||||
'fixed_addresses': fields.ListOfStringsField(nullable=True,
|
||||
default=[])
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _from_dict(cls, data_dict):
|
||||
type = data_dict.get('type')
|
||||
subnet_id = data_dict.get('subnet_id')
|
||||
fixed_addresses = data_dict.get('fixed_addresses', [])
|
||||
|
||||
obj = cls(type=type, subnet_id=subnet_id,
|
||||
fixed_addresses=fixed_addresses)
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class ExtLinkPortData(base.TackerObject):
|
||||
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'id': fields.UUIDField(nullable=False),
|
||||
'resource_handle': fields.ObjectField(
|
||||
'ResourceHandle', nullable=False),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def obj_from_primitive(cls, primitive, context):
|
||||
if 'tacker_object.name' in primitive:
|
||||
obj_link_port_data = super(
|
||||
ExtLinkPortData, cls).obj_from_primitive(primitive, context)
|
||||
else:
|
||||
if 'resource_handle' in primitive.keys():
|
||||
obj_data = objects.ResourceHandle._from_dict(primitive.get(
|
||||
'resource_handle', []))
|
||||
primitive.update({'resource_handle': obj_data})
|
||||
|
||||
obj_link_port_data = ExtLinkPortData._from_dict(primitive)
|
||||
|
||||
return obj_link_port_data
|
||||
|
||||
@classmethod
|
||||
def _from_dict(cls, data_dict):
|
||||
id = data_dict.get('id')
|
||||
resource_handle = data_dict.get('resource_handle')
|
||||
|
||||
obj = cls(id=id, resource_handle=resource_handle)
|
||||
return obj
|
@ -19,6 +19,7 @@ from oslo_utils import timeutils
|
||||
from oslo_utils import uuidutils
|
||||
from oslo_versionedobjects import base as ovoo_base
|
||||
from sqlalchemy.orm import joinedload
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
from tacker._i18n import _
|
||||
from tacker.common import exceptions
|
||||
@ -238,7 +239,8 @@ def _make_vnf_packages_list(context, vnf_package_list, db_vnf_package_list,
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class VnfPackage(base.TackerObject, base.TackerPersistentObject):
|
||||
class VnfPackage(base.TackerObject, base.TackerPersistentObject,
|
||||
base.TackerObjectDictCompat):
|
||||