Support VNF update operations based on ETSI NFV
Supported ETSI SOL003 Modify VNF function. Added processing for receiving Individual VNF Instances(PATCH). * PATCH /vnflcm/v1/vnf_instances/{vnfInstanceId} Implements: blueprint support-etsi-nfv-specs Spec: https://specs.openstack.org/openstack/tacker-specs/specs/victoria/support-vnf-update-api-based-on-etsi-nfv-sol.html Change-Id: If8c37bcf2fcd9094f3be4d212bdf79c322ae2705
This commit is contained in:
parent
172095279b
commit
797554a378
|
@ -1189,6 +1189,50 @@ vnf_instance_metadata:
|
|||
in: body
|
||||
required: false
|
||||
type: object
|
||||
vnf_instance_modify_request_description:
|
||||
description: |
|
||||
New value of the "vnfInstanceDescription" attribute in
|
||||
"VnfInstance", or "null" to remove the attribute.
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
vnf_instance_modify_request_metadata:
|
||||
description: |
|
||||
Modifications of the "metadata" attribute in
|
||||
"VnfInstance". If present, these modifications shall be
|
||||
applied according to the rules of JSON Merge PATCH
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
vnf_instance_modify_request_name:
|
||||
description: |
|
||||
New value of the "vnfInstanceName" attribute in
|
||||
"VnfInstance", or "null" to remove the attribute.
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
vnf_instance_modify_request_vim_connection_info:
|
||||
description: |
|
||||
New content of certain entries in the
|
||||
"vimConnectionInfo" attribute array in "VnfInstance", as
|
||||
defined below this table.
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
vnf_instance_modify_request_vnf_pkg_id:
|
||||
description: |
|
||||
New value of the "vnfPkgId" attribute in "VnfInstance". The
|
||||
value "null" is not permitted.
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
vnf_instance_modify_request_vnfd_id:
|
||||
description: |
|
||||
New value of the "vnfdId" attribute in "VnfInstance". The
|
||||
value "null" is not permitted.
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
vnf_instance_name:
|
||||
description: |
|
||||
Name of the VNF instance.
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"vnfdId": "093c38b5-a731-4593-a578-d12e42596b3e"
|
||||
}
|
|
@ -570,6 +570,66 @@ Response Example
|
|||
.. literalinclude:: samples/vnflcm/list-vnf-instance-response.json
|
||||
:language: javascript
|
||||
|
||||
Modify a VNF instance
|
||||
========================
|
||||
|
||||
.. rest_method:: POST /vnflcm/v1/vnf_instances/{vnfInstanceId}
|
||||
|
||||
This method modifies an "Individual VNF instance" resource.
|
||||
|
||||
Changes to the VNF configurable properties are applied to the configuration in the VNF instance, and are reflected in
|
||||
the representation of this resource. Other changes are applied to the VNF instance information managed by the VNFM,
|
||||
and are reflected in the representation of this resource.
|
||||
|
||||
According to the ETSI NFV SOL document, there is no API request/response
|
||||
specification for Etag yet, and transactions using Etag are not defined
|
||||
by standardization. Therefore, the Victoria release does not support
|
||||
`Error Code: 412 Precondition Failed`. Once a standard specification
|
||||
for this is established, it will be installed on the tacker.
|
||||
|
||||
Response Codes
|
||||
--------------
|
||||
|
||||
.. rest_status_code:: success status.yaml
|
||||
|
||||
- 202
|
||||
|
||||
.. rest_status_code:: error status.yaml
|
||||
|
||||
- 400
|
||||
- 401
|
||||
- 403
|
||||
- 404
|
||||
- 409
|
||||
|
||||
Request Parameters
|
||||
------------------
|
||||
|
||||
.. rest_parameters:: parameters_vnflcm.yaml
|
||||
|
||||
- vnfInstanceId: vnf_instance_id
|
||||
- vnfInstanceName: vnf_instance_modify_request_name
|
||||
- vnfInstanceDescription: vnf_instance_modify_request_description
|
||||
- vnfdId: vnf_instance_modify_request_vnfd_id
|
||||
- metadata: vnf_instance_modify_request_metadata
|
||||
- vimConnectionInfo: vnf_instance_modify_request_vim_connection_info
|
||||
- id: vim_connection_info_id
|
||||
- vimId: vim_connection_info_vim_id
|
||||
- vimType: vim_connection_info_vim_type
|
||||
- interfaceInfo: vim_connection_info_interface_info
|
||||
- endpoint: vim_connection_info_interface_info_endpoint
|
||||
- accessInfo: vim_connection_info_access_info
|
||||
- username: vim_connection_info_access_info_username
|
||||
- region: vim_connection_info_access_info_region
|
||||
- password: vim_connection_info_access_info_password
|
||||
- tenant: vim_connection_info_access_info_tenant
|
||||
|
||||
Request Example
|
||||
---------------
|
||||
|
||||
.. literalinclude:: samples/vnflcm/modify-vnf-instance-request.json
|
||||
:language: javascript
|
||||
|
||||
Show VNF LCM operation occurrence
|
||||
=================================
|
||||
|
||||
|
|
|
@ -239,3 +239,16 @@ register_subscription = {
|
|||
'required': ['callbackUri'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
update = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'vnfdId': parameter_types.uuid,
|
||||
'vnfInstanceName': parameter_types.name_allow_zero_min_length,
|
||||
'vnfInstanceDescription': parameter_types.description,
|
||||
'vnfPkgId': parameter_types.uuid,
|
||||
'metadata': parameter_types.keyvalue_pairs,
|
||||
'vimConnectionInfo': _vimConnectionInfo,
|
||||
},
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
|
|
@ -695,6 +695,100 @@ class VnfLcmController(wsgi.Controller):
|
|||
|
||||
return self._view_builder.show_lcm_op_occs(vnf_lcm_op_occs)
|
||||
|
||||
@wsgi.response(http_client.OK)
|
||||
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND))
|
||||
def update(self, request, id, body):
|
||||
context = request.environ['tacker.context']
|
||||
context.can(vnf_lcm_policies.VNFLCM % 'update_vnf')
|
||||
|
||||
# get body
|
||||
req_body = utils.convert_camelcase_to_snakecase(body)
|
||||
|
||||
# According to the ETSI NFV SOL document,
|
||||
# there is no API request/response
|
||||
# specification for Etag yet,
|
||||
# and transactions using Etag are not defined
|
||||
# by standardization. Therefore, the Victoria release does not support
|
||||
# `Error Code: 412 Precondition Failed`. Once a standard specification
|
||||
# for this is established, it will be installed on the tacker.
|
||||
|
||||
# Confirmation of update target
|
||||
try:
|
||||
vnf_data = objects.VNF.vnf_index_list(id, context)
|
||||
if not vnf_data:
|
||||
msg = _("Can not find requested vnf data: %s") % id
|
||||
return self._make_problem_detail(msg, 404, title='Not Found')
|
||||
except Exception as e:
|
||||
return self._make_problem_detail(
|
||||
str(e), 500, 'Internal Server Error')
|
||||
|
||||
if (vnf_data.get("status") != fields.VnfStatus.ACTIVE and
|
||||
vnf_data.get("status") != fields.VnfStatus.INACTIVE):
|
||||
msg = _("VNF %(id)s status is %(state)s")
|
||||
return self._make_problem_detail(msg % {"id": id,
|
||||
"state": vnf_data.get("status")}, 409, 'Conflict')
|
||||
|
||||
try:
|
||||
vnf_instance_data = objects.VnfInstanceList.vnf_instance_list(
|
||||
vnf_data.get('vnfd_id'), context)
|
||||
if not vnf_instance_data:
|
||||
msg = _("Can not find requested vnf instance data: %s") \
|
||||
% vnf_data.get('vnfd_id')
|
||||
return self._make_problem_detail(msg, 404, title='Not Found')
|
||||
except Exception as e:
|
||||
return self._make_problem_detail(
|
||||
str(e), 500, 'Internal Server Error')
|
||||
|
||||
if req_body['vnfd_id']:
|
||||
try:
|
||||
pkg_obj = objects.VnfPackageVnfd(context=context)
|
||||
vnfd_pkg = pkg_obj.get_vnf_package_vnfd(req_body['vnfd_id'])
|
||||
if not vnfd_pkg:
|
||||
msg = _(
|
||||
"Can not find requested vnf package vnfd: %s") %\
|
||||
req_body['vnfd_id']
|
||||
return self._make_problem_detail(msg, 400, 'Bad Request')
|
||||
except Exception as e:
|
||||
return self._make_problem_detail(
|
||||
str(e), 500, 'Internal Server Error')
|
||||
vnfd_pkg_data = {}
|
||||
vnfd_pkg_data['vnf_provider'] = vnfd_pkg.get('vnf_provider')
|
||||
vnfd_pkg_data['vnf_product_name'] = vnfd_pkg.get(
|
||||
'vnf_product_name')
|
||||
vnfd_pkg_data['vnf_software_version'] = vnfd_pkg.get(
|
||||
'vnf_software_version')
|
||||
vnfd_pkg_data['vnfd_version'] = vnfd_pkg.get('vnfd_version')
|
||||
vnfd_pkg_data['package_uuid'] = vnfd_pkg.get('package_uuid')
|
||||
|
||||
# make op_occs_uuid
|
||||
op_occs_uuid = uuidutils.generate_uuid()
|
||||
|
||||
# process vnf
|
||||
if not req_body['vnfd_id']:
|
||||
vnfd_pkg_data = ""
|
||||
vnf_lcm_opoccs = {
|
||||
'vnf_instance_id': id,
|
||||
'id': op_occs_uuid,
|
||||
'state_entered_time': vnf_data.get('updated_at'),
|
||||
'operationParams': str(body)}
|
||||
|
||||
self.rpc_api.update(
|
||||
context,
|
||||
vnf_lcm_opoccs,
|
||||
req_body,
|
||||
vnfd_pkg_data,
|
||||
vnf_data.get('vnfd_id'))
|
||||
|
||||
# make response
|
||||
res = webob.Response(content_type='application/json')
|
||||
res.status_int = 202
|
||||
loc_url = CONF.vnf_lcm.endpoint_url + \
|
||||
'/vnflcm/v1/vnf_lcm_op_occs/' + op_occs_uuid
|
||||
location = ('Location', loc_url)
|
||||
res.headerlist.append(location)
|
||||
|
||||
return res
|
||||
|
||||
@wsgi.response(http_client.CREATED)
|
||||
@validation.schema(vnf_lcm.register_subscription)
|
||||
def register_subscription(self, request, body):
|
||||
|
|
|
@ -59,7 +59,7 @@ class VnflcmAPIRouter(wsgi.Router):
|
|||
|
||||
# Allowed methods on
|
||||
# /vnflcm/v1/vnf_instances/{vnfInstanceId} resource
|
||||
methods = {"DELETE": "delete", "GET": "show"}
|
||||
methods = {"DELETE": "delete", "GET": "show", "PATCH": "update"}
|
||||
self._setup_route(mapper, "/vnf_instances/{id}",
|
||||
methods, controller, default_resource)
|
||||
|
||||
|
|
|
@ -157,6 +157,79 @@ def revert_upload_vnf_package(function):
|
|||
return decorated_function
|
||||
|
||||
|
||||
@utils.expects_func_args('vnf_lcm_opoccs')
|
||||
def revert_update_lcm(function):
|
||||
"""Decorator to revert update_lcm on failure."""
|
||||
@functools.wraps(function)
|
||||
def decorated_function(self, context, *args, **kwargs):
|
||||
try:
|
||||
return function(self, context, *args, **kwargs)
|
||||
except Exception as exp:
|
||||
LOG.error("update vnf_instance failed %s" % exp)
|
||||
with excutils.save_and_reraise_exception():
|
||||
wrapped_func = safe_utils.get_wrapped_function(function)
|
||||
keyed_args = inspect.getcallargs(wrapped_func, self, context,
|
||||
*args, **kwargs)
|
||||
context = keyed_args['context']
|
||||
vnf_lcm_opoccs = keyed_args['vnf_lcm_opoccs']
|
||||
|
||||
try:
|
||||
# update vnf
|
||||
vnf_now = timeutils.utcnow()
|
||||
vnf_obj = objects.vnf.VNF(context=context)
|
||||
vnf_obj.id = vnf_lcm_opoccs.get('vnf_instance_id')
|
||||
vnf_obj.status = 'ERROR'
|
||||
vnf_obj.updated_at = vnf_now
|
||||
vnf_obj.save()
|
||||
|
||||
e_msg = str(exp)
|
||||
|
||||
# update lcm_op_occs
|
||||
problem_obj = objects.vnf_lcm_op_occs.ProblemDetails()
|
||||
problem_obj.status = '500'
|
||||
problem_obj.detail = e_msg
|
||||
|
||||
lcm_op_obj = objects.vnf_lcm_op_occs.VnfLcmOpOcc(
|
||||
context=context)
|
||||
lcm_op_obj.id = vnf_lcm_opoccs.get('id')
|
||||
lcm_op_obj.operation_state =\
|
||||
fields.LcmOccsOperationState.FAILED_TEMP
|
||||
lcm_op_obj.error = problem_obj
|
||||
lcm_op_obj.state_entered_time = vnf_now
|
||||
lcm_op_obj.updated_at = vnf_now
|
||||
lcm_op_obj.save()
|
||||
|
||||
# Notification
|
||||
notification = {}
|
||||
notification['notificationType'] = \
|
||||
'VnfLcmOperationOccurrenceNotification'
|
||||
notification['notificationStatus'] = 'RESULT'
|
||||
notification['operationState'] = \
|
||||
fields.LcmOccsOperationState.FAILED_TEMP
|
||||
notification['vnfInstanceId'] = vnf_lcm_opoccs.get(
|
||||
'vnf_instance_id')
|
||||
notification['operation'] = 'MODIFY_INFO'
|
||||
notification['isAutomaticInvocation'] = 'False'
|
||||
notification['vnfLcmOpOccId'] = vnf_lcm_opoccs.get('id')
|
||||
notification['error'] = jsonutils.dumps(
|
||||
problem_obj.to_dict())
|
||||
instance_url = self._get_vnf_instance_href(
|
||||
vnf_lcm_opoccs.get('vnf_instance_id'))
|
||||
lcm_url = self._get_vnf_lcm_op_occs_href(
|
||||
vnf_lcm_opoccs.get('id'))
|
||||
notification['_links'] = {
|
||||
'vnfInstance': {
|
||||
'href': instance_url},
|
||||
'vnfLcmOpOcc': {
|
||||
'href': lcm_url}}
|
||||
self.send_notification(context, notification)
|
||||
|
||||
except Exception as msg:
|
||||
LOG.error("revert_update_lcm failed %s" % str(msg))
|
||||
|
||||
return decorated_function
|
||||
|
||||
|
||||
class Conductor(manager.Manager):
|
||||
def __init__(self, host, conf=None):
|
||||
if conf:
|
||||
|
@ -180,6 +253,12 @@ class Conductor(manager.Manager):
|
|||
glance_store.initialize_glance_store()
|
||||
self._basic_config_check()
|
||||
|
||||
def _get_vnf_instance_href(self, vnf_instance_id):
|
||||
return '/vnflcm/v1/vnf_instances/%s' % vnf_instance_id
|
||||
|
||||
def _get_vnf_lcm_op_occs_href(self, vnf_lcm_op_occs_id):
|
||||
return '/vnflcm/v1/vnf_lcm_op_occs/%s' % vnf_lcm_op_occs_id
|
||||
|
||||
def _basic_config_check(self):
|
||||
if not os.path.isdir(CONF.vnf_package.vnf_package_csar_path):
|
||||
LOG.error("Config option 'vnf_package_csar_path' is not "
|
||||
|
@ -1100,6 +1179,100 @@ class Conductor(manager.Manager):
|
|||
auth_type=auth_type,
|
||||
auth_params=auth_params)
|
||||
|
||||
@revert_update_lcm
|
||||
def update(
|
||||
self,
|
||||
context,
|
||||
vnf_lcm_opoccs,
|
||||
body_data,
|
||||
vnfd_pkg_data,
|
||||
vnfd_id):
|
||||
# input vnf_lcm_op_occs
|
||||
now = timeutils.utcnow()
|
||||
lcm_op_obj = objects.vnf_lcm_op_occs.VnfLcmOpOcc(context=context)
|
||||
lcm_op_obj.id = vnf_lcm_opoccs.get('id')
|
||||
lcm_op_obj.operation_state = fields.LcmOccsOperationState.PROCESSING
|
||||
lcm_op_obj.state_entered_time = vnf_lcm_opoccs.get(
|
||||
'state_entered_time')
|
||||
lcm_op_obj.start_time = now
|
||||
lcm_op_obj.vnf_instance_id = vnf_lcm_opoccs.get('vnf_instance_id')
|
||||
lcm_op_obj.operation = fields.InstanceOperation.MODIFY_INFO
|
||||
lcm_op_obj.is_automatic_invocation = 0
|
||||
lcm_op_obj.is_cancel_pending = 0
|
||||
lcm_op_obj.operationParams = vnf_lcm_opoccs.get('operationParams')
|
||||
|
||||
try:
|
||||
lcm_op_obj.create()
|
||||
except Exception as msg:
|
||||
raise Exception(str(msg))
|
||||
|
||||
# Notification
|
||||
instance_url = self._get_vnf_instance_href(
|
||||
vnf_lcm_opoccs.get('vnf_instance_id'))
|
||||
lcm_url = self._get_vnf_lcm_op_occs_href(vnf_lcm_opoccs.get('id'))
|
||||
|
||||
notification_data = {
|
||||
'notificationType':
|
||||
fields.LcmOccsNotificationType.VNF_OP_OCC_NOTIFICATION,
|
||||
'notificationStatus': fields.LcmOccsNotificationStatus.START,
|
||||
'operationState': fields.LcmOccsOperationState.PROCESSING,
|
||||
'vnfInstanceId': vnf_lcm_opoccs.get('vnf_instance_id'),
|
||||
'operation': fields.InstanceOperation.MODIFY_INFO,
|
||||
'isAutomaticInvocation': False,
|
||||
'vnfLcmOpOccId': vnf_lcm_opoccs.get('id'),
|
||||
'_links': {
|
||||
'vnfInstance': {
|
||||
'href': instance_url},
|
||||
'vnfLcmOpOcc': {
|
||||
'href': lcm_url}}}
|
||||
|
||||
self.send_notification(context, notification_data)
|
||||
|
||||
# update vnf_instances
|
||||
try:
|
||||
ins_obj = objects.vnf_instance.VnfInstance(context=context)
|
||||
result = ins_obj.update(
|
||||
context,
|
||||
vnf_lcm_opoccs,
|
||||
body_data,
|
||||
vnfd_pkg_data,
|
||||
vnfd_id)
|
||||
except Exception as msg:
|
||||
raise Exception(str(msg))
|
||||
|
||||
# update lcm_op_occs
|
||||
changed_info = objects.vnf_lcm_op_occs.VnfInfoModifications()
|
||||
changed_info.vnf_instance_name = body_data.get('vnf_instance_name')
|
||||
changed_info.vnf_instance_description = body_data.get(
|
||||
'vnf_instance_description')
|
||||
if body_data.get('vnfd_id'):
|
||||
changed_info.vnfd_id = body_data.get('vnfd_id')
|
||||
changed_info.vnf_provider = vnfd_pkg_data.get('vnf_provider')
|
||||
changed_info.vnf_product_name = vnfd_pkg_data.get(
|
||||
'vnf_product_name')
|
||||
changed_info.vnf_software_version = vnfd_pkg_data.get(
|
||||
'vnf_software_version')
|
||||
changed_info.vnfd_version = vnfd_pkg_data.get('vnfd_version')
|
||||
|
||||
# update vnf_lcm_op_occs
|
||||
now = timeutils.utcnow()
|
||||
lcm_op_obj.id = vnf_lcm_opoccs.get('id')
|
||||
lcm_op_obj.operation_state = fields.LcmOccsOperationState.COMPLETED
|
||||
lcm_op_obj.state_entered_time = result
|
||||
lcm_op_obj.updated_at = now
|
||||
lcm_op_obj.changed_info = changed_info
|
||||
|
||||
try:
|
||||
lcm_op_obj.save()
|
||||
except Exception as msg:
|
||||
raise Exception(str(msg))
|
||||
|
||||
# Notification
|
||||
notification_data['notificationStatus'] = 'RESULT'
|
||||
notification_data['operationState'] = 'COMPLETED'
|
||||
notification_data['changed_info'] = changed_info.to_dict()
|
||||
self.send_notification(context, notification_data)
|
||||
|
||||
|
||||
def init(args, **kwargs):
|
||||
CONF(args=args, project='tacker',
|
||||
|
|
|
@ -71,6 +71,20 @@ class VNFLcmRPCAPI(object):
|
|||
heal_vnf_request=heal_vnf_request,
|
||||
vnf_lcm_op_occs_id=vnf_lcm_op_occs_id)
|
||||
|
||||
def update(self, context, vnf_lcm_opoccs, body_data,
|
||||
vnfd_pkg_data, vnfd_id, 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, 'update',
|
||||
vnf_lcm_opoccs=vnf_lcm_opoccs,
|
||||
body_data=body_data,
|
||||
vnfd_pkg_data=vnfd_pkg_data,
|
||||
vnfd_id=vnfd_id)
|
||||
|
||||
def send_notification(self, context, notification, cast=True):
|
||||
serializer = objects_base.TackerObjectSerializer()
|
||||
|
||||
|
|
|
@ -201,7 +201,6 @@ class VnfInstance(model_base.BASE, models.SoftDeleteMixin,
|
|||
task_state = sa.Column(sa.String(255), nullable=True)
|
||||
vim_connection_info = sa.Column(sa.JSON(), nullable=True)
|
||||
tenant_id = sa.Column('tenant_id', sa.String(length=64), nullable=False)
|
||||
vnf_pkg_id = sa.Column(types.Uuid, nullable=False)
|
||||
vnf_metadata = sa.Column(sa.JSON(), nullable=True)
|
||||
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ def register_all():
|
|||
# function in order for it to be registered by services that may
|
||||
# need to receive it via RPC.
|
||||
__import__('tacker.objects.heal_vnf_request')
|
||||
__import__('tacker.objects.vnfd')
|
||||
__import__('tacker.objects.vnf_package')
|
||||
__import__('tacker.objects.vnf_package_vnfd')
|
||||
__import__('tacker.objects.vnf_deployment_flavour')
|
||||
|
@ -34,6 +35,7 @@ def register_all():
|
|||
__import__('tacker.objects.vim_connection')
|
||||
__import__('tacker.objects.instantiate_vnf_req')
|
||||
__import__('tacker.objects.vnf_resources')
|
||||
__import__('tacker.objects.vnf')
|
||||
__import__('tacker.objects.vnfd')
|
||||
__import__('tacker.objects.vnf_lcm_op_occs')
|
||||
__import__('tacker.objects.terminate_vnf_req')
|
||||
|
|
|
@ -255,3 +255,14 @@ class LcmOccsNotificationType(BaseTackerEnum):
|
|||
VNF_ID_CREATION_NOTIFICATION = 'VnfIdentifierCreationNotification'
|
||||
|
||||
ALL = (VNF_OP_OCC_NOTIFICATION)
|
||||
|
||||
|
||||
class VnfStatus(BaseTackerEnum):
|
||||
ACTIVE = 'ACTIVE'
|
||||
INACTIVE = 'INACTIVE'
|
||||
|
||||
ALL = (ACTIVE, INACTIVE)
|
||||
|
||||
|
||||
class InstanceOperation(BaseTackerEnum):
|
||||
MODIFY_INFO = 'MODIFY_INFO'
|
||||
|
|
|
@ -26,6 +26,8 @@ class VimConnectionInfo(base.TackerObject, base.TackerPersistentObject):
|
|||
'id': fields.StringField(nullable=False),
|
||||
'vim_id': fields.StringField(nullable=True, default=None),
|
||||
'vim_type': fields.StringField(nullable=False),
|
||||
'interface_info': fields.DictOfNullableStringsField(nullable=True,
|
||||
default={}),
|
||||
'access_info': fields.DictOfNullableStringsField(nullable=True,
|
||||
default={}),
|
||||
}
|
||||
|
@ -36,7 +38,11 @@ class VimConnectionInfo(base.TackerObject, base.TackerPersistentObject):
|
|||
vim_id = data_dict.get('vim_id')
|
||||
vim_type = data_dict.get('vim_type')
|
||||
access_info = data_dict.get('access_info', {})
|
||||
obj = cls(id=id, vim_id=vim_id, vim_type=vim_type,
|
||||
interface_info = data_dict.get('interface_info', {})
|
||||
obj = cls(id=id,
|
||||
vim_id=vim_id,
|
||||
vim_type=vim_type,
|
||||
interface_info=interface_info,
|
||||
access_info=access_info)
|
||||
return obj
|
||||
|
||||
|
@ -55,4 +61,5 @@ class VimConnectionInfo(base.TackerObject, base.TackerPersistentObject):
|
|||
return {'id': self.id,
|
||||
'vim_id': self.vim_id,
|
||||
'vim_type': self.vim_type,
|
||||
'interface_info': self.interface_info,
|
||||
'access_info': self.access_info}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
# 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 oslo_versionedobjects import base as ovoo_base
|
||||
from tacker.db import api as db_api
|
||||
from tacker.db.db_sqlalchemy import api
|
||||
from tacker.db.vnfm import vnfm_db
|
||||
from tacker.objects import base
|
||||
from tacker.objects import fields
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _vnf_update(context, values):
|
||||
update = {'status': values.status,
|
||||
'updated_at': values.updated_at}
|
||||
|
||||
api.model_query(context, vnfm_db.VNF). \
|
||||
filter_by(id=values.id). \
|
||||
update(update, synchronize_session=False)
|
||||
|
||||
|
||||
@db_api.context_manager.reader
|
||||
def _vnf_get(context, id, columns_to_join=None):
|
||||
vnf_data = api.model_query(
|
||||
context,
|
||||
vnfm_db.VNF).filter_by(
|
||||
id=id).filter_by(
|
||||
deleted_at='0001-01-01 00:00:00').first()
|
||||
if vnf_data:
|
||||
vnf_data = vnf_data.__dict__
|
||||
vnf_attribute_data = api.model_query(
|
||||
context, vnfm_db.VNFAttribute).filter_by(
|
||||
vnf_id=vnf_data.get('id')).first()
|
||||
vnf_data['vnf_attribute'] = vnf_attribute_data.__dict__
|
||||
vnfd_data = api.model_query(
|
||||
context, vnfm_db.VNFD).filter_by(
|
||||
id=vnf_data.get('vnfd_id')).first()
|
||||
vnf_data['vnfd'] = vnfd_data.__dict__
|
||||
vnfd_attribute_data = api.model_query(
|
||||
context, vnfm_db.VNFDAttribute).filter_by(
|
||||
vnfd_id=vnf_data.get('vnfd_id')).first()
|
||||
vnf_data['vnfd_attribute'] = vnfd_attribute_data.__dict__
|
||||
else:
|
||||
vnf_data = ""
|
||||
|
||||
return vnf_data
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class VNF(base.TackerObject, base.TackerObjectDictCompat,
|
||||
base.TackerPersistentObject):
|
||||
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'id': fields.UUIDField(nullable=False),
|
||||
'tenant_id': fields.UUIDField(nullable=False),
|
||||
'name': fields.StringField(nullable=False),
|
||||
'vnfd_id': fields.UUIDField(nullable=False),
|
||||
'instance_id': fields.StringField(nullable=True),
|
||||
'mgmt_ip_address': fields.StringField(nullable=True),
|
||||
'status': fields.StringField(nullable=True),
|
||||
'description': fields.StringField(nullable=True),
|
||||
'placement_attr': fields.StringField(nullable=True),
|
||||
'vim_id': fields.StringField(nullable=False),
|
||||
'error_reason': fields.StringField(nullable=True),
|
||||
'vnf_attribute': fields.ObjectField(
|
||||
'VNFAttribute', nullable=True),
|
||||
'vnfd': fields.ObjectField('VNFD', nullable=True),
|
||||
}
|
||||
|
||||
@base.remotable
|
||||
def save(self):
|
||||
updates = self.obj_clone()
|
||||
_vnf_update(self._context, updates)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def vnf_index_list(cls, id, context):
|
||||
# get vnf_instance data
|
||||
expected_attrs = ["vnf_attribute", "vnfd"]
|
||||
db_vnf = _vnf_get(context, id, columns_to_join=expected_attrs)
|
||||
return db_vnf
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class VnfList(ovoo_base.ObjectListBase, base.TackerObject):
|
||||
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('VNF')
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def vnf_index_list(cls, id, context):
|
||||
# get vnf_instance data
|
||||
expected_attrs = ["vnf_attribute", "vnfd"]
|
||||
db_vnf = _vnf_get(context, id, columns_to_join=expected_attrs)
|
||||
return db_vnf
|
|
@ -28,10 +28,13 @@ from tacker.common import utils
|
|||
from tacker.db import api as db_api
|
||||
from tacker.db.db_sqlalchemy import api
|
||||
from tacker.db.db_sqlalchemy import models
|
||||
from tacker.db.vnfm import vnfm_db
|
||||
from tacker import objects
|
||||
from tacker.objects import base
|
||||
from tacker.objects import fields
|
||||
from tacker.objects import vnf_instantiated_info
|
||||
from tacker.objects import vnf_package as vnf_package_obj
|
||||
from tacker.objects import vnf_package_vnfd as vnf_package_vnfd
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -149,6 +152,151 @@ def _wrap_object_error(method):
|
|||
return wrapper
|
||||
|
||||
|
||||
@db_api.context_manager.reader
|
||||
def _get_vnf_instance(context, id):
|
||||
vnf_instance = api.model_query(
|
||||
context, models.VnfInstance).filter_by(
|
||||
vnfd_id=id).first()
|
||||
return vnf_instance
|
||||
|
||||
|
||||
@db_api.context_manager.reader
|
||||
def _vnf_instance_get(context, vnfd_id, columns_to_join=None):
|
||||
query = api.model_query(context, models.VnfInstance, read_deleted="no",
|
||||
project_only=True).filter_by(vnfd_id=vnfd_id)
|
||||
|
||||
if columns_to_join:
|
||||
for column in columns_to_join:
|
||||
query = query.options(joinedload(column))
|
||||
|
||||
return query.first()
|
||||
|
||||
|
||||
def _merge_vim_connection_info(
|
||||
pre_vim_connection_info_list,
|
||||
update_vim_connection_info_list):
|
||||
|
||||
def update_nested_element(pre_data, update_data):
|
||||
for key, val in update_data.items():
|
||||
if not isinstance(val, dict):
|
||||
pre_data[key] = val
|
||||
continue
|
||||
|
||||
if key in pre_data:
|
||||
pre_data[key].update(val)
|
||||
else:
|
||||
pre_data.update({key: val})
|
||||
|
||||
result = []
|
||||
clone_pre_list = copy.deepcopy(pre_vim_connection_info_list)
|
||||
|
||||
for update_vim_connection in update_vim_connection_info_list:
|
||||
pre_data = None
|
||||
for i in range(0, len(clone_pre_list) - 1):
|
||||
if clone_pre_list[i].id == update_vim_connection.get('id'):
|
||||
pre_data = clone_pre_list.pop(i)
|
||||
|
||||
if pre_data is None:
|
||||
# new elm.
|
||||
result.append(objects.VimConnectionInfo._from_dict(
|
||||
update_vim_connection))
|
||||
continue
|
||||
|
||||
convert_dict = pre_data.to_dict()
|
||||
update_nested_element(convert_dict, update_vim_connection)
|
||||
result.append(objects.VimConnectionInfo._from_dict(
|
||||
convert_dict))
|
||||
|
||||
# Reflecting unupdated data
|
||||
result.extend(clone_pre_list)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@db_api.context_manager.writer
|
||||
def _update_vnf_instances(
|
||||
context,
|
||||
vnf_lcm_opoccs,
|
||||
body_data,
|
||||
vnfd_pkg_data,
|
||||
vnfd_id):
|
||||
updated_values = {}
|
||||
updated_values['vnf_instance_name'] = body_data.get('vnf_instance_name')
|
||||
updated_values['vnf_instance_description'] = body_data.get(
|
||||
'vnf_instance_description')
|
||||
|
||||
# get vnf_instances
|
||||
vnf_instance = _get_vnf_instance(context, vnfd_id)
|
||||
if body_data.get('metadata'):
|
||||
vnf_instance.vnf_metadata.update(body_data.get('metadata'))
|
||||
updated_values['vnf_metadata'] = vnf_instance.vnf_metadata
|
||||
|
||||
if body_data.get('vim_connection_info'):
|
||||
merge_vim_connection_info = _merge_vim_connection_info(
|
||||
vnf_instance.vim_connection_info,
|
||||
body_data.get('vim_connection_info'))
|
||||
|
||||
updated_values['vim_connection_info'] = merge_vim_connection_info
|
||||
|
||||
if body_data.get('vnfd_id'):
|
||||
updated_values['vnfd_id'] = body_data.get('vnfd_id')
|
||||
updated_values['vnf_provider'] = vnfd_pkg_data.get('vnf_provider')
|
||||
updated_values['vnf_product_name'] = vnfd_pkg_data.get(
|
||||
'vnf_product_name')
|
||||
updated_values['vnf_software_version'] = vnfd_pkg_data.get(
|
||||
'vnf_software_version')
|
||||
|
||||
api.model_query(context, models.VnfInstance). \
|
||||
filter_by(id=vnf_lcm_opoccs.get('vnf_instance_id')). \
|
||||
update(updated_values, synchronize_session=False)
|
||||
|
||||
vnf_now = timeutils.utcnow()
|
||||
if body_data.get('vnfd_id'):
|
||||
# update vnf
|
||||
updated_values = {'vnfd_id': body_data.get('vnfd_id'),
|
||||
'updated_at': vnf_now
|
||||
}
|
||||
api.model_query(context, vnfm_db.VNF).\
|
||||
filter_by(id=vnf_lcm_opoccs.get('vnf_instance_id')). \
|
||||
update(updated_values, synchronize_session=False)
|
||||
|
||||
# get vnf_packages
|
||||
id = vnfd_pkg_data.get('package_uuid')
|
||||
try:
|
||||
vnf_package = vnf_package_obj.VnfPackage.get_by_id(context, id)
|
||||
except exceptions.VnfPackageNotFound:
|
||||
raise exceptions.VnfPackageNotFound(id=id)
|
||||
|
||||
if vnf_package.usage_state == 'NOT_IN_USE':
|
||||
# update vnf_packages
|
||||
now = timeutils.utcnow()
|
||||
updated_values = {'usage_state': 'IN_USE',
|
||||
'updated_at': now
|
||||
}
|
||||
api.model_query(context, models.VnfPackage).\
|
||||
filter_by(id=id). \
|
||||
update(updated_values, synchronize_session=False)
|
||||
|
||||
# get vnf_instances
|
||||
vnf_instance = _get_vnf_instance(context, vnfd_id)
|
||||
|
||||
if not vnf_instance:
|
||||
# get vnf_package_vnfd
|
||||
vnfd_data = vnf_package_vnfd.VnfPackageVnfd.get_by_vnfdId(
|
||||
context, vnfd_id)
|
||||
|
||||
# update vnf_packages
|
||||
now = timeutils.utcnow()
|
||||
updated_values = {'usage_state': 'NOT_IN_USE',
|
||||
'updated_at': now
|
||||
}
|
||||
api.model_query(context, models.VnfPackage).\
|
||||
filter_by(id=vnfd_data.package_uuid). \
|
||||
update(updated_values, synchronize_session=False)
|
||||
|
||||
return vnf_now
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class VnfInstance(base.TackerObject, base.TackerPersistentObject,
|
||||
base.TackerObjectDictCompat):
|
||||
|
@ -171,7 +319,6 @@ class VnfInstance(base.TackerObject, base.TackerPersistentObject,
|
|||
'vim_connection_info': fields.ListOfObjectsField(
|
||||
'VimConnectionInfo', nullable=True, default=[]),
|
||||
'tenant_id': fields.StringField(nullable=False),
|
||||
'vnf_pkg_id': fields.StringField(nullable=False),
|
||||
'vnf_metadata': fields.DictOfStringsField(nullable=True, default={}),
|
||||
'instantiated_vnf_info': fields.ObjectField('InstantiatedVnfInfo',
|
||||
nullable=True, default=None)
|
||||
|
@ -329,7 +476,6 @@ class VnfInstance(base.TackerObject, base.TackerPersistentObject,
|
|||
'vnf_product_name': self.vnf_product_name,
|
||||
'vnf_software_version': self.vnf_software_version,
|
||||
'vnfd_version': self.vnfd_version,
|
||||
'vnf_pkg_id': self.vnf_pkg_id,
|
||||
'vnf_metadata': self.vnf_metadata}
|
||||
|
||||
if (self.instantiation_state == fields.VnfInstanceState.INSTANTIATED
|
||||
|
@ -344,6 +490,23 @@ class VnfInstance(base.TackerObject, base.TackerPersistentObject,
|
|||
|
||||
return data
|
||||
|
||||
@base.remotable
|
||||
def update(
|
||||
self,
|
||||
context,
|
||||
vnf_lcm_opoccs,
|
||||
body_data,
|
||||
vnfd_pkg_data,
|
||||
vnfd_id):
|
||||
|
||||
# update vnf_instances
|
||||
return _update_vnf_instances(
|
||||
context,
|
||||
vnf_lcm_opoccs,
|
||||
body_data,
|
||||
vnfd_pkg_data,
|
||||
vnfd_id)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_id(cls, context, id):
|
||||
expected_attrs = ["instantiated_vnf_info"]
|
||||
|
@ -380,3 +543,19 @@ class VnfInstanceList(ovoo_base.ObjectListBase, base.TackerObject):
|
|||
|
||||
return _make_vnf_instance_list(context, cls(), db_vnf_instances,
|
||||
expected_attrs)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def vnf_instance_list(cls, vnfd_id, context):
|
||||
# get vnf_instance data
|
||||
expected_attrs = ["instantiated_vnf_info"]
|
||||
db_vnf_instances = _vnf_instance_get(context, vnfd_id,
|
||||
columns_to_join=expected_attrs)
|
||||
|
||||
vnf_instance_cls = VnfInstance
|
||||
vnf_instance_data = ""
|
||||
vnf_instance_obj = vnf_instance_cls._from_db_object(
|
||||
context, vnf_instance_cls(context), db_vnf_instances,
|
||||
expected_attrs=expected_attrs)
|
||||
vnf_instance_data = vnf_instance_obj
|
||||
|
||||
return vnf_instance_data
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# under the License.
|
||||
|
||||
from oslo_db import exception as db_exc
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker.common import exceptions
|
||||
|
@ -23,6 +24,8 @@ from tacker.db.db_sqlalchemy import models
|
|||
from tacker.objects import base
|
||||
from tacker.objects import fields
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@db_api.context_manager.writer
|
||||
def _vnf_package_vnfd_create(context, values):
|
||||
|
@ -39,6 +42,49 @@ def _vnf_package_vnfd_create(context, values):
|
|||
return vnf_package_vnfd
|
||||
|
||||
|
||||
@db_api.context_manager.reader
|
||||
def _get_vnf_package_vnfd(context, id, package_uuid=None, del_flg=None):
|
||||
if package_uuid and not del_flg:
|
||||
query = api.model_query(
|
||||
context,
|
||||
models.VnfPackageVnfd).filter_by(
|
||||
package_uuid=id).filter_by(
|
||||
deleted=0)
|
||||
elif package_uuid and del_flg:
|
||||
query = api.model_query(
|
||||
context, models.VnfPackageVnfd).filter_by(
|
||||
package_uuid=id)
|
||||
else:
|
||||
query = api.model_query(
|
||||
context,
|
||||
models.VnfPackageVnfd).filter_by(
|
||||
vnfd_id=id).filter_by(
|
||||
deleted=0)
|
||||
try:
|
||||
result = query.all()
|
||||
result_line = ""
|
||||
for line in result:
|
||||
result_line = line
|
||||
|
||||
except Exception:
|
||||
LOG.error("select vnf_package_vnfd failed")
|
||||
|
||||
if result_line:
|
||||
return result_line
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
@db_api.context_manager.writer
|
||||
def _vnf_package_vnfd_delete(context, id):
|
||||
try:
|
||||
api.model_query(
|
||||
context, models.VnfPackageVnfd).filter_by(
|
||||
package_uuid=id).delete()
|
||||
except Exception:
|
||||
LOG.error("delete vnf_package_vnfd failed")
|
||||
|
||||
|
||||
@db_api.context_manager.reader
|
||||
def _vnf_package_vnfd_get_by_id(context, vnfd_id):
|
||||
|
||||
|
@ -59,6 +105,38 @@ def _vnf_package_vnfd_get_by_id(context, vnfd_id):
|
|||
return result
|
||||
|
||||
|
||||
def _vnf_package_vnfd_get_by_packageId(context, packageId):
|
||||
|
||||
query = api.model_query(
|
||||
context,
|
||||
models.VnfPackageVnfd,
|
||||
read_deleted="no",
|
||||
project_only=True).filter_by(
|
||||
package_uuid=packageId)
|
||||
|
||||
result = query.first()
|
||||
|
||||
if not result:
|
||||
return None
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@db_api.context_manager.reader
|
||||
def _vnf_package_vnfd_get_by_vnfdId(context, vnfdId):
|
||||
query = api.model_query(context,
|
||||
models.VnfPackageVnfd,
|
||||
read_deleted="no",
|
||||
project_only=True).filter_by(vnfd_id=vnfdId)
|
||||
|
||||
result = query.first()
|
||||
|
||||
if not result:
|
||||
return None
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@db_api.context_manager.reader
|
||||
def _get_vnf_package_vnfd_by_vnfid(context, vnfpkgid):
|
||||
|
||||
|
@ -127,10 +205,18 @@ class VnfPackageVnfd(base.TackerObject, base.TackerObjectDictCompat,
|
|||
self._context, updates)
|
||||
self._from_db_object(self._context, self, db_vnf_package_vnfd)
|
||||
|
||||
@base.remotable
|
||||
def get_vnf_package_vnfd(self, id, package_uuid=None, del_flg=None):
|
||||
return _get_vnf_package_vnfd(self._context, id, package_uuid, del_flg)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_vnf_package_vnfd_by_vnfid(self, context, vnfid):
|
||||
return _get_vnf_package_vnfd_by_vnfid(context, vnfid)
|
||||
|
||||
@base.remotable
|
||||
def delete(self, id):
|
||||
_vnf_package_vnfd_delete(self._context, id)
|
||||
|
||||
@classmethod
|
||||
def obj_from_db_obj(cls, context, db_obj):
|
||||
return cls._from_db_object(context, cls(), db_obj)
|
||||
|
@ -139,3 +225,19 @@ class VnfPackageVnfd(base.TackerObject, base.TackerObjectDictCompat,
|
|||
def get_by_id(cls, context, id):
|
||||
db_vnf_package_vnfd = _vnf_package_vnfd_get_by_id(context, id)
|
||||
return cls._from_db_object(context, cls(), db_vnf_package_vnfd)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_vnfdId(cls, context, id):
|
||||
db_vnf_package_vnfd = _vnf_package_vnfd_get_by_vnfdId(
|
||||
context, id)
|
||||
if not db_vnf_package_vnfd:
|
||||
return db_vnf_package_vnfd
|
||||
return cls._from_db_object(context, cls(), db_vnf_package_vnfd)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_packageId(cls, context, id):
|
||||
db_vnf_package_vnfd = _vnf_package_vnfd_get_by_packageId(
|
||||
context, id)
|
||||
if not db_vnf_package_vnfd:
|
||||
return db_vnf_package_vnfd
|
||||
return cls._from_db_object(context, cls(), db_vnf_package_vnfd)
|
||||
|
|
|
@ -110,6 +110,17 @@ rules = [
|
|||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=VNFLCM % 'update_vnf',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description="Update an Individual VNF instance.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'PATCH',
|
||||
'path': '/vnflcm/v1/vnf_instances/{vnfInstanceId}'
|
||||
}
|
||||
]
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -15,9 +15,11 @@
|
|||
|
||||
import datetime
|
||||
import iso8601
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from tacker import objects
|
||||
from tacker.objects import fields
|
||||
from tacker.tests import constants
|
||||
import tempfile
|
||||
import uuid
|
||||
import yaml
|
||||
|
@ -25,6 +27,7 @@ import zipfile
|
|||
|
||||
from oslo_config import cfg
|
||||
|
||||
from tacker.db.db_sqlalchemy import models
|
||||
from tacker.tests import utils
|
||||
from tacker.tests import uuidsentinel
|
||||
|
||||
|
@ -150,3 +153,137 @@ def get_expected_vnfd_data(zip_file=None):
|
|||
|
||||
shutil.rmtree(csar_temp_dir)
|
||||
return file_path_and_data
|
||||
|
||||
|
||||
def fake_vnf_package_vnfd_model_dict(**updates):
|
||||
vnfd = {
|
||||
'package_uuid': uuidsentinel.package_uuid,
|
||||
'deleted': False,
|
||||
'deleted_at': None,
|
||||
'updated_at': None,
|
||||
'created_at': datetime.datetime(2020, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
'vnf_product_name': 'Sample VNF',
|
||||
'vnf_provider': 'test vnf provider',
|
||||
'vnf_software_version': '1.0',
|
||||
'vnfd_id': uuidsentinel.vnfd_id,
|
||||
'vnfd_version': '1.0',
|
||||
'id': constants.UUID,
|
||||
}
|
||||
|
||||
if updates:
|
||||
vnfd.update(updates)
|
||||
|
||||
return vnfd
|
||||
|
||||
|
||||
def return_vnf_package_vnfd():
|
||||
model_obj = models.VnfPackageVnfd()
|
||||
model_obj.update(fake_vnf_package_vnfd_model_dict())
|
||||
return model_obj
|
||||
|
||||
|
||||
def _model_non_instantiated_vnf_instance(**updates):
|
||||
vnf_instance = {
|
||||
'created_at': datetime.datetime(2020, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
'deleted': False,
|
||||
'deleted_at': None,
|
||||
'id': uuidsentinel.vnf_instance_id,
|
||||
'instantiated_vnf_info': None,
|
||||
'instantiation_state': fields.VnfInstanceState.NOT_INSTANTIATED,
|
||||
'updated_at': None,
|
||||
'vim_connection_info': [],
|
||||
'vnf_instance_description': 'Vnf instance description',
|
||||
'vnf_instance_name': 'Vnf instance name',
|
||||
'vnf_product_name': 'Sample VNF',
|
||||
'vnf_provider': 'Vnf provider',
|
||||
'vnf_software_version': '1.0',
|
||||
'tenant_id': uuidsentinel.tenant_id,
|
||||
'vnfd_id': uuidsentinel.vnfd_id,
|
||||
'vnfd_version': '1.0'}
|
||||
|
||||
if updates:
|
||||
vnf_instance.update(**updates)
|
||||
|
||||
return vnf_instance
|
||||
|
||||
|
||||
def return_vnf_instance(
|
||||
instantiated_state=fields.VnfInstanceState.NOT_INSTANTIATED,
|
||||
scale_status=None,
|
||||
**updates):
|
||||
|
||||
if instantiated_state == fields.VnfInstanceState.NOT_INSTANTIATED:
|
||||
data = _model_non_instantiated_vnf_instance(**updates)
|
||||
data['instantiation_state'] = instantiated_state
|
||||
vnf_instance_obj = objects.VnfInstance(**data)
|
||||
|
||||
elif scale_status:
|
||||
data = _model_non_instantiated_vnf_instance(**updates)
|
||||
data['instantiation_state'] = instantiated_state
|
||||
vnf_instance_obj = objects.VnfInstance(**data)
|
||||
|
||||
get_instantiated_vnf_info = {
|
||||
'flavour_id': uuidsentinel.flavour_id,
|
||||
'vnf_state': 'STARTED',
|
||||
'instance_id': uuidsentinel.instance_id
|
||||
}
|
||||
instantiated_vnf_info = get_instantiated_vnf_info
|
||||
|
||||
s_status = {"aspect_id": "SP1", "scale_level": 1}
|
||||
scale_status = objects.ScaleInfo(**s_status)
|
||||
|
||||
instantiated_vnf_info.update(
|
||||
{"ext_cp_info": [],
|
||||
'ext_virtual_link_info': [],
|
||||
'ext_managed_virtual_link_info': [],
|
||||
'vnfc_resource_info': [],
|
||||
'vnf_virtual_link_resource_info': [],
|
||||
'virtual_storage_resource_info': [],
|
||||
"flavour_id": "simple",
|
||||
"scale_status": [scale_status],
|
||||
"vnf_instance_id": "171f3af2-a753-468a-b5a7-e3e048160a79",
|
||||
"additional_params": {"key": "value"},
|
||||
'vnf_state': "STARTED"})
|
||||
info_data = objects.InstantiatedVnfInfo(**instantiated_vnf_info)
|
||||
|
||||
vnf_instance_obj.instantiated_vnf_info = info_data
|
||||
else:
|
||||
data = _model_non_instantiated_vnf_instance(**updates)
|
||||
data['instantiation_state'] = instantiated_state
|
||||
vnf_instance_obj = objects.VnfInstance(**data)
|
||||
inst_vnf_info = objects.InstantiatedVnfInfo.obj_from_primitive({
|
||||
"ext_cp_info": [],
|
||||
'ext_virtual_link_info': [],
|
||||
'ext_managed_virtual_link_info': [],
|
||||
'vnfc_resource_info': [],
|
||||
'vnf_virtual_link_resource_info': [],
|
||||
'virtual_storage_resource_info': [],
|
||||
"flavour_id": "simple",
|
||||
"additional_params": {"key": "value"},
|
||||
'vnf_state': "STARTED"}, None)
|
||||
|
||||
vnf_instance_obj.instantiated_vnf_info = inst_vnf_info
|
||||
|
||||
return vnf_instance_obj
|
||||
|
||||
|
||||
def _get_vnf(**updates):
|
||||
vnf_data = {
|
||||
'tenant_id': uuidsentinel.tenant_id,
|
||||
'name': "fake_name",
|
||||
'vnfd_id': uuidsentinel.vnfd_id,
|
||||
'vnf_instance_id': uuidsentinel.instance_id,
|
||||
'mgmt_ip_address': "fake_mgmt_ip_address",
|
||||
'status': 'ACTIVE',
|
||||
'description': 'fake_description',
|
||||
'placement_attr': 'fake_placement_attr',
|
||||
'vim_id': 'uuidsentinel.vim_id',
|
||||
'error_reason': 'fake_error_reason',
|
||||
}
|
||||
|
||||
if updates:
|
||||
vnf_data.update(**updates)
|
||||
|
||||
return vnf_data
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
# limitations under the License.
|
||||
|
||||
import base64
|
||||
import datetime
|
||||
import fixtures
|
||||
import iso8601
|
||||
import json
|
||||
import os
|
||||
import requests
|
||||
|
@ -33,6 +35,7 @@ from tacker.common import csar_utils
|
|||
from tacker.common import exceptions
|
||||
from tacker.conductor import conductor_server
|
||||
from tacker import context
|
||||
from tacker import context as t_context
|
||||
from tacker.glance_store import store as glance_store
|
||||
from tacker import objects
|
||||
from tacker.objects import fields
|
||||
|
@ -78,6 +81,9 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
|
|||
self.vnf_package = self._create_vnf_package()
|
||||
self.instance_uuid = uuidsentinel.instance_id
|
||||
self.temp_dir = self.useFixture(fixtures.TempDir()).path
|
||||
self.body_data = self._create_body_data()
|
||||
self.vnf_lcm_opoccs = self._create_vnf_lcm_opoccs()
|
||||
self.vnfd_pkg_data = self._create_vnfd_pkg_data()
|
||||
|
||||
def _mock_vnfm_plugin(self):
|
||||
self.vnfm_plugin = mock.Mock(wraps=FakeVNFMPlugin())
|
||||
|
@ -120,6 +126,40 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
|
|||
|
||||
return [DummyLcmSubscription(auth_params)]
|
||||
|
||||
def _create_body_data(self):
|
||||
body_data = {}
|
||||
body_data['vnf_instance_name'] = "new_instance_name"
|
||||
body_data['vnf_instance_description'] = "new_instance_discription"
|
||||
body_data['vnfd_id'] = "2c69a161-0000-4b0f-bcf8-391f8fc76600"
|
||||
body_data['vnf_configurable_properties'] = {"test": "test_value"}
|
||||
body_data['vnfc_info_modifications_delete_ids'] = ["test1"]
|
||||
return body_data
|
||||
|
||||
def _create_vnf_lcm_opoccs(self):
|
||||
vnf_lcm_opoccs = {
|
||||
'vnf_instance_id': uuidsentinel.vnf_instance_id,
|
||||
'id': uuidsentinel.id,
|
||||
'state_entered_time': datetime.datetime(
|
||||
1900, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
'operationParams': {
|
||||
"key": "value"}}
|
||||
return vnf_lcm_opoccs
|
||||
|
||||
def _create_vnfd_pkg_data(self):
|
||||
vnfd_pkg_data = {}
|
||||
vnfd_pkg_data['vnf_provider'] = fakes.return_vnf_package_vnfd().get(
|
||||
'vnf_provider')
|
||||
vnfd_pkg_data['vnf_product_name'] =\
|
||||
fakes.return_vnf_package_vnfd().get('vnf_product_name')
|
||||
vnfd_pkg_data['vnf_software_version'] =\
|
||||
fakes.return_vnf_package_vnfd().get('vnf_software_version')
|
||||
vnfd_pkg_data['vnfd_version'] = fakes.return_vnf_package_vnfd().get(
|
||||
'vnfd_version')
|
||||
vnfd_pkg_data['package_uuid'] = fakes.return_vnf_package_vnfd().get(
|
||||
'package_uuid')
|
||||
return vnfd_pkg_data
|
||||
|
||||
def assert_auth_basic(
|
||||
self,
|
||||
acutual_request,
|
||||
|
@ -958,3 +998,24 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
|
|||
|
||||
self.assertEqual(result, 99)
|
||||
mock_subscriptions_get.assert_called()
|
||||
|
||||
@mock.patch.object(conductor_server, 'revert_update_lcm')
|
||||
@mock.patch.object(t_context.get_admin_context().session, "add")
|
||||
@mock.patch.object(objects.vnf_lcm_op_occs.VnfLcmOpOcc, "save")
|
||||
@mock.patch.object(objects.VnfInstance, "update")
|
||||
@mock.patch.object(objects.vnf_lcm_op_occs.VnfLcmOpOcc, "create")
|
||||
def test_update(self, mock_create, mock_update, mock_save, mock_add,
|
||||
mock_revert):
|
||||
mock_create.return_value = "OK"
|
||||
mock_update.return_value = datetime.datetime(
|
||||
1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||
mock_add.return_value = "OK"
|
||||
mock_save.return_value = "OK"
|
||||
vnfd_id = "2c69a161-0000-4b0f-bcf8-391f8fc76600"
|
||||
|
||||
self.conductor.update(
|
||||
self.context,
|
||||
self.vnf_lcm_opoccs,
|
||||
self.body_data,
|
||||
self.vnfd_pkg_data,
|
||||
vnfd_id)
|
||||
|
|
|
@ -45,6 +45,14 @@ software_image = {
|
|||
'metadata': {'key1': 'value1'}
|
||||
}
|
||||
|
||||
artifact_data = {
|
||||
'file_name': 'test', 'type': 'test',
|
||||
'algorithm': 'sha-256',
|
||||
'hash': 'b9c3036539fd7a5f87a1bf38eb05fdde8b556a1'
|
||||
'a7e664dbeda90ed3cd74b4f9d',
|
||||
'metadata': {'key1': 'value1'}
|
||||
}
|
||||
|
||||
artifacts = {
|
||||
'json_data': 'test data',
|
||||
'type': 'tosca.artifacts.nfv.SwImage',
|
||||
|
@ -94,6 +102,42 @@ subscription_data = {
|
|||
'created_at': "2020-06-11 09:39:58"
|
||||
}
|
||||
|
||||
vnfd_data = {
|
||||
"tenant_id": uuidsentinel.tenant_id,
|
||||
'name': 'test',
|
||||
'description': 'test_description',
|
||||
'mgmt_driver': 'test_mgmt_driver'
|
||||
}
|
||||
|
||||
vnfd_attribute = {
|
||||
'key': 'test_key',
|
||||
'value': 'test_value',
|
||||
}
|
||||
|
||||
lcm_op_occs_data = {
|
||||
"tenant_id": uuidsentinel.tenant_id,
|
||||
'operation_state': 'PROCESSING',
|
||||
'state_entered_time': datetime.datetime(1900, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
'start_time': datetime.datetime(1900, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
'operation': 'MODIFY_INFO',
|
||||
'is_automatic_invocation': 0,
|
||||
'is_cancel_pending': 0,
|
||||
}
|
||||
|
||||
vim_data = {
|
||||
'id': uuidsentinel.vim_id,
|
||||
'type': 'test_type',
|
||||
"tenant_id": uuidsentinel.tenant_id,
|
||||
'name': "test_name",
|
||||
'description': "test_description",
|
||||
'placement_attr': "test_placement_attr",
|
||||
'shared': 0,
|
||||
'status': "REACHABLE",
|
||||
'is_default': 0
|
||||
}
|
||||
|
||||
fake_vnf_package_response = copy.deepcopy(vnf_package_data)
|
||||
fake_vnf_package_response.pop('user_data')
|
||||
fake_vnf_package_response.update({'id': uuidsentinel.package_uuid})
|
||||
|
@ -430,3 +474,16 @@ def get_changed_info_data():
|
|||
"vnf_software_version": "1.0",
|
||||
"vnfd_version": "MME_1.0"
|
||||
}
|
||||
|
||||
|
||||
def get_vnf(vnfd_id, vim_id):
|
||||
return {
|
||||
'tenant_id': uuidsentinel.tenant_id,
|
||||
'name': "test_name",
|
||||
'vnfd_id': vnfd_id,
|
||||
'mgmt_ip_address': "test_mgmt_ip_address",
|
||||
'status': "ACTIVE",
|
||||
'description': "test_description",
|
||||
'placement_attr': "test_placement_attr",
|
||||
'vim_id': vim_id
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# 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 unittest import mock
|
||||
|
||||
import datetime
|
||||
import iso8601
|
||||
from tacker import context
|
||||
from tacker.db.nfvo import nfvo_db
|
||||
from tacker import objects
|
||||
from tacker.tests.unit.db.base import SqlTestCase
|
||||
from tacker.tests.unit.objects import fakes
|
||||
from tacker.tests import uuidsentinel
|
||||
|
||||
|
||||
class TestVnf(SqlTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestVnf, self).setUp()
|
||||
self.context = context.get_admin_context()
|
||||
self.vnfd = self._create_vnfd()
|
||||
self.vims = self._create_vims()
|
||||
|
||||
def _create_vnfd(self):
|
||||
vnfd_obj = objects.Vnfd(context=self.context, **fakes.vnfd_data)
|
||||
vnfd_obj.create()
|
||||
|
||||
return vnfd_obj
|
||||
|
||||
def _create_vims(self):
|
||||
vim_obj = nfvo_db.Vim(**fakes.vim_data)
|
||||
|
||||
return vim_obj
|
||||
|
||||
def test_save(self):
|
||||
vnf_data = fakes.get_vnf(self.vnfd.id, self.vims.id)
|
||||
vnf_obj = objects.vnf.VNF(context=self.context, **vnf_data)
|
||||
|
||||
vnf_obj.id = uuidsentinel.instance_id
|
||||
vnf_obj.status = 'ERROR'
|
||||
vnf_obj.updated_at = datetime.datetime(
|
||||
1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||
vnf_obj.save()
|
||||
|
||||
self.assertEqual('ERROR', vnf_obj.status)
|
||||
|
||||
@mock.patch('tacker.objects.vnf._vnf_get')
|
||||
def test_vnf_index_list(self, mock_vnf_get):
|
||||
vnf_data = fakes.get_vnf(self.vnfd.id, self.vims.id)
|
||||
vnf_obj = objects.vnf.VNF(context=self.context, **vnf_data)
|
||||
|
||||
vnf_obj.id = uuidsentinel.instance_id
|
||||
vnf_obj.updated_at = datetime.datetime(
|
||||
1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||
vnf_obj.save()
|
||||
|
||||
mock_vnf_get.return_value = vnf_obj
|
||||
vnf_data_result = vnf_obj.vnf_index_list(vnf_obj.id, self.context)
|
||||
self.assertEqual('ACTIVE', vnf_data_result.status)
|
|
@ -18,6 +18,8 @@ from unittest import mock
|
|||
from tacker.common import exceptions
|
||||
from tacker import context
|
||||
from tacker.db import api as sqlalchemy_api
|
||||
from tacker.db.db_sqlalchemy import api
|
||||
from tacker.db.nfvo import nfvo_db
|
||||
from tacker import objects
|
||||
from tacker.tests.unit.db.base import SqlTestCase
|
||||
from tacker.tests.unit.objects import fakes
|
||||
|
@ -26,12 +28,41 @@ from tacker.tests import uuidsentinel
|
|||
get_engine = sqlalchemy_api.get_engine
|
||||
|
||||
|
||||
class FakeApiModelQuery:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
callback_filter_by=None,
|
||||
callback_update=None,
|
||||
callback_options=None):
|
||||
self.callback_filter_by = callback_filter_by
|
||||
self.callback_update = callback_update
|
||||
self.callback_options = callback_options
|
||||
|
||||
def options(self, *args, **kwargs):
|
||||
if self.callback_options:
|
||||
self.callback_options(*args, **kwargs)
|
||||
|
||||
def filter_by(self, *args, **kwargs):
|
||||
if self.callback_filter_by:
|
||||
self.callback_filter_by(*args, **kwargs)
|
||||
return self
|
||||
|
||||
def update(self, *args, **kwargs):
|
||||
if self.callback_update:
|
||||
self.callback_update(*args, **kwargs)
|
||||
return self
|
||||
|
||||
|
||||
class TestVnfInstance(SqlTestCase):
|
||||
|
||||
maxDiff = None
|
||||
|
||||
def setUp(self):
|
||||
super(TestVnfInstance, self).setUp()
|
||||
self.context = context.get_admin_context()
|
||||
self.vnf_package = self._create_and_upload_vnf_package()
|
||||
self.vims = nfvo_db.Vim(**fakes.vim_data)
|
||||
self.engine = get_engine()
|
||||
self.conn = self.engine.connect()
|
||||
|
||||
|
@ -157,3 +188,156 @@ class TestVnfInstance(SqlTestCase):
|
|||
vnf_instance_obj = objects.VnfInstance(context=self.context)
|
||||
self.assertRaises(exceptions.ObjectActionError,
|
||||
vnf_instance_obj.destroy, self.context)
|
||||
|
||||
@mock.patch('tacker.objects.vnf_instance._get_vnf_instance')
|
||||
@mock.patch('tacker.objects.vnf_package.VnfPackage.get_by_id')
|
||||
@mock.patch.object(api, 'model_query')
|
||||
def test_update_vnf_instances(
|
||||
self,
|
||||
mock_model_query,
|
||||
mock_get_vnf_package,
|
||||
mock_get_vnf):
|
||||
|
||||
vnf_instance_data = fakes.fake_vnf_instance_model_dict(**{
|
||||
"vim_connection_info": [
|
||||
objects.VimConnectionInfo._from_dict({
|
||||
"id": "testid",
|
||||
"vim_id": "aaa",
|
||||
"vim_type": "openstack-1",
|
||||
"interface_info": {"endpoint": "endpoint"},
|
||||
"access_info": {"username": "xxxxx",
|
||||
"region": "region",
|
||||
"password": "password",
|
||||
"tenant": "tenant"}}),
|
||||
objects.VimConnectionInfo._from_dict({
|
||||
"id": "testid3",
|
||||
"vim_id": "ccc",
|
||||
"vim_type": "openstack-2",
|
||||
"interface_info": {"endpoint": "endpoint22"},
|
||||
"access_info": {"username": "xxxxx",
|
||||
"region": "region",
|
||||
"password": "password"}}),
|
||||
objects.VimConnectionInfo._from_dict({
|
||||
"id": "testid5",
|
||||
"vim_id": "eee",
|
||||
"vim_type": "openstack-4"})
|
||||
],
|
||||
"vnf_metadata": {"testkey": "test_value"}})
|
||||
vnf_instance = objects.VnfInstance(
|
||||
context=self.context, **vnf_instance_data)
|
||||
mock_get_vnf.return_value = \
|
||||
fakes.vnf_instance_model_object(vnf_instance)
|
||||
|
||||
def mock_filter(id=None):
|
||||
print('### mock_filter ###', id)
|
||||
|
||||
def mock_update(updated_values, synchronize_session=False):
|
||||
print('### mock_update ###', updated_values)
|
||||
|
||||
if 'vim_connection_info' not in updated_values:
|
||||
return
|
||||
|
||||
compar_updated_values = {}
|
||||
compar_updated_values['vnf_instance_name'] = "new_instance_name"
|
||||
compar_updated_values['vnf_instance_description'] = \
|
||||
"new_instance_discription"
|
||||
compar_updated_values['vnf_metadata'] = {
|
||||
"testkey": "test_value1", "testkey2": "test_value2"}
|
||||
compar_updated_values['vim_connection_info'] = [
|
||||
objects.VimConnectionInfo._from_dict({
|
||||
"id": "testid",
|
||||
"vim_id": "bbb",
|
||||
"vim_type": "openstack-1A",
|
||||
"interface_info": {"endpoint": "endpoint11"},
|
||||
"access_info": {"username": "xxxxx1",
|
||||
"region": "region1",
|
||||
"password": "password1",
|
||||
"tenant": "tenant1"}}),
|
||||
objects.VimConnectionInfo._from_dict({
|
||||
"id": "testid3",
|
||||
"vim_id": "ccc",
|
||||
"vim_type": "openstack-2",
|
||||
"interface_info": {"endpoint": "endpoint22"},
|
||||
"access_info": {"username": "xxxxx",
|
||||
"region": "region",
|
||||
"password": "password2",
|
||||
"tenant": "tenant2"}}),
|
||||
objects.VimConnectionInfo._from_dict({
|
||||
"id": "testid5",
|
||||
"vim_id": "eee",
|
||||
"vim_type": "openstack-4"}),
|
||||
objects.VimConnectionInfo._from_dict({
|
||||
"id": "testid7",
|
||||
"vim_id": "fff",
|
||||
"vim_type": "openstack-5A",
|
||||
"interface_info": {"endpoint": "endpoint55"},
|
||||
"access_info": {"username": "xxxxx5",
|
||||
"region": "region5",
|
||||
"password": "password5",
|
||||
"tenant": "tenant5"}})
|
||||
]
|
||||
compar_updated_values['vnfd_id'] = \
|
||||
"2c69a161-0000-4b0f-bcf8-391f8fc76600"
|
||||
compar_updated_values['vnf_provider'] = \
|
||||
self.vnf_package.get('vnf_provider')
|
||||
compar_updated_values['vnf_product_name'] = \
|
||||
self.vnf_package.get('vnf_product_name')
|
||||
compar_updated_values['vnf_software_version'] = \
|
||||
self.vnf_package.get('vnf_software_version')
|
||||
|
||||
expected_vci = sorted(compar_updated_values.pop(
|
||||
'vim_connection_info'), key=lambda x: x.id)
|
||||
actual_vci = sorted(
|
||||
updated_values.pop('vim_connection_info'),
|
||||
key=lambda x: x.id)
|
||||
for e, a in zip(expected_vci, actual_vci):
|
||||
self.assertDictEqual(
|
||||
e.to_dict(),
|
||||
a.to_dict())
|
||||
|
||||
self.assertDictEqual(
|
||||
compar_updated_values,
|
||||
updated_values)
|
||||
|
||||
fake_api_model_query = FakeApiModelQuery(
|
||||
callback_filter_by=mock_filter, callback_update=mock_update)
|
||||
mock_model_query.return_value = fake_api_model_query
|
||||
|
||||
vnf_lcm_opoccs = {}
|
||||
|
||||
body = {"vnf_instance_name": "new_instance_name",
|
||||
"vnf_instance_description": "new_instance_discription",
|
||||
"vnfd_id": "2c69a161-0000-4b0f-bcf8-391f8fc76600",
|
||||
"vnf_configurable_properties": {"test": "test_value1"},
|
||||
"vnfc_info_modifications_delete_ids": ["test1"],
|
||||
"metadata": {"testkey": "test_value1",
|
||||
"testkey2": "test_value2"},
|
||||
"vim_connection_info": [
|
||||
{"id": "testid",
|
||||
"vim_id": "bbb",
|
||||
"vim_type": "openstack-1A",
|
||||
"interface_info": {"endpoint": "endpoint11"},
|
||||
"access_info": {"username": "xxxxx1",
|
||||
"region": "region1",
|
||||
"password": "password1",
|
||||
"tenant": "tenant1"}},
|
||||
{"id": "testid3",
|
||||
"vim_type": "openstack-2",
|
||||
"access_info": {"password": "password2",
|
||||
"tenant": "tenant2"}},
|
||||
{"id": "testid7",
|
||||
"vim_id": "fff",
|
||||
"vim_type": "openstack-5A",
|
||||
"interface_info": {"endpoint": "endpoint55"},
|
||||
"access_info": {"username": "xxxxx5",
|
||||
"region": "region5",
|
||||
"password": "password5",
|
||||
"tenant": "tenant5"}},
|
||||
]}
|
||||
|
||||
vnf_instance.update(
|
||||
self.context,
|
||||
vnf_lcm_opoccs,
|
||||
body,
|
||||
self.vnf_package,
|
||||
self.vnf_package.id)
|
||||
|
|
|
@ -120,7 +120,6 @@ def _model_non_instantiated_vnf_instance(**updates):
|
|||
'tenant_id': uuidsentinel.tenant_id,
|
||||
'vnfd_id': uuidsentinel.vnfd_id,
|
||||
'vnfd_version': '1.0',
|
||||
'vnf_pkg_id': uuidsentinel.vnf_pkg_id,
|
||||
'vnf_metadata': {"key": "value"}}
|
||||
|
||||
if updates:
|
||||
|
@ -231,7 +230,6 @@ def _fake_vnf_instance_not_instantiated_response(
|
|||
'vnfdId': uuidsentinel.vnfd_id,
|
||||
'vnfdVersion': '1.0',
|
||||
'vnfSoftwareVersion': '1.0',
|
||||
'vnfPkgId': uuidsentinel.vnf_pkg_id,
|
||||
'id': uuidsentinel.vnf_instance_id,
|
||||
'metadata': {'key': 'value'}
|
||||
}
|
||||
|
|
|
@ -244,6 +244,7 @@ class TestController(base.TestCase):
|
|||
@mock.patch.object(objects.VnfPackage, 'get_by_id')
|
||||
@mock.patch('tacker.api.vnflcm.v1.controller.'
|
||||
'VnfLcmController._create_vnf')
|
||||
@mock.patch.object(objects.vnf_package.VnfPackage, 'save')
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM': FakeVNFMPlugin()})
|
||||
@mock.patch.object(objects.vnf_instance, '_vnf_instance_create')
|
||||
|
@ -252,57 +253,14 @@ class TestController(base.TestCase):
|
|||
self, mock_get_by_id,
|
||||
mock_vnf_instance_create,
|
||||
mock_get_service_plugins,
|
||||
mock_package_save,
|
||||
mock_private_create_vnf,
|
||||
mock_vnf_package_get_by_id,
|
||||
mock_update_package_usage_state,
|
||||
mock_get_vim):
|
||||
mock_get_by_id.return_value = fakes.return_vnf_package_vnfd()
|
||||
|
||||
updates = {'vnfd_id': uuidsentinel.vnfd_id,
|
||||
'vnf_instance_description': None,
|
||||
'vnf_instance_name': None,
|
||||
'vnf_pkg_id': uuidsentinel.vnf_pkg_id,
|
||||
'vnf_metadata': {"key": "value"}}
|
||||
mock_vnf_instance_create.return_value =\
|
||||
fakes.return_vnf_instance_model(**updates)
|
||||
|
||||
req = fake_request.HTTPRequest.blank('/vnf_instances')
|
||||
body = {'vnfdId': uuidsentinel.vnfd_id,
|
||||
'metadata': {"key": "value"}}
|
||||
req.body = jsonutils.dump_as_bytes(body)
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.method = 'POST'
|
||||
|
||||
# Call create API
|
||||
resp = req.get_response(self.app)
|
||||
|
||||
self.assertEqual(http_client.CREATED, resp.status_code)
|
||||
|
||||
updates = {'vnfInstanceDescription': None, 'vnfInstanceName': None}
|
||||
expected_vnf = fakes.fake_vnf_instance_response(**updates)
|
||||
location_header = ('http://localhost/vnflcm/v1/vnf_instances/%s'
|
||||
% resp.json['id'])
|
||||
|
||||
self.assertEqual(expected_vnf, resp.json)
|
||||
self.assertEqual(location_header, resp.headers['location'])
|
||||
|
||||
@mock.patch('tacker.api.vnflcm.v1.controller.'
|
||||
'VnfLcmController._create_vnf')
|
||||
@mock.patch.object(objects.VnfInstance, 'save')
|
||||
@mock.patch.object(vim_client.VimClient, "get_vim")
|
||||
@mock.patch.object(objects.vnf_package.VnfPackage, 'get_by_id')
|
||||
@mock.patch.object(objects.vnf_package.VnfPackage, 'save')
|
||||
@mock.patch.object(objects.vnf_instance, '_vnf_instance_create')
|
||||
@mock.patch.object(objects.vnf_package_vnfd.VnfPackageVnfd, 'get_by_id')
|
||||
def test_create_with_name_and_description(
|
||||
self, mock_get_by_id_package_vnfd,
|
||||
mock_vnf_instance_create, mock_package_save,
|
||||
mock_get_by_id_package, mock_get_vim,
|
||||
mock_save, mock_create_vnf):
|
||||
mock_get_vim.return_value = self.vim_info
|
||||
mock_get_by_id_package_vnfd.return_value = \
|
||||
fakes.return_vnf_package_vnfd()
|
||||
mock_get_by_id_package.return_value = \
|
||||
mock_get_by_id.return_value = fakes.return_vnf_package_vnfd()
|
||||
mock_vnf_package_get_by_id.return_value = \
|
||||
fakes.return_vnf_package_with_deployment_flavour()
|
||||
|
||||
updates = {'vnfd_id': uuidsentinel.vnfd_id,
|
||||
|
@ -1199,8 +1157,11 @@ class TestController(base.TestCase):
|
|||
constants.INVALID_UUID,
|
||||
resp.json['itemNotFound']['message'])
|
||||
|
||||
@ddt.data('PATCH', 'HEAD', 'PUT', 'POST')
|
||||
def test_show_invalid_http_method(self, http_method):
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM': FakeVNFMPlugin()})
|
||||
@ddt.data('HEAD', 'PUT', 'POST')
|
||||
def test_show_invalid_http_method(self, http_method,
|
||||
mock_get_service_plugins):
|
||||
req = fake_request.HTTPRequest.blank(
|
||||
'/vnf_instances/%s' % constants.UUID)
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
|
@ -1888,3 +1849,187 @@ class TestController(base.TestCase):
|
|||
resp = req.get_response(self.app)
|
||||
|
||||
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM': FakeVNFMPlugin()})
|
||||
@mock.patch.object(objects.VNF, "vnf_index_list")
|
||||
@mock.patch.object(objects.VnfInstanceList, "vnf_instance_list")
|
||||
@mock.patch.object(objects.VnfPackageVnfd, 'get_vnf_package_vnfd')
|
||||
@mock.patch.object(VNFLcmRPCAPI, "update")
|
||||
def test_update_vnf(
|
||||
self,
|
||||
mock_update,
|
||||
mock_vnf_package_vnf_get_vnf_package_vnfd,
|
||||
mock_vnf_instance_list,
|
||||
mock_vnf_index_list,
|
||||
mock_get_service_plugins):
|
||||
|
||||
mock_vnf_index_list.return_value = fakes._get_vnf()
|
||||
mock_vnf_instance_list.return_value = fakes.return_vnf_instance(
|
||||
fields.VnfInstanceState.INSTANTIATED)
|
||||
mock_vnf_package_vnf_get_vnf_package_vnfd.return_value =\
|
||||
fakes.return_vnf_package_vnfd()
|
||||
|
||||
body = {"vnfInstanceName": "new_instance_name",
|
||||
"vnfInstanceDescription": "new_instance_discription",
|
||||
"vnfdId": "2c69a161-0000-4b0f-bcf8-391f8fc76600",
|
||||
"vnfConfigurableProperties": {
|
||||
"test": "test_value"
|
||||
},
|
||||
"vnfcInfoModificationsDeleteIds": ["test1"],
|
||||
"metadata": {"testkey": "test_value"},
|
||||
"vimConnectionInfo": {"id": "testid"}}
|
||||
req = fake_request.HTTPRequest.blank(
|
||||
'/vnf_instances/%s' % constants.UUID)
|
||||
req.body = jsonutils.dump_as_bytes(body)
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.method = 'PATCH'
|
||||
|
||||
# Call Instantiate API
|
||||
resp = req.get_response(self.app)
|
||||
print(resp)
|
||||
self.assertEqual(http_client.ACCEPTED, resp.status_code)
|
||||
mock_update.assert_called_once()
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM': FakeVNFMPlugin()})
|
||||
@mock.patch.object(objects.VNF, "vnf_index_list")
|
||||
def test_update_vnf_none_vnf_data(
|
||||
self,
|
||||
mock_vnf_index_list,
|
||||
mock_get_service_plugins):
|
||||
|
||||
mock_vnf_index_list.return_value = ""
|
||||
|
||||
body = {"vnfInstanceName": "new_instance_name",
|
||||
"vnfInstanceDescription": "new_instance_discription",
|
||||
"vnfdId": "2c69a161-0000-4b0f-bcf8-391f8fc76600",
|
||||
"vnfConfigurableProperties": {
|
||||
"test": "test_value"
|
||||
},
|
||||
"vnfcInfoModificationsDeleteIds": ["test1"],
|
||||
"metadata": {"testkey": "test_value"},
|
||||
"vimConnectionInfo": {"id": "testid"}}
|
||||
req = fake_request.HTTPRequest.blank(
|
||||
'/vnf_instances/%s' % constants.UUID)
|
||||
req.body = jsonutils.dump_as_bytes(body)
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.method = 'PATCH'
|
||||
|
||||
msg = _("Can not find requested vnf data: %s") % constants.UUID
|
||||
res = self._make_problem_detail(msg, 404, title='Not Found')
|
||||
|
||||
resp = req.get_response(self.app)
|
||||
self.assertEqual(res.text, resp.text)
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM': FakeVNFMPlugin()})
|
||||
@mock.patch.object(objects.VNF, "vnf_index_list")
|
||||
def test_update_vnf_status_err(
|
||||
self,
|
||||
mock_vnf_index_list,
|
||||
mock_get_service_plugins):
|
||||
updates = {'status': 'ERROR'}
|
||||
mock_vnf_index_list.return_value = fakes._get_vnf(**updates)
|
||||
|
||||
body = {"vnfInstanceName": "new_instance_name",
|
||||
"vnfInstanceDescription": "new_instance_discription",
|
||||
"vnfdId": "2c69a161-0000-4b0f-bcf8-391f8fc76600",
|
||||
"vnfConfigurableProperties": {
|
||||
"test": "test_value"
|
||||
},
|
||||
"vnfcInfoModificationsDeleteIds": ["test1"],
|
||||
"metadata": {"testkey": "test_value"},
|
||||
"vimConnectionInfo": {"id": "testid"}}
|
||||
req = fake_request.HTTPRequest.blank(
|
||||
'/vnf_instances/%s' % constants.UUID)
|
||||
req.body = jsonutils.dump_as_bytes(body)
|
||||
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.method = 'PATCH'
|
||||
|
||||
msg = _("VNF %(id)s status is %(state)s") % {
|
||||
"id": constants.UUID, "state": "ERROR"}
|
||||
res = self._make_problem_detail(msg %
|
||||
{"state": "ERROR"}, 409, 'Conflict')
|
||||
|
||||
resp = req.get_response(self.app)
|
||||
self.assertEqual(res.text, resp.text)
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM': FakeVNFMPlugin()})
|
||||
@mock.patch.object(objects.VNF, "vnf_index_list")
|
||||
@mock.patch.object(objects.VnfInstanceList, "vnf_instance_list")
|
||||
def test_update_vnf_none_instance_data(
|
||||
self,
|
||||
mock_vnf_instance_list,
|
||||
mock_vnf_index_list,
|
||||
mock_get_service_plugins):
|
||||
|
||||
mock_vnf_index_list.return_value = fakes._get_vnf()
|
||||
mock_vnf_instance_list.return_value = ""
|
||||
|
||||
body = {"vnfInstanceName": "new_instance_name",
|
||||
"vnfInstanceDescription": "new_instance_discription",
|
||||
"vnfdId": "2c69a161-0000-4b0f-bcf8-391f8fc76600",
|
||||
"vnfConfigurableProperties": {
|
||||
"test": "test_value"
|
||||
},
|
||||
"vnfcInfoModificationsDeleteIds": ["test1"],
|
||||
"metadata": {"testkey": "test_value"},
|
||||
"vimConnectionInfo": {"id": "testid"}}
|
||||
req = fake_request.HTTPRequest.blank(
|
||||
'/vnf_instances/%s' % constants.UUID)
|
||||
req.body = jsonutils.dump_as_bytes(body)
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.method = 'PATCH'
|
||||
|
||||
vnf_data = fakes._get_vnf()
|
||||
msg = _("Can not find requested vnf instance data: %s") % vnf_data.get(
|
||||
'vnfd_id')
|
||||
res = self._make_problem_detail(msg, 404, title='Not Found')
|
||||
|
||||
resp = req.get_response(self.app)
|
||||
self.assertEqual(res.text, resp.text)
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM': FakeVNFMPlugin()})
|
||||
@mock.patch.object(objects.VNF, "vnf_index_list")
|
||||
@mock.patch.object(objects.VnfInstanceList, "vnf_instance_list")
|
||||
@mock.patch.object(objects.VnfPackageVnfd, 'get_vnf_package_vnfd')
|
||||
@mock.patch.object(VNFLcmRPCAPI, "update")
|
||||
def test_update_vnf_none_vnfd_data(
|
||||
self,
|
||||
mock_update,
|
||||
mock_vnf_package_vnf_get_vnf_package_vnfd,
|
||||
mock_vnf_instance_list,
|
||||
mock_vnf_index_list,
|
||||
mock_get_service_plugins):
|
||||
|
||||
mock_vnf_index_list.return_value = fakes._get_vnf()
|
||||
mock_vnf_instance_list.return_value = fakes.return_vnf_instance(
|
||||
fields.VnfInstanceState.INSTANTIATED)
|
||||
mock_vnf_package_vnf_get_vnf_package_vnfd.return_value = ""
|
||||
|
||||
body = {"vnfInstanceName": "new_instance_name",
|
||||
"vnfInstanceDescription": "new_instance_discription",
|
||||
"vnfdId": "2c69a161-0000-4b0f-bcf8-391f8fc76600",
|
||||
"vnfConfigurableProperties": {
|
||||
"test": "test_value"
|
||||
},
|
||||
"vnfcInfoModificationsDeleteIds": ["test1"],
|
||||
"metadata": {"testkey": "test_value"},
|
||||
"vimConnectionInfo": {"id": "testid"}}
|
||||
req = fake_request.HTTPRequest.blank(
|
||||
'/vnf_instances/%s' % constants.UUID)
|
||||
req.body = jsonutils.dump_as_bytes(body)
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.method = 'PATCH'
|
||||
|
||||
fakes._get_vnf()
|
||||
msg = _("Can not find requested vnf package vnfd: %s") %\
|
||||
body.get('vnfdId')
|
||||
res = self._make_problem_detail(msg, 400, 'Bad Request')
|
||||
|
||||
resp = req.get_response(self.app)
|
||||
self.assertEqual(res.text, resp.text)
|
||||
|
|
Loading…
Reference in New Issue