Support for grant request with Synchronous response

Supported grant for the following sequences:
   Instantiation
   Healing
   Scaling
   Termination

Implements: blueprint support-vnfm-operations
Spec: https://specs.openstack.org/openstack/tacker-specs/specs/victoria/support-sol003-vnfm-operations.html

Change-Id: I47a6a995dc25256eb1bd10f2e03a1f810fdb3f69
This commit is contained in:
Aldinson Esto 2020-08-25 18:57:35 +09:00
parent 49be782dc2
commit ffe4c40921
41 changed files with 4258 additions and 106 deletions

View File

@ -455,7 +455,7 @@ class VnfLcmController(wsgi.Controller):
'vnfInstance': {
'href': self._get_vnf_instance_href(vnf_instance)}}}
# call sendNotification
# call send_notification
self.rpc_api.send_notification(context, notification)
result = self._view_builder.create(vnf_instance)

View File

@ -25,7 +25,6 @@ import time
import traceback
import yaml
from glance_store import exceptions as store_exceptions
from oslo_config import cfg
from oslo_log import log as logging
@ -51,6 +50,7 @@ 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.db_sqlalchemy import models
from tacker.db.nfvo import nfvo_db
from tacker.db.vnfm import vnfm_db
from tacker.extensions import nfvo
@ -66,6 +66,7 @@ from tacker import service as tacker_service
from tacker import version
from tacker.vnflcm import utils as vnflcm_utils
from tacker.vnflcm import vnflcm_driver
from tacker.vnfm import nfvo_client
from tacker.vnfm import plugin
CONF = tacker.conf.CONF
@ -230,6 +231,65 @@ def revert_update_lcm(function):
return decorated_function
@utils.expects_func_args('vnf_instance', 'vnf_lcm_op_occ_id')
def grant_error_common(function):
"""Decorator to revert upload_vnf_package on failure."""
@functools.wraps(function)
def decorated_function(self, context, *args, **kwargs):
try:
return function(self, context, *args, **kwargs)
except Exception:
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_instance = keyed_args['vnf_instance']
vnf_lcm_op_occ_id = keyed_args['vnf_lcm_op_occ_id']
try:
vnf_lcm_op_occs = objects.VnfLcmOpOcc.get_by_id(
context, vnf_lcm_op_occ_id)
timestamp = datetime.datetime.utcnow()
vnf_lcm_op_occs.operation_state = 'ROLLED_BACK'
vnf_lcm_op_occs.state_entered_time = timestamp
vnf_lcm_op_occs.save()
except Exception as e:
LOG.warning("Failed to update vnf_lcm_op_occ for vnf "
"instance %(id)s. Error: %(error)s",
{"id": vnf_instance.id, "error": e})
try:
notification = {}
notification['notificationType'] = \
'VnfLcmOperationOccurrenceNotification'
notification['vnfInstanceId'] = vnf_instance.id
notification['notificationStatus'] = 'RESULT'
notification['operation'] = vnf_lcm_op_occs.operation
notification['operationState'] = 'ROLLED_BACK'
notification['isAutomaticInvocation'] = \
vnf_lcm_op_occs.is_automatic_invocation
notification['vnfLcmOpOccId'] = vnf_lcm_op_occ_id
insta_url = CONF.vnf_lcm.endpoint_url + \
"/vnflcm/v1/vnf_instances/" + \
vnf_instance.id
vnflcm_url = CONF.vnf_lcm.endpoint_url + \
"/vnflcm/v1/vnf_lcm_op_occs/" + \
vnf_lcm_op_occ_id
notification['_links'] = {}
notification['_links']['vnfInstance'] = {}
notification['_links']['vnfInstance']['href'] = insta_url
notification['_links']['vnfLcmOpOcc'] = {}
notification['_links']['vnfLcmOpOcc']['href'] = vnflcm_url
self.send_notification(context, notification)
except Exception as e:
LOG.warning("Failed notification for vnf "
"instance %(id)s. Error: %(error)s",
{"id": vnf_instance.id, "error": e})
return decorated_function
class Conductor(manager.Manager):
def __init__(self, host, conf=None):
if conf:
@ -661,9 +721,10 @@ class Conductor(manager.Manager):
vim_connection_info = objects.VimConnectionInfo.\
obj_from_primitive(vim_info, context)
vnflcm_utils._build_instantiated_vnf_info(vnfd_dict,
instantiate_vnf_req, vnf_instance,
vim_id=vim_connection_info.vim_id)
if not vnf_instance.instantiated_vnf_info.instance_id:
vnflcm_utils._build_instantiated_vnf_info(
vnfd_dict, instantiate_vnf_req, vnf_instance,
vim_id=vim_connection_info.vim_id)
if vnf_instance.instantiated_vnf_info.instance_id:
self.vnf_manager.invoke(vim_connection_info.vim_type,
@ -761,6 +822,522 @@ class Conductor(manager.Manager):
{'zip': csar_path, 'folder': csar_zip_temp_path,
'uuid': vnf_pack.id})
def _grant(self, context, grant_request):
LOG.info(
"grant start grant_request[%s]" %
grant_request.to_request_body())
response = nfvo_client.GrantRequest().grants(
json=grant_request.to_request_body())
res_body = response.json()
res_dict = utils.convert_camelcase_to_snakecase(res_body)
LOG.info("grant end res_body[%s]" % res_dict)
grant_obj = objects.Grant.obj_from_primitive(
res_dict, context=context)
if len(grant_request.add_resources) != len(grant_obj.add_resources):
msg = "grant add resource error"
raise exceptions.ValidationError(detail=msg)
if len(
grant_request.remove_resources) != len(
grant_obj.remove_resources):
msg = "grant remove resource error"
raise exceptions.ValidationError(detail=msg)
self._check_res_add_remove_rsc(context, grant_request, grant_obj)
return grant_obj
def _check_res_add_remove_rsc(self, context, grant_request, grant_obj):
for add_resource in grant_request.add_resources:
match_flg = False
for rsc in grant_obj.add_resources:
if add_resource.id == rsc.resource_definition_id:
match_flg = True
break
if not match_flg:
msg = "grant add resource error"
raise exceptions.ValidationError(detail=msg)
for remove_resource in grant_request.remove_resources:
match_flg = False
for rsc in grant_obj.remove_resources:
if remove_resource.id == rsc.resource_definition_id:
match_flg = True
break
if not match_flg:
msg = "grant remove resource error"
raise exceptions.ValidationError(detail=msg)
@grant_error_common
def _instantiate_grant(self,
context,
vnf_instance,
vnf_dict,
instantiate_vnf_request,
vnf_lcm_op_occ_id):
vnfd_dict = vnflcm_utils._get_vnfd_dict(context,
vnf_dict['vnfd']['id'],
instantiate_vnf_request.flavour_id)
inst_level = instantiate_vnf_request.instantiation_level_id
vnf_instance.instantiated_vnf_info = objects.InstantiatedVnfInfo(
flavour_id=instantiate_vnf_request.flavour_id,
instantiation_level_id=inst_level,
vnf_instance_id=vnf_instance.id)
vnf_instance.instantiated_vnf_info.reinitialize()
vnflcm_utils._build_instantiated_vnf_info(vnfd_dict,
instantiate_vnf_request, vnf_instance, '')
if not self._get_grant_execute():
return
add_resources = []
vnf_inf = vnf_instance.instantiated_vnf_info
for vnfc_resource in vnf_inf.vnfc_resource_info:
resource = objects.ResourceDefinition()
resource.id = vnfc_resource.id
resource.type = constants.TYPE_COMPUTE
resource.vdu_id = vnfc_resource.vdu_id
resource.resource_template_id = vnfc_resource.vdu_id
add_resources.append(resource)
for vl_resource in vnf_inf.vnf_virtual_link_resource_info:
resource = objects.ResourceDefinition()
resource.id = vl_resource.id
resource.type = constants.TYPE_VL
resource.resource_template_id = \
vl_resource.vnf_virtual_link_desc_id
add_resources.append(resource)
for cp_resource in vl_resource.vnf_link_ports:
for vnfc_resource in vnf_inf.vnfc_resource_info:
for vnfc_cp_resource in vnfc_resource.vnfc_cp_info:
if cp_resource.cp_instance_id == vnfc_cp_resource.id:
resource = objects.ResourceDefinition()
resource.id = cp_resource.id
resource.type = constants.TYPE_LINKPORT
resource.vdu_id = vnfc_resource.vdu_id
resource.resource_template_id = \
vnfc_cp_resource.cpd_id
add_resources.append(resource)
for storage_resource in vnf_inf.virtual_storage_resource_info:
for vnfc_resource in vnf_inf.vnfc_resource_info:
if storage_resource.id in vnfc_resource.storage_resource_ids:
resource = objects.ResourceDefinition()
resource.id = storage_resource.id
resource.type = constants.TYPE_STORAGE
resource.vdu_id = vnfc_resource.vdu_id
resource.resource_template_id = \
storage_resource.virtual_storage_desc_id
add_resources.append(resource)
p_c_list = []
placement_obj_list = []
topo_temp = vnfd_dict.get('topology_template', {})
for policy in topo_temp.get('policies', []):
for policy_name, policy_dict in policy.items():
key_type = 'tosca.policies.nfv.AntiAffinityRule'
if policy_dict['type'] == key_type:
placement_constraint = objects.PlacementConstraint()
placement_constraint.affinity_or_anti_affinity = \
'ANTI_AFFINITY'
placement_constraint.scope = 'ZONE'
placement_constraint.resource = []
placement_constraint.fallback_best_effort = True
for target in policy_dict.get('targets', []):
if target in topo_temp.get('groups', []):
for member in topo_temp['groups']['members']:
for vnfc_rsc in vnf_inf.vnfc_resource_info:
if member == vnfc_rsc.vdu_id:
resource = \
objects.ConstraintResourceRef()
resource.id_type = 'GRANT'
resource.resource_id = vnfc_rsc.id
p_rsc = \
placement_constraint.resource
p_rsc.append(resource)
break
else:
for vnfc_rsc in vnf_inf.vnfc_resource_info:
if target == vnfc_rsc.vdu_id:
resource = \
objects.ConstraintResourceRef()
resource.id_type = 'GRANT'
resource.resource_id = vnfc_rsc.id
p_rsc = placement_constraint.resource
p_rsc.append(resource)
break
p_c_list.append(placement_constraint)
placement_obj = models.PlacementConstraint()
placement_obj.id = uuidutils.generate_uuid()
placement_obj.vnf_instance_id = vnf_instance.id
placement_obj.affinity_or_anti_affinity = \
placement_constraint.affinity_or_anti_affinity
placement_obj.scope = placement_constraint.scope
placement_obj.server_group_name = policy_name
p_c_dict = placement_constraint.to_dict()
res_dict = p_c_dict.get('resource', {})
res_json = json.dumps(res_dict)
placement_obj.resource = res_json
placement_obj.created_at = timeutils.utcnow()
placement_obj.deleted_at = datetime.datetime.min
placement_obj_list.append(placement_obj)
g_request = self._make_grant_request(context,
vnf_instance,
vnf_lcm_op_occ_id,
'INSTANTIATE',
False,
add_resources=add_resources,
placement_constraints=p_c_list)
vnf_dict['placement_obj_list'] = placement_obj_list
vnf_dict['grant'] = self._grant(context, g_request)
def _get_placement(self, context, vnf_instance):
return self.vnfm_plugin.get_placement_constraint(context,
vnf_instance.id)
@grant_error_common
def _scale_grant(
self,
context,
vnf_dict,
vnf_instance,
scale_vnf_request,
vnf_lcm_op_occ_id):
# Check if vnf is in instantiated state.
vnf_instance = objects.VnfInstance.get_by_id(context,
vnf_instance.id)
if vnf_instance.instantiation_state == \
fields.VnfInstanceState.NOT_INSTANTIATED:
LOG.error("Scale action cannot be performed on vnf %(id)s "
"which is in %(state)s state.",
{"id": vnf_instance.id,
"state": vnf_instance.instantiation_state})
raise Exception("Scale action cannot be performed on vnf")
vim_info = vnflcm_utils._get_vim(
context, vnf_instance.vim_connection_info)
vim_connection_info = objects.VimConnectionInfo.obj_from_primitive(
vim_info, context)
if scale_vnf_request.type == 'SCALE_IN':
vnf_dict['action'] = 'in'
reverse = scale_vnf_request.additional_params.get('is_reverse')
region_name = vim_connection_info.access_info.get('region_name')
scale_id_list, scale_name_list, grp_id, res_num = \
self.vnf_manager.invoke(vim_connection_info.vim_type,
'get_scale_in_ids',
plugin=self,
context=context,
vnf_dict=vnf_dict,
is_reverse=reverse,
auth_attr=vim_connection_info.access_info,
region_name=region_name,
number_of_steps=scale_vnf_request.number_of_steps)
vnf_dict['res_num'] = res_num
else:
scale_id_list = []
if not self._get_grant_execute():
return None, []
placement_obj_list = self.vnfm_plugin.get_placement_constraint(
context, vnf_instance.id)
self.vnf_manager.invoke(
vim_connection_info.vim_type,
'get_grant_resource',
plugin=self,
vnf_instance=vnf_instance,
vnf_info=vnf_dict,
scale_vnf_request=scale_vnf_request,
placement_obj_list=placement_obj_list,
vim_connection_info=vim_connection_info,
del_list=scale_id_list
)
vnf_dict['placement_obj_list'] = placement_obj_list
grant_request = self._make_grant_request(
context,
vnf_instance,
vnf_lcm_op_occ_id,
'SCALE',
False,
add_resources=vnf_dict['addResources'],
remove_resources=vnf_dict['removeResources'],
placement_constraints=vnf_dict['placement_constraint_list'])
vnf_dict['grant'] = self._grant(context, grant_request)
@grant_error_common
def _heal_grant(self,
context,
vnf_instance,
vnf_dict,
heal_vnf_request,
vnf_lcm_op_occ_id):
vnf_inf = vnf_instance.instantiated_vnf_info
if not self._get_grant_execute():
return
placement_obj_list = self._get_placement(context, vnf_instance)
vim_info = vnflcm_utils._get_vim(context,
vnf_instance.vim_connection_info)
vim_connection_info = \
objects.VimConnectionInfo.obj_from_primitive(vim_info, context)
cinder_list = self.vnf_manager.invoke(vim_connection_info.vim_type,
'get_cinder_list',
vnf_info=vnf_dict)
vnf_instantiated_info_after = copy.deepcopy(vnf_inf)
del_cre_vdu_list = []
add_resources = []
rm_resources = []
affinity_list = []
for vnfc_resource in vnf_instantiated_info_after.vnfc_resource_info:
vnfc_key = vnfc_resource.compute_resource.resource_id
if not heal_vnf_request.vnfc_instance_id or \
vnfc_key in heal_vnf_request.vnfc_instance_id:
if vnfc_resource.vdu_id not in del_cre_vdu_list:
del_cre_vdu_list.append(vnfc_resource.vdu_id)
for vnfc_resource in vnf_instantiated_info_after.vnfc_resource_info:
if vnfc_resource.vdu_id in del_cre_vdu_list:
resource = objects.ResourceDefinition()
resource.id = vnfc_resource.id
resource.type = constants.TYPE_COMPUTE
resource.vdu_id = vnfc_resource.vdu_id
resource.resource_template_id = vnfc_resource.vdu_id
vim_id = vnfc_resource.compute_resource.vim_connection_id
rsc_id = vnfc_resource.compute_resource.resource_id
vnfc_rh = objects.ResourceHandle(
vim_connection_id=vim_id,
resource_id=rsc_id)
resource.resource = vnfc_rh
rm_resources.append(resource)
add_uuid = uuidutils.generate_uuid()
resource = objects.ResourceDefinition()
resource.id = add_uuid
resource.type = constants.TYPE_COMPUTE
resource.vdu_id = vnfc_resource.vdu_id
resource.resource_template_id = vnfc_resource.vdu_id
add_resources.append(resource)
key_id = vnfc_resource.compute_resource.resource_id
for placement_obj in placement_obj_list:
resource_dict = jsonutils.loads(placement_obj.resource)
set_flg = False
for resource in resource_dict:
if resource.get('resource_id') == key_id:
resource['id_type'] = 'GRANT'
resource['resource_id'] = add_uuid
g_name = placement_obj.server_group_name
affinity_list.append(g_name)
set_flg = True
res_json = jsonutils.dump_as_bytes(resource_dict)
placement_obj.resource = res_json
break
if set_flg:
break
vnfc_resource.id = add_uuid
vnfc_resource.compute_resource = objects.ResourceHandle()
st_info = vnf_instantiated_info_after.virtual_storage_resource_info
for storage_resource in st_info:
if storage_resource.virtual_storage_desc_id in cinder_list:
for vnfc_resource in vnf_inf.vnfc_resource_info:
id_list = vnfc_resource.storage_resource_ids
if storage_resource.id in id_list:
resource = objects.ResourceDefinition()
resource.id = storage_resource.id
resource.type = constants.TYPE_STORAGE
resource.vdu_id = vnfc_resource.vdu_id
resource.resource_template_id = \
storage_resource.virtual_storage_desc_id
st_rh = objects.ResourceHandle()
st_rh.vim_connection_id = \
storage_resource.storage_resource.vim_connection_id
st_rh.resource_id = \
storage_resource.storage_resource.resource_id
resource.resource = st_rh
rm_resources.append(resource)
add_uuid = uuidutils.generate_uuid()
resource = objects.ResourceDefinition()
resource = objects.ResourceDefinition()
resource.id = add_uuid
resource.type = constants.TYPE_STORAGE
resource.vdu_id = vnfc_resource.vdu_id
resource.resource_template_id = \
storage_resource.virtual_storage_desc_id
add_resources.append(resource)
storage_resource.id = add_uuid
storage_resource.storage_resource = \
objects.ResourceHandle()
p_c_list = []
for placement_obj in placement_obj_list:
p_constraint = objects.PlacementConstraint()
p_constraint.affinity_or_anti_affinity = \
placement_obj.affinity_or_anti_affinity
p_constraint.scope = placement_obj.scope
resource_dict = jsonutils.loads(placement_obj.resource)
p_constraint.resource = []
for rsc in resource_dict:
rsc_obj = objects.ConstraintResourceRef()
rsc_obj.id_type = rsc.get('id_type')
rsc_obj.resource_id = rsc.get('resource_id')
p_constraint.resource.append(rsc_obj)
p_constraint.fallback_best_effort = True
p_c_list.append(p_constraint)
g_request = self._make_grant_request(context,
vnf_instance,
vnf_lcm_op_occ_id,
'HEAL',
False,
add_resources=add_resources,
remove_resources=rm_resources,
placement_constraints=p_c_list)
vnf_dict['placement_obj_list'] = placement_obj_list
vnf_dict['grant'] = self._grant(context, g_request)
vnf_dict['vnf_instantiated_info_after'] = vnf_instantiated_info_after
@grant_error_common
def _terminate_grant(self, context, vnf_instance, vnf_lcm_op_occ_id):
vnf_inf = vnf_instance.instantiated_vnf_info
if not self._get_grant_execute():
return
rm_resources = []
for vnfc_resource in vnf_inf.vnfc_resource_info:
resource = objects.ResourceDefinition()
resource.id = vnfc_resource.id
resource.type = constants.TYPE_COMPUTE
resource.vdu_id = vnfc_resource.vdu_id
resource.resource_template_id = vnfc_resource.vdu_id
vim_id = vnfc_resource.compute_resource.vim_connection_id
rsc_id = vnfc_resource.compute_resource.resource_id
vnfc_rh = objects.ResourceHandle(
vim_connection_id=vim_id,
resource_id=rsc_id)
resource.resource = vnfc_rh
rm_resources.append(resource)
for vl_resource in vnf_inf.vnf_virtual_link_resource_info:
resource = objects.ResourceDefinition()
resource.id = vl_resource.id
resource.type = constants.TYPE_VL
resource.resource_template_id = \
vl_resource.vnf_virtual_link_desc_id
vim_id = vl_resource.network_resource.vim_connection_id
rsc_id = vl_resource.network_resource.resource_id
vl_rh = objects.ResourceHandle(
vim_connection_id=vim_id,
resource_id=rsc_id)
resource.resource = vl_rh
rm_resources.append(resource)
for cp_resource in vl_resource.vnf_link_ports:
for vnfc_resource in vnf_inf.vnfc_resource_info:
for vnfc_cp_resource in vnfc_resource.vnfc_cp_info:
if cp_resource.cp_instance_id == vnfc_cp_resource.id:
resource = objects.ResourceDefinition()
resource.id = cp_resource.id
resource.type = constants.TYPE_LINKPORT
resource.vdu_id = vnfc_resource.vdu_id
resource.resource_template_id = \
vnfc_cp_resource.cpd_id
vim_id = \
cp_resource.resource_handle.vim_connection_id
rsc_id = cp_resource.resource_handle.resource_id
cp_rh = objects.ResourceHandle(
vim_connection_id=vim_id,
resource_id=rsc_id)
resource.resource = cp_rh
rm_resources.append(resource)
for storage_resource in vnf_inf.virtual_storage_resource_info:
for vnfc_resource in vnf_inf.vnfc_resource_info:
if storage_resource.id in vnfc_resource.storage_resource_ids:
resource = objects.ResourceDefinition()
resource.id = storage_resource.id
resource.type = constants.TYPE_STORAGE
resource.vdu_id = vnfc_resource.vdu_id
resource.resource_template_id = \
storage_resource.virtual_storage_desc_id
vim_id = \
storage_resource.storage_resource.vim_connection_id
rsc_id = storage_resource.storage_resource.resource_id
st_rh = objects.ResourceHandle(
vim_connection_id=vim_id,
resource_id=rsc_id)
resource.resource = st_rh
rm_resources.append(resource)
grant_request = self._make_grant_request(context,
vnf_instance,
vnf_lcm_op_occ_id,
'TERMINATE',
False,
remove_resources=rm_resources)
self._grant(context, grant_request)
def _get_grant_execute(self):
try:
nfvo_client.GrantRequest().validate()
except nfvo_client.UndefinedExternalSettingException:
return False
return True
def _make_grant_request(self, context, vnf_instance,
vnf_lcm_op_occ_id, operation,
is_automatic_invocation,
add_resources=[],
remove_resources=[],
placement_constraints=[]):
grant_request = objects.GrantRequest()
grant_request.vnf_instance_id = vnf_instance.id
grant_request.vnf_lcm_op_occ_id = vnf_lcm_op_occ_id
grant_request.vnfd_id = vnf_instance.vnfd_id
grant_request.flavour_id = \
vnf_instance.instantiated_vnf_info.flavour_id
grant_request.operation = operation
grant_request.is_automatic_invocation = is_automatic_invocation
vnflcm_url = CONF.vnf_lcm.endpoint_url + \
"/vnflcm/v1/vnf_lcm_op_occs/" + vnf_lcm_op_occ_id
insta_url = CONF.vnf_lcm.endpoint_url + \
"/vnflcm/v1/vnf_instances/" + vnf_instance.id
link_vnflcm = objects.Link(href=vnflcm_url)
link_insta = objects.Link(href=insta_url)
link = objects.Links(vnf_lcm_op_occ=link_vnflcm,
vnf_instance=link_insta)
grant_request._links = link
if add_resources:
grant_request.add_resources = add_resources
if remove_resources:
grant_request.remove_resources = remove_resources
if placement_constraints:
grant_request.placement_constraints = placement_constraints
return grant_request
def _create_placement(self, context, vnf_dict):
p_list = vnf_dict.get('placement_obj_list', [])
if len(p_list) == 0:
return
self.vnfm_plugin.create_placement_constraint(context,
p_list)
def _update_placement(self, context, vnf_dict, vnf_instance):
self.vnfm_plugin.update_placement_constraint(context,
vnf_dict,
vnf_instance)
def _delete_placement(self, context, vnf_instance_id):
self.vnfm_plugin.delete_placement_constraint(context,
vnf_instance_id)
@log.log
def _get_vnf_notify(self, context, id):
try:
@ -790,6 +1367,7 @@ class Conductor(manager.Manager):
is_automatic_invocation = \
kwargs.get('is_automatic_invocation', False)
error = kwargs.get('error', None)
# Used for timing control when a failure occurs
if old_vnf_instance:
vnf_instance_id = old_vnf_instance.id
@ -918,7 +1496,7 @@ class Conductor(manager.Manager):
self.__set_auth_subscription(line)
for num in range(CONF.vnf_lcm.retry_num):
LOG.info("send notify[%s]" % json.dumps(notification))
LOG.warn("send notify[%s]" % json.dumps(notification))
auth_client = auth.auth_manager.get_auth_client(
notification['subscriptionId'])
response = auth_client.post(
@ -965,6 +1543,12 @@ class Conductor(manager.Manager):
instantiate_vnf,
vnf_lcm_op_occs_id):
self._instantiate_grant(context,
vnf_instance,
vnf_dict,
instantiate_vnf,
vnf_lcm_op_occs_id)
try:
# Update vnf_lcm_op_occs table and send notification "PROCESSING"
self._send_lcm_op_occ_notification(
@ -993,6 +1577,8 @@ class Conductor(manager.Manager):
instantiation_state=fields.VnfInstanceState.
INSTANTIATED, task_state=None)
self._create_placement(context, vnf_dict)
# Update vnf_lcm_op_occs table and send notification "COMPLETED"
self._send_lcm_op_occ_notification(
context=context,
@ -1024,6 +1610,11 @@ class Conductor(manager.Manager):
@coordination.synchronized('{vnf_instance[id]}')
def terminate(self, context, vnf_lcm_op_occs_id,
vnf_instance, terminate_vnf_req, vnf_dict):
self._terminate_grant(context,
vnf_instance,
vnf_lcm_op_occs_id)
try:
old_vnf_instance = copy.deepcopy(vnf_instance)
@ -1042,6 +1633,9 @@ class Conductor(manager.Manager):
self.vnflcm_driver.terminate_vnf(context, vnf_instance,
terminate_vnf_req)
self._delete_placement(context, vnf_instance.id)
self._change_vnf_status(context, vnf_instance.id,
_PENDING_STATUS, 'INACTIVE')
@ -1086,6 +1680,12 @@ class Conductor(manager.Manager):
heal_vnf_request,
vnf_lcm_op_occs_id):
self._heal_grant(context,
vnf_instance,
vnf_dict,
heal_vnf_request,
vnf_lcm_op_occs_id)
try:
old_vnf_instance = copy.deepcopy(vnf_instance)
@ -1117,6 +1717,8 @@ class Conductor(manager.Manager):
self.vnflcm_driver._vnf_instance_update(context, vnf_instance,
task_state=None)
self._update_placement(context, vnf_dict, vnf_instance)
# update vnf_lcm_op_occs and send notification "COMPLETED"
self._send_lcm_op_occ_notification(
context=context,
@ -1150,16 +1752,15 @@ class Conductor(manager.Manager):
@coordination.synchronized('{vnf_instance[id]}')
def scale(self, context, vnf_info, vnf_instance, scale_vnf_request):
# Check if vnf is in instantiated state.
vnf_instance = objects.VnfInstance.get_by_id(context,
vnf_instance.id)
if vnf_instance.instantiation_state == \
fields.VnfInstanceState.NOT_INSTANTIATED:
LOG.error("Scale action cannot be performed on vnf %(id)s "
"which is in %(state)s state.",
{"id": vnf_instance.id,
"state": vnf_instance.instantiation_state})
return
vnf_lcm_op_occ = vnf_info['vnf_lcm_op_occ']
vnf_lcm_op_occ_id = vnf_lcm_op_occ.id
self._scale_grant(
context,
vnf_info,
vnf_instance,
scale_vnf_request,
vnf_lcm_op_occ_id)
self.vnflcm_driver.scale_vnf(
context, vnf_info, vnf_instance, scale_vnf_request)

View File

@ -301,7 +301,7 @@ class VnfLcmOpOccs(model_base.BASE, models.SoftDeleteMixin,
"""VNF LCM OP OCCS Fields"""
__tablename__ = 'vnf_lcm_op_occs'
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
id = sa.Column(sa.String(16), primary_key=True)
vnf_instance_id = sa.Column(sa.String(36),
sa.ForeignKey('vnf_instances.id'),
nullable=False)
@ -316,3 +316,17 @@ class VnfLcmOpOccs(model_base.BASE, models.SoftDeleteMixin,
resource_changes = sa.Column(sa.JSON(), nullable=True)
changed_info = sa.Column(sa.JSON(), nullable=True)
error_point = sa.Column(sa.Integer, nullable=False)
class PlacementConstraint(model_base.BASE, models.SoftDeleteMixin,
models.TimestampMixin, models_v1.HasId):
"""Represents a Vnf Placement Constraint."""
__tablename__ = 'placement_constraint'
vnf_instance_id = sa.Column(sa.String(36),
sa.ForeignKey('vnf_instances.id'),
nullable=False)
affinity_or_anti_affinity = sa.Column(sa.String(255), nullable=False)
scope = sa.Column(sa.String(255), nullable=False)
server_group_name = sa.Column(sa.String(255), nullable=False)
resource = sa.Column(sa.JSON(), nullable=True)

View File

@ -0,0 +1,52 @@
# Copyright 2020 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""add placement table
Revision ID: 2c5211036579
Revises: ee98bbc0789d
Create Date: 2020-09-11 20:47:46.345771
"""
# flake8: noqa: E402
# revision identifiers, used by Alembic.
revision = '2c5211036579'
down_revision = 'ee98bbc0789d'
from alembic import op
import sqlalchemy as sa
from sqlalchemy import Boolean
from tacker.db import types
def upgrade(active_plugins=None, options=None):
op.create_table(
'placement_constraint',
sa.Column('id', types.Uuid(length=36), nullable=False),
sa.Column('vnf_instance_id', types.Uuid(length=36), nullable=False),
sa.Column('affinity_or_anti_affinity',
sa.String(length=255), nullable=False),
sa.Column('scope', sa.String(length=255), nullable=False),
sa.Column('server_group_name', sa.String(length=255), nullable=False),
sa.Column('resource', sa.JSON(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', Boolean, default=False),
sa.PrimaryKeyConstraint('id'),
mysql_engine='InnoDB'
)

View File

@ -1 +1 @@
ee98bbc0789d
2c5211036579

View File

@ -18,6 +18,7 @@ from datetime import datetime
from oslo_db.exception import DBDuplicateEntry
from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import timeutils
from oslo_utils import uuidutils
@ -29,9 +30,11 @@ from sqlalchemy import schema
from tacker._i18n import _
from tacker.api.v1 import attributes
from tacker.common import exceptions
import tacker.conf
from tacker import context as t_context
from tacker.db.common_services import common_services_db_plugin
from tacker.db import db_base
from tacker.db.db_sqlalchemy import models
from tacker.db import model_base
from tacker.db import models_v1
from tacker.db.nfvo import ns_db
@ -40,6 +43,8 @@ from tacker.extensions import vnfm
from tacker import manager
from tacker.plugins.common import constants
CONF = tacker.conf.CONF
LOG = logging.getLogger(__name__)
_ACTIVE_UPDATE = (constants.ACTIVE, constants.PENDING_UPDATE,
constants.PENDING_HEAL)
@ -812,3 +817,51 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
constants.ERROR]
return self._mark_vnf_status(
vnf_id, exclude_status, constants.DEAD)
def create_placement_constraint(self, context, placement_obj_list):
context.session.add_all(placement_obj_list)
def get_placement_constraint(self, context, vnf_instance_id):
placement_constraint = (
self._model_query(context, models.PlacementConstraint).filter(
models.PlacementConstraint.vnf_instance_id == vnf_instance_id).
filter(models.PlacementConstraint.deleted == 0).all())
return placement_constraint
def update_placement_constraint_heal(self, context,
vnf_info,
vnf_instance):
if not vnf_info.get('grant'):
return
placement_obj_list = vnf_info['placement_obj_list']
inst_info = vnf_instance.instantiated_vnf_info
for vnfc in inst_info.vnfc_resource_info:
for placement_obj in placement_obj_list:
rsc_dict = jsonutils.loads(placement_obj.resource)
for rsc in rsc_dict:
if vnfc.id == rsc.get('resource_id') and\
rsc.get('id_type') == 'GRANT':
rsc['id_type'] = 'RES_MGMT'
rsc['resource_id'] = vnfc.\
compute_resource.resource_id
rsc['vim_connection_id'] = vnfc.\
compute_resource.vim_connection_id
placement_obj.resource = jsonutils.dumps(rsc_dict)
self.update_placement_constraint(context, placement_obj)
def delete_placement_constraint(self, context, vnf_instance_id):
(self._model_query(context, models.PlacementConstraint).
filter(
models.PlacementConstraint.vnf_instance_id == vnf_instance_id).
filter(models.PlacementConstraint.deleted == 0).
update({'deleted': 0, 'deleted_at': timeutils.utcnow()}))
def update_placement_constraint(self, context, placement_obj):
(self._model_query(
context,
models.PlacementConstraint).filter(
models.PlacementConstraint.id == placement_obj.id).
filter(models.PlacementConstraint.deleted == 0).
update({
'resource': placement_obj.resource,
'updated_at': timeutils.utcnow()}))

View File

@ -42,3 +42,7 @@ def register_all():
__import__('tacker.objects.vnf_artifact')
__import__('tacker.objects.vnf_lcm_subscriptions')
__import__('tacker.objects.scale_vnf_request')
__import__('tacker.objects.grant')
__import__('tacker.objects.grant_request')
__import__('tacker.objects.vnfd')
__import__('tacker.objects.vnfd_attribute')

287
tacker/objects/grant.py Normal file
View File

@ -0,0 +1,287 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tacker import objects
from tacker.objects import base
from tacker.objects import fields
@base.TackerObjectRegistry.register
class Grant(base.TackerObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'id': fields.StringField(nullable=False),
'vnf_instance_id': fields.StringField(nullable=False),
'vnf_lcm_op_occ_id': fields.StringField(nullable=False),
'vim_connections': fields.ListOfObjectsField(
'VimConnectionInfo', nullable=True, default=[]),
'zones': fields.ListOfObjectsField(
'ZoneInfo', nullable=True, default=[]),
'add_resources': fields.ListOfObjectsField(
'GrantInfo', nullable=True, default=[]),
'remove_resources': fields.ListOfObjectsField(
'GrantInfo', nullable=True, default=[]),
'vim_assets': fields.ObjectField(
'VimAssets', nullable=True)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
obj_grant = super(
Grant, cls).obj_from_primitive(primitive, context)
else:
if 'vim_connections' in primitive.keys():
obj_data = [objects.VimConnectionInfo._from_dict(
vim_conn) for vim_conn in primitive.get(
'vim_connections', [])]
primitive.update({'vim_connections': obj_data})
if 'zones' in primitive.keys():
obj_data = [ZoneInfo._from_dict(
zone) for zone in primitive.get(
'zones', [])]
primitive.update({'zones': obj_data})
if 'add_resources' in primitive.keys():
obj_data = [GrantInfo._from_dict(
add_rsc) for add_rsc in primitive.get(
'add_resources', [])]
primitive.update({'add_resources': obj_data})
if 'remove_resources' in primitive.keys():
obj_data = [GrantInfo._from_dict(
remove_rsc) for remove_rsc in primitive.get(
'remove_resources', [])]
primitive.update({'remove_resources': obj_data})
if 'vim_assets' in primitive.keys():
obj_data = VimAssets.obj_from_primitive(
primitive.get('vim_assets'), context)
primitive.update({'vim_assets': obj_data})
obj_grant = Grant._from_dict(primitive)
return obj_grant
@classmethod
def _from_dict(cls, data_dict):
id = data_dict.get('id')
vnf_instance_id = data_dict.get('vnf_instance_id')
vnf_lcm_op_occ_id = data_dict.get('vnf_lcm_op_occ_id')
vim_connections = data_dict.get('vim_connections', [])
zones = data_dict.get('zones', [])
add_resources = data_dict.get('add_resources', [])
remove_resources = data_dict.get('remove_resources', [])
vim_assets = data_dict.get('vim_assets')
obj = cls(
id=id,
vnf_instance_id=vnf_instance_id,
vnf_lcm_op_occ_id=vnf_lcm_op_occ_id,
vim_connections=vim_connections,
zones=zones,
add_resources=add_resources,
remove_resources=remove_resources,
vim_assets=vim_assets)
return obj
@base.TackerObjectRegistry.register
class ZoneInfo(base.TackerObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'id': fields.StringField(nullable=False),
'zone_id': fields.StringField(nullable=False),
'vim_connection_id': fields.StringField(nullable=True)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
obj_zone_info = super(
ZoneInfo, cls).obj_from_primitive(primitive, context)
else:
obj_zone_info = ZoneInfo._from_dict(primitive)
return obj_zone_info
@classmethod
def _from_dict(cls, data_dict):
id = data_dict.get('id')
zone_id = data_dict.get('zone_id')
vim_connection_id = data_dict.get('vim_connection_id')
obj = cls(
id=id,
zone_id=zone_id,
vim_connection_id=vim_connection_id)
return obj
@base.TackerObjectRegistry.register
class GrantInfo(base.TackerObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'resource_definition_id': fields.StringField(nullable=False),
'vim_connection_id': fields.StringField(nullable=True),
'zone_id': fields.StringField(nullable=True)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
obj_grant_info = super(
GrantInfo, cls).obj_from_primitive(primitive, context)
else:
obj_grant_info = GrantInfo._from_dict(primitive)
return obj_grant_info
@classmethod
def _from_dict(cls, data_dict):
resource_definition_id = data_dict.get('resource_definition_id')
vim_connection_id = data_dict.get('vim_connection_id')
zone_id = data_dict.get('zone_id')
obj = cls(
resource_definition_id=resource_definition_id,
vim_connection_id=vim_connection_id,
zone_id=zone_id)
return obj
@base.TackerObjectRegistry.register
class VimAssets(base.TackerObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'compute_resource_flavours': fields.ListOfObjectsField(
'VimComputeResourceFlavour', nullable=True, default=[]),
'software_images': fields.ListOfObjectsField(
'VimSoftwareImage', nullable=True, default=[])
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
obj_vim_assets = super(
VimAssets, cls).obj_from_primitive(primitive, context)
else:
if 'compute_resource_flavours' in primitive.keys():
obj_data = [VimComputeResourceFlavour._from_dict(
flavour) for flavour in primitive.get(
'compute_resource_flavours', [])]
primitive.update({'compute_resource_flavours': obj_data})
if 'software_images' in primitive.keys():
obj_data = [VimSoftwareImage._from_dict(
img) for img in primitive.get(
'software_images', [])]
primitive.update({'software_images': obj_data})
obj_vim_assets = VimAssets._from_dict(primitive)
return obj_vim_assets
@classmethod
def _from_dict(cls, data_dict):
compute_resource_flavours = data_dict.get(
'compute_resource_flavours', [])
software_images = data_dict.get('software_images', [])
obj = cls(
compute_resource_flavours=compute_resource_flavours,
software_images=software_images)
return obj
@base.TackerObjectRegistry.register
class VimComputeResourceFlavour(base.TackerObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'vim_connection_id': fields.StringField(nullable=True),
'vnfd_virtual_compute_desc_id': fields.StringField(nullable=False),
'vim_flavour_id': fields.StringField(nullable=False)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
obj_flavour = super(
VimComputeResourceFlavour,
cls).obj_from_primitive(
primitive,
context)
else:
obj_flavour = VimComputeResourceFlavour._from_dict(primitive)
return obj_flavour
@classmethod
def _from_dict(cls, data_dict):
vim_connection_id = data_dict.get('vim_connection_id')
vnfd_virtual_compute_desc_id = data_dict.get(
'vnfd_virtual_compute_desc_id')
vim_flavour_id = data_dict.get('vim_flavour_id')
obj = cls(
vim_connection_id=vim_connection_id,
vnfd_virtual_compute_desc_id=vnfd_virtual_compute_desc_id,
vim_flavour_id=vim_flavour_id)
return obj
@base.TackerObjectRegistry.register
class VimSoftwareImage(base.TackerObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'vim_connection_id': fields.StringField(nullable=True),
'vnfd_software_image_id': fields.StringField(nullable=False),
'vim_software_image_id': fields.StringField(nullable=False)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
obj_img = super(
VimSoftwareImage, cls).obj_from_primitive(primitive, context)
else:
obj_img = VimSoftwareImage._from_dict(primitive)
return obj_img
@classmethod
def _from_dict(cls, data_dict):
vim_connection_id = data_dict.get('vim_connection_id')
vnfd_software_image_id = data_dict.get('vnfd_software_image_id')
vim_software_image_id = data_dict.get('vim_software_image_id')
obj = cls(
vim_connection_id=vim_connection_id,
vnfd_software_image_id=vnfd_software_image_id,
vim_software_image_id=vim_software_image_id)
return obj

View File

@ -0,0 +1,407 @@
# 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_serialization import jsonutils
from tacker.common import utils
from tacker.objects import base
from tacker.objects import fields
@base.TackerObjectRegistry.register
class GrantRequest(base.TackerObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'vnf_instance_id': fields.StringField(nullable=False),
'vnf_lcm_op_occ_id': fields.StringField(nullable=False),
'vnfd_id': fields.StringField(nullable=False),
'flavour_id': fields.StringField(nullable=True),
'operation': fields.StringField(nullable=False),
'is_automatic_invocation': fields.BooleanField(nullable=False,
default=False),
'add_resources': fields.ListOfObjectsField(
'ResourceDefinition', nullable=True, default=[]),
'remove_resources': fields.ListOfObjectsField(
'ResourceDefinition', nullable=True, default=[]),
'placement_constraints': fields.ListOfObjectsField(
'PlacementConstraint', nullable=True, default=[]),
'_links': fields.ObjectField(
'Links', nullable=False)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
obj_grant_req = super(
GrantRequest, cls).obj_from_primitive(primitive, context)
else:
if 'add_resources' in primitive.keys():
obj_data = [ResourceDefinition._from_dict(
add_rsc) for add_rsc in primitive.get(
'add_resources', [])]
primitive.update({'add_resources': obj_data})
if 'remove_resources' in primitive.keys():
obj_data = [ResourceDefinition._from_dict(
remove_rsc) for remove_rsc in primitive.get(
'remove_resources', [])]
primitive.update({'add_resources': obj_data})
if 'placement_constraints' in primitive.keys():
obj_data = [PlacementConstraint._from_dict(
place) for place in primitive.get(
'placement_constraints', [])]
primitive.update({'add_resources': obj_data})
obj_grant_req = GrantRequest._from_dict(primitive)
return obj_grant_req
@classmethod
def _from_dict(cls, data_dict):
vnf_instance_id = data_dict.get('vnf_instance_id')
vnf_lcm_op_occ_id = data_dict.get('vnf_lcm_op_occ_id')
vnfd_id = data_dict.get('vnfd_id')
flavour_id = data_dict.get('flavour_id')
operation = data_dict.get('operation')
is_automatic_invocation = data_dict.get('is_automatic_invocation')
add_resources = data_dict.get('add_resources', [])
remove_resources = data_dict.get('remove_resources', [])
placement_constraints = data_dict.get('placement_constraints', [])
links = data_dict.get('_links')
obj = cls(
vnf_instance_id=vnf_instance_id,
vnf_lcm_op_occ_id=vnf_lcm_op_occ_id,
vnfd_id=vnfd_id,
flavour_id=flavour_id,
operation=operation,
is_automatic_invocation=is_automatic_invocation,
add_resources=add_resources,
remove_resources=remove_resources,
placement_constraints=placement_constraints,
_links=links)
return obj
def to_dict(self):
data = {'vnf_instance_id': self.vnf_instance_id,
'vnf_lcm_op_occ_id': self.vnf_lcm_op_occ_id,
'vnfd_id': self.vnfd_id,
'flavour_id': self.flavour_id,
'operation': self.operation,
'is_automatic_invocation': self.is_automatic_invocation,
'_links': self._links.to_dict()}
if self.add_resources:
add_resources_list = []
for add_resource in self.add_resources:
add_resources_list.append(add_resource.to_dict())
data.update({'add_resources': add_resources_list})
if self.remove_resources:
remove_resources_list = []
for remove_resource in self.remove_resources:
remove_resources_list.append(remove_resource.to_dict())
data.update({'remove_resources': remove_resources_list})
if self.placement_constraints:
placement_constraints_list = []
for placement_constraint in self.placement_constraints:
placement_constraints_list.append(
placement_constraint.to_dict())
data.update({'placement_constraints': placement_constraints_list})
return data
def to_request_body(self):
req_dict = self.to_dict()
req_dict = utils.convert_snakecase_to_camelcase(req_dict)
return jsonutils.dumps(req_dict).replace('Links', '_links')
@base.TackerObjectRegistry.register
class ResourceDefinition(base.TackerObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'id': fields.StringField(nullable=False),
'type': fields.StringField(nullable=False),
'vdu_id': fields.StringField(nullable=True, default=None),
'resource_template_id': fields.StringField(nullable=False),
'resource': fields.ObjectField(
'ResourceHandle', nullable=True, default=None)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
obj_grant_req = super(
ResourceDefinition, cls).\
obj_from_primitive(primitive, context)
else:
if 'resource' in primitive.keys():
obj_data = ResourceHandle._from_dict(
primitive.get('resource'))
primitive.update({'resource': obj_data})
obj_grant_req = ResourceDefinition._from_dict(primitive)
return obj_grant_req
@classmethod
def _from_dict(cls, data_dict):
id = data_dict.get('id')
type = data_dict.get('type')
vdu_id = data_dict.get('vdu_id')
resource_template_id = data_dict.get('resource_template_id')
resource = data_dict.get('resource')
obj = cls(
id=id,
type=type,
vdu_id=vdu_id,
resource_template_id=resource_template_id,
resource=resource)
return obj
def to_dict(self):
data = {'id': self.id,
'type': self.type,
'resource_template_id': self.resource_template_id}
if self.vdu_id:
data.update({'vdu_id': self.vdu_id})
if self.resource:
data.update({'resource': self.resource.to_dict()})
return data
@base.TackerObjectRegistry.register
class PlacementConstraint(base.TackerObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'affinity_or_anti_affinity': fields.StringField(nullable=False),
'scope': fields.StringField(nullable=False),
'resource': fields.ListOfObjectsField(
'ConstraintResourceRef', nullable=False, default=[]),
'fallback_best_effort': fields.BooleanField(nullable=False),
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
obj_placement_constraint = super(
PlacementConstraint, cls).obj_from_primitive(
primitive, context)
else:
if 'resource' in primitive.keys():
obj_data = [ConstraintResourceRef._from_dict(
add_rsc) for add_rsc in primitive.get(
'resource', [])]
primitive.update({'resource': obj_data})
obj_placement_constraint = PlacementConstraint._from_dict(
primitive)
return obj_placement_constraint
@classmethod
def _from_dict(cls, data_dict):
affinity_or_anti_affinity = data_dict.get('affinity_or_anti_affinity')
scope = data_dict.get('scope')
resource = data_dict.get('resource')
fallback_best_effort = data_dict.get('fallback_best_effort')
obj = cls(
affinity_or_anti_affinity=affinity_or_anti_affinity,
scope=scope,
resource=resource,
fallback_best_effort=fallback_best_effort)
return obj
def to_dict(self):
data = {'affinity_or_anti_affinity': self.affinity_or_anti_affinity,
'scope': self.scope,
'fallback_best_effort': self.fallback_best_effort}
if self.resource:
resource_list = []
for rsc in self.resource:
resource_list.append(rsc.to_dict())
data.update({'resource': resource_list})
return data
@base.TackerObjectRegistry.register
class ConstraintResourceRef(base.TackerObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'id_type': fields.StringField(nullable=False),
'resource_id': fields.StringField(nullable=False),
'vim_connection_id': fields.StringField(nullable=True, default=None),
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
obj_placement_constraint = super(
ConstraintResourceRef, cls).obj_from_primitive(
primitive, context)
else:
obj_placement_constraint = ConstraintResourceRef._from_dict(
primitive)
return obj_placement_constraint
@classmethod
def _from_dict(cls, data_dict):
id_type = data_dict.get('id_type')
resource_id = data_dict.get('resource_id')
vim_connection_id = data_dict.get('vim_connection_id')
obj = cls(
id_type=id_type,
resource_id=resource_id,
vim_connection_id=vim_connection_id)
return obj
def to_dict(self):
data = {'id_type': self.id_type,
'resource_id': self.resource_id}
if self.vim_connection_id:
data.update({'vim_connection_id': self.vim_connection_id})
return data
@base.TackerObjectRegistry.register
class Links(base.TackerObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'vnf_lcm_op_occ': fields.ObjectField(
'Link', nullable=False),
'vnf_instance': fields.ObjectField(
'Link', nullable=False)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
obj_links = super(
Links, cls).obj_from_primitive(primitive, context)
else:
if 'vnf_lcm_op_occ' in primitive.keys():
obj_data = Link._from_dict(
primitive.get('vnf_lcm_op_occ'))
primitive.update({'vnf_lcm_op_occ': obj_data})
if 'vnf_instance' in primitive.keys():
obj_data = Link._from_dict(
primitive.get('vnf_instance'))
primitive.update({'vnf_instance': obj_data})
obj_links = Links._from_dict(primitive)
return obj_links
@classmethod
def _from_dict(cls, data_dict):
vnf_lcm_op_occ = data_dict.get('vnf_lcm_op_occ')
vnf_instance = data_dict.get('vnf_instance')
obj = cls(
vnf_lcm_op_occ=vnf_lcm_op_occ,
vnf_instance=vnf_instance)
return obj
def to_dict(self):
return {'vnf_lcm_op_occ': self.vnf_lcm_op_occ.to_dict(),
'vnf_instance': self.vnf_instance.to_dict()}
@base.TackerObjectRegistry.register
class Link(base.TackerObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'href': fields.StringField(nullable=False)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
obj_link = super(
Link, cls).obj_from_primitive(primitive, context)
else:
obj_link = Link._from_dict(primitive)
return obj_link
@classmethod
def _from_dict(cls, data_dict):
href = data_dict.get('href')
obj = cls(
href=href)
return obj
def to_dict(self):
return {'href': self.href}
@base.TackerObjectRegistry.register
class ResourceHandle(base.TackerObject,
base.TackerPersistentObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'vim_connection_id': fields.StringField(nullable=True,
default=None),
'resource_id': fields.StringField(nullable=False, default=""),
'vim_level_resource_type': fields.StringField(nullable=True,
default=None)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
resource_handle = super(
ResourceHandle, cls).obj_from_primitive(
primitive, context)
else:
resource_handle = ResourceHandle._from_dict(primitive)
return resource_handle
@classmethod
def _from_dict(cls, data_dict):
vim_connection_id = data_dict.get('vim_connection_id')
resource_id = data_dict.get('resource_id', "")
vim_level_resource_type = data_dict.get('vim_level_resource_type')
obj = cls(vim_connection_id=vim_connection_id,
resource_id=resource_id,
vim_level_resource_type=vim_level_resource_type)
return obj
def to_dict(self):
return {'vim_connection_id': self.vim_connection_id,
'resource_id': self.resource_id,
'vim_level_resource_type': self.vim_level_resource_type}

View File

@ -32,14 +32,14 @@ COMMON_PREFIXES = {
ACTIVE = "ACTIVE"
ACK = "ACK"
INACTIVE = "INACTIVE"
PENDING_INSTANTIATE = "PENDING_INSTANTIATE"
PENDING_CREATE = "PENDING_CREATE"
PENDING_UPDATE = "PENDING_UPDATE"
PENDING_DELETE = "PENDING_DELETE"
PENDING_SCALE_IN = "PENDING_SCALE_IN"
PENDING_SCALE_OUT = "PENDING_SCALE_OUT"
PENDING_HEAL = "PENDING_HEAL"
PENDING_TERMINATE = "PENDING_TERMINATE"
DEAD = "DEAD"
ERROR = "ERROR"
NACK = "NACK"
@ -55,6 +55,7 @@ POLICY_SCALING_ACTIONS = (ACTION_SCALE_OUT,
ACTION_SCALE_IN) = ('out', 'in')
POLICY_ACTIONS = {POLICY_SCALING: POLICY_SCALING_ACTIONS}
POLICY_ALARMING = 'tosca.policies.tacker.Alarming'
POLICY_EVENT_ALARMING = 'tosca.policies.tacker.EventAlarming'
VALID_POLICY_TYPES = [POLICY_SCALING, POLICY_ALARMING]
POLICY_RESERVATION = 'tosca.policies.tacker.Reservation'
RESERVATION_POLICY_ACTIONS = ['start_actions',
@ -68,6 +69,8 @@ RES_TYPE_VNF = "vnf"
RES_TYPE_VIM = "vim"
RES_EVT_CREATE = "CREATE"
RES_EVT_INSTANTIATE = "INSTANTIATE"
RES_EVT_TERMINATE = "TERMINATE"
RES_EVT_DELETE = "DELETE"
RES_EVT_UPDATE = "UPDATE"
RES_EVT_MONITOR = "MONITOR"
@ -90,3 +93,8 @@ VNF_STATUS_TO_EVT_TYPES = {PENDING_CREATE: RES_EVT_CREATE,
RES_EVT_CREATED_FLD = "created_at"
RES_EVT_DELETED_FLD = "deleted_at"
RES_EVT_UPDATED_FLD = "updated_at"
TYPE_COMPUTE = "COMPUTE"
TYPE_LINKPORT = "LINKPORT"
TYPE_STORAGE = "STORAGE"
TYPE_VL = "VL"

0
tacker/tests/contrib/post_test_hook.sh Executable file → Normal file
View File

View File

View File

View File

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,7 @@ tosca_cvnf_vnfd = _get_template('test_tosca_cvnf.yaml')
tosca_vnfd_openwrt = _get_template('test_tosca_openwrt.yaml')
tosca_vnfd_openwrt_param = _get_template('test_tosca_openwrt_param.yaml')
tosca_invalid_vnfd = _get_template('test_tosca_parser_failure.yaml')
etsi_vnfd = _get_template('etsi_nfv/tosca_vnfd.yaml')
config_data = _get_template('config_data.yaml')
update_config_data = _get_template('update_config_data.yaml')
hot_data = _get_template('hot_data.yaml')
@ -74,6 +75,11 @@ vnffgd_wrong_cp_number_template = yaml.safe_load(_get_template(
'tosca_vnffgd_wrong_cp_number_template.yaml'))
vnfd_instance_reservation_alarm_scale_tosca_template = _get_template(
'test_tosca-vnfd-instance-reservation.yaml')
hot_grant = _get_template('hot_grant.yaml')
hot_scale_grant = _get_template('hot_scale_grant.yaml')
hot_scale_nest_grant = _get_template('hot_scale_nest_grant.yaml')
hot_scale_initial = _get_template('hot_scale_initial.yaml')
hot_scale_nest_initial = _get_template('hot_scale_nest_initial.yaml')
def get_dummy_vnfd_obj():
@ -190,6 +196,57 @@ def get_dummy_vnf(status='PENDING_CREATE', scaling_group=False,
return dummy_vnf
def get_dummy_vnf_test(status='PENDING_CREATE', scaling_group=False,
instance_id=None):
dummy_vnf = {'status': status, 'instance_id': instance_id, 'name':
u'test_openwrt', 'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
'vnfd_id': u'eb094833-995e-49f0-a047-dfb56aaf7c4e',
'vnfd': {
'service_types': [{'service_type': u'vnfd',
'id': u'4a4c2d44-8a52-4895-9a75-9d1c76c3e738'}],
'description': u'OpenWRT with services',
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
'mgmt_driver': u'openwrt',
'attributes': {u'vnfd_simple': tosca_vnfd_openwrt},
'id': u'fb048660-dc1b-4f0f-bd89-b023666650ec',
'name': u'openwrt_services'},
'mgmt_ip_address': None, 'service_context': [],
'attributes': {u'param_values': u''},
'id': 'eb84260e-5ff7-4332-b032-50a14d6c1123',
'description': u'OpenWRT with services'}
if scaling_group:
dummy_vnf['attributes'].update({'scaling_group_names':
'{"SP1": "SP1_group"}',
'heat_template': 'test'})
return dummy_vnf
def get_dummy_vnf_etsi(status='PENDING_CREATE', scaling_group=False,
instance_id=None, flavour='Simple'):
vnfd_key = 'vnfd_' + flavour
dummy_vnf = {'status': status, 'instance_id': instance_id, 'name':
u'test_openwrt', 'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
'vnfd_id': u'eb094833-995e-49f0-a047-dfb56aaf7c4e',
'vnfd': {
'service_types': [{'service_type': u'vnfd',
'id': u'4a4c2d44-8a52-4895-9a75-9d1c76c3e738'}],
'description': u'OpenWRT with services',
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
'mgmt_driver': u'openwrt',
'attributes': {vnfd_key: etsi_vnfd},
'id': u'fb048660-dc1b-4f0f-bd89-b023666650ec',
'name': u'openwrt_services'},
'mgmt_ip_address': None, 'service_context': [],
'attributes': {u'param_values': u''},
'id': 'eb84260e-5ff7-4332-b032-50a14d6c1123',
'description': u'OpenWRT with services'}
if scaling_group:
dummy_vnf['attributes'].update({'scaling_group_names':
'{"SP1": "SP1_group"}',
'heat_template': 'test'})
return dummy_vnf
def get_dummy_vnf_config_attr():
return {'status': 'PENDING_CREATE', 'instance_id': None, 'name':
u'test_openwrt', 'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
@ -483,3 +540,23 @@ def get_dummy_vim_connection_info():
'created_at': '', 'deleted': False, 'deleted_at': '',
'id': 'fake_id', 'updated_at': '',
'vim_id': 'fake_vim_id', 'vim_type': 'openstack'}
def get_dummy_grant_hot():
return str(hot_grant)
def get_dummy_scale_grant_hot():
return str(hot_scale_grant)
def get_dummy_scale_nest_grant_hot():
return str(hot_scale_nest_grant)
def get_dummy_scale_initial_hot():
return str(hot_scale_initial)
def get_dummy_scale_nest_initial_hot():
return str(hot_scale_nest_initial)

View File

@ -245,8 +245,9 @@ def get_vnf_instance_data_with_id(vnfd_id):
}
def get_lcm_op_occs_data(vnf_instance_id):
def get_lcm_op_occs_data(id, vnf_instance_id):
return {
"id": id,
"tenant_id": uuidsentinel.tenant_id,
'operation_state': 'PROCESSING',
'state_entered_time': datetime.datetime(1900, 1, 1, 1, 1, 1,

View File

@ -16,6 +16,8 @@
import ddt
from unittest import mock
from oslo_utils import uuidutils
from tacker.common import exceptions
from tacker import context
from tacker.db import api as sqlalchemy_api
@ -231,7 +233,9 @@ class TestVnfInstance(SqlTestCase):
vnf_instance = objects.VnfInstance(context=self.context,
**vnf_instance_data)
vnf_instance.create()
id = uuidutils.generate_uuid()
vnf_lcm_oppccs = fakes.get_lcm_op_occs_data(
id,
vnf_instance.id)
vnf_instance.update(

View File

@ -11,6 +11,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from oslo_utils import uuidutils
from tacker import context
from tacker import objects
from tacker.tests.unit.db.base import SqlTestCase
@ -58,21 +60,27 @@ class TestVnfLcmOpOcc(SqlTestCase):
return vnf_instance
def _create_vnf_lcm_op_occs(self):
vnf_lcm_op_occs_data = fakes.get_lcm_op_occs_data(self.vnf_instance.id)
id = uuidutils.generate_uuid()
vnf_lcm_op_occs_data = \
fakes.get_lcm_op_occs_data(id, self.vnf_instance.id)
vnf_lcm_op_occs = objects.vnf_lcm_op_occs.VnfLcmOpOcc(
context=self.context, **vnf_lcm_op_occs_data)
vnf_lcm_op_occs.create()
return vnf_lcm_op_occs
def test_create(self):
vnf_lcm_op_occs_data = fakes.get_lcm_op_occs_data(self.vnf_instance.id)
id = uuidutils.generate_uuid()
vnf_lcm_op_occs_data = \
fakes.get_lcm_op_occs_data(id, self.vnf_instance.id)
vnf_lcm_op_occs = objects.vnf_lcm_op_occs.VnfLcmOpOcc(
context=self.context, **vnf_lcm_op_occs_data)
vnf_lcm_op_occs.create()
self.assertTrue(vnf_lcm_op_occs.vnf_instance_id)
def test_save(self):
vnf_lcm_op_occs_data = fakes.get_lcm_op_occs_data(self.vnf_instance.id)
id = uuidutils.generate_uuid()
vnf_lcm_op_occs_data = \
fakes.get_lcm_op_occs_data(id, self.vnf_instance.id)
vnf_lcm_op_occs = objects.vnf_lcm_op_occs.VnfLcmOpOcc(
context=self.context, **vnf_lcm_op_occs_data)
vnf_lcm_op_occs.create()

View File

@ -473,6 +473,10 @@ def get_instantiate_vnf_request_obj():
ext_managed_virtual_link_data = ExtManagedVirtualLinkData()
vim_connection_info = VimConnectionInfo()
ext_virtual_link_data = ExtVirtualLinkData()
ext_managed_virtual_link_data.id = uuidsentinel.ext_id
ext_managed_virtual_link_data.vnf_virtual_link_desc_id = 'VL3'
ext_managed_virtual_link_data.resource_id = \
'f8c35bd0-4d67-4436-9f11-14b8a84c92aa'
instantiate_vnf_req.additional_params = None
instantiate_vnf_req.deleted = 0
instantiate_vnf_req.ext_managed_virtual_links = \

View File

@ -113,6 +113,10 @@ class FakeDriverManager(mock.Mock):
if self.fail_method_name and \
self.fail_method_name == 'heal_vnf':
raise InfraDriverException("heal_vnf failed")
elif 'heal_vnf_standard' in args:
if self.fail_method_name and \
self.fail_method_name == 'heal_vnf_standard':
raise InfraDriverException("heal_vnf_standard failed")
elif 'heal_vnf_wait' in args:
if self.fail_method_name and \
self.fail_method_name == 'heal_vnf_wait':
@ -196,10 +200,8 @@ class TestVnflcmDriver(db_base.SqlTestCase):
driver.instantiate_vnf(self.context, vnf_instance_obj, vnf_dict,
instantiate_vnf_req_obj)
self.assertEqual("INSTANTIATED", vnf_instance_obj.instantiation_state)
self.assertEqual(2, mock_vnf_instance_save.call_count)
self.assertEqual(4, self._vnf_manager.invoke.call_count)
mock_final_vnf_dict.assert_called_once()
self.assertEqual(1, mock_vnf_instance_save.call_count)
self.assertEqual(3, self._vnf_manager.invoke.call_count)
shutil.rmtree(fake_csar)
@mock.patch('tacker.vnflcm.utils._make_final_vnf_dict')
@ -232,10 +234,8 @@ class TestVnflcmDriver(db_base.SqlTestCase):
driver.instantiate_vnf(self.context, vnf_instance_obj, vnf_dict,
instantiate_vnf_req_obj)
self.assertEqual("INSTANTIATED", vnf_instance_obj.instantiation_state)
self.assertEqual(2, mock_vnf_instance_save.call_count)
self.assertEqual(4, self._vnf_manager.invoke.call_count)
mock_final_vnf_dict.assert_called_once()
self.assertEqual(1, mock_vnf_instance_save.call_count)
self.assertEqual(3, self._vnf_manager.invoke.call_count)
shutil.rmtree(fake_csar)
@mock.patch('tacker.vnflcm.utils._make_final_vnf_dict')
@ -268,10 +268,8 @@ class TestVnflcmDriver(db_base.SqlTestCase):
driver.instantiate_vnf(self.context, vnf_instance_obj, vnf_dict,
instantiate_vnf_req_obj)
self.assertEqual("INSTANTIATED", vnf_instance_obj.instantiation_state)
self.assertEqual(2, mock_vnf_instance_save.call_count)
self.assertEqual(4, self._vnf_manager.invoke.call_count)
mock_final_vnf_dict.assert_called_once()
self.assertEqual(1, mock_vnf_instance_save.call_count)
self.assertEqual(3, self._vnf_manager.invoke.call_count)
shutil.rmtree(fake_csar)
@mock.patch('tacker.vnflcm.utils._make_final_vnf_dict')
@ -335,6 +333,12 @@ class TestVnflcmDriver(db_base.SqlTestCase):
objects.InstantiateVnfRequest.obj_from_primitive(
instantiate_vnf_req_dict, self.context)
vnf_instance_obj = fakes.return_vnf_instance()
level = instantiate_vnf_req_obj.instantiation_level_id
vnf_instance_obj.instantiated_vnf_info = objects.InstantiatedVnfInfo(
flavour_id=instantiate_vnf_req_obj.flavour_id,
instantiation_level_id=level,
vnf_instance_id=vnf_instance_obj.id,
ext_cp_info=[])
fake_csar = os.path.join(self.temp_dir, vnf_package_id)
cfg.CONF.set_override('vnf_package_csar_path', self.temp_dir,
@ -386,7 +390,8 @@ class TestVnflcmDriver(db_base.SqlTestCase):
driver.instantiate_vnf(self.context, vnf_instance_obj, vnf_dict,
instantiate_vnf_req_obj)
self.assertEqual(2, mock_create.call_count)
self.assertEqual("INSTANTIATED", vnf_instance_obj.instantiation_state)
self.assertEqual("NOT_INSTANTIATED",
vnf_instance_obj.instantiation_state)
mock_final_vnf_dict.assert_called_once()
shutil.rmtree(fake_csar)
@ -419,7 +424,6 @@ class TestVnflcmDriver(db_base.SqlTestCase):
driver.instantiate_vnf(self.context, vnf_instance_obj, vnf_dict,
instantiate_vnf_req_obj)
self.assertEqual(2, mock_create.call_count)
self.assertEqual("INSTANTIATED", vnf_instance_obj.instantiation_state)
mock_final_vnf_dict.assert_called_once()
shutil.rmtree(fake_csar)
@ -540,6 +544,7 @@ class TestVnflcmDriver(db_base.SqlTestCase):
@mock.patch('tacker.vnflcm.utils._make_final_vnf_dict')
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.vnflcm.utils._make_final_vnf_dict')
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.VnfResource, "create")
@ -550,7 +555,8 @@ class TestVnflcmDriver(db_base.SqlTestCase):
def test_heal_vnf_without_vnfc_instance(self, mock_log, mock_save,
mock_vnf_resource_list, mock_resource_destroy,
mock_resource_create, mock_vim, mock_vnf_package_vnfd,
mock_get_service_plugins, mock_final_vnf_dict):
mock_make_final_vnf_dict, mock_get_service_plugins,
mock_final_vnf_dict):
vnf_package_vnfd = fakes.return_vnf_package_vnfd()
vnf_package_id = vnf_package_vnfd.package_uuid
mock_vnf_package_vnfd.return_value = vnf_package_vnfd
@ -582,6 +588,7 @@ class TestVnflcmDriver(db_base.SqlTestCase):
self._mock_vnf_manager()
driver = vnflcm_driver.VnfLcmDriver()
vnf_dict = {"attributes": {}}
mock_make_final_vnf_dict.return_value = {}
driver.heal_vnf(self.context, vnf_instance, vnf_dict, heal_vnf_req)
self.assertEqual(1, mock_save.call_count)
# vnf resource software images will be deleted during
@ -590,9 +597,9 @@ class TestVnflcmDriver(db_base.SqlTestCase):
# Vnf resource software images will be created during
# instantiation.
self.assertEqual(1, mock_resource_create.call_count)
# Invoke will be called 7 times, 3 for deleting the vnf
# resources and 4 during instantiation.
self.assertEqual(7, self._vnf_manager.invoke.call_count)
# Invoke will be called 6 times, 3 for deleting the vnf
# resources and 3 during instantiation.
self.assertEqual(6, self._vnf_manager.invoke.call_count)
expected_msg = ("Request received for healing vnf '%s' "
"is completed successfully")
mock_log.info.assert_called_with(expected_msg,
@ -632,6 +639,7 @@ class TestVnflcmDriver(db_base.SqlTestCase):
@mock.patch('tacker.vnflcm.utils._make_final_vnf_dict')
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.vnflcm.utils._make_final_vnf_dict')
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.VnfResource, "create")
@ -642,8 +650,8 @@ class TestVnflcmDriver(db_base.SqlTestCase):
def test_heal_vnf_without_vnfc_instance_infra_instantiate_vnf_fail(self,
mock_log, mock_save, mock_vnf_resource_list,
mock_resource_destroy, mock_resource_create, mock_vim,
mock_vnf_package_vnfd, mock_get_service_plugins,
mock_final_vnf_dict):
mock_vnf_package_vnfd, mock_make_final_vnf_dict,
mock_get_service_plugins, mock_final_vnf_dict):
vnf_package_vnfd = fakes.return_vnf_package_vnfd()
vnf_package_id = vnf_package_vnfd.package_uuid
mock_vnf_package_vnfd.return_value = vnf_package_vnfd
@ -664,6 +672,7 @@ class TestVnflcmDriver(db_base.SqlTestCase):
self._mock_vnf_manager(fail_method_name='instantiate_vnf')
driver = vnflcm_driver.VnfLcmDriver()
vnf_dict = {"fake": "fake_dict"}
mock_make_final_vnf_dict.return_value = {}
self.assertRaises(exceptions.VnfHealFailed,
driver.heal_vnf, self.context,
vnf_instance, vnf_dict, heal_vnf_req)
@ -814,6 +823,10 @@ class TestVnflcmDriver(db_base.SqlTestCase):
@mock.patch.object(driver_manager.DriverManager, "invoke")
def test_scale_true(self, mock_invoke, mock_get_service_plugins):
vnf_info = fakes._get_vnf()
vnf_info['attributes']['scale_group'] = '{\"scaleGroupDict\": ' + \
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
'\"initialLevel\": 0, \"default\": 0 }}}'
scale_vnf_request = fakes.scale_request("SCALE_IN", 1, "True")
vim_connection_info = vim_connection.VimConnectionInfo(
vim_type="fake_type")
@ -830,6 +843,10 @@ class TestVnflcmDriver(db_base.SqlTestCase):
def test_scale_false_in(self, mock_invoke, mock_safe_load,
mock_get_service_plugins):
vnf_info = fakes._get_vnf()
vnf_info['attributes']['scale_group'] = '{\"scaleGroupDict\": ' + \
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
'\"initialLevel\": 0, \"default\": 0 }}}'
scale_vnf_request = fakes.scale_request("SCALE_IN", 1, "False")
vim_connection_info = vim_connection.VimConnectionInfo(
vim_type="fake_type")
@ -846,9 +863,36 @@ class TestVnflcmDriver(db_base.SqlTestCase):
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch.object(yaml, "safe_load")
@mock.patch.object(driver_manager.DriverManager, "invoke")
def test_scale_false_out(self, mock_invoke, mock_safe_load,
mock_get_service_plugins):
def test_scale_false_out_initial(self, mock_invoke, mock_safe_load,
mock_get_service_plugins):
vnf_info = fakes._get_vnf()
vnf_info['attributes']['scale_group'] = '{\"scaleGroupDict\": ' + \
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
'\"initialLevel\": 0, \"default\": 0 }}}'
scale_vnf_request = fakes.scale_request("SCALE_OUT", 1, "False")
vim_connection_info = vim_connection.VimConnectionInfo(
vim_type="fake_type")
scale_name_list = ["fake"]
grp_id = "fake_id"
with open(vnf_info["attributes"]["heat_template"], "r") as f:
mock_safe_load.return_value = yaml.safe_load(f)
print(mock_safe_load.return_value)
driver = vnflcm_driver.VnfLcmDriver()
driver.scale(self.context, vnf_info, scale_vnf_request,
vim_connection_info, scale_name_list, grp_id)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch.object(yaml, "safe_load")
@mock.patch.object(driver_manager.DriverManager, "invoke")
def test_scale_false_out_level_up(self, mock_invoke, mock_safe_load,
mock_get_service_plugins):
vnf_info = fakes._get_vnf()
vnf_info['attributes']['scale_group'] = '{\"scaleGroupDict\": ' + \
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
'\"initialLevel\": 0, \"default\": 1 }}}'
scale_vnf_request = fakes.scale_request("SCALE_OUT", 1, "False")
vim_connection_info = vim_connection.VimConnectionInfo(
vim_type="fake_type")

View File

@ -0,0 +1,98 @@
tosca_definitions_version: tosca_simple_yaml_1_2
description: >
Template for test _generate_hot_from_tosca().
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
node_types:
topology_template:
node_templates:
VDU1:
type: tosca.nodes.nfv.Vdu.Compute
properties:
name: VDU1
description: VDU1 compute node
vdu_profile:
min_number_of_instances: 1
max_number_of_instances: 1
sw_image_data:
name: Software of VDU1
version: '0.4.0'
checksum:
algorithm: sha-256
hash: b9c3036539fd7a5f87a1bf38eb05fdde8b556a1a7e664dbeda90ed3cd74b4f9d
container_format: bare
disk_format: qcow2
min_disk: 1 GiB
size: 1 GiB
artifacts:
sw_image:
type: tosca.artifacts.nfv.SwImage
file: Files/images/cirros-0.4.0-x86_64-disk.img
capabilities:
virtual_compute:
properties:
virtual_memory:
virtual_mem_size: 512 MiB
virtual_cpu:
num_virtual_cpu: 1
virtual_local_storage:
- size_of_storage: 1 GiB
requirements:
- virtual_storage: VB1
VB1:
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
properties:
virtual_block_storage_data:
size_of_storage: 100 GB
rdma_enabled: true
sw_image_data:
name: cirros
version: '0.0.0'
checksum:
algorithm: sha512
hash: f0fd1b50420dce4ca382ccfbb528eef3a38bbeff00b54e95e3876b9bafe7ed2d6f919ca35d9046d437c6d2d8698b1174a335fbd66035bb3edc525d2cdb187232
container_format: bare
disk_format: qcow2
min_disk: 0 B
min_ram: 0 B
size: 13267968 B
CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 0
requirements:
- virtual_binding: VDU1
- virtual_link: VL3
VL3:
type: tosca.nodes.nfv.VnfVirtualLink
properties:
connectivity_type:
layer_protocols: [ ipv4 ]
description: Internal Virtual link in the VNF
vl_profile:
max_bitrate_requirements:
root: 1048576
leaf: 1048576
min_bitrate_requirements:
root: 1048576
leaf: 1048576
virtual_link_protocol_data:
- associated_layer_protocol: ipv4
l3_protocol_data:
ip_version: ipv4
cidr: 33.33.0.0/24
policies:
- policy_affinity_local_VDU1:
type: tosca.policies.nfv.AntiAffinityRule
targets: [ VDU1 ]
properties:
scope: zone

View File

@ -0,0 +1,27 @@
heat_template_version: 2016-10-14
description: test
parameters:
DB11_image:
type: string
default: cirros
resources:
DB11:
type: OS::Nova::Server
properties:
user_data_format: SOFTWARE_CONFIG
availability_zone: nova
block_device_mapping_v2:
- device_name: vda
volume_id: {get_resource: ST1}
flavor: m1.tiny
networks:
- port: {get_resource: DB11-CP}
config_drive: false
DB11-CP:
type: OS::Neutron::Port
properties:
network: net_mgmt
outputs:
id-DB11:
value:
get_attr: [DB11-CP, fixed_ips, 0, ip_address]

View File

@ -0,0 +1,31 @@
heat_template_version: 2013-05-23
description: test
parameters:
current_num:
type: number
nfv:
type: json
resources:
SP1_scale_out:
type: OS::Heat::ScalingPolicy
properties:
auto_scaling_group_id: {get_resource: SP1_group}
adjustment_type: change_in_capacity
scaling_adjustment: 1
SP1_group:
type: OS::Heat::AutoScalingGroup
properties:
min_size: 0
desired_capacity: {get_param: current_num}
resource:
type: SP1_res.yaml
properties:
nfv: {get_param: nfv}
max_size: 3
SP1_scale_in:
type: OS::Heat::ScalingPolicy
properties:
auto_scaling_group_id: {get_resource: SP1_group}
adjustment_type: change_in_capacity
scaling_adjustment: -1
outputs: {}

View File

@ -0,0 +1,31 @@
heat_template_version: 2013-05-23
description: test
parameters:
current_num:
type: number
nfv:
type: json
resources:
SP1_scale_out:
type: OS::Heat::ScalingPolicy
properties:
auto_scaling_group_id: {get_resource: SP1_group}
adjustment_type: change_in_capacity
scaling_adjustment: 1
SP1_group:
type: OS::Heat::AutoScalingGroup
properties:
min_size: 0
desired_capacity: {get_param: current_num}
resource:
type: SP1_res.yaml
properties:
nfv: {get_param: nfv}
max_size: 3
SP1_scale_in:
type: OS::Heat::ScalingPolicy
properties:
auto_scaling_group_id: {get_resource: SP1_group}
adjustment_type: change_in_capacity
scaling_adjustment: -1
outputs: {}

View File

@ -0,0 +1,57 @@
heat_template_version: 2013-05-23
description: test
parameters:
DB11_image:
type: string
default: cirros
my_compute_placement_policy:
type: string
default: ssss
resources:
DB11:
type: OS::Nova::Server
properties:
user_data_format: SOFTWARE_CONFIG
availability_zone: nova
scheduler_hints:
group: {get_resource: my_compute_placement_policy}
block_device_mapping_v2:
- device_name: vda
volume_id: {get_resource: ENUMm0-VB}
flavor: m1.tiny
networks:
- port: {get_resource: DB11-CP}
config_drive: false
DB11-CP:
type: OS::Neutron::Port
properties:
network: {get_resource: DB11-VL}
DB12:
type: OS::Nova::Server
properties:
user_data_format: SOFTWARE_CONFIG
availability_zone: nova
image: cirros
flavor: m1.tiny
networks:
- port: {get_resource: DB12-CP}
config_drive: false
DB12-CP:
type: OS::Neutron::Port
properties:
network: {get_resource: DB11-VL}
DB12-VB:
properties: {image: cirros, size: \'1\'}
type: OS::Cinder::Volume
ENUMm0-VB:
properties:
image: {get_param: DB11_image}
size: \'1\'
type: OS::Cinder::Volume
DB12-CB:
properties:
instance_uuid: {get_resource: DB12}
mountpoint: /dev/vdb
volume_id: {get_resource: DB12-VB}
type: OS::Cinder::VolumeAttachment
outputs: {}

View File

@ -0,0 +1,27 @@
heat_template_version: 2016-10-14
description: test
parameters:
DB11_image:
type: string
default: cirros
resources:
DB11:
type: OS::Nova::Server
properties:
user_data_format: SOFTWARE_CONFIG
availability_zone: nova
block_device_mapping_v2:
- device_name: vda
volume_id: {get_resource: ST1}
flavor: m1.tiny
networks:
- port: {get_resource: DB11-CP}
config_drive: false
DB11-CP:
type: OS::Neutron::Port
properties:
network: net_mgmt
outputs:
id-DB11:
value:
get_attr: [DB11-CP, fixed_ips, 0, ip_address]

View File

@ -23,8 +23,12 @@ import ddt
import requests
import yaml
from heatclient.v1 import resources
from oslo_serialization import jsonutils
from tacker.common import exceptions
from tacker.common import utils as cutils
from tacker import context
from tacker.db.db_sqlalchemy import models
from tacker.extensions import vnfm
from tacker import objects
from tacker.tests.common import helpers
@ -39,6 +43,21 @@ from tacker.vnfm.infra_drivers.openstack import heat_client as hc
from tacker.vnfm.infra_drivers.openstack import openstack
vnf_dict = {
'instance_id': 'd1121d3c-368b-4ac2-b39d-835aa3e4ccd8'
}
class FakeAlarmPlugin():
def add_alarm_url_to_vnf(self, context, vnf):
return
class FakePlugin():
def get_vnf(self, context, id):
return vnf_dict
@ddt.ddt
class TestOpenStack(base.FixturedTestCase):
client_fixture_class = client.ClientFixture
@ -144,7 +163,8 @@ class TestOpenStack(base.FixturedTestCase):
mock_get_base_hot_dict,
mock_get_vnflcm_interface,
mock_format_base_hot):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf = utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour='simple')
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath(
@ -156,7 +176,7 @@ class TestOpenStack(base.FixturedTestCase):
'instantiate_vnf_request_lcm_userdata.json')
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
inst_req_info_test.flavour_id = 'simple'
vnf_resource = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}}
@ -171,6 +191,105 @@ class TestOpenStack(base.FixturedTestCase):
grant_info=grant_info_test,
vnf_instance=vnf_instance)
@mock.patch('tacker.vnfm.infra_drivers.openstack.openstack'
'.OpenStack._format_base_hot')
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_grant(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict,
mock_format_base_hot):
vnf = utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour='simple')
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath(
os.path.join(os.path.dirname(__file__),
"../../../../etc/samples/etsi/nfv",
"user_data_sample_normal"))
inst_req_info_test = type('', (), {})
test_json = self._json_load(
'instantiate_vnf_request_lcm_userdata.json')
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = 'simple'
vnf_resource = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}}
nested_hot_dict = {'parameters': {'vnf': 'test'}}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
vimAssets = {'compute_resource_flavours': [
{'vim_connection_id': uuidsentinel.vim_id,
'vnfd_virtual_compute_desc_id': 'VDU1',
'vim_flavour_id': 'm1.tiny'}],
'softwareImages': [
{'vim_connection_id': uuidsentinel.vim_id,
'vnfd_software_image_id': 'VDU1',
'vim_software_image_id': 'cirros'}]}
resAddResource = []
resource = {
'resource_definition_id': '2c6e5cc7-240d-4458-a683-1fe648351280',
'vim_connection_id': uuidsentinel.vim_id,
'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'}
resAddResource.append(resource)
resource = {
'resource_definition_id': 'faf14707-da7c-4eec-be99-8099fa1e9fa9',
'vim_connection_id': uuidsentinel.vim_id,
'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'}
resAddResource.append(resource)
resource = {
'resource_definition_id': 'faf14707-da7c-4eec-be99-8099fa1e9fa0',
'vim_connection_id': uuidsentinel.vim_id,
'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'}
resAddResource.append(resource)
resource = {
'resource_definition_id': 'faf14707-da7c-4eec-be99-8099fa1e9fa1',
'vim_connection_id': uuidsentinel.vim_id,
'zone_id': '5e4da3c3-4a55-412a-b624-843921f8b51d'}
resAddResource.append(resource)
zone = {
'id': '5e4da3c3-4a55-412a-b624-843921f8b51d',
'zone_id': 'nova',
'vim_connection_id': uuidsentinel.vim_id}
vim_obj = {'id': '0b9c66bb-9e1f-4bb2-92c3-913074e52e2b',
'vim_id': uuidsentinel.vim_id,
'vim_type': 'openstack',
'access_info': {
'password': 'test_pw',
'username': 'test_user',
'region': 'test_region',
'tenant': uuidsentinel.tenant}}
grant_dict = {}
grant_dict['id'] = 'c213e465-8220-487e-9464-f79104e81e96'
grant_dict['vnf_instance_id'] = uuidsentinel.vnf_instance_id
grant_dict['vnf_lcm_op_occ_id'] = uuidsentinel.vnf_lcm_op_occ_id
grant_dict['add_resources'] = []
grant_dict['add_resources'].extend(resAddResource)
grant_dict['vim_assets'] = vimAssets
grant_dict['zones'] = [zone]
grant_dict['vim_connections'] = [vim_obj]
grant_obj = objects.Grant.obj_from_primitive(
grant_dict, context=self.context)
vnf['grant'] = grant_obj
vnf_instance = fd_utils.get_vnf_instance_object()
vnf_instance.instantiated_vnf_info.reinitialize()
vnfc_obj = objects.VnfcResourceInfo()
vnfc_obj.id = '2c6e5cc7-240d-4458-a683-1fe648351280'
vnfc_obj.vdu_id = 'VDU1'
vnfc_obj.storage_resource_ids = \
['faf14707-da7c-4eec-be99-8099fa1e9fa0']
compute_resource = objects.ResourceHandle(
vim_connection_id=uuidsentinel.vim_id,
resource_id='6e1c286d-c023-4b34-8369-831c6e84cce2')
vnfc_obj.compute_resource = compute_resource
vnf_instance.instantiated_vnf_info.vnfc_resource_info = [vnfc_obj]
self.openstack.create(self.plugin, self.context, vnf,
self.auth_attr, inst_req_info=inst_req_info_test,
vnf_package_path=vnf_package_path_test,
base_hot_dict=base_hot_dict_test,
grant_info=grant_info_test,
vnf_instance=vnf_instance)
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_heat_stack(self, mock_OpenstackClients_heat):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
@ -236,7 +355,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_userdata_null(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf = utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour='simple')
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath(
@ -251,7 +371,7 @@ class TestOpenStack(base.FixturedTestCase):
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
inst_req_info_test.flavour_id = 'simple'
grant_info_test = None
nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \
@ -270,7 +390,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_userdataclass_null(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf = utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour='simple')
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath(
@ -285,7 +406,7 @@ class TestOpenStack(base.FixturedTestCase):
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
inst_req_info_test.flavour_id = 'simple'
grant_info_test = None
nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \
@ -304,7 +425,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_import_module_exception(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf = utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour='simple')
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath(
@ -316,7 +438,7 @@ class TestOpenStack(base.FixturedTestCase):
'instantiate_vnf_request_lcm_userdata.json')
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
inst_req_info_test.flavour_id = 'simple'
grant_info_test = None
nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \
@ -337,7 +459,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_getattr_none(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf = utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour='simple')
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath(
@ -370,7 +493,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_missing_file(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf = utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour='simple')
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath(
@ -382,7 +506,7 @@ class TestOpenStack(base.FixturedTestCase):
'instantiate_vnf_request_lcm_userdata.json')
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
inst_req_info_test.flavour_id = 'simple'
vnf_resource = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}}
@ -403,7 +527,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_return_none_dict(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf = utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour='simple')
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath(
@ -417,7 +542,7 @@ class TestOpenStack(base.FixturedTestCase):
'UserData/lcm_user_data_non_dict.py'
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
inst_req_info_test.flavour_id = 'simple'
vnf_resource = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}}
@ -438,7 +563,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_none_base_hot_dict(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf = utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour='simple')
vnf['placement_attr'] = {'region_name': 'dummy_region'}
inst_req_info_test = type('', (), {})
test_json = self._json_load(
@ -465,7 +591,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_invalid_user_data(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf = utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour='simple')
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath(
@ -481,7 +608,7 @@ class TestOpenStack(base.FixturedTestCase):
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
inst_req_info_test.flavour_id = 'simple'
grant_info_test = None
nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \
@ -501,7 +628,8 @@ class TestOpenStack(base.FixturedTestCase):
def test_create_invalid_user_data_class(self,
mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf = utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour='simple')
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath(
@ -517,7 +645,7 @@ class TestOpenStack(base.FixturedTestCase):
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
inst_req_info_test.flavour_id = 'simple'
grant_info_test = None
nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \
@ -536,7 +664,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_lcm_user_data_and_user_data_class_no_value(self,
mock_OpenstackClients_heat, mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf = utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour='simple')
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath(
@ -553,7 +682,7 @@ class TestOpenStack(base.FixturedTestCase):
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = test_json['extVirtualLinks']
inst_req_info_test.flavour_id = test_json['flavourId']
inst_req_info_test.flavour_id = 'simple'
vnf_resource = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}}
@ -620,7 +749,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients')
def test_create_instance_exception(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf = utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour='simple')
vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath(
@ -634,7 +764,7 @@ class TestOpenStack(base.FixturedTestCase):
'UserData/lcm_user_data_invalid_script.py'
inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None
inst_req_info_test.flavour_id = test_json['flavourId']
inst_req_info_test.flavour_id = 'simple'
vnf_resource = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}}
@ -1633,3 +1763,190 @@ class TestOpenStack(base.FixturedTestCase):
"stack_id %s") % (vnf_instance.id,
uuidsentinel.stack_id)
self.assertEqual(expected_msg, str(result))
def test_get_grant_resource_scale_out(self):
vnfc_resource_info = fd_utils.get_vnfc_resource_info()
inst_vnf_info = fd_utils.get_vnf_instantiated_info(
vnfc_resource_info=[vnfc_resource_info])
vnf_instance = fd_utils.get_vnf_instance_object(
instantiated_vnf_info=inst_vnf_info)
vim_connection_info = fd_utils.get_vim_connection_info_object()
scale_vnf_request = objects.ScaleVnfRequest(type='SCALE_OUT',
aspect_id='SP1',
number_of_steps=1)
test_res = '[{"id_type": "RES_MGMT", "resource_id": ' + \
'"2c6e5cc7-240d-4458-a683-1fe648351200",' + \
' "vim_connection_id": ' + \
'"2a63bee3-0c43-4568-bcfa-b0cb733e064c"}]'
placemnt = models.PlacementConstraint(
id='c2947d8a-2c67-4e8f-ad6f-c0889b351c17',
vnf_instance_id=uuidsentinel.vnf_instance_id,
affinity_or_anti_affinity='ANTI_AFFINITY',
scope='ZONE',
server_group_name='my_compute_placement_policy',
resource=test_res)
placement_obj_list = [placemnt]
del_list = []
vnf_info = {}
vnf_info['attributes'] = {}
vnf_info['attributes']['scale_group'] = '{\"scaleGroupDict\": ' + \
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
'\"initialLevel\": 0, \"default\": 0 }}}'
vnf_info['attributes']['heat_template'] = utils.\
get_dummy_scale_grant_hot()
vnf_info['attributes']['SP1_res.yaml'] = utils.\
get_dummy_scale_nest_grant_hot()
self.openstack.get_grant_resource(
vnf_instance,
vnf_info,
scale_vnf_request,
placement_obj_list,
vim_connection_info,
del_list)
self.assertEqual(1, len(vnf_info['placement_constraint_list']))
@mock.patch.object(hc.HeatClient, "resource_get_list")
def test_get_grant_resource_scale_in(self, mock_list):
v_s_resource_info = fd_utils.get_virtual_storage_resource_info(
desc_id="storage1", set_resource_id=False)
storage_resource_ids = [v_s_resource_info.id]
vnfc_resource_info = fd_utils.get_vnfc_resource_info(vdu_id="VDU_VNF",
storage_resource_ids=storage_resource_ids, set_resource_id=False)
v_l_resource_info = fd_utils.get_virtual_link_resource_info(
vnfc_resource_info.vnfc_cp_info[0].vnf_link_port_id,
vnfc_resource_info.vnfc_cp_info[0].id)
inst_vnf_info = fd_utils.get_vnf_instantiated_info(
virtual_storage_resource_info=[v_s_resource_info],
vnf_virtual_link_resource_info=[v_l_resource_info],
vnfc_resource_info=[vnfc_resource_info])
vnf_instance = fd_utils.get_vnf_instance_object(
instantiated_vnf_info=inst_vnf_info)
vim_connection_info = fd_utils.get_vim_connection_info_object()
scale_vnf_request = objects.ScaleVnfRequest(type='SCALE_IN',
aspect_id='SP1',
number_of_steps=1)
placement_obj_list = []
del_list = ['58337c61-3148-4c29-892f-68b043c009ea']
vnf_info = {}
res_list = []
vdu_id = uuidsentinel.vdu_resource_id
vnf_instance.instantiated_vnf_info.vnfc_resource_info[0].\
compute_resource.resource_id = vdu_id
resource2 = resources.Resource(None, {
'resource_name': 'aaaaaaaa',
'resource_type': 'OS::Nova::Server',
'creation_time': '2020-01-01T00:00:00',
'resource_status': 'CREATE_COMPLETE',
'physical_resource_id': vdu_id,
'id': '1111'
})
res_list.append(resource2)
port_id = uuidsentinel.virtual_link_port_resource_id
vnf_instance.instantiated_vnf_info.vnf_virtual_link_resource_info[
0].vnf_link_ports[0].resource_handle.resource_id = port_id
resource3 = resources.Resource(None, {
'resource_name': 'bbbbbbbb',
'resource_type': 'OS::Neutron::Port',
'creation_time': '2020-01-01T00:00:00',
'resource_status': 'CREATE_COMPLETE',
'physical_resource_id': port_id,
'id': '1111'
})
res_list.append(resource3)
st_id = uuidsentinel.vdu_resource_id
vnf_instance.instantiated_vnf_info.virtual_storage_resource_info[
0].storage_resource.resource_id = st_id
resource4 = resources.Resource(None, {
'resource_name': 'cccccccc',
'resource_type': 'OS::Cinder::Volume',
'creation_time': '2020-01-01T00:00:01',
'resource_status': 'CREATE_COMPLETE',
'physical_resource_id': st_id,
'id': '1111'
})
res_list.append(resource4)
mock_list.return_value = res_list
self.openstack.get_grant_resource(
vnf_instance,
vnf_info,
scale_vnf_request,
placement_obj_list,
vim_connection_info,
del_list)
self.assertEqual(3, len(vnf_info['removeResources']))
@mock.patch.object(hc.HeatClient, "update")
def test_scale_out_initial(self, mock_update):
scale_vnf_request = objects.ScaleVnfRequest(type='SCALE_OUT',
aspect_id='SP1',
number_of_steps=1)
vnf_info = {}
vnf_info['attributes'] = {}
vnf_info['attributes']['scale_group'] = '{\"scaleGroupDict\": ' + \
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
'\"initialLevel\": 0, \"default\": 0 }}}'
vnf_info['attributes']['heat_template'] = \
utils.get_dummy_scale_initial_hot()
vnf_info['attributes']['SP1_res.yaml'] = \
utils.get_dummy_scale_nest_initial_hot()
stack_param_dict = {}
stack_param_dict['nfv'] = {}
stack_param_dict['nfv']['VDU'] = {}
stack_param_dict['nfv']['VDU']['VDU1'] = {}
stack_param_dict['nfv']['VDU']['VDU1']['zone'] = ''
stack_param_dict['nfv']['VDU']['VDU1']['flavor'] = ''
stack_param_dict['nfv']['VDU']['VDU1']['image'] = ''
vnf_info['attributes'].update({'stack_param': str(stack_param_dict)})
vnf_info['instance_id'] = uuidsentinel.stack_id
testjson = '{"id": "c213e465-8220-487e-9464-f79104e81e96", ' + \
'"vnf_instance_id": ' + \
'"47101fb6-bd18-4e04-b2b5-22370a023448", ' + \
'"vnf_lcm_op_occ_id": ' + \
'"f26f181d-7891-4720-b022-b074ec1733ef", ' + \
'"add_resources": [{"resource_definition_id": ' + \
'"2c6e5cc7-240d-4458-a683-1fe648351280", ' + \
'"vim_connection_id": ' + \
'"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187", ' + \
'"zone_id": "5e4da3c3-4a55-412a-b624-843921f8b51d"}' + \
', {"resource_definition_id": ' + \
'"faf14707-da7c-4eec-be99-8099fa1e9fa9", ' + \
'"vim_connection_id": ' + \
'"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187", ' + \
'"zone_id": "5e4da3c3-4a55-412a-b624-843921f8b51d"}' + \
', {"resource_definition_id": ' + \
'"faf14707-da7c-4eec-be99-8099fa1e9fa9", ' + \
'"vim_connection_id": ' + \
'"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187", ' + \
'"zone_id": ' + \
'"5e4da3c3-4a55-412a-b624-843921f8b51d"}], ' + \
'"vim_assets": {"compute_resource_flavours": ' + \
'[{"vim_connection_id": ' + \
'"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187", ' + \
'"vnfd_virtual_compute_desc_id": "VDU1", ' + \
'"vim_flavour_id": "m1.tiny"}], "software_images": ' + \
'[{"vim_connection_id": ' + \
'"b6eacd1b-5a9e-41ea-a33b-9d7196cd9187", ' + \
'"vnfd_software_image_id": "VDU1", ' + \
'"vim_software_image_id": "cirros"}]}}'
res_body = jsonutils.loads(testjson)
res_dict = cutils.convert_camelcase_to_snakecase(res_body)
grant_obj = objects.Grant.obj_from_primitive(
res_dict, context=context)
vnf_info['grant'] = grant_obj
self.openstack.scale_out_initial(context=self.context,
plugin=self,
auth_attr=None,
vnf_info=vnf_info,
scale_vnf_request=scale_vnf_request,
region_name=None
)

View File

@ -18,6 +18,7 @@ from unittest import mock
from unittest.mock import patch
import ddt
import iso8601
from oslo_utils import uuidutils
import yaml
@ -25,12 +26,15 @@ from tacker._i18n import _
from tacker.common import exceptions
from tacker import context
from tacker.db.common_services import common_services_db_plugin
from tacker.db.db_sqlalchemy import models
from tacker.db.nfvo import nfvo_db
from tacker.db.nfvo import ns_db
from tacker.db.vnfm import vnfm_db
from tacker.extensions import vnfm
from tacker import objects
from tacker.objects import heal_vnf_request
from tacker.plugins.common import constants
from tacker.tests.unit.conductor import fakes
from tacker.tests.unit.db import base as db_base
from tacker.tests.unit.db import utils
from tacker.vnfm import monitor
@ -1160,3 +1164,165 @@ class TestVNFMPlugin(db_base.SqlTestCase):
mock_get_vnf.return_value = dummy_vnf
self._test_create_vnf_trigger(policy_name="start_actions",
action_value="SP_RSV-out")
def test_create_placement_constraint(self):
res_str = '[{"id_type": "RES_MGMT", "resource_id": ' + \
'"2c6e5cc7-240d-4458-a683-1fe648351200", ' + \
'"vim_connection_id": ' + \
'"2a63bee3-0c43-4568-bcfa-b0cb733e064c"}]'
placemnt = models.PlacementConstraint(
id='c2947d8a-2c67-4e8f-ad6f-c0889b351c17',
vnf_instance_id='7ddc38c3-a116-48b0-bfc1-68d7f306f467',
affinity_or_anti_affinity='ANTI_AFFINITY',
scope='ZONE',
server_group_name='my_compute_placement_policy',
resource=res_str,
deleted_at=datetime.min)
pls_list = [placemnt]
vnf_inst = models.VnfInstance(
id='7ddc38c3-a116-48b0-bfc1-68d7f306f467',
vnf_provider=' ',
vnf_product_name=' ',
vnf_software_version=' ',
vnfd_version=' ',
vnfd_id='8d86480e-d4e6-4ee0-ba4d-08217118d6cb',
instantiation_state=' ',
tenant_id='9b3f0518-bf6b-4982-af32-d282ce577c8f',
created_at=datetime(
2020, 1, 1, 1, 1, 1,
tzinfo=iso8601.UTC),
vnf_pkg_id=uuidutils.generate_uuid())
self.context.session.add(vnf_inst)
self.context.session.flush()
self.vnfm_plugin.create_placement_constraint(
self.context, pls_list)
def test_get_placement_constraint(self):
res_str = '[{"id_type": "RES_MGMT", "resource_id": ' + \
'"2c6e5cc7-240d-4458-a683-1fe648351200", ' + \
'"vim_connection_id": ' + \
'"2a63bee3-0c43-4568-bcfa-b0cb733e064c"}]'
placemnt = models.PlacementConstraint(
id='c2947d8a-2c67-4e8f-ad6f-c0889b351c17',
vnf_instance_id='7ddc38c3-a116-48b0-bfc1-68d7f306f467',
affinity_or_anti_affinity='ANTI_AFFINITY',
scope='ZONE',
server_group_name='my_compute_placement_policy',
resource=res_str,
deleted_at=datetime.min)
vnf_inst = models.VnfInstance(
id='7ddc38c3-a116-48b0-bfc1-68d7f306f467',
vnf_provider=' ',
vnf_product_name=' ',
vnf_software_version=' ',
vnfd_version=' ',
vnfd_id='8d86480e-d4e6-4ee0-ba4d-08217118d6cb',
instantiation_state=' ',
tenant_id='9b3f0518-bf6b-4982-af32-d282ce577c8f',
created_at=datetime(
2020, 1, 1, 1, 1, 1,
tzinfo=iso8601.UTC),
vnf_pkg_id=uuidutils.generate_uuid())
self.context.session.add(vnf_inst)
self.context.session.flush()
self.context.session.add(placemnt)
self.context.session.flush()
res = self.vnfm_plugin.get_placement_constraint(
self.context, '7ddc38c3-a116-48b0-bfc1-68d7f306f467')
self.assertEqual(1, len(res))
def test_update_placement_constraint_heal(self):
res_str = '[{"id_type": "RES_MGMT", "resource_id": ' + \
'"2c6e5cc7-240d-4458-a683-1fe648351200", ' + \
'"vim_connection_id": ' + \
'"2a63bee3-0c43-4568-bcfa-b0cb733e064c"}]'
placemnt = models.PlacementConstraint(
id='c2947d8a-2c67-4e8f-ad6f-c0889b351c17',
vnf_instance_id='7ddc38c3-a116-48b0-bfc1-68d7f306f467',
affinity_or_anti_affinity='ANTI_AFFINITY',
scope='ZONE',
server_group_name='my_compute_placement_policy',
resource=res_str,
deleted_at=datetime.min)
res_str2 = '[{"id_type": "GRANT", "resource_id": ' + \
'"4cef1b7e-8e5f-430e-b32e-a7585a61d61c"}]'
placemnt2 = models.PlacementConstraint(
id='c2947d8a-2c67-4e8f-ad6f-c0889b351c17',
vnf_instance_id='7ddc38c3-a116-48b0-bfc1-68d7f306f467',
affinity_or_anti_affinity='ANTI_AFFINITY',
scope='ZONE',
server_group_name='my_compute_placement_policy',
resource=res_str2,
deleted_at=datetime.min)
vnf_inst = models.VnfInstance(
id='7ddc38c3-a116-48b0-bfc1-68d7f306f467',
vnf_provider=' ',
vnf_product_name=' ',
vnf_software_version=' ',
vnfd_version=' ',
vnfd_id='8d86480e-d4e6-4ee0-ba4d-08217118d6cb',
instantiation_state=' ',
tenant_id='9b3f0518-bf6b-4982-af32-d282ce577c8f',
created_at=datetime(
2020, 1, 1, 1, 1, 1,
tzinfo=iso8601.UTC),
vnf_pkg_id=uuidutils.generate_uuid())
self.context.session.add(vnf_inst)
self.context.session.flush()
self.context.session.add(placemnt)
self.context.session.flush()
vnf_info = {}
vnf_info['grant'] = objects.Grant()
placement_obj_list = []
placement_obj_list.append(placemnt2)
vnf_info['placement_obj_list'] = placement_obj_list
insta = fakes.return_vnf_instance('INSTANTIATED')
vnfc = objects.VnfcResourceInfo()
vnfc.id = '4cef1b7e-8e5f-430e-b32e-a7585a61d61c'
vnfc.vdu_id = 'VDU1'
c_rsc = objects.ResourceHandle()
c_rsc.vim_connection_id = '2a63bee3-0c43-4568-bcfa-b0cb733e064c'
c_rsc.resource_id = '9aa1e075-2aa1-46ce-a27a-35a581190219'
vnfc.compute_resource = c_rsc
insta.instantiated_vnf_info.vnfc_resource_info.append(vnfc)
self.vnfm_plugin.update_placement_constraint_heal(
self.context, vnf_info, insta)
def test_delete_placement_constraint(self):
res_str = '[{"id_type": "RES_MGMT", "resource_id": ' + \
'"2c6e5cc7-240d-4458-a683-1fe648351200", ' + \
'"vim_connection_id": ' + \
'"2a63bee3-0c43-4568-bcfa-b0cb733e064c"}]'
placemnt = models.PlacementConstraint(
id='c2947d8a-2c67-4e8f-ad6f-c0889b351c17',
vnf_instance_id='7ddc38c3-a116-48b0-bfc1-68d7f306f467',
affinity_or_anti_affinity='ANTI_AFFINITY',
scope='ZONE',
server_group_name='my_compute_placement_policy',
resource=res_str,
deleted_at=datetime.min)
vnf_inst = models.VnfInstance(
id='7ddc38c3-a116-48b0-bfc1-68d7f306f467',
vnf_provider=' ',
vnf_product_name=' ',
vnf_software_version=' ',
vnfd_version=' ',
vnfd_id='8d86480e-d4e6-4ee0-ba4d-08217118d6cb',
instantiation_state=' ',
tenant_id='9b3f0518-bf6b-4982-af32-d282ce577c8f',
created_at=datetime(
2020, 1, 1, 1, 1, 1,
tzinfo=iso8601.UTC),
vnf_pkg_id=uuidutils.generate_uuid())
self.context.session.add(vnf_inst)
self.context.session.flush()
self.context.session.add(placemnt)
self.context.session.flush()
self.vnfm_plugin.delete_placement_constraint(
self.context, '7ddc38c3-a116-48b0-bfc1-68d7f306f467')

View File

@ -95,9 +95,11 @@ def _get_vnflcm_interface(context, interface, vnf_instance, flavour_id):
if vnfd_dict.get('topology_template'):
topology_template = vnfd_dict.get('topology_template')
if topology_template.get('node_templates'):
for a_val in topology_template.get('node_templates').values():
if 'interfaces' in a_val.keys():
interfaces = a_val.get('interfaces')
node_templates = topology_template.get('node_templates')
if node_templates.get('VNF'):
vnf = node_templates.get('VNF')
if vnf.get('interfaces'):
interfaces = vnf.get('interfaces')
if interfaces.get('Vnflcm'):
vnflcm = interfaces.get('Vnflcm')
if vnflcm:

View File

@ -276,7 +276,8 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
try:
instance_id = self._vnf_manager.invoke(
vim_connection_info.vim_type, 'instantiate_vnf',
context=context, vnf_instance=vnf_instance,
context=context, plugin=self._vnfm_plugin,
vnf_instance=vnf_instance,
vnfd_dict=final_vnf_dict, grant_response=vnf_resources,
vim_connection_info=vim_connection_info,
base_hot_dict=base_hot_dict,
@ -293,12 +294,9 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp))
vnf_instance.instantiated_vnf_info = objects.InstantiatedVnfInfo(
flavour_id=instantiate_vnf_req.flavour_id,
instantiation_level_id=instantiate_vnf_req.instantiation_level_id,
vnf_instance_id=vnf_instance.id,
instance_id=instance_id,
ext_cp_info=[])
if vnf_instance.instantiated_vnf_info and\
not vnf_instance.instantiated_vnf_info.instance_id:
vnf_instance.instantiated_vnf_info.instance_id = instance_id
if vnf_dict['attributes'].get('scaling_group_names'):
vnf_instance.instantiated_vnf_info.scale_status = \
vnf_dict['scale_status']
@ -306,7 +304,7 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
try:
self._vnf_manager.invoke(
vim_connection_info.vim_type, 'create_wait',
plugin=self, context=context,
plugin=self._vnfm_plugin, context=context,
vnf_dict=final_vnf_dict,
vnf_id=final_vnf_dict['instance_id'],
auth_attr=vim_connection_info.access_info)
@ -322,14 +320,6 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp))
vnflcm_utils._build_instantiated_vnf_info(vnfd_dict,
instantiate_vnf_req, vnf_instance, vim_connection_info.vim_id)
self._vnf_manager.invoke(vim_connection_info.vim_type,
'post_vnf_instantiation', context=context,
vnf_instance=vnf_instance,
vim_connection_info=vim_connection_info)
@log.log
@rollback_vnf_instantiated_resources
def instantiate_vnf(self, context, vnf_instance, vnf_dict,
@ -351,10 +341,6 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
self._instantiate_vnf(context, vnf_instance, vnf_dict,
vim_connection_info, instantiate_vnf_req)
self._vnf_instance_update(context, vnf_instance,
instantiation_state=fields.VnfInstanceState.INSTANTIATED,
task_state=None)
@log.log
@revert_to_error_task_state
def terminate_vnf(self, context, vnf_instance, terminate_vnf_req):
@ -929,7 +915,12 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
LOG.debug(
"is_reverse: %s",
scale_vnf_request.additional_params.get('is_reverse'))
if scale_vnf_request.additional_params['is_reverse'] == 'True':
scale_json = vnf_info['attributes']['scale_group']
scaleGroupDict = jsonutils.loads(scale_json)
key_aspect = scale_vnf_request.aspect_id
default = scaleGroupDict['scaleGroupDict'][key_aspect]['default']
if (scale_vnf_request.type == 'SCALE_IN' and
scale_vnf_request.additional_params['is_reverse'] == 'True'):
self._vnf_manager.invoke(
vim_connection_info.vim_type,
'scale_in_reverse',
@ -944,7 +935,27 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
)
self._vnf_manager.invoke(
vim_connection_info.vim_type,
'scale_in_reverse_wait',
'scale_update_wait',
plugin=self,
context=context,
auth_attr=vim_connection_info.access_info,
vnf_info=vnf_info,
region_name=vim_connection_info.access_info.get('region_name')
)
elif scale_vnf_request.type == 'SCALE_OUT' and default == 0:
self._vnf_manager.invoke(
vim_connection_info.vim_type,
'scale_out_initial',
plugin=self,
context=context,
auth_attr=vim_connection_info.access_info,
vnf_info=vnf_info,
scale_vnf_request=scale_vnf_request,
region_name=vim_connection_info.access_info.get('region_name')
)
self._vnf_manager.invoke(
vim_connection_info.vim_type,
'scale_update_wait',
plugin=self,
context=context,
auth_attr=vim_connection_info.access_info,

View File

@ -1350,10 +1350,32 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
grp_id):
pass
def scale_in_reverse_wait(self,
def scale_out_initial(self,
context,
plugin,
auth_attr,
vnf_info,
scale_vnf_request,
region_name):
pass
def scale_update_wait(self,
context,
plugin,
auth_attr,
vnf_info,
region_name):
pass
def get_cinder_list(self,
vnf_info):
pass
def get_grant_resource(self,
vnf_instance,
vnf_info,
scale_vnf_request,
placement_obj_list,
vim_connection_info,
del_list):
pass

View File

@ -34,10 +34,12 @@ from tacker._i18n import _
from tacker.common import exceptions
from tacker.common import log
from tacker.common import utils
from tacker.db.common_services import common_services_db_plugin
from tacker.extensions import vnflcm
from tacker.extensions import vnfm
from tacker import objects
from tacker.objects import fields
from tacker.plugins.common import constants
from tacker.tosca.utils import represent_odict
from tacker.vnflcm import utils as vnflcm_utils
from tacker.vnfm.infra_drivers import abstract_driver
@ -52,6 +54,11 @@ from tacker.vnfm.lcm_user_data.constants import USER_DATA_TIMEOUT
eventlet.monkey_patch(time=True)
SCALING_GROUP_RESOURCE = "OS::Heat::AutoScalingGroup"
NOVA_SERVER_RESOURCE = "OS::Nova::Server"
VNF_PACKAGE_HOT_DIR = 'Files'
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
@ -108,6 +115,9 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
self.STACK_RETRY_WAIT = cfg.CONF.openstack_vim.stack_retry_wait
self.IMAGE_RETRIES = 10
self.IMAGE_RETRY_WAIT = 10
self.LOCK_RETRIES = 10
self.LOCK_RETRY_WAIT = 10
self._cos_db_plg = common_services_db_plugin.CommonServicesPluginDb()
def get_type(self):
return 'openstack'
@ -124,7 +134,17 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
inst_req_info=None, grant_info=None,
vnf_instance=None):
LOG.debug('vnf %s', vnf)
region_name = vnf.get('placement_attr', {}).get('region_name', None)
if vnf.get('grant'):
if vnf['grant'].vim_connections:
vim_con = vnf['grant'].vim_connections[0]
auth_attr = vim_con.access_info
region_name = auth_attr.get('region')
else:
region_name = vnf.get('placement_attr', {}).\
get('region_name', None)
else:
region_name = vnf.get('placement_attr', {}).\
get('region_name', None)
heatclient = hc.HeatClient(auth_attr, region_name)
additional_param = None
if inst_req_info is not None:
@ -157,7 +177,8 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
for name, hot in nested_hot_dict.items():
vnf['attributes'][name] = self._format_base_hot(hot)
vnfd_str = vnf['vnfd']['attributes']['vnfd']
vnfd_str = vnf['vnfd']['attributes']['vnfd_' +
inst_req_info.flavour_id]
vnfd_dict = yaml.safe_load(vnfd_str)
LOG.debug('VNFD: %s', vnfd_dict)
LOG.debug('VNF package path: %s', vnf_package_path)
@ -220,6 +241,30 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
for name, value in scaleGroupDict['scaleGroupDict'].items():
hot_param_dict[name + '_desired_capacity'] = \
value['default']
if vnf.get('grant'):
grant = vnf['grant']
ins_inf = vnf_instance.instantiated_vnf_info.vnfc_resource_info
for addrsc in grant.add_resources:
for zone in grant.zones:
if zone.id == addrsc.zone_id:
vdu_name = None
for rsc in ins_inf:
if addrsc.resource_definition_id == rsc.id:
vdu_name = rsc.vdu_id
break
if not vdu_name:
continue
hot_param_dict['nfv']['VDU'][vdu_name]['zone'] = \
zone.zone_id
if 'vim_assets' in grant and grant.vim_assets:
for flavour in grant.vim_assets.compute_resource_flavours:
vdu_name = flavour.vnfd_virtual_compute_desc_id
hot_param_dict['nfv']['VDU'][vdu_name]['flavor'] = \
flavour.vim_flavour_id
for image in grant.vim_assets.software_images:
vdu_name = image.vnfd_software_image_id
hot_param_dict['nfv']['VDU'][vdu_name]['image'] = \
image.vim_software_image_id
# Add stack param to vnf_attributes
vnf['attributes'].update({'stack_param': str(hot_param_dict)})
@ -1482,15 +1527,63 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
mark_unhealthy=True,
resource_status_reason='Scale')
paramDict = {}
paramDict[scale_vnf_request.aspect_id +
'_desired_capacity'] = vnf_info['res_num']
scale_json = vnf_info['attributes']['scale_group']
scaleGroupDict = jsonutils.loads(scale_json)
for name, value in scaleGroupDict['scaleGroupDict'].items():
paramDict[name + '_desired_capacity'] = value['default']
paramDict[scale_vnf_request.aspect_id + '_desired_capacity'] = \
vnf_info['res_num']
stack_update_param = {
'parameters': paramDict,
'existing': True}
heatclient.update(vnf_info['instance_id'], **stack_update_param)
stack_param = jsonutils.loads(vnf_info['attributes']['stack_param'])
stack_param.update(paramDict)
vnf_info['attributes'].update({'stack_param': str(paramDict)})
@log.log
def scale_in_reverse_wait(
def scale_out_initial(self, context, plugin, auth_attr, vnf_info,
scale_vnf_request, region_name):
scale_json = vnf_info['attributes']['scale_group']
scaleGroupDict = jsonutils.loads(scale_json)
key_aspect = scale_vnf_request.aspect_id
num = scaleGroupDict['scaleGroupDict'][key_aspect]['num']
vnf_info['res_num'] = num * scale_vnf_request.number_of_steps
heatclient = hc.HeatClient(auth_attr, region_name)
paramDict = {}
for name, value in scaleGroupDict['scaleGroupDict'].items():
paramDict[name + '_desired_capacity'] = value['default']
paramDict[scale_vnf_request.aspect_id +
'_desired_capacity'] = vnf_info['res_num']
stack_param = yaml.safe_load(vnf_info['attributes']['stack_param'])
grant = vnf_info['grant']
for addrsc in grant.add_resources:
for zone in grant.zones:
if zone.id == addrsc.zone_id:
for rsc in vnf_info['addResources']:
if addrsc.id == rsc.id:
vdu_name = rsc.vdu_id
break
stack_param['nfv']['VDU'][vdu_name]['zone'] = zone.zone_id
if 'vim_assets' in grant and grant.vim_assets:
for flavour in grant.vim_assets.compute_resource_flavours:
vdu_name = flavour.vnfd_virtual_compute_desc_id
stack_param['nfv']['VDU'][vdu_name]['flavor'] = \
flavour.vim_flavour_id
for image in grant.vim_assets.software_images:
vdu_name = image.vnfd_software_image_id
stack_param['nfv']['VDU'][vdu_name]['image'] = \
image.vim_software_image_id
paramDict['nfv'] = stack_param['nfv']
stack_update_param = {
'parameters': paramDict,
'existing': True}
heatclient.update(vnf_info['instance_id'], **stack_update_param)
vnf_info['attributes'].update({'stack_param': str(paramDict)})
@log.log
def scale_update_wait(
self,
context,
plugin,
@ -1501,3 +1594,251 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
auth_attr, infra_cnst.STACK_UPDATE_IN_PROGRESS,
infra_cnst.STACK_UPDATE_COMPLETE,
vnfm.VNFScaleWaitFailed, region_name=region_name)
def get_cinder_list(self, vnf_info):
cinder_list = []
block_key = 'block_device_mapping_v2'
if not vnf_info['attributes'].get('scale_group'):
heat_yaml = vnf_info['attributes']['heat_template']
heat_dict = yaml.safe_load(heat_yaml)
for resource_name, resource in heat_dict['resources'].items():
if resource.get('properties') and resource.get(
'properties').get(block_key):
for cinder in resource['properties'][block_key]:
if cinder['volume_id'].get('get_resource'):
cinder_list.append(
cinder['volume_id']['get_resource'])
else:
for resource_name, resource in vnf_info['attributes'].items():
if '.yaml' in resource_name:
heat_dict = yaml.safe_load(resource)
for resource_name, resource in heat_dict['resources'].\
items():
if resource.get('properties') and resource.get(
'properties').get(block_key):
for cinder in resource['properties'][block_key]:
if cinder['volume_id'].get('get_resource'):
cinder_list.append(
cinder['volume_id']['get_resource'])
return cinder_list
def get_grant_resource(
self,
vnf_instance,
vnf_info,
scale_vnf_request,
placement_obj_list,
vim_connection_info,
del_list):
if scale_vnf_request.type == 'SCALE_OUT':
self._get_grant_resource_scale_out(vnf_info,
scale_vnf_request,
placement_obj_list)
else:
self.get_grant_resource_scale_in(vnf_instance,
vnf_info,
vim_connection_info,
del_list)
def _get_grant_resource_scale_out(
self,
vnf_info,
scale_vnf_request,
placement_obj_list):
add_resources = []
affinity_list = []
placement_constraint_list = []
uuid_list = []
port_uuid_list = []
storage_uuid_list = []
heat_template = vnf_info['attributes']['heat_template']
heat_resource = yaml.safe_load(heat_template)
key_vnfd = scale_vnf_request.aspect_id + '_scale_out'
ajust_prop = heat_resource['resources'][key_vnfd]['properties']
ajust = ajust_prop['scaling_adjustment']
size = ajust * scale_vnf_request.number_of_steps
yaml_name = scale_vnf_request.aspect_id + '_res.yaml'
if not vnf_info['attributes'].get(yaml_name):
yaml_name = scale_vnf_request.aspect_id + '.hot.yaml'
nested_hot = yaml.safe_load(
vnf_info['attributes'][yaml_name])
for resource_name, resource in nested_hot['resources'].items():
if resource['type'] == 'OS::Nova::Server':
for i in range(size):
add_uuid = uuidutils.generate_uuid()
rsc = objects.ResourceDefinition(
id=add_uuid,
type=constants.TYPE_COMPUTE,
vdu_id=resource_name,
resource_template_id=resource_name)
add_resources.append(rsc)
uuid_list.append(add_uuid)
for net in resource.get('networks', []):
add_uuid = uuidutils.generate_uuid()
port_rsc = net['port']['get_resource']
rsc = objects.ResourceDefinition(
id=add_uuid,
type=constants.TYPE_LINKPORT,
vdu_id=resource_name,
resource_template_id=port_rsc)
add_resources.append(rsc)
port_uuid_list.append(add_uuid)
if resource['properties'].get('block_device_mapping_v2'):
for i in range(size):
for cinder in resource['properties'].get(
'block_device_mapping_v2', []):
add_uuid = uuidutils.generate_uuid()
vol_rsc = cinder['volume_id']['get_resource']
rsc = objects.ResourceDefinition(
id=add_uuid,
type=constants.TYPE_STORAGE,
vdu_id=resource_name,
resource_template_id=vol_rsc)
add_resources.append(rsc)
storage_uuid_list.append(add_uuid)
if resource['properties'].get('scheduler_hints'):
sch_hint = resource['properties']['scheduler_hints']
if sch_hint['group'].get('get_param'):
affinity_name = sch_hint['group']['get_param']
else:
affinity_name = sch_hint['group']['get_resource']
for placement in placement_obj_list:
if placement.server_group_name == affinity_name:
for uuid in uuid_list:
rsc = objects.ConstraintResourceRef(
id_type='GRANT', resource_id=uuid)
plm = jsonutils.loads(placement.resource)
plm.append(rsc.to_dict())
placement.resource = jsonutils.dumps(plm)
affinity_list.append(affinity_name)
break
if resource['type'] == 'OS::Cinder::VolumeAttachment':
for i in range(size):
add_uuid = uuidutils.generate_uuid()
vol_rsc = resource['properties']['instance_uuid']
rsc = objects.ResourceDefinition(
id=add_uuid,
type=constants.TYPE_STORAGE,
vdu_id=vol_rsc['get_resource'],
resource_template_id=resource_name)
add_resources.append(rsc)
storage_uuid_list.append(add_uuid)
vnf_info['uuid_list'] = uuid_list
vnf_info['port_uuid_list'] = port_uuid_list
vnf_info['storage_uuid_list'] = storage_uuid_list
for placement in placement_obj_list:
if placement.server_group_name in affinity_list:
plm = jsonutils.loads(placement.resource)
addRsc = []
for pl in plm:
vim_id = pl.get('vim_connection_id')
addRsc.append(
objects.ConstraintResourceRef(
id_type=pl['id_type'],
resource_id=pl['resource_id'],
vim_connection_id=vim_id))
placement_constraint = objects.PlacementConstraint(
affinity_or_anti_affinity='ANTI_AFFINITY',
scope='ZONE',
resource=addRsc,
fallback_best_effort=True)
placement_constraint_list.append(placement_constraint)
vnf_info['addResources'] = add_resources
vnf_info['removeResources'] = []
vnf_info['affinity_list'] = affinity_list
vnf_info['placement_constraint_list'] = placement_constraint_list
def get_grant_resource_scale_in(
self,
vnf_instance,
vnf_info,
vim_connection_info,
del_list):
remove_resources = []
access_info = vim_connection_info.access_info
heatclient = hc.HeatClient(access_info,
region_name=access_info.get('region'))
inst_info = vnf_instance.instantiated_vnf_info
for del_rsc in del_list:
scale_resurce_list = heatclient.resource_get_list(del_rsc)
for rsc in scale_resurce_list:
if rsc.resource_type == 'OS::Nova::Server':
for vnfc_resource in inst_info.vnfc_resource_info:
if vnfc_resource.\
compute_resource.resource_id == \
rsc.physical_resource_id:
cmo_rsc = vnfc_resource.compute_resource
vim_id = cmo_rsc.vim_connection_id
rsc_id = cmo_rsc.resource_id
resource = objects.ResourceDefinition(
id=vnfc_resource.id,
type=constants.TYPE_COMPUTE,
vdu_id=vnfc_resource.vdu_id,
resource_template_id=vnfc_resource.vdu_id,
resource=objects.ResourceHandle(
vim_connection_id=vim_id,
resource_id=rsc_id))
remove_resources.append(resource)
if rsc.resource_type == 'OS::Neutron::Port':
for vl_resource in \
inst_info.vnf_virtual_link_resource_info:
for cp_resource in vl_resource.vnf_link_ports:
cp_handl = cp_resource.resource_handle
cp_id = cp_handl.resource_id
vim_id = cp_handl.vim_connection_id
if cp_id == rsc.physical_resource_id:
for vnfc_resource in inst_info.\
vnfc_resource_info:
for vnfc_cp_rsc in vnfc_resource.\
vnfc_cp_info:
if cp_resource.\
cp_instance_id == \
vnfc_cp_rsc.id:
p_id = cp_resource.id
v_id = vnfc_resource.vdu_id
d_id = vnfc_cp_rsc.cpd_id
r_hd = objects.\
ResourceHandle()
r_hd.\
vim_connection_id = \
vim_id
r_hd.resource_id = cp_id
rs = objects.\
ResourceDefinition()
rs.id = p_id
rs.type = 'constants.\
TYPE_LINKPORT'
rs.vdu_id = v_id
rs.resource_template_id = d_id
rs.resource = r_hd
remove_resources.append(
rs)
if rsc.resource_type == 'OS::Cinder::Volume':
st_info = inst_info.virtual_storage_resource_info
for storage_resource in st_info:
st_rsc = storage_resource.storage_resource
st_id = st_rsc.resource_id
if st_id == rsc.physical_resource_id:
ins_vnfc = inst_info.vnfc_resource_info
for vnfc_resource in ins_vnfc:
s_ids = vnfc_resource.storage_resource_ids
if storage_resource.id in s_ids:
rs = objects.ResourceDefinition()
rs.id = storage_resource.id
rs.type = 'STORAGE'
rs.vdu_id = vnfc_resource.vdu_id
tmp_id = storage_resource.\
virtual_storage_desc_id
rs.resource_template_id = tmp_id
r_hd = objects.ResourceHandle()
vim_id = st_rsc.vim_connection_id
rsc_id = st_rsc.resource_id
r_hd.vim_connection_id = vim_id
r_hd.resource_id = rsc_id
rs.resource = r_hd
remove_resources.append(rs)
vnf_info['addResources'] = []
vnf_info['removeResources'] = remove_resources
vnf_info['affinity_list'] = []
vnf_info['placement_constraint_list'] = []

View File

@ -80,10 +80,35 @@ class VnfScaleAbstractDriver(extensions.PluginInterface):
pass
@abc.abstractmethod
def scale_in_reverse_wait(self,
def scale_out_initial(self,
context,
plugin,
auth_attr,
vnf_info,
scale_vnf_request,
region_name):
pass
@abc.abstractmethod
def scale_update_wait(self,
context,
plugin,
auth_attr,
vnf_info,
region_name):
pass
@abc.abstractmethod
def get_cinder_list(self,
vnf_info):
pass
@abc.abstractmethod
def get_grant_resource(self,
vnf_instance,
vnf_info,
scale_vnf_request,
placement_obj_list,
vim_connection_info,
del_list):
pass