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': { 'vnfInstance': {
'href': self._get_vnf_instance_href(vnf_instance)}}} 'href': self._get_vnf_instance_href(vnf_instance)}}}
# call sendNotification # call send_notification
self.rpc_api.send_notification(context, notification) self.rpc_api.send_notification(context, notification)
result = self._view_builder.create(vnf_instance) result = self._view_builder.create(vnf_instance)

View File

@ -25,7 +25,6 @@ import time
import traceback import traceback
import yaml import yaml
from glance_store import exceptions as store_exceptions from glance_store import exceptions as store_exceptions
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
@ -51,6 +50,7 @@ from tacker.common import utils
import tacker.conf import tacker.conf
from tacker import context as t_context from tacker import context as t_context
from tacker.db.common_services import common_services_db 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.nfvo import nfvo_db
from tacker.db.vnfm import vnfm_db from tacker.db.vnfm import vnfm_db
from tacker.extensions import nfvo from tacker.extensions import nfvo
@ -66,6 +66,7 @@ from tacker import service as tacker_service
from tacker import version from tacker import version
from tacker.vnflcm import utils as vnflcm_utils from tacker.vnflcm import utils as vnflcm_utils
from tacker.vnflcm import vnflcm_driver from tacker.vnflcm import vnflcm_driver
from tacker.vnfm import nfvo_client
from tacker.vnfm import plugin from tacker.vnfm import plugin
CONF = tacker.conf.CONF CONF = tacker.conf.CONF
@ -230,6 +231,65 @@ def revert_update_lcm(function):
return decorated_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): class Conductor(manager.Manager):
def __init__(self, host, conf=None): def __init__(self, host, conf=None):
if conf: if conf:
@ -661,9 +721,10 @@ class Conductor(manager.Manager):
vim_connection_info = objects.VimConnectionInfo.\ vim_connection_info = objects.VimConnectionInfo.\
obj_from_primitive(vim_info, context) obj_from_primitive(vim_info, context)
vnflcm_utils._build_instantiated_vnf_info(vnfd_dict, if not vnf_instance.instantiated_vnf_info.instance_id:
instantiate_vnf_req, vnf_instance, vnflcm_utils._build_instantiated_vnf_info(
vim_id=vim_connection_info.vim_id) vnfd_dict, instantiate_vnf_req, vnf_instance,
vim_id=vim_connection_info.vim_id)
if vnf_instance.instantiated_vnf_info.instance_id: if vnf_instance.instantiated_vnf_info.instance_id:
self.vnf_manager.invoke(vim_connection_info.vim_type, 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, {'zip': csar_path, 'folder': csar_zip_temp_path,
'uuid': vnf_pack.id}) '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 @log.log
def _get_vnf_notify(self, context, id): def _get_vnf_notify(self, context, id):
try: try:
@ -790,6 +1367,7 @@ class Conductor(manager.Manager):
is_automatic_invocation = \ is_automatic_invocation = \
kwargs.get('is_automatic_invocation', False) kwargs.get('is_automatic_invocation', False)
error = kwargs.get('error', None) error = kwargs.get('error', None)
# Used for timing control when a failure occurs
if old_vnf_instance: if old_vnf_instance:
vnf_instance_id = old_vnf_instance.id vnf_instance_id = old_vnf_instance.id
@ -918,7 +1496,7 @@ class Conductor(manager.Manager):
self.__set_auth_subscription(line) self.__set_auth_subscription(line)
for num in range(CONF.vnf_lcm.retry_num): 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( auth_client = auth.auth_manager.get_auth_client(
notification['subscriptionId']) notification['subscriptionId'])
response = auth_client.post( response = auth_client.post(
@ -965,6 +1543,12 @@ class Conductor(manager.Manager):
instantiate_vnf, instantiate_vnf,
vnf_lcm_op_occs_id): vnf_lcm_op_occs_id):
self._instantiate_grant(context,
vnf_instance,
vnf_dict,
instantiate_vnf,
vnf_lcm_op_occs_id)
try: try:
# Update vnf_lcm_op_occs table and send notification "PROCESSING" # Update vnf_lcm_op_occs table and send notification "PROCESSING"
self._send_lcm_op_occ_notification( self._send_lcm_op_occ_notification(
@ -993,6 +1577,8 @@ class Conductor(manager.Manager):
instantiation_state=fields.VnfInstanceState. instantiation_state=fields.VnfInstanceState.
INSTANTIATED, task_state=None) INSTANTIATED, task_state=None)
self._create_placement(context, vnf_dict)
# Update vnf_lcm_op_occs table and send notification "COMPLETED" # Update vnf_lcm_op_occs table and send notification "COMPLETED"
self._send_lcm_op_occ_notification( self._send_lcm_op_occ_notification(
context=context, context=context,
@ -1024,6 +1610,11 @@ class Conductor(manager.Manager):
@coordination.synchronized('{vnf_instance[id]}') @coordination.synchronized('{vnf_instance[id]}')
def terminate(self, context, vnf_lcm_op_occs_id, def terminate(self, context, vnf_lcm_op_occs_id,
vnf_instance, terminate_vnf_req, vnf_dict): vnf_instance, terminate_vnf_req, vnf_dict):
self._terminate_grant(context,
vnf_instance,
vnf_lcm_op_occs_id)
try: try:
old_vnf_instance = copy.deepcopy(vnf_instance) old_vnf_instance = copy.deepcopy(vnf_instance)
@ -1042,6 +1633,9 @@ class Conductor(manager.Manager):
self.vnflcm_driver.terminate_vnf(context, vnf_instance, self.vnflcm_driver.terminate_vnf(context, vnf_instance,
terminate_vnf_req) terminate_vnf_req)
self._delete_placement(context, vnf_instance.id)
self._change_vnf_status(context, vnf_instance.id, self._change_vnf_status(context, vnf_instance.id,
_PENDING_STATUS, 'INACTIVE') _PENDING_STATUS, 'INACTIVE')
@ -1086,6 +1680,12 @@ class Conductor(manager.Manager):
heal_vnf_request, heal_vnf_request,
vnf_lcm_op_occs_id): vnf_lcm_op_occs_id):
self._heal_grant(context,
vnf_instance,
vnf_dict,
heal_vnf_request,
vnf_lcm_op_occs_id)
try: try:
old_vnf_instance = copy.deepcopy(vnf_instance) old_vnf_instance = copy.deepcopy(vnf_instance)
@ -1117,6 +1717,8 @@ class Conductor(manager.Manager):
self.vnflcm_driver._vnf_instance_update(context, vnf_instance, self.vnflcm_driver._vnf_instance_update(context, vnf_instance,
task_state=None) task_state=None)
self._update_placement(context, vnf_dict, vnf_instance)
# update vnf_lcm_op_occs and send notification "COMPLETED" # update vnf_lcm_op_occs and send notification "COMPLETED"
self._send_lcm_op_occ_notification( self._send_lcm_op_occ_notification(
context=context, context=context,
@ -1150,16 +1752,15 @@ class Conductor(manager.Manager):
@coordination.synchronized('{vnf_instance[id]}') @coordination.synchronized('{vnf_instance[id]}')
def scale(self, context, vnf_info, vnf_instance, scale_vnf_request): def scale(self, context, vnf_info, vnf_instance, scale_vnf_request):
# Check if vnf is in instantiated state. vnf_lcm_op_occ = vnf_info['vnf_lcm_op_occ']
vnf_instance = objects.VnfInstance.get_by_id(context, vnf_lcm_op_occ_id = vnf_lcm_op_occ.id
vnf_instance.id)
if vnf_instance.instantiation_state == \ self._scale_grant(
fields.VnfInstanceState.NOT_INSTANTIATED: context,
LOG.error("Scale action cannot be performed on vnf %(id)s " vnf_info,
"which is in %(state)s state.", vnf_instance,
{"id": vnf_instance.id, scale_vnf_request,
"state": vnf_instance.instantiation_state}) vnf_lcm_op_occ_id)
return
self.vnflcm_driver.scale_vnf( self.vnflcm_driver.scale_vnf(
context, vnf_info, vnf_instance, scale_vnf_request) 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""" """VNF LCM OP OCCS Fields"""
__tablename__ = 'vnf_lcm_op_occs' __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), vnf_instance_id = sa.Column(sa.String(36),
sa.ForeignKey('vnf_instances.id'), sa.ForeignKey('vnf_instances.id'),
nullable=False) nullable=False)
@ -316,3 +316,17 @@ class VnfLcmOpOccs(model_base.BASE, models.SoftDeleteMixin,
resource_changes = sa.Column(sa.JSON(), nullable=True) resource_changes = sa.Column(sa.JSON(), nullable=True)
changed_info = sa.Column(sa.JSON(), nullable=True) changed_info = sa.Column(sa.JSON(), nullable=True)
error_point = sa.Column(sa.Integer, nullable=False) 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_db.exception import DBDuplicateEntry
from oslo_log import log as logging from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import timeutils from oslo_utils import timeutils
from oslo_utils import uuidutils from oslo_utils import uuidutils
@ -29,9 +30,11 @@ from sqlalchemy import schema
from tacker._i18n import _ from tacker._i18n import _
from tacker.api.v1 import attributes from tacker.api.v1 import attributes
from tacker.common import exceptions from tacker.common import exceptions
import tacker.conf
from tacker import context as t_context from tacker import context as t_context
from tacker.db.common_services import common_services_db_plugin from tacker.db.common_services import common_services_db_plugin
from tacker.db import db_base from tacker.db import db_base
from tacker.db.db_sqlalchemy import models
from tacker.db import model_base from tacker.db import model_base
from tacker.db import models_v1 from tacker.db import models_v1
from tacker.db.nfvo import ns_db from tacker.db.nfvo import ns_db
@ -40,6 +43,8 @@ from tacker.extensions import vnfm
from tacker import manager from tacker import manager
from tacker.plugins.common import constants from tacker.plugins.common import constants
CONF = tacker.conf.CONF
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
_ACTIVE_UPDATE = (constants.ACTIVE, constants.PENDING_UPDATE, _ACTIVE_UPDATE = (constants.ACTIVE, constants.PENDING_UPDATE,
constants.PENDING_HEAL) constants.PENDING_HEAL)
@ -812,3 +817,51 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
constants.ERROR] constants.ERROR]
return self._mark_vnf_status( return self._mark_vnf_status(
vnf_id, exclude_status, constants.DEAD) 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_artifact')
__import__('tacker.objects.vnf_lcm_subscriptions') __import__('tacker.objects.vnf_lcm_subscriptions')
__import__('tacker.objects.scale_vnf_request') __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" ACTIVE = "ACTIVE"
ACK = "ACK" ACK = "ACK"
INACTIVE = "INACTIVE" INACTIVE = "INACTIVE"
PENDING_INSTANTIATE = "PENDING_INSTANTIATE"
PENDING_CREATE = "PENDING_CREATE" PENDING_CREATE = "PENDING_CREATE"
PENDING_UPDATE = "PENDING_UPDATE" PENDING_UPDATE = "PENDING_UPDATE"
PENDING_DELETE = "PENDING_DELETE" PENDING_DELETE = "PENDING_DELETE"
PENDING_SCALE_IN = "PENDING_SCALE_IN" PENDING_SCALE_IN = "PENDING_SCALE_IN"
PENDING_SCALE_OUT = "PENDING_SCALE_OUT" PENDING_SCALE_OUT = "PENDING_SCALE_OUT"
PENDING_HEAL = "PENDING_HEAL" PENDING_HEAL = "PENDING_HEAL"
PENDING_TERMINATE = "PENDING_TERMINATE"
DEAD = "DEAD" DEAD = "DEAD"
ERROR = "ERROR" ERROR = "ERROR"
NACK = "NACK" NACK = "NACK"
@ -55,6 +55,7 @@ POLICY_SCALING_ACTIONS = (ACTION_SCALE_OUT,
ACTION_SCALE_IN) = ('out', 'in') ACTION_SCALE_IN) = ('out', 'in')
POLICY_ACTIONS = {POLICY_SCALING: POLICY_SCALING_ACTIONS} POLICY_ACTIONS = {POLICY_SCALING: POLICY_SCALING_ACTIONS}
POLICY_ALARMING = 'tosca.policies.tacker.Alarming' POLICY_ALARMING = 'tosca.policies.tacker.Alarming'
POLICY_EVENT_ALARMING = 'tosca.policies.tacker.EventAlarming'
VALID_POLICY_TYPES = [POLICY_SCALING, POLICY_ALARMING] VALID_POLICY_TYPES = [POLICY_SCALING, POLICY_ALARMING]
POLICY_RESERVATION = 'tosca.policies.tacker.Reservation' POLICY_RESERVATION = 'tosca.policies.tacker.Reservation'
RESERVATION_POLICY_ACTIONS = ['start_actions', RESERVATION_POLICY_ACTIONS = ['start_actions',
@ -68,6 +69,8 @@ RES_TYPE_VNF = "vnf"
RES_TYPE_VIM = "vim" RES_TYPE_VIM = "vim"
RES_EVT_CREATE = "CREATE" RES_EVT_CREATE = "CREATE"
RES_EVT_INSTANTIATE = "INSTANTIATE"
RES_EVT_TERMINATE = "TERMINATE"
RES_EVT_DELETE = "DELETE" RES_EVT_DELETE = "DELETE"
RES_EVT_UPDATE = "UPDATE" RES_EVT_UPDATE = "UPDATE"
RES_EVT_MONITOR = "MONITOR" 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_CREATED_FLD = "created_at"
RES_EVT_DELETED_FLD = "deleted_at" RES_EVT_DELETED_FLD = "deleted_at"
RES_EVT_UPDATED_FLD = "updated_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 = _get_template('test_tosca_openwrt.yaml')
tosca_vnfd_openwrt_param = _get_template('test_tosca_openwrt_param.yaml') tosca_vnfd_openwrt_param = _get_template('test_tosca_openwrt_param.yaml')
tosca_invalid_vnfd = _get_template('test_tosca_parser_failure.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') config_data = _get_template('config_data.yaml')
update_config_data = _get_template('update_config_data.yaml') update_config_data = _get_template('update_config_data.yaml')
hot_data = _get_template('hot_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')) 'tosca_vnffgd_wrong_cp_number_template.yaml'))
vnfd_instance_reservation_alarm_scale_tosca_template = _get_template( vnfd_instance_reservation_alarm_scale_tosca_template = _get_template(
'test_tosca-vnfd-instance-reservation.yaml') '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(): def get_dummy_vnfd_obj():
@ -190,6 +196,57 @@ def get_dummy_vnf(status='PENDING_CREATE', scaling_group=False,
return dummy_vnf 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(): def get_dummy_vnf_config_attr():
return {'status': 'PENDING_CREATE', 'instance_id': None, 'name': return {'status': 'PENDING_CREATE', 'instance_id': None, 'name':
u'test_openwrt', 'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437', u'test_openwrt', 'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
@ -483,3 +540,23 @@ def get_dummy_vim_connection_info():
'created_at': '', 'deleted': False, 'deleted_at': '', 'created_at': '', 'deleted': False, 'deleted_at': '',
'id': 'fake_id', 'updated_at': '', 'id': 'fake_id', 'updated_at': '',
'vim_id': 'fake_vim_id', 'vim_type': 'openstack'} '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 { return {
"id": id,
"tenant_id": uuidsentinel.tenant_id, "tenant_id": uuidsentinel.tenant_id,
'operation_state': 'PROCESSING', 'operation_state': 'PROCESSING',
'state_entered_time': datetime.datetime(1900, 1, 1, 1, 1, 1, 'state_entered_time': datetime.datetime(1900, 1, 1, 1, 1, 1,

View File

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

View File

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

View File

@ -473,6 +473,10 @@ def get_instantiate_vnf_request_obj():
ext_managed_virtual_link_data = ExtManagedVirtualLinkData() ext_managed_virtual_link_data = ExtManagedVirtualLinkData()
vim_connection_info = VimConnectionInfo() vim_connection_info = VimConnectionInfo()
ext_virtual_link_data = ExtVirtualLinkData() 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.additional_params = None
instantiate_vnf_req.deleted = 0 instantiate_vnf_req.deleted = 0
instantiate_vnf_req.ext_managed_virtual_links = \ instantiate_vnf_req.ext_managed_virtual_links = \

View File

@ -113,6 +113,10 @@ class FakeDriverManager(mock.Mock):
if self.fail_method_name and \ if self.fail_method_name and \
self.fail_method_name == 'heal_vnf': self.fail_method_name == 'heal_vnf':
raise InfraDriverException("heal_vnf failed") 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: elif 'heal_vnf_wait' in args:
if self.fail_method_name and \ if self.fail_method_name and \
self.fail_method_name == 'heal_vnf_wait': 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, driver.instantiate_vnf(self.context, vnf_instance_obj, vnf_dict,
instantiate_vnf_req_obj) instantiate_vnf_req_obj)
self.assertEqual("INSTANTIATED", vnf_instance_obj.instantiation_state) self.assertEqual(1, mock_vnf_instance_save.call_count)
self.assertEqual(2, mock_vnf_instance_save.call_count) self.assertEqual(3, self._vnf_manager.invoke.call_count)
self.assertEqual(4, self._vnf_manager.invoke.call_count)
mock_final_vnf_dict.assert_called_once()
shutil.rmtree(fake_csar) shutil.rmtree(fake_csar)
@mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') @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, driver.instantiate_vnf(self.context, vnf_instance_obj, vnf_dict,
instantiate_vnf_req_obj) instantiate_vnf_req_obj)
self.assertEqual("INSTANTIATED", vnf_instance_obj.instantiation_state) self.assertEqual(1, mock_vnf_instance_save.call_count)
self.assertEqual(2, mock_vnf_instance_save.call_count) self.assertEqual(3, self._vnf_manager.invoke.call_count)
self.assertEqual(4, self._vnf_manager.invoke.call_count)
mock_final_vnf_dict.assert_called_once()
shutil.rmtree(fake_csar) shutil.rmtree(fake_csar)
@mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') @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, driver.instantiate_vnf(self.context, vnf_instance_obj, vnf_dict,
instantiate_vnf_req_obj) instantiate_vnf_req_obj)
self.assertEqual("INSTANTIATED", vnf_instance_obj.instantiation_state) self.assertEqual(1, mock_vnf_instance_save.call_count)
self.assertEqual(2, mock_vnf_instance_save.call_count) self.assertEqual(3, self._vnf_manager.invoke.call_count)
self.assertEqual(4, self._vnf_manager.invoke.call_count)
mock_final_vnf_dict.assert_called_once()
shutil.rmtree(fake_csar) shutil.rmtree(fake_csar)
@mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict')
@ -335,6 +333,12 @@ class TestVnflcmDriver(db_base.SqlTestCase):
objects.InstantiateVnfRequest.obj_from_primitive( objects.InstantiateVnfRequest.obj_from_primitive(
instantiate_vnf_req_dict, self.context) instantiate_vnf_req_dict, self.context)
vnf_instance_obj = fakes.return_vnf_instance() 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) fake_csar = os.path.join(self.temp_dir, vnf_package_id)
cfg.CONF.set_override('vnf_package_csar_path', self.temp_dir, 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, driver.instantiate_vnf(self.context, vnf_instance_obj, vnf_dict,
instantiate_vnf_req_obj) instantiate_vnf_req_obj)
self.assertEqual(2, mock_create.call_count) 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() mock_final_vnf_dict.assert_called_once()
shutil.rmtree(fake_csar) shutil.rmtree(fake_csar)
@ -419,7 +424,6 @@ class TestVnflcmDriver(db_base.SqlTestCase):
driver.instantiate_vnf(self.context, vnf_instance_obj, vnf_dict, driver.instantiate_vnf(self.context, vnf_instance_obj, vnf_dict,
instantiate_vnf_req_obj) instantiate_vnf_req_obj)
self.assertEqual(2, mock_create.call_count) self.assertEqual(2, mock_create.call_count)
self.assertEqual("INSTANTIATED", vnf_instance_obj.instantiation_state)
mock_final_vnf_dict.assert_called_once() mock_final_vnf_dict.assert_called_once()
shutil.rmtree(fake_csar) shutil.rmtree(fake_csar)
@ -540,6 +544,7 @@ class TestVnflcmDriver(db_base.SqlTestCase):
@mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict')
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()}) return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.vnflcm.utils._make_final_vnf_dict')
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id') @mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(vim_client.VimClient, "get_vim") @mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.VnfResource, "create") @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, def test_heal_vnf_without_vnfc_instance(self, mock_log, mock_save,
mock_vnf_resource_list, mock_resource_destroy, mock_vnf_resource_list, mock_resource_destroy,
mock_resource_create, mock_vim, mock_vnf_package_vnfd, 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_vnfd = fakes.return_vnf_package_vnfd()
vnf_package_id = vnf_package_vnfd.package_uuid vnf_package_id = vnf_package_vnfd.package_uuid
mock_vnf_package_vnfd.return_value = vnf_package_vnfd mock_vnf_package_vnfd.return_value = vnf_package_vnfd
@ -582,6 +588,7 @@ class TestVnflcmDriver(db_base.SqlTestCase):
self._mock_vnf_manager() self._mock_vnf_manager()
driver = vnflcm_driver.VnfLcmDriver() driver = vnflcm_driver.VnfLcmDriver()
vnf_dict = {"attributes": {}} vnf_dict = {"attributes": {}}
mock_make_final_vnf_dict.return_value = {}
driver.heal_vnf(self.context, vnf_instance, vnf_dict, heal_vnf_req) driver.heal_vnf(self.context, vnf_instance, vnf_dict, heal_vnf_req)
self.assertEqual(1, mock_save.call_count) self.assertEqual(1, mock_save.call_count)
# vnf resource software images will be deleted during # 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 # Vnf resource software images will be created during
# instantiation. # instantiation.
self.assertEqual(1, mock_resource_create.call_count) self.assertEqual(1, mock_resource_create.call_count)
# Invoke will be called 7 times, 3 for deleting the vnf # Invoke will be called 6 times, 3 for deleting the vnf
# resources and 4 during instantiation. # resources and 3 during instantiation.
self.assertEqual(7, self._vnf_manager.invoke.call_count) self.assertEqual(6, self._vnf_manager.invoke.call_count)
expected_msg = ("Request received for healing vnf '%s' " expected_msg = ("Request received for healing vnf '%s' "
"is completed successfully") "is completed successfully")
mock_log.info.assert_called_with(expected_msg, 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('tacker.vnflcm.utils._make_final_vnf_dict')
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()}) return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.vnflcm.utils._make_final_vnf_dict')
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id') @mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(vim_client.VimClient, "get_vim") @mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.VnfResource, "create") @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, def test_heal_vnf_without_vnfc_instance_infra_instantiate_vnf_fail(self,
mock_log, mock_save, mock_vnf_resource_list, mock_log, mock_save, mock_vnf_resource_list,
mock_resource_destroy, mock_resource_create, mock_vim, mock_resource_destroy, mock_resource_create, mock_vim,
mock_vnf_package_vnfd, mock_get_service_plugins, mock_vnf_package_vnfd, mock_make_final_vnf_dict,
mock_final_vnf_dict): mock_get_service_plugins, mock_final_vnf_dict):
vnf_package_vnfd = fakes.return_vnf_package_vnfd() vnf_package_vnfd = fakes.return_vnf_package_vnfd()
vnf_package_id = vnf_package_vnfd.package_uuid vnf_package_id = vnf_package_vnfd.package_uuid
mock_vnf_package_vnfd.return_value = vnf_package_vnfd 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') self._mock_vnf_manager(fail_method_name='instantiate_vnf')
driver = vnflcm_driver.VnfLcmDriver() driver = vnflcm_driver.VnfLcmDriver()
vnf_dict = {"fake": "fake_dict"} vnf_dict = {"fake": "fake_dict"}
mock_make_final_vnf_dict.return_value = {}
self.assertRaises(exceptions.VnfHealFailed, self.assertRaises(exceptions.VnfHealFailed,
driver.heal_vnf, self.context, driver.heal_vnf, self.context,
vnf_instance, vnf_dict, heal_vnf_req) vnf_instance, vnf_dict, heal_vnf_req)
@ -814,6 +823,10 @@ class TestVnflcmDriver(db_base.SqlTestCase):
@mock.patch.object(driver_manager.DriverManager, "invoke") @mock.patch.object(driver_manager.DriverManager, "invoke")
def test_scale_true(self, mock_invoke, mock_get_service_plugins): def test_scale_true(self, mock_invoke, mock_get_service_plugins):
vnf_info = fakes._get_vnf() 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") scale_vnf_request = fakes.scale_request("SCALE_IN", 1, "True")
vim_connection_info = vim_connection.VimConnectionInfo( vim_connection_info = vim_connection.VimConnectionInfo(
vim_type="fake_type") vim_type="fake_type")
@ -830,6 +843,10 @@ class TestVnflcmDriver(db_base.SqlTestCase):
def test_scale_false_in(self, mock_invoke, mock_safe_load, def test_scale_false_in(self, mock_invoke, mock_safe_load,
mock_get_service_plugins): mock_get_service_plugins):
vnf_info = fakes._get_vnf() 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") scale_vnf_request = fakes.scale_request("SCALE_IN", 1, "False")
vim_connection_info = vim_connection.VimConnectionInfo( vim_connection_info = vim_connection.VimConnectionInfo(
vim_type="fake_type") vim_type="fake_type")
@ -846,9 +863,36 @@ class TestVnflcmDriver(db_base.SqlTestCase):
return_value={'VNFM': FakeVNFMPlugin()}) return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch.object(yaml, "safe_load") @mock.patch.object(yaml, "safe_load")
@mock.patch.object(driver_manager.DriverManager, "invoke") @mock.patch.object(driver_manager.DriverManager, "invoke")
def test_scale_false_out(self, mock_invoke, mock_safe_load, def test_scale_false_out_initial(self, mock_invoke, mock_safe_load,
mock_get_service_plugins): mock_get_service_plugins):
vnf_info = fakes._get_vnf() 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") scale_vnf_request = fakes.scale_request("SCALE_OUT", 1, "False")
vim_connection_info = vim_connection.VimConnectionInfo( vim_connection_info = vim_connection.VimConnectionInfo(
vim_type="fake_type") 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 requests
import yaml import yaml
from heatclient.v1 import resources
from oslo_serialization import jsonutils
from tacker.common import exceptions from tacker.common import exceptions
from tacker.common import utils as cutils
from tacker import context from tacker import context
from tacker.db.db_sqlalchemy import models
from tacker.extensions import vnfm from tacker.extensions import vnfm
from tacker import objects from tacker import objects
from tacker.tests.common import helpers 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 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 @ddt.ddt
class TestOpenStack(base.FixturedTestCase): class TestOpenStack(base.FixturedTestCase):
client_fixture_class = client.ClientFixture client_fixture_class = client.ClientFixture
@ -144,7 +163,8 @@ class TestOpenStack(base.FixturedTestCase):
mock_get_base_hot_dict, mock_get_base_hot_dict,
mock_get_vnflcm_interface, mock_get_vnflcm_interface,
mock_format_base_hot): 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'} vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file() base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath( vnf_package_path_test = os.path.abspath(
@ -156,7 +176,7 @@ class TestOpenStack(base.FixturedTestCase):
'instantiate_vnf_request_lcm_userdata.json') 'instantiate_vnf_request_lcm_userdata.json')
inst_req_info_test.additional_params = test_json['additionalParams'] inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None 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 = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}} grant_info_test = {'vdu_name': {vnf_resource}}
@ -171,6 +191,105 @@ class TestOpenStack(base.FixturedTestCase):
grant_info=grant_info_test, grant_info=grant_info_test,
vnf_instance=vnf_instance) 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') @mock.patch('tacker.common.clients.OpenstackClients')
def test_create_heat_stack(self, mock_OpenstackClients_heat): def test_create_heat_stack(self, mock_OpenstackClients_heat):
vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid) vnf = utils.get_dummy_vnf(instance_id=self.instance_uuid)
@ -236,7 +355,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients') @mock.patch('tacker.common.clients.OpenstackClients')
def test_create_userdata_null(self, mock_OpenstackClients_heat, def test_create_userdata_null(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict): 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'} vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file() base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath( 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.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None 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 grant_info_test = None
nested_hot_dict = {'test': 'test'} nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \ mock_get_base_hot_dict.return_value = \
@ -270,7 +390,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients') @mock.patch('tacker.common.clients.OpenstackClients')
def test_create_userdataclass_null(self, mock_OpenstackClients_heat, def test_create_userdataclass_null(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict): 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'} vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file() base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath( 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.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None 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 grant_info_test = None
nested_hot_dict = {'test': 'test'} nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \ mock_get_base_hot_dict.return_value = \
@ -304,7 +425,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients') @mock.patch('tacker.common.clients.OpenstackClients')
def test_create_import_module_exception(self, mock_OpenstackClients_heat, def test_create_import_module_exception(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict): 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'} vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file() base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath( vnf_package_path_test = os.path.abspath(
@ -316,7 +438,7 @@ class TestOpenStack(base.FixturedTestCase):
'instantiate_vnf_request_lcm_userdata.json') 'instantiate_vnf_request_lcm_userdata.json')
inst_req_info_test.additional_params = test_json['additionalParams'] inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None 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 grant_info_test = None
nested_hot_dict = {'test': 'test'} nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \ mock_get_base_hot_dict.return_value = \
@ -337,7 +459,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients') @mock.patch('tacker.common.clients.OpenstackClients')
def test_create_getattr_none(self, mock_OpenstackClients_heat, def test_create_getattr_none(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict): 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'} vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file() base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath( vnf_package_path_test = os.path.abspath(
@ -370,7 +493,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients') @mock.patch('tacker.common.clients.OpenstackClients')
def test_create_missing_file(self, mock_OpenstackClients_heat, def test_create_missing_file(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict): 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'} vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file() base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath( vnf_package_path_test = os.path.abspath(
@ -382,7 +506,7 @@ class TestOpenStack(base.FixturedTestCase):
'instantiate_vnf_request_lcm_userdata.json') 'instantiate_vnf_request_lcm_userdata.json')
inst_req_info_test.additional_params = test_json['additionalParams'] inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None 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 = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}} grant_info_test = {'vdu_name': {vnf_resource}}
@ -403,7 +527,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients') @mock.patch('tacker.common.clients.OpenstackClients')
def test_create_return_none_dict(self, mock_OpenstackClients_heat, def test_create_return_none_dict(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict): 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'} vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file() base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath( vnf_package_path_test = os.path.abspath(
@ -417,7 +542,7 @@ class TestOpenStack(base.FixturedTestCase):
'UserData/lcm_user_data_non_dict.py' 'UserData/lcm_user_data_non_dict.py'
inst_req_info_test.additional_params = test_json['additionalParams'] inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None 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 = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}} grant_info_test = {'vdu_name': {vnf_resource}}
@ -438,7 +563,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients') @mock.patch('tacker.common.clients.OpenstackClients')
def test_create_none_base_hot_dict(self, mock_OpenstackClients_heat, def test_create_none_base_hot_dict(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict): 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'} vnf['placement_attr'] = {'region_name': 'dummy_region'}
inst_req_info_test = type('', (), {}) inst_req_info_test = type('', (), {})
test_json = self._json_load( test_json = self._json_load(
@ -465,7 +591,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients') @mock.patch('tacker.common.clients.OpenstackClients')
def test_create_invalid_user_data(self, mock_OpenstackClients_heat, def test_create_invalid_user_data(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict): 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'} vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file() base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath( 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.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None 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 grant_info_test = None
nested_hot_dict = {'test': 'test'} nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \ mock_get_base_hot_dict.return_value = \
@ -501,7 +628,8 @@ class TestOpenStack(base.FixturedTestCase):
def test_create_invalid_user_data_class(self, def test_create_invalid_user_data_class(self,
mock_OpenstackClients_heat, mock_OpenstackClients_heat,
mock_get_base_hot_dict): 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'} vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file() base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath( 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.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None 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 grant_info_test = None
nested_hot_dict = {'test': 'test'} nested_hot_dict = {'test': 'test'}
mock_get_base_hot_dict.return_value = \ mock_get_base_hot_dict.return_value = \
@ -536,7 +664,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients') @mock.patch('tacker.common.clients.OpenstackClients')
def test_create_lcm_user_data_and_user_data_class_no_value(self, def test_create_lcm_user_data_and_user_data_class_no_value(self,
mock_OpenstackClients_heat, mock_get_base_hot_dict): 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'} vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file() base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath( 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.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = test_json['extVirtualLinks'] 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 = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}} grant_info_test = {'vdu_name': {vnf_resource}}
@ -620,7 +749,8 @@ class TestOpenStack(base.FixturedTestCase):
@mock.patch('tacker.common.clients.OpenstackClients') @mock.patch('tacker.common.clients.OpenstackClients')
def test_create_instance_exception(self, mock_OpenstackClients_heat, def test_create_instance_exception(self, mock_OpenstackClients_heat,
mock_get_base_hot_dict): 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'} vnf['placement_attr'] = {'region_name': 'dummy_region'}
base_hot_dict_test = self._read_file() base_hot_dict_test = self._read_file()
vnf_package_path_test = os.path.abspath( vnf_package_path_test = os.path.abspath(
@ -634,7 +764,7 @@ class TestOpenStack(base.FixturedTestCase):
'UserData/lcm_user_data_invalid_script.py' 'UserData/lcm_user_data_invalid_script.py'
inst_req_info_test.additional_params = test_json['additionalParams'] inst_req_info_test.additional_params = test_json['additionalParams']
inst_req_info_test.ext_virtual_links = None 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 = type('', (), {})
vnf_resource.resource_identifier = constants.INVALID_UUID vnf_resource.resource_identifier = constants.INVALID_UUID
grant_info_test = {'vdu_name': {vnf_resource}} grant_info_test = {'vdu_name': {vnf_resource}}
@ -1633,3 +1763,190 @@ class TestOpenStack(base.FixturedTestCase):
"stack_id %s") % (vnf_instance.id, "stack_id %s") % (vnf_instance.id,
uuidsentinel.stack_id) uuidsentinel.stack_id)
self.assertEqual(expected_msg, str(result)) 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 from unittest.mock import patch
import ddt import ddt
import iso8601
from oslo_utils import uuidutils from oslo_utils import uuidutils
import yaml import yaml
@ -25,12 +26,15 @@ from tacker._i18n import _
from tacker.common import exceptions from tacker.common import exceptions
from tacker import context from tacker import context
from tacker.db.common_services import common_services_db_plugin 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 nfvo_db
from tacker.db.nfvo import ns_db from tacker.db.nfvo import ns_db
from tacker.db.vnfm import vnfm_db from tacker.db.vnfm import vnfm_db
from tacker.extensions import vnfm from tacker.extensions import vnfm
from tacker import objects
from tacker.objects import heal_vnf_request from tacker.objects import heal_vnf_request
from tacker.plugins.common import constants 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 base as db_base
from tacker.tests.unit.db import utils from tacker.tests.unit.db import utils
from tacker.vnfm import monitor from tacker.vnfm import monitor
@ -1160,3 +1164,165 @@ class TestVNFMPlugin(db_base.SqlTestCase):
mock_get_vnf.return_value = dummy_vnf mock_get_vnf.return_value = dummy_vnf
self._test_create_vnf_trigger(policy_name="start_actions", self._test_create_vnf_trigger(policy_name="start_actions",
action_value="SP_RSV-out") 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'): if vnfd_dict.get('topology_template'):
topology_template = vnfd_dict.get('topology_template') topology_template = vnfd_dict.get('topology_template')
if topology_template.get('node_templates'): if topology_template.get('node_templates'):
for a_val in topology_template.get('node_templates').values(): node_templates = topology_template.get('node_templates')
if 'interfaces' in a_val.keys(): if node_templates.get('VNF'):
interfaces = a_val.get('interfaces') vnf = node_templates.get('VNF')
if vnf.get('interfaces'):
interfaces = vnf.get('interfaces')
if interfaces.get('Vnflcm'): if interfaces.get('Vnflcm'):
vnflcm = interfaces.get('Vnflcm') vnflcm = interfaces.get('Vnflcm')
if vnflcm: if vnflcm:

View File

@ -276,7 +276,8 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
try: try:
instance_id = self._vnf_manager.invoke( instance_id = self._vnf_manager.invoke(
vim_connection_info.vim_type, 'instantiate_vnf', 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, vnfd_dict=final_vnf_dict, grant_response=vnf_resources,
vim_connection_info=vim_connection_info, vim_connection_info=vim_connection_info,
base_hot_dict=base_hot_dict, base_hot_dict=base_hot_dict,
@ -293,12 +294,9 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
id=vnf_instance.id, id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp)) error=encodeutils.exception_to_unicode(exp))
vnf_instance.instantiated_vnf_info = objects.InstantiatedVnfInfo( if vnf_instance.instantiated_vnf_info and\
flavour_id=instantiate_vnf_req.flavour_id, not vnf_instance.instantiated_vnf_info.instance_id:
instantiation_level_id=instantiate_vnf_req.instantiation_level_id, vnf_instance.instantiated_vnf_info.instance_id = instance_id
vnf_instance_id=vnf_instance.id,
instance_id=instance_id,
ext_cp_info=[])
if vnf_dict['attributes'].get('scaling_group_names'): if vnf_dict['attributes'].get('scaling_group_names'):
vnf_instance.instantiated_vnf_info.scale_status = \ vnf_instance.instantiated_vnf_info.scale_status = \
vnf_dict['scale_status'] vnf_dict['scale_status']
@ -306,7 +304,7 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
try: try:
self._vnf_manager.invoke( self._vnf_manager.invoke(
vim_connection_info.vim_type, 'create_wait', vim_connection_info.vim_type, 'create_wait',
plugin=self, context=context, plugin=self._vnfm_plugin, context=context,
vnf_dict=final_vnf_dict, vnf_dict=final_vnf_dict,
vnf_id=final_vnf_dict['instance_id'], vnf_id=final_vnf_dict['instance_id'],
auth_attr=vim_connection_info.access_info) auth_attr=vim_connection_info.access_info)
@ -322,14 +320,6 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
id=vnf_instance.id, id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp)) 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 @log.log
@rollback_vnf_instantiated_resources @rollback_vnf_instantiated_resources
def instantiate_vnf(self, context, vnf_instance, vnf_dict, 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, self._instantiate_vnf(context, vnf_instance, vnf_dict,
vim_connection_info, instantiate_vnf_req) vim_connection_info, instantiate_vnf_req)
self._vnf_instance_update(context, vnf_instance,
instantiation_state=fields.VnfInstanceState.INSTANTIATED,
task_state=None)
@log.log @log.log
@revert_to_error_task_state @revert_to_error_task_state
def terminate_vnf(self, context, vnf_instance, terminate_vnf_req): def terminate_vnf(self, context, vnf_instance, terminate_vnf_req):
@ -929,7 +915,12 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
LOG.debug( LOG.debug(
"is_reverse: %s", "is_reverse: %s",
scale_vnf_request.additional_params.get('is_reverse')) 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( self._vnf_manager.invoke(
vim_connection_info.vim_type, vim_connection_info.vim_type,
'scale_in_reverse', 'scale_in_reverse',
@ -944,7 +935,27 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
) )
self._vnf_manager.invoke( self._vnf_manager.invoke(
vim_connection_info.vim_type, 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, plugin=self,
context=context, context=context,
auth_attr=vim_connection_info.access_info, auth_attr=vim_connection_info.access_info,

View File

@ -1350,10 +1350,32 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
grp_id): grp_id):
pass 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, context,
plugin, plugin,
auth_attr, auth_attr,
vnf_info, vnf_info,
region_name): region_name):
pass 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 exceptions
from tacker.common import log from tacker.common import log
from tacker.common import utils from tacker.common import utils
from tacker.db.common_services import common_services_db_plugin
from tacker.extensions import vnflcm from tacker.extensions import vnflcm
from tacker.extensions import vnfm from tacker.extensions import vnfm
from tacker import objects from tacker import objects
from tacker.objects import fields from tacker.objects import fields
from tacker.plugins.common import constants
from tacker.tosca.utils import represent_odict from tacker.tosca.utils import represent_odict
from tacker.vnflcm import utils as vnflcm_utils from tacker.vnflcm import utils as vnflcm_utils
from tacker.vnfm.infra_drivers import abstract_driver 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) 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__) LOG = logging.getLogger(__name__)
CONF = cfg.CONF CONF = cfg.CONF
@ -108,6 +115,9 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
self.STACK_RETRY_WAIT = cfg.CONF.openstack_vim.stack_retry_wait self.STACK_RETRY_WAIT = cfg.CONF.openstack_vim.stack_retry_wait
self.IMAGE_RETRIES = 10 self.IMAGE_RETRIES = 10
self.IMAGE_RETRY_WAIT = 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): def get_type(self):
return 'openstack' return 'openstack'
@ -124,7 +134,17 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
inst_req_info=None, grant_info=None, inst_req_info=None, grant_info=None,
vnf_instance=None): vnf_instance=None):
LOG.debug('vnf %s', vnf) 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) heatclient = hc.HeatClient(auth_attr, region_name)
additional_param = None additional_param = None
if inst_req_info is not 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(): for name, hot in nested_hot_dict.items():
vnf['attributes'][name] = self._format_base_hot(hot) 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) vnfd_dict = yaml.safe_load(vnfd_str)
LOG.debug('VNFD: %s', vnfd_dict) LOG.debug('VNFD: %s', vnfd_dict)
LOG.debug('VNF package path: %s', vnf_package_path) 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(): for name, value in scaleGroupDict['scaleGroupDict'].items():
hot_param_dict[name + '_desired_capacity'] = \ hot_param_dict[name + '_desired_capacity'] = \
value['default'] 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 # Add stack param to vnf_attributes
vnf['attributes'].update({'stack_param': str(hot_param_dict)}) vnf['attributes'].update({'stack_param': str(hot_param_dict)})
@ -1482,15 +1527,63 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
mark_unhealthy=True, mark_unhealthy=True,
resource_status_reason='Scale') resource_status_reason='Scale')
paramDict = {} paramDict = {}
paramDict[scale_vnf_request.aspect_id + scale_json = vnf_info['attributes']['scale_group']
'_desired_capacity'] = vnf_info['res_num'] 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 = { stack_update_param = {
'parameters': paramDict, 'parameters': paramDict,
'existing': True} 'existing': True}
heatclient.update(vnf_info['instance_id'], **stack_update_param) 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 @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, self,
context, context,
plugin, plugin,
@ -1501,3 +1594,251 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
auth_attr, infra_cnst.STACK_UPDATE_IN_PROGRESS, auth_attr, infra_cnst.STACK_UPDATE_IN_PROGRESS,
infra_cnst.STACK_UPDATE_COMPLETE, infra_cnst.STACK_UPDATE_COMPLETE,
vnfm.VNFScaleWaitFailed, region_name=region_name) 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 pass
@abc.abstractmethod @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, context,
plugin, plugin,
auth_attr, auth_attr,
vnf_info, vnf_info,
region_name): region_name):
pass 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