Support CNF scale operations based on ETSI NFV
Implements Container based VNF scale operation support with ETSI NFV-SOL003 v2.6.1 VNF Lifecycle Management. Users can scale the number of pod replicas managed by controller resources such as Kubernetes Deployment, StatefulSet, and ReplicaSet. Other changes: * Check the vnf_state before scale operation in controller as other LCM operations. * Enable to store multiple flavour to `vnfd_attribute` when `Conductor._onboard_vnfd()` method called. * For FT, sqlalchemy is used, which calls functions in pymysql.py, so the dependency module PyMySQL needs to be added. Implements: blueprint support-cnf-scale Spec: https://specs.openstack.org/openstack/tacker-specs/specs/wallaby/support-cnf-scale.rst Change-Id: I739fa60a18323b248bc913f6062ef2138ad2e6ee
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Add Container based VNF scale operation support with ETSI NFV-SOL003
|
||||||
|
v2.6.1 VNF Lifecycle Management. Users can scale the number of pod replicas
|
||||||
|
managed by controller resources such as Kubernetes Deployment, StatefulSet,
|
||||||
|
and ReplicaSet.
|
@@ -26,6 +26,7 @@ from oslo_utils import excutils
|
|||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
from sqlalchemy import exc as sqlexc
|
from sqlalchemy import exc as sqlexc
|
||||||
|
from toscaparser import tosca_template
|
||||||
|
|
||||||
import ast
|
import ast
|
||||||
import functools
|
import functools
|
||||||
@@ -53,6 +54,8 @@ from tacker.objects import fields
|
|||||||
from tacker.objects import vnf_lcm_subscriptions as subscription_obj
|
from tacker.objects import vnf_lcm_subscriptions as subscription_obj
|
||||||
from tacker.plugins.common import constants
|
from tacker.plugins.common import constants
|
||||||
from tacker.policies import vnf_lcm as vnf_lcm_policies
|
from tacker.policies import vnf_lcm as vnf_lcm_policies
|
||||||
|
from tacker.tosca import utils as toscautils
|
||||||
|
from tacker.vnflcm import utils as vnflcm_utils
|
||||||
import tacker.vnfm.nfvo_client as nfvo_client
|
import tacker.vnfm.nfvo_client as nfvo_client
|
||||||
from tacker.vnfm import vim_client
|
from tacker.vnfm import vim_client
|
||||||
from tacker import wsgi
|
from tacker import wsgi
|
||||||
@@ -977,7 +980,24 @@ class VnfLcmController(wsgi.Controller):
|
|||||||
return self._make_problem_detail(
|
return self._make_problem_detail(
|
||||||
str(e), 500, title='Internal Server Error')
|
str(e), 500, title='Internal Server Error')
|
||||||
|
|
||||||
def _scale(self, context, vnf_info, vnf_instance, request_body):
|
def _get_scale_max_level_from_vnfd(self, context, vnf_instance, aspect_id):
|
||||||
|
vnfd_dict = vnflcm_utils._get_vnfd_dict(context,
|
||||||
|
vnf_instance.vnfd_id,
|
||||||
|
vnf_instance.instantiated_vnf_info.flavour_id)
|
||||||
|
tosca = tosca_template.ToscaTemplate(parsed_params={}, a_file=False,
|
||||||
|
yaml_dict_tpl=vnfd_dict)
|
||||||
|
tosca_policies = tosca.topology_template.policies
|
||||||
|
|
||||||
|
aspect_max_level_dict = {}
|
||||||
|
toscautils._extract_policy_info(
|
||||||
|
tosca_policies, {}, {}, {}, {}, {}, aspect_max_level_dict)
|
||||||
|
|
||||||
|
return aspect_max_level_dict.get(aspect_id)
|
||||||
|
|
||||||
|
@check_vnf_state(action="scale",
|
||||||
|
instantiation_state=[fields.VnfInstanceState.INSTANTIATED],
|
||||||
|
task_state=[None])
|
||||||
|
def _scale(self, context, vnf_instance, vnf_info, request_body):
|
||||||
req_body = utils.convert_camelcase_to_snakecase(request_body)
|
req_body = utils.convert_camelcase_to_snakecase(request_body)
|
||||||
scale_vnf_request = objects.ScaleVnfRequest.obj_from_primitive(
|
scale_vnf_request = objects.ScaleVnfRequest.obj_from_primitive(
|
||||||
req_body, context=context)
|
req_body, context=context)
|
||||||
@@ -1005,18 +1025,34 @@ class VnfLcmController(wsgi.Controller):
|
|||||||
if not scale_vnf_request.additional_params.get('is_auto'):
|
if not scale_vnf_request.additional_params.get('is_auto'):
|
||||||
scale_vnf_request.additional_params['is_auto'] = "False"
|
scale_vnf_request.additional_params['is_auto'] = "False"
|
||||||
|
|
||||||
|
vim_type = vnf_instance.vim_connection_info[0].vim_type
|
||||||
if scale_vnf_request.type == 'SCALE_IN':
|
if scale_vnf_request.type == 'SCALE_IN':
|
||||||
if current_level == 0 or\
|
if current_level == 0 or\
|
||||||
current_level < scale_vnf_request.number_of_steps:
|
current_level < scale_vnf_request.number_of_steps:
|
||||||
return self._make_problem_detail(
|
return self._make_problem_detail(
|
||||||
'can not scale_in', 400, title='can not scale_in')
|
'can not scale_in', 400, title='can not scale_in')
|
||||||
|
if vim_type == "kubernetes" and\
|
||||||
|
scale_vnf_request.additional_params['is_reverse'] == "True":
|
||||||
|
return self._make_problem_detail(
|
||||||
|
'is_reverse option is not supported when Kubernetes '
|
||||||
|
'scale operation',
|
||||||
|
400,
|
||||||
|
title='is_reverse option is not supported when Kubernetes '
|
||||||
|
'scale operation')
|
||||||
scale_level = current_level - scale_vnf_request.number_of_steps
|
scale_level = current_level - scale_vnf_request.number_of_steps
|
||||||
|
|
||||||
elif scale_vnf_request.type == 'SCALE_OUT':
|
elif scale_vnf_request.type == 'SCALE_OUT':
|
||||||
scaleGroupDict = jsonutils.loads(
|
if vim_type == "kubernetes":
|
||||||
vnf_info['attributes']['scale_group'])
|
max_level = self._get_scale_max_level_from_vnfd(
|
||||||
max_level = (scaleGroupDict['scaleGroupDict']
|
context=context,
|
||||||
[scale_vnf_request.aspect_id]['maxLevel'])
|
vnf_instance=vnf_instance,
|
||||||
|
aspect_id=scale_vnf_request.aspect_id)
|
||||||
|
else:
|
||||||
|
scaleGroupDict = jsonutils.loads(
|
||||||
|
vnf_info['attributes']['scale_group'])
|
||||||
|
max_level = (scaleGroupDict['scaleGroupDict']
|
||||||
|
[scale_vnf_request.aspect_id]['maxLevel'])
|
||||||
|
|
||||||
scale_level = current_level + scale_vnf_request.number_of_steps
|
scale_level = current_level + scale_vnf_request.number_of_steps
|
||||||
if max_level < scale_level:
|
if max_level < scale_level:
|
||||||
return self._make_problem_detail(
|
return self._make_problem_detail(
|
||||||
@@ -1043,6 +1079,9 @@ class VnfLcmController(wsgi.Controller):
|
|||||||
error_point=1)
|
error_point=1)
|
||||||
vnf_lcm_op_occ.create()
|
vnf_lcm_op_occ.create()
|
||||||
|
|
||||||
|
vnf_instance.task_state = fields.VnfInstanceTaskState.SCALING
|
||||||
|
vnf_instance.save()
|
||||||
|
|
||||||
vnflcm_url = CONF.vnf_lcm.endpoint_url + \
|
vnflcm_url = CONF.vnf_lcm.endpoint_url + \
|
||||||
"/vnflcm/v1/vnf_lcm_op_occs/" + vnf_lcm_op_occs_id
|
"/vnflcm/v1/vnf_lcm_op_occs/" + vnf_lcm_op_occs_id
|
||||||
insta_url = CONF.vnf_lcm.endpoint_url + \
|
insta_url = CONF.vnf_lcm.endpoint_url + \
|
||||||
@@ -1096,7 +1135,7 @@ class VnfLcmController(wsgi.Controller):
|
|||||||
if not vnf_instance.instantiated_vnf_info.scale_status:
|
if not vnf_instance.instantiated_vnf_info.scale_status:
|
||||||
return self._make_problem_detail(
|
return self._make_problem_detail(
|
||||||
'NOT SCALE VNF', 409, title='NOT SCALE VNF')
|
'NOT SCALE VNF', 409, title='NOT SCALE VNF')
|
||||||
return self._scale(context, vnf_info, vnf_instance, body)
|
return self._scale(context, vnf_instance, vnf_info, body)
|
||||||
except vnfm.VNFNotFound as vnf_e:
|
except vnfm.VNFNotFound as vnf_e:
|
||||||
return self._make_problem_detail(
|
return self._make_problem_detail(
|
||||||
str(vnf_e), 404, title='VNF NOT FOUND')
|
str(vnf_e), 404, title='VNF NOT FOUND')
|
||||||
|
@@ -453,8 +453,6 @@ class Conductor(manager.Manager):
|
|||||||
yaml.dump(flavour.get('tpl_dict'), default_flow_style=False)
|
yaml.dump(flavour.get('tpl_dict'), default_flow_style=False)
|
||||||
vnfd_attribute.create()
|
vnfd_attribute.create()
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
@revert_upload_vnf_package
|
@revert_upload_vnf_package
|
||||||
def upload_vnf_package_content(self, context, vnf_package):
|
def upload_vnf_package_content(self, context, vnf_package):
|
||||||
vnf_package.onboarding_state = (
|
vnf_package.onboarding_state = (
|
||||||
|
@@ -109,6 +109,14 @@ class CNFCreateWaitFailed(exceptions.TackerException):
|
|||||||
message = _('CNF Create Failed with reason: %(reason)s')
|
message = _('CNF Create Failed with reason: %(reason)s')
|
||||||
|
|
||||||
|
|
||||||
|
class CNFScaleFailed(exceptions.TackerException):
|
||||||
|
message = _('CNF Scale Failed with reason: %(reason)s')
|
||||||
|
|
||||||
|
|
||||||
|
class CNFScaleWaitFailed(exceptions.TackerException):
|
||||||
|
message = _('CNF Scale Wait Failed with reason: %(reason)s')
|
||||||
|
|
||||||
|
|
||||||
class ServiceTypeNotFound(exceptions.NotFound):
|
class ServiceTypeNotFound(exceptions.NotFound):
|
||||||
message = _('service type %(service_type_id)s could not be found')
|
message = _('service type %(service_type_id)s could not be found')
|
||||||
|
|
||||||
|
@@ -137,9 +137,10 @@ class VnfInstanceTaskState(BaseTackerEnum):
|
|||||||
INSTANTIATING = 'INSTANTIATING'
|
INSTANTIATING = 'INSTANTIATING'
|
||||||
HEALING = 'HEALING'
|
HEALING = 'HEALING'
|
||||||
TERMINATING = 'TERMINATING'
|
TERMINATING = 'TERMINATING'
|
||||||
|
SCALING = 'SCALING'
|
||||||
ERROR = 'ERROR'
|
ERROR = 'ERROR'
|
||||||
|
|
||||||
ALL = (INSTANTIATING, HEALING, TERMINATING, ERROR)
|
ALL = (INSTANTIATING, HEALING, TERMINATING, SCALING, ERROR)
|
||||||
|
|
||||||
|
|
||||||
class VnfInstanceTaskStateField(BaseEnumField):
|
class VnfInstanceTaskStateField(BaseEnumField):
|
||||||
|
@@ -0,0 +1,105 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||||
|
|
||||||
|
description: Sample VNF
|
||||||
|
|
||||||
|
imports:
|
||||||
|
- etsi_nfv_sol001_common_types.yaml
|
||||||
|
- etsi_nfv_sol001_vnfd_types.yaml
|
||||||
|
- helloworld3_types.yaml
|
||||||
|
|
||||||
|
topology_template:
|
||||||
|
inputs:
|
||||||
|
descriptor_id:
|
||||||
|
type: string
|
||||||
|
descriptor_version:
|
||||||
|
type: string
|
||||||
|
provider:
|
||||||
|
type: string
|
||||||
|
product_name:
|
||||||
|
type: string
|
||||||
|
software_version:
|
||||||
|
type: string
|
||||||
|
vnfm_info:
|
||||||
|
type: list
|
||||||
|
entry_schema:
|
||||||
|
type: string
|
||||||
|
flavour_id:
|
||||||
|
type: string
|
||||||
|
flavour_description:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
substitution_mappings:
|
||||||
|
node_type: company.provider.VNF
|
||||||
|
properties:
|
||||||
|
flavour_id: scalingsteps
|
||||||
|
requirements:
|
||||||
|
virtual_link_external: []
|
||||||
|
|
||||||
|
node_templates:
|
||||||
|
VNF:
|
||||||
|
type: company.provider.VNF
|
||||||
|
properties:
|
||||||
|
flavour_description: A flavour for setting scaling_step to 2
|
||||||
|
|
||||||
|
VDU1:
|
||||||
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
|
properties:
|
||||||
|
name: vdu1
|
||||||
|
description: kubernetes controller resource as VDU
|
||||||
|
vdu_profile:
|
||||||
|
min_number_of_instances: 1
|
||||||
|
max_number_of_instances: 5
|
||||||
|
|
||||||
|
policies:
|
||||||
|
- scaling_aspects:
|
||||||
|
type: tosca.policies.nfv.ScalingAspects
|
||||||
|
properties:
|
||||||
|
aspects:
|
||||||
|
vdu1_aspect:
|
||||||
|
name: vdu1_aspect
|
||||||
|
description: vdu1 scaling aspect
|
||||||
|
max_scale_level: 2
|
||||||
|
step_deltas:
|
||||||
|
- delta_1
|
||||||
|
|
||||||
|
- vdu1_initial_delta:
|
||||||
|
type: tosca.policies.nfv.VduInitialDelta
|
||||||
|
properties:
|
||||||
|
initial_delta:
|
||||||
|
number_of_instances: 1
|
||||||
|
targets: [ VDU1 ]
|
||||||
|
|
||||||
|
- vdu1_scaling_aspect_deltas:
|
||||||
|
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||||
|
properties:
|
||||||
|
aspect: vdu1_aspect
|
||||||
|
deltas:
|
||||||
|
delta_1:
|
||||||
|
number_of_instances: 2
|
||||||
|
targets: [ VDU1 ]
|
||||||
|
|
||||||
|
- instantiation_levels:
|
||||||
|
type: tosca.policies.nfv.InstantiationLevels
|
||||||
|
properties:
|
||||||
|
levels:
|
||||||
|
instantiation_level_1:
|
||||||
|
description: Smallest size
|
||||||
|
scale_info:
|
||||||
|
vdu1_aspect:
|
||||||
|
scale_level: 0
|
||||||
|
instantiation_level_2:
|
||||||
|
description: Largest size
|
||||||
|
scale_info:
|
||||||
|
vdu1_aspect:
|
||||||
|
scale_level: 2
|
||||||
|
default_level: instantiation_level_1
|
||||||
|
|
||||||
|
- vdu1_instantiation_levels:
|
||||||
|
type: tosca.policies.nfv.VduInstantiationLevels
|
||||||
|
properties:
|
||||||
|
levels:
|
||||||
|
instantiation_level_1:
|
||||||
|
number_of_instances: 1
|
||||||
|
instantiation_level_2:
|
||||||
|
number_of_instances: 5
|
||||||
|
targets: [ VDU1 ]
|
@@ -0,0 +1,105 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||||
|
|
||||||
|
description: Sample VNF
|
||||||
|
|
||||||
|
imports:
|
||||||
|
- etsi_nfv_sol001_common_types.yaml
|
||||||
|
- etsi_nfv_sol001_vnfd_types.yaml
|
||||||
|
- helloworld3_types.yaml
|
||||||
|
|
||||||
|
topology_template:
|
||||||
|
inputs:
|
||||||
|
descriptor_id:
|
||||||
|
type: string
|
||||||
|
descriptor_version:
|
||||||
|
type: string
|
||||||
|
provider:
|
||||||
|
type: string
|
||||||
|
product_name:
|
||||||
|
type: string
|
||||||
|
software_version:
|
||||||
|
type: string
|
||||||
|
vnfm_info:
|
||||||
|
type: list
|
||||||
|
entry_schema:
|
||||||
|
type: string
|
||||||
|
flavour_id:
|
||||||
|
type: string
|
||||||
|
flavour_description:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
substitution_mappings:
|
||||||
|
node_type: company.provider.VNF
|
||||||
|
properties:
|
||||||
|
flavour_id: simple
|
||||||
|
requirements:
|
||||||
|
virtual_link_external: []
|
||||||
|
|
||||||
|
node_templates:
|
||||||
|
VNF:
|
||||||
|
type: company.provider.VNF
|
||||||
|
properties:
|
||||||
|
flavour_description: A simple flavour
|
||||||
|
|
||||||
|
VDU1:
|
||||||
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
|
properties:
|
||||||
|
name: vdu1
|
||||||
|
description: kubernetes controller resource as VDU
|
||||||
|
vdu_profile:
|
||||||
|
min_number_of_instances: 1
|
||||||
|
max_number_of_instances: 3
|
||||||
|
|
||||||
|
policies:
|
||||||
|
- scaling_aspects:
|
||||||
|
type: tosca.policies.nfv.ScalingAspects
|
||||||
|
properties:
|
||||||
|
aspects:
|
||||||
|
vdu1_aspect:
|
||||||
|
name: vdu1_aspect
|
||||||
|
description: vdu1 scaling aspect
|
||||||
|
max_scale_level: 2
|
||||||
|
step_deltas:
|
||||||
|
- delta_1
|
||||||
|
|
||||||
|
- vdu1_initial_delta:
|
||||||
|
type: tosca.policies.nfv.VduInitialDelta
|
||||||
|
properties:
|
||||||
|
initial_delta:
|
||||||
|
number_of_instances: 1
|
||||||
|
targets: [ VDU1 ]
|
||||||
|
|
||||||
|
- vdu1_scaling_aspect_deltas:
|
||||||
|
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||||
|
properties:
|
||||||
|
aspect: vdu1_aspect
|
||||||
|
deltas:
|
||||||
|
delta_1:
|
||||||
|
number_of_instances: 1
|
||||||
|
targets: [ VDU1 ]
|
||||||
|
|
||||||
|
- instantiation_levels:
|
||||||
|
type: tosca.policies.nfv.InstantiationLevels
|
||||||
|
properties:
|
||||||
|
levels:
|
||||||
|
instantiation_level_1:
|
||||||
|
description: Smallest size
|
||||||
|
scale_info:
|
||||||
|
vdu1_aspect:
|
||||||
|
scale_level: 0
|
||||||
|
instantiation_level_2:
|
||||||
|
description: Largest size
|
||||||
|
scale_info:
|
||||||
|
vdu1_aspect:
|
||||||
|
scale_level: 2
|
||||||
|
default_level: instantiation_level_1
|
||||||
|
|
||||||
|
- vdu1_instantiation_levels:
|
||||||
|
type: tosca.policies.nfv.VduInstantiationLevels
|
||||||
|
properties:
|
||||||
|
levels:
|
||||||
|
instantiation_level_1:
|
||||||
|
number_of_instances: 1
|
||||||
|
instantiation_level_2:
|
||||||
|
number_of_instances: 3
|
||||||
|
targets: [ VDU1 ]
|
@@ -0,0 +1,32 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||||
|
|
||||||
|
description: Sample VNF
|
||||||
|
|
||||||
|
imports:
|
||||||
|
- etsi_nfv_sol001_common_types.yaml
|
||||||
|
- etsi_nfv_sol001_vnfd_types.yaml
|
||||||
|
- helloworld3_types.yaml
|
||||||
|
- helloworld3_df_simple.yaml
|
||||||
|
- helloworld3_df_scalingsteps.yaml
|
||||||
|
|
||||||
|
topology_template:
|
||||||
|
inputs:
|
||||||
|
selected_flavour:
|
||||||
|
type: string
|
||||||
|
description: VNF deployment flavour selected by the consumer. It is provided in the API
|
||||||
|
|
||||||
|
node_templates:
|
||||||
|
VNF:
|
||||||
|
type: company.provider.VNF
|
||||||
|
properties:
|
||||||
|
flavour_id: { get_input: selected_flavour }
|
||||||
|
descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d70a1177
|
||||||
|
provider: Company
|
||||||
|
product_name: Sample VNF
|
||||||
|
software_version: '1.0'
|
||||||
|
descriptor_version: '1.0'
|
||||||
|
vnfm_info:
|
||||||
|
- Tacker
|
||||||
|
requirements:
|
||||||
|
#- virtual_link_external # mapped in lower-level templates
|
||||||
|
#- virtual_link_internal # mapped in lower-level templates
|
@@ -0,0 +1,53 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||||
|
|
||||||
|
description: VNF type definition
|
||||||
|
|
||||||
|
imports:
|
||||||
|
- etsi_nfv_sol001_common_types.yaml
|
||||||
|
- etsi_nfv_sol001_vnfd_types.yaml
|
||||||
|
|
||||||
|
node_types:
|
||||||
|
company.provider.VNF:
|
||||||
|
derived_from: tosca.nodes.nfv.VNF
|
||||||
|
properties:
|
||||||
|
descriptor_id:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d70a1177 ] ]
|
||||||
|
default: b1bb0ce7-ebca-4fa7-95ed-4840d70a1177
|
||||||
|
descriptor_version:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ '1.0' ] ]
|
||||||
|
default: '1.0'
|
||||||
|
provider:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ 'Company' ] ]
|
||||||
|
default: 'Company'
|
||||||
|
product_name:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ 'Sample VNF' ] ]
|
||||||
|
default: 'Sample VNF'
|
||||||
|
software_version:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ '1.0' ] ]
|
||||||
|
default: '1.0'
|
||||||
|
vnfm_info:
|
||||||
|
type: list
|
||||||
|
entry_schema:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ Tacker ] ]
|
||||||
|
default: [ Tacker ]
|
||||||
|
flavour_id:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ simple,scalingsteps ] ]
|
||||||
|
default: simple
|
||||||
|
flavour_description:
|
||||||
|
type: string
|
||||||
|
default: ""
|
||||||
|
requirements:
|
||||||
|
- virtual_link_external:
|
||||||
|
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||||
|
- virtual_link_internal:
|
||||||
|
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||||
|
interfaces:
|
||||||
|
Vnflcm:
|
||||||
|
type: tosca.interfaces.nfv.Vnflcm
|
@@ -0,0 +1,22 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: vdu1
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: webserver
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: webserver
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
protocol: TCP
|
@@ -0,0 +1,22 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: ReplicaSet
|
||||||
|
metadata:
|
||||||
|
name: vdu1
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: webserver
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: webserver
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
protocol: TCP
|
@@ -0,0 +1,61 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: vdu1
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
serviceName: "nginx"
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
terminationGracePeriodSeconds: 10
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: k8s.gcr.io/nginx-slim:0.8
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
name: web
|
||||||
|
volumeMounts:
|
||||||
|
- name: www
|
||||||
|
mountPath: /usr/share/nginx/html
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- metadata:
|
||||||
|
name: www
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 64Mi
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: www-vdu1-0
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
capacity:
|
||||||
|
storage: 64Mi
|
||||||
|
hostPath:
|
||||||
|
path: /data
|
||||||
|
type: DirectoryOrCreate
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: www-vdu1-1
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
capacity:
|
||||||
|
storage: 64Mi
|
||||||
|
hostPath:
|
||||||
|
path: /data
|
||||||
|
type: DirectoryOrCreate
|
@@ -0,0 +1,19 @@
|
|||||||
|
TOSCA-Meta-File-Version: 1.0
|
||||||
|
Created-by: dummy_user
|
||||||
|
CSAR-Version: 1.1
|
||||||
|
Entry-Definitions: Definitions/helloworld3_top.vnfd.yaml
|
||||||
|
|
||||||
|
Name: Files/kubernetes/deployment_scale.yaml
|
||||||
|
Content-Type: application/yaml
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: 8f8c45d63880fe9190e49c42458bf40c6aa7752ec84049db1027389715e12840
|
||||||
|
|
||||||
|
Name: Files/kubernetes/statefulset_scale.yaml
|
||||||
|
Content-Type: application/yaml
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: a9c35728419fb1e72ba73362fb17472afc63010a82ad4e3b84f3cf52161186e4
|
||||||
|
|
||||||
|
Name: Files/kubernetes/replicaset_scale.yaml
|
||||||
|
Content-Type: application/yaml
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: 6585f8e34425c3c5ffd5ca40a262acaea1ede7fc7caa9920e5af8deb2d8dd83c
|
@@ -0,0 +1,484 @@
|
|||||||
|
# Copyright (C) 2020 FUJITSU
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from oslo_serialization import jsonutils
|
||||||
|
from oslo_utils import uuidutils
|
||||||
|
from sqlalchemy import desc
|
||||||
|
from sqlalchemy.orm import joinedload
|
||||||
|
|
||||||
|
from tacker.common import exceptions
|
||||||
|
from tacker import context
|
||||||
|
from tacker.db import api as db_api
|
||||||
|
from tacker.db.db_sqlalchemy import api
|
||||||
|
from tacker.db.db_sqlalchemy import models
|
||||||
|
from tacker.objects import fields
|
||||||
|
from tacker.objects import vnf_lcm_op_occs
|
||||||
|
from tacker.tests.functional import base
|
||||||
|
from tacker.tests import utils
|
||||||
|
|
||||||
|
VNF_PACKAGE_UPLOAD_TIMEOUT = 300
|
||||||
|
VNF_INSTANTIATE_TIMEOUT = 600
|
||||||
|
VNF_TERMINATE_TIMEOUT = 600
|
||||||
|
VNF_SCALE_TIMEOUT = 600
|
||||||
|
RETRY_WAIT_TIME = 5
|
||||||
|
|
||||||
|
|
||||||
|
def _create_and_upload_vnf_package(tacker_client, csar_package_name,
|
||||||
|
user_defined_data):
|
||||||
|
# create vnf package
|
||||||
|
body = jsonutils.dumps({"userDefinedData": user_defined_data})
|
||||||
|
resp, vnf_package = tacker_client.do_request(
|
||||||
|
'/vnfpkgm/v1/vnf_packages', "POST", body=body)
|
||||||
|
|
||||||
|
# upload vnf package
|
||||||
|
csar_package_path = "../../../etc/samples/etsi/nfv/%s" % csar_package_name
|
||||||
|
file_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||||
|
csar_package_path))
|
||||||
|
|
||||||
|
# Generating unique vnfd id. This is required when multiple workers
|
||||||
|
# are running concurrently. The call below creates a new temporary
|
||||||
|
# CSAR with unique vnfd id.
|
||||||
|
file_path, uniqueid = utils.create_csar_with_unique_vnfd_id(file_path)
|
||||||
|
|
||||||
|
with open(file_path, 'rb') as file_object:
|
||||||
|
resp, resp_body = tacker_client.do_request(
|
||||||
|
'/vnfpkgm/v1/vnf_packages/{id}/package_content'.format(
|
||||||
|
id=vnf_package['id']),
|
||||||
|
"PUT", body=file_object, content_type='application/zip')
|
||||||
|
|
||||||
|
# wait for onboard
|
||||||
|
timeout = VNF_PACKAGE_UPLOAD_TIMEOUT
|
||||||
|
start_time = int(time.time())
|
||||||
|
show_url = os.path.join('/vnfpkgm/v1/vnf_packages', vnf_package['id'])
|
||||||
|
vnfd_id = None
|
||||||
|
while True:
|
||||||
|
resp, body = tacker_client.do_request(show_url, "GET")
|
||||||
|
if body['onboardingState'] == "ONBOARDED":
|
||||||
|
vnfd_id = body['vnfdId']
|
||||||
|
break
|
||||||
|
|
||||||
|
if ((int(time.time()) - start_time) > timeout):
|
||||||
|
raise Exception("Failed to onboard vnf package")
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# remove temporarily created CSAR file
|
||||||
|
os.remove(file_path)
|
||||||
|
return vnf_package['id'], vnfd_id
|
||||||
|
|
||||||
|
|
||||||
|
def _delete_wait_vnf_instance(tacker_client, id):
|
||||||
|
timeout = VNF_TERMINATE_TIMEOUT
|
||||||
|
url = os.path.join("/vnflcm/v1/vnf_instances", id)
|
||||||
|
start_time = int(time.time())
|
||||||
|
while True:
|
||||||
|
resp, body = tacker_client.do_request(url, "DELETE")
|
||||||
|
if 204 == resp.status_code:
|
||||||
|
break
|
||||||
|
|
||||||
|
if ((int(time.time()) - start_time) > timeout):
|
||||||
|
raise Exception("Failed to delete vnf instance")
|
||||||
|
|
||||||
|
time.sleep(RETRY_WAIT_TIME)
|
||||||
|
|
||||||
|
|
||||||
|
def _delete_vnf_instance(tacker_client, id):
|
||||||
|
_delete_wait_vnf_instance(tacker_client, id)
|
||||||
|
|
||||||
|
# verify vnf instance is deleted
|
||||||
|
url = os.path.join("/vnflcm/v1/vnf_instances", id)
|
||||||
|
resp, body = tacker_client.do_request(url, "GET")
|
||||||
|
|
||||||
|
|
||||||
|
def _show_vnf_instance(tacker_client, id):
|
||||||
|
show_url = os.path.join("/vnflcm/v1/vnf_instances", id)
|
||||||
|
resp, vnf_instance = tacker_client.do_request(show_url, "GET")
|
||||||
|
|
||||||
|
return vnf_instance
|
||||||
|
|
||||||
|
|
||||||
|
def _vnf_instance_wait(
|
||||||
|
tacker_client, id,
|
||||||
|
instantiation_state=fields.VnfInstanceState.INSTANTIATED,
|
||||||
|
timeout=VNF_INSTANTIATE_TIMEOUT):
|
||||||
|
show_url = os.path.join("/vnflcm/v1/vnf_instances", id)
|
||||||
|
start_time = int(time.time())
|
||||||
|
while True:
|
||||||
|
resp, body = tacker_client.do_request(show_url, "GET")
|
||||||
|
if body['instantiationState'] == instantiation_state:
|
||||||
|
break
|
||||||
|
|
||||||
|
if ((int(time.time()) - start_time) > timeout):
|
||||||
|
raise Exception("Failed to wait vnf instance")
|
||||||
|
|
||||||
|
time.sleep(RETRY_WAIT_TIME)
|
||||||
|
|
||||||
|
|
||||||
|
def _terminate_vnf_instance(tacker_client, id, request_body):
|
||||||
|
url = os.path.join("/vnflcm/v1/vnf_instances", id, "terminate")
|
||||||
|
resp, body = tacker_client.do_request(
|
||||||
|
url, "POST", body=jsonutils.dumps(request_body))
|
||||||
|
|
||||||
|
timeout = request_body.get('gracefulTerminationTimeout')
|
||||||
|
start_time = int(time.time())
|
||||||
|
|
||||||
|
_vnf_instance_wait(
|
||||||
|
tacker_client, id,
|
||||||
|
instantiation_state=fields.VnfInstanceState.NOT_INSTANTIATED,
|
||||||
|
timeout=VNF_TERMINATE_TIMEOUT)
|
||||||
|
|
||||||
|
# If gracefulTerminationTimeout is set, check whether vnf
|
||||||
|
# instantiation_state is set to NOT_INSTANTIATED after
|
||||||
|
# gracefulTerminationTimeout seconds.
|
||||||
|
if timeout and int(time.time()) - start_time < timeout:
|
||||||
|
raise Exception("Failed to terminate vnf instance")
|
||||||
|
|
||||||
|
|
||||||
|
class VnfLcmKubernetesScaleTest(base.BaseTackerTest):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.tacker_client = base.BaseTackerTest.tacker_http_client()
|
||||||
|
cls.vnf_package_resource, cls.vnfd_id_resource = \
|
||||||
|
_create_and_upload_vnf_package(
|
||||||
|
cls.tacker_client, "test_cnf_scale",
|
||||||
|
{"key": "sample_scale_functional"})
|
||||||
|
cls.vnf_instance_ids = []
|
||||||
|
super(VnfLcmKubernetesScaleTest, cls).setUpClass()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
# Terminate vnf forcefully
|
||||||
|
terminate_req_body = {
|
||||||
|
"terminationType": fields.VnfInstanceTerminationType.FORCEFUL,
|
||||||
|
}
|
||||||
|
for id in cls.vnf_instance_ids:
|
||||||
|
_terminate_vnf_instance(cls.tacker_client, id,
|
||||||
|
terminate_req_body)
|
||||||
|
_delete_vnf_instance(cls.tacker_client, id)
|
||||||
|
|
||||||
|
# Update vnf package operational state to DISABLED
|
||||||
|
update_req_body = jsonutils.dumps({
|
||||||
|
"operationalState": "DISABLED"})
|
||||||
|
base_path = "/vnfpkgm/v1/vnf_packages"
|
||||||
|
for package_id in [cls.vnf_package_resource]:
|
||||||
|
resp, resp_body = cls.tacker_client.do_request(
|
||||||
|
'{base_path}/{id}'.format(id=package_id,
|
||||||
|
base_path=base_path),
|
||||||
|
"PATCH", content_type='application/json',
|
||||||
|
body=update_req_body)
|
||||||
|
|
||||||
|
# Delete vnf package
|
||||||
|
url = '/vnfpkgm/v1/vnf_packages/%s' % package_id
|
||||||
|
cls.tacker_client.do_request(url, "DELETE")
|
||||||
|
|
||||||
|
super(VnfLcmKubernetesScaleTest, cls).tearDownClass()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(VnfLcmKubernetesScaleTest, self).setUp()
|
||||||
|
self.base_vnf_instances_url = "/vnflcm/v1/vnf_instances"
|
||||||
|
self.base_vnf_lcm_op_occs_url = "/vnflcm/v1/vnf_lcm_op_occs"
|
||||||
|
self.context = context.get_admin_context()
|
||||||
|
vim_list = self.client.list_vims()
|
||||||
|
if not vim_list:
|
||||||
|
self.skipTest("Vims are not configured")
|
||||||
|
|
||||||
|
vim_id = 'vim-kubernetes'
|
||||||
|
vim = self.get_vim(vim_list, vim_id)
|
||||||
|
if not vim:
|
||||||
|
self.skipTest("Kubernetes VIM '%s' is missing" % vim_id)
|
||||||
|
self.vim_id = vim['id']
|
||||||
|
|
||||||
|
def _instantiate_vnf_instance_request(
|
||||||
|
self, flavour_id, vim_id=None, additional_param=None):
|
||||||
|
request_body = {"flavourId": flavour_id}
|
||||||
|
|
||||||
|
if vim_id:
|
||||||
|
request_body["vimConnectionInfo"] = [
|
||||||
|
{"id": uuidutils.generate_uuid(),
|
||||||
|
"vimId": vim_id,
|
||||||
|
"vimType": "kubernetes"}]
|
||||||
|
|
||||||
|
if additional_param:
|
||||||
|
request_body["additionalParams"] = additional_param
|
||||||
|
|
||||||
|
return request_body
|
||||||
|
|
||||||
|
def _create_vnf_instance(self, vnfd_id, vnf_instance_name=None,
|
||||||
|
vnf_instance_description=None):
|
||||||
|
request_body = {'vnfdId': vnfd_id}
|
||||||
|
if vnf_instance_name:
|
||||||
|
request_body['vnfInstanceName'] = vnf_instance_name
|
||||||
|
|
||||||
|
if vnf_instance_description:
|
||||||
|
request_body['vnfInstanceDescription'] = vnf_instance_description
|
||||||
|
|
||||||
|
resp, response_body = self.http_client.do_request(
|
||||||
|
self.base_vnf_instances_url, "POST",
|
||||||
|
body=jsonutils.dumps(request_body))
|
||||||
|
return resp, response_body
|
||||||
|
|
||||||
|
def _instantiate_vnf_instance(self, id, request_body):
|
||||||
|
url = os.path.join(self.base_vnf_instances_url, id, "instantiate")
|
||||||
|
resp, body = self.http_client.do_request(
|
||||||
|
url, "POST", body=jsonutils.dumps(request_body))
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
_vnf_instance_wait(self.tacker_client, id)
|
||||||
|
|
||||||
|
def _create_and_instantiate_vnf_instance(self, flavour_id,
|
||||||
|
additional_params):
|
||||||
|
# create vnf instance
|
||||||
|
vnf_instance_name = "test_vnf_instance_for_cnf_scale-%s" % \
|
||||||
|
uuidutils.generate_uuid()
|
||||||
|
vnf_instance_description = "vnf instance for cnf scale testing"
|
||||||
|
resp, vnf_instance = self._create_vnf_instance(
|
||||||
|
self.vnfd_id_resource, vnf_instance_name=vnf_instance_name,
|
||||||
|
vnf_instance_description=vnf_instance_description)
|
||||||
|
|
||||||
|
# instantiate vnf instance
|
||||||
|
additional_param = additional_params
|
||||||
|
request_body = self._instantiate_vnf_instance_request(
|
||||||
|
flavour_id, vim_id=self.vim_id, additional_param=additional_param)
|
||||||
|
|
||||||
|
self._instantiate_vnf_instance(vnf_instance['id'], request_body)
|
||||||
|
vnf_instance = _show_vnf_instance(
|
||||||
|
self.tacker_client, vnf_instance['id'])
|
||||||
|
self.vnf_instance_ids.append(vnf_instance['id'])
|
||||||
|
|
||||||
|
return vnf_instance
|
||||||
|
|
||||||
|
def _scale_vnf_instance(self, id, type, aspect_id,
|
||||||
|
number_of_steps=1):
|
||||||
|
url = os.path.join(self.base_vnf_instances_url, id, "scale")
|
||||||
|
# generate body
|
||||||
|
request_body = {
|
||||||
|
"type": type,
|
||||||
|
"aspectId": aspect_id,
|
||||||
|
"numberOfSteps": number_of_steps}
|
||||||
|
resp, body = self.http_client.do_request(
|
||||||
|
url, "POST", body=jsonutils.dumps(request_body))
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
|
||||||
|
def _test_scale_cnf(self, id, type, aspect_id, previous_level,
|
||||||
|
delta_num=1, number_of_steps=1):
|
||||||
|
# scale operation
|
||||||
|
self._scale_vnf_instance(id, type, aspect_id, number_of_steps)
|
||||||
|
# wait vnflcm_op_occs.operation_state become COMPLETE
|
||||||
|
self._wait_vnflcm_op_occs(self.context, id)
|
||||||
|
# check scaleStatus after scale operation
|
||||||
|
vnf_instance = _show_vnf_instance(
|
||||||
|
self.tacker_client, id)
|
||||||
|
scale_status_after = \
|
||||||
|
vnf_instance['instantiatedVnfInfo']['scaleStatus']
|
||||||
|
if type == 'SCALE_OUT':
|
||||||
|
expected_level = previous_level + number_of_steps
|
||||||
|
else:
|
||||||
|
expected_level = previous_level - number_of_steps
|
||||||
|
for status in scale_status_after:
|
||||||
|
if status.get('aspectId') == aspect_id:
|
||||||
|
self.assertEqual(status.get('scaleLevel'), expected_level)
|
||||||
|
previous_level = status.get('scaleLevel')
|
||||||
|
|
||||||
|
return previous_level
|
||||||
|
|
||||||
|
def _test_scale_cnf_fail(self, id, type, aspect_id, previous_level,
|
||||||
|
delta_num=1, number_of_steps=1):
|
||||||
|
# scale operation
|
||||||
|
self._scale_vnf_instance(id, type, aspect_id, number_of_steps)
|
||||||
|
# wait vnflcm_op_occs.operation_state become FAILED_TEMP
|
||||||
|
self._wait_vnflcm_op_occs(self.context, id, "FAILED_TEMP")
|
||||||
|
# check scaleStatus after scale operation
|
||||||
|
vnf_instance = _show_vnf_instance(
|
||||||
|
self.tacker_client, id)
|
||||||
|
scale_status_after = \
|
||||||
|
vnf_instance['instantiatedVnfInfo']['scaleStatus']
|
||||||
|
expected_level = previous_level
|
||||||
|
for status in scale_status_after:
|
||||||
|
if status.get('aspectId') == aspect_id:
|
||||||
|
self.assertEqual(status.get('scaleLevel'), expected_level)
|
||||||
|
previous_level = status.get('scaleLevel')
|
||||||
|
|
||||||
|
return previous_level
|
||||||
|
|
||||||
|
def _rollback_vnf_instance(self, vnf_lcm_op_occ_id):
|
||||||
|
url = os.path.join(
|
||||||
|
self.base_vnf_lcm_op_occs_url, vnf_lcm_op_occ_id, "rollback")
|
||||||
|
# generate body
|
||||||
|
resp, body = self.http_client.do_request(url, "POST")
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
|
||||||
|
def _test_rollback_cnf(self, id, aspect_id, previous_level,
|
||||||
|
delta_num=1, number_of_steps=1):
|
||||||
|
# get vnflcm_op_occ id for rollback
|
||||||
|
vnflcm_op_occ = self._vnf_notify_get_by_id(self.context, id)
|
||||||
|
vnf_lcm_op_occ_id = vnflcm_op_occ.id
|
||||||
|
|
||||||
|
# rollback operation
|
||||||
|
self._rollback_vnf_instance(vnf_lcm_op_occ_id)
|
||||||
|
# wait vnflcm_op_occs.operation_state become ROLLED_BACK
|
||||||
|
self._wait_vnflcm_op_occs(self.context, id, "ROLLED_BACK")
|
||||||
|
# check scaleStatus after scale operation
|
||||||
|
vnf_instance = _show_vnf_instance(
|
||||||
|
self.tacker_client, id)
|
||||||
|
scale_status_after = \
|
||||||
|
vnf_instance['instantiatedVnfInfo']['scaleStatus']
|
||||||
|
expected_level = previous_level
|
||||||
|
for status in scale_status_after:
|
||||||
|
if status.get('aspectId') == aspect_id:
|
||||||
|
self.assertEqual(status.get('scaleLevel'), expected_level)
|
||||||
|
previous_level = status.get('scaleLevel')
|
||||||
|
|
||||||
|
@db_api.context_manager.reader
|
||||||
|
def _vnf_notify_get_by_id(self, context, vnf_instance_id,
|
||||||
|
columns_to_join=None):
|
||||||
|
query = api.model_query(
|
||||||
|
context, models.VnfLcmOpOccs,
|
||||||
|
read_deleted="no", project_only=True).filter_by(
|
||||||
|
vnf_instance_id=vnf_instance_id).order_by(
|
||||||
|
desc("created_at"))
|
||||||
|
|
||||||
|
if columns_to_join:
|
||||||
|
for column in columns_to_join:
|
||||||
|
query = query.options(joinedload(column))
|
||||||
|
|
||||||
|
db_vnflcm_op_occ = query.first()
|
||||||
|
|
||||||
|
if not db_vnflcm_op_occ:
|
||||||
|
raise exceptions.VnfInstanceNotFound(id=vnf_instance_id)
|
||||||
|
|
||||||
|
vnflcm_op_occ = vnf_lcm_op_occs.VnfLcmOpOcc.obj_from_db_obj(
|
||||||
|
context, db_vnflcm_op_occ)
|
||||||
|
return vnflcm_op_occ
|
||||||
|
|
||||||
|
def _wait_vnflcm_op_occs(
|
||||||
|
self, context, vnf_instance_id,
|
||||||
|
operation_state='COMPLETED'):
|
||||||
|
timeout = VNF_SCALE_TIMEOUT
|
||||||
|
start_time = int(time.time())
|
||||||
|
while True:
|
||||||
|
vnflcm_op_occ = self._vnf_notify_get_by_id(
|
||||||
|
context, vnf_instance_id)
|
||||||
|
|
||||||
|
if vnflcm_op_occ.operation_state == operation_state:
|
||||||
|
break
|
||||||
|
|
||||||
|
if ((int(time.time()) - start_time) > timeout):
|
||||||
|
raise Exception("Failed to wait scale instance")
|
||||||
|
|
||||||
|
time.sleep(RETRY_WAIT_TIME)
|
||||||
|
|
||||||
|
def test_scale_cnf_with_statefulset(self):
|
||||||
|
inst_additional_param = {
|
||||||
|
"lcm-kubernetes-def-files": [
|
||||||
|
"Files/kubernetes/statefulset_scale.yaml"]}
|
||||||
|
vnf_instance = self._create_and_instantiate_vnf_instance(
|
||||||
|
"simple", inst_additional_param)
|
||||||
|
aspect_id = "vdu1_aspect"
|
||||||
|
scale_status_initial = \
|
||||||
|
vnf_instance['instantiatedVnfInfo']['scaleStatus']
|
||||||
|
self.assertTrue(len(scale_status_initial) > 0)
|
||||||
|
for status in scale_status_initial:
|
||||||
|
self.assertIsNotNone(status.get('aspectId'))
|
||||||
|
self.assertIsNotNone(status.get('scaleLevel'))
|
||||||
|
if status.get('aspectId') == aspect_id:
|
||||||
|
previous_level = status.get('scaleLevel')
|
||||||
|
|
||||||
|
# test scale out
|
||||||
|
previous_level = self._test_scale_cnf(
|
||||||
|
vnf_instance['id'], 'SCALE_OUT', aspect_id, previous_level)
|
||||||
|
|
||||||
|
# test scale in
|
||||||
|
previous_level = self._test_scale_cnf(
|
||||||
|
vnf_instance['id'], 'SCALE_IN', aspect_id, previous_level)
|
||||||
|
|
||||||
|
def test_scale_cnf_with_replicaset(self):
|
||||||
|
inst_additional_param = {
|
||||||
|
"lcm-kubernetes-def-files": [
|
||||||
|
"Files/kubernetes/replicaset_scale.yaml"]}
|
||||||
|
vnf_instance = self._create_and_instantiate_vnf_instance(
|
||||||
|
"simple", inst_additional_param)
|
||||||
|
aspect_id = "vdu1_aspect"
|
||||||
|
scale_status_initial = \
|
||||||
|
vnf_instance['instantiatedVnfInfo']['scaleStatus']
|
||||||
|
self.assertTrue(len(scale_status_initial) > 0)
|
||||||
|
for status in scale_status_initial:
|
||||||
|
self.assertIsNotNone(status.get('aspectId'))
|
||||||
|
self.assertIsNotNone(status.get('scaleLevel'))
|
||||||
|
if status.get('aspectId') == aspect_id:
|
||||||
|
previous_level = status.get('scaleLevel')
|
||||||
|
|
||||||
|
# test scale out
|
||||||
|
previous_level = self._test_scale_cnf(
|
||||||
|
vnf_instance['id'], 'SCALE_OUT', aspect_id, previous_level)
|
||||||
|
|
||||||
|
# test scale in
|
||||||
|
previous_level = self._test_scale_cnf(
|
||||||
|
vnf_instance['id'], 'SCALE_IN', aspect_id, previous_level)
|
||||||
|
|
||||||
|
def test_scale_cnf_deployment_with_scaling_and_delta_two(self):
|
||||||
|
inst_additional_param = {
|
||||||
|
"lcm-kubernetes-def-files": [
|
||||||
|
"Files/kubernetes/deployment_scale.yaml"]}
|
||||||
|
# Use flavour_id scalingsteps that is set to delta_num=2
|
||||||
|
vnf_instance = self._create_and_instantiate_vnf_instance(
|
||||||
|
"scalingsteps", inst_additional_param)
|
||||||
|
aspect_id = "vdu1_aspect"
|
||||||
|
scale_status_initial = \
|
||||||
|
vnf_instance['instantiatedVnfInfo']['scaleStatus']
|
||||||
|
self.assertTrue(len(scale_status_initial) > 0)
|
||||||
|
for status in scale_status_initial:
|
||||||
|
self.assertIsNotNone(status.get('aspectId'))
|
||||||
|
self.assertIsNotNone(status.get('scaleLevel'))
|
||||||
|
if status.get('aspectId') == aspect_id:
|
||||||
|
previous_level = status.get('scaleLevel')
|
||||||
|
|
||||||
|
# test scale out (test for delta_num=2 and number_of_steps=2)
|
||||||
|
previous_level = self._test_scale_cnf(
|
||||||
|
vnf_instance['id'], 'SCALE_OUT', aspect_id, previous_level,
|
||||||
|
delta_num=2, number_of_steps=2)
|
||||||
|
|
||||||
|
# test scale in (test for delta_num=2 and number_of_steps=2)
|
||||||
|
previous_level = self._test_scale_cnf(
|
||||||
|
vnf_instance['id'], 'SCALE_IN', aspect_id, previous_level,
|
||||||
|
delta_num=2, number_of_steps=2)
|
||||||
|
|
||||||
|
@unittest.skip("Reduce test time")
|
||||||
|
def test_scale_out_cnf_rollback(self):
|
||||||
|
inst_additional_param = {
|
||||||
|
"lcm-kubernetes-def-files": [
|
||||||
|
"Files/kubernetes/statefulset_scale.yaml"]}
|
||||||
|
vnf_instance = self._create_and_instantiate_vnf_instance(
|
||||||
|
"simple", inst_additional_param)
|
||||||
|
aspect_id = "vdu1_aspect"
|
||||||
|
scale_status_initial = \
|
||||||
|
vnf_instance['instantiatedVnfInfo']['scaleStatus']
|
||||||
|
self.assertTrue(len(scale_status_initial) > 0)
|
||||||
|
for status in scale_status_initial:
|
||||||
|
self.assertIsNotNone(status.get('aspectId'))
|
||||||
|
self.assertIsNotNone(status.get('scaleLevel'))
|
||||||
|
if status.get('aspectId') == aspect_id:
|
||||||
|
previous_level = status.get('scaleLevel')
|
||||||
|
|
||||||
|
# fail scale out for rollback
|
||||||
|
previous_level = self._test_scale_cnf_fail(
|
||||||
|
vnf_instance['id'], 'SCALE_OUT', aspect_id, previous_level,
|
||||||
|
number_of_steps=2)
|
||||||
|
|
||||||
|
# test rollback
|
||||||
|
self._test_rollback_cnf(vnf_instance['id'], aspect_id, previous_level)
|
@@ -213,6 +213,9 @@ class FakeVNFMPlugin(mock.Mock):
|
|||||||
'name': 'dummy_vnf_update',
|
'name': 'dummy_vnf_update',
|
||||||
'attributes': {}}
|
'attributes': {}}
|
||||||
|
|
||||||
|
def _update_vnf_scaling(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TestNfvoPlugin(db_base.SqlTestCase):
|
class TestNfvoPlugin(db_base.SqlTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@@ -184,8 +184,9 @@ def return_vnf_instance(
|
|||||||
}
|
}
|
||||||
instantiated_vnf_info = get_instantiated_vnf_info
|
instantiated_vnf_info = get_instantiated_vnf_info
|
||||||
|
|
||||||
s_status = {"aspect_id": "SP1", "scale_level": 1}
|
if scale_status == "scale_status":
|
||||||
scale_status = objects.ScaleInfo(**s_status)
|
s_status = {"aspect_id": "SP1", "scale_level": 1}
|
||||||
|
scale_status = objects.ScaleInfo(**s_status)
|
||||||
|
|
||||||
instantiated_vnf_info.update(
|
instantiated_vnf_info.update(
|
||||||
{"ext_cp_info": [],
|
{"ext_cp_info": [],
|
||||||
@@ -792,10 +793,10 @@ def _get_vnf(**updates):
|
|||||||
return vnf_data
|
return vnf_data
|
||||||
|
|
||||||
|
|
||||||
def scale_request(type, number_of_steps, is_reverse):
|
def scale_request(type, aspect_id, number_of_steps, is_reverse):
|
||||||
scale_request_data = {
|
scale_request_data = {
|
||||||
'type': type,
|
'type': type,
|
||||||
'aspect_id': "SP1",
|
'aspect_id': aspect_id,
|
||||||
'number_of_steps': number_of_steps,
|
'number_of_steps': number_of_steps,
|
||||||
'scale_level': 1,
|
'scale_level': 1,
|
||||||
'additional_params': {"is_reverse": is_reverse},
|
'additional_params': {"is_reverse": is_reverse},
|
||||||
@@ -844,12 +845,41 @@ def vnf_scale():
|
|||||||
vim_id=uuidsentinel.vim_id)
|
vim_id=uuidsentinel.vim_id)
|
||||||
|
|
||||||
|
|
||||||
def vnflcm_rollback(error_point=7):
|
def vnflcm_scale_in_cnf():
|
||||||
|
dt = datetime.datetime(2000, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||||
return objects.VnfLcmOpOcc(
|
return objects.VnfLcmOpOcc(
|
||||||
state_entered_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
state_entered_time=dt,
|
||||||
tzinfo=iso8601.UTC),
|
start_time=dt,
|
||||||
start_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
||||||
tzinfo=iso8601.UTC),
|
operation='SCALE',
|
||||||
|
operation_state='STARTING',
|
||||||
|
is_automatic_invocation=False,
|
||||||
|
operation_params='{"type": "SCALE_IN", "aspect_id": "vdu1_aspect"}',
|
||||||
|
error_point=1,
|
||||||
|
id=constants.UUID,
|
||||||
|
created_at=dt)
|
||||||
|
|
||||||
|
|
||||||
|
def vnflcm_scale_out_cnf():
|
||||||
|
dt = datetime.datetime(2000, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||||
|
return objects.VnfLcmOpOcc(
|
||||||
|
state_entered_time=dt,
|
||||||
|
start_time=dt,
|
||||||
|
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
||||||
|
operation='SCALE',
|
||||||
|
operation_state='STARTING',
|
||||||
|
is_automatic_invocation=False,
|
||||||
|
operation_params='{"type": "SCALE_OUT", "aspect_id": "vdu1_aspect"}',
|
||||||
|
error_point=1,
|
||||||
|
id=constants.UUID,
|
||||||
|
created_at=dt)
|
||||||
|
|
||||||
|
|
||||||
|
def vnflcm_rollback(error_point=7):
|
||||||
|
dt = datetime.datetime(2000, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||||
|
return objects.VnfLcmOpOcc(
|
||||||
|
state_entered_time=dt,
|
||||||
|
start_time=dt,
|
||||||
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
||||||
operation='SCALE',
|
operation='SCALE',
|
||||||
operation_state='FAILED_TEMP',
|
operation_state='FAILED_TEMP',
|
||||||
@@ -857,16 +887,14 @@ def vnflcm_rollback(error_point=7):
|
|||||||
operation_params='{"type": "SCALE_OUT", "aspect_id": "SP1"}',
|
operation_params='{"type": "SCALE_OUT", "aspect_id": "SP1"}',
|
||||||
error_point=error_point,
|
error_point=error_point,
|
||||||
id=constants.UUID,
|
id=constants.UUID,
|
||||||
created_at=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
created_at=dt)
|
||||||
tzinfo=iso8601.UTC))
|
|
||||||
|
|
||||||
|
|
||||||
def vnflcm_rollback_insta(error_point=7):
|
def vnflcm_rollback_insta(error_point=7):
|
||||||
|
dt = datetime.datetime(2000, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||||
return objects.VnfLcmOpOcc(
|
return objects.VnfLcmOpOcc(
|
||||||
state_entered_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
state_entered_time=dt,
|
||||||
tzinfo=iso8601.UTC),
|
start_time=dt,
|
||||||
start_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
|
||||||
tzinfo=iso8601.UTC),
|
|
||||||
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
||||||
operation='INSTANTIATE',
|
operation='INSTANTIATE',
|
||||||
operation_state='FAILED_TEMP',
|
operation_state='FAILED_TEMP',
|
||||||
@@ -874,16 +902,14 @@ def vnflcm_rollback_insta(error_point=7):
|
|||||||
operation_params='{}',
|
operation_params='{}',
|
||||||
error_point=error_point,
|
error_point=error_point,
|
||||||
id=constants.UUID,
|
id=constants.UUID,
|
||||||
created_at=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
created_at=dt)
|
||||||
tzinfo=iso8601.UTC))
|
|
||||||
|
|
||||||
|
|
||||||
def vnflcm_rollback_active():
|
def vnflcm_rollback_active():
|
||||||
|
dt = datetime.datetime(2000, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||||
return objects.VnfLcmOpOcc(
|
return objects.VnfLcmOpOcc(
|
||||||
state_entered_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
state_entered_time=dt,
|
||||||
tzinfo=iso8601.UTC),
|
start_time=dt,
|
||||||
start_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
|
||||||
tzinfo=iso8601.UTC),
|
|
||||||
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
||||||
operation='SCALE',
|
operation='SCALE',
|
||||||
operation_state='ACTIVE',
|
operation_state='ACTIVE',
|
||||||
@@ -891,16 +917,14 @@ def vnflcm_rollback_active():
|
|||||||
operation_params='{"type": "SCALE_OUT", "aspect_id": "SP1"}',
|
operation_params='{"type": "SCALE_OUT", "aspect_id": "SP1"}',
|
||||||
error_point=7,
|
error_point=7,
|
||||||
id=constants.UUID,
|
id=constants.UUID,
|
||||||
created_at=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
created_at=dt)
|
||||||
tzinfo=iso8601.UTC))
|
|
||||||
|
|
||||||
|
|
||||||
def vnflcm_rollback_ope():
|
def vnflcm_rollback_ope():
|
||||||
|
dt = datetime.datetime(2000, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||||
return objects.VnfLcmOpOcc(
|
return objects.VnfLcmOpOcc(
|
||||||
state_entered_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
state_entered_time=dt,
|
||||||
tzinfo=iso8601.UTC),
|
start_time=dt,
|
||||||
start_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
|
||||||
tzinfo=iso8601.UTC),
|
|
||||||
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
||||||
operation='HEAL',
|
operation='HEAL',
|
||||||
operation_state='FAILED_TEMP',
|
operation_state='FAILED_TEMP',
|
||||||
@@ -908,16 +932,14 @@ def vnflcm_rollback_ope():
|
|||||||
operation_params='{}',
|
operation_params='{}',
|
||||||
error_point=7,
|
error_point=7,
|
||||||
id=constants.UUID,
|
id=constants.UUID,
|
||||||
created_at=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
created_at=dt)
|
||||||
tzinfo=iso8601.UTC))
|
|
||||||
|
|
||||||
|
|
||||||
def vnflcm_rollback_scale_in():
|
def vnflcm_rollback_scale_in():
|
||||||
|
dt = datetime.datetime(2000, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||||
return objects.VnfLcmOpOcc(
|
return objects.VnfLcmOpOcc(
|
||||||
state_entered_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
state_entered_time=dt,
|
||||||
tzinfo=iso8601.UTC),
|
start_time=dt,
|
||||||
start_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
|
||||||
tzinfo=iso8601.UTC),
|
|
||||||
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
||||||
operation='SCALE',
|
operation='SCALE',
|
||||||
operation_state='FAILED_TEMP',
|
operation_state='FAILED_TEMP',
|
||||||
@@ -925,8 +947,7 @@ def vnflcm_rollback_scale_in():
|
|||||||
operation_params='{"type": "SCALE_IN", "aspect_id": "SP1"}',
|
operation_params='{"type": "SCALE_IN", "aspect_id": "SP1"}',
|
||||||
error_point=7,
|
error_point=7,
|
||||||
id=constants.UUID,
|
id=constants.UUID,
|
||||||
created_at=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
created_at=dt)
|
||||||
tzinfo=iso8601.UTC))
|
|
||||||
|
|
||||||
|
|
||||||
def vnf_rollback():
|
def vnf_rollback():
|
||||||
@@ -1090,6 +1111,110 @@ def vnf_dict():
|
|||||||
return vnf_dict
|
return vnf_dict
|
||||||
|
|
||||||
|
|
||||||
|
def vnf_dict_cnf():
|
||||||
|
vnf_dict = {
|
||||||
|
'attributes': {},
|
||||||
|
'status': 'ACTIVE',
|
||||||
|
'vnfd_id': 'e889e4fe-52fe-437d-b1e1-a690dc95e3f8',
|
||||||
|
'tenant_id': '13d2ca8de70d48b2a2e0dbac2c327c0b',
|
||||||
|
'vim_id': '3f41faa7-5630-47d2-9d4a-1216953c8887',
|
||||||
|
'instance_id': 'd1121d3c-368b-4ac2-b39d-835aa3e4ccd8',
|
||||||
|
'placement_attr': {'vim_name': 'kubernetes-vim'},
|
||||||
|
'id': '436aaa6e-2db6-4d6e-a3fc-e728b2f0ac56',
|
||||||
|
'name': 'cnf_create_1',
|
||||||
|
'vnfd': {
|
||||||
|
'attributes': {
|
||||||
|
'vnfd_simple': 'dummy'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vnf_dict
|
||||||
|
|
||||||
|
|
||||||
|
def vnfd_dict_cnf():
|
||||||
|
tacker_dir = os.getcwd()
|
||||||
|
def_dir = tacker_dir + "/samples/vnf_packages/Definitions/"
|
||||||
|
vnfd_dict = {
|
||||||
|
"tosca_definitions_version": "tosca_simple_yaml_1_2",
|
||||||
|
"description": "Sample VNF flavour for Sample VNF",
|
||||||
|
"imports": [
|
||||||
|
def_dir + "etsi_nfv_sol001_common_types.yaml",
|
||||||
|
def_dir + "etsi_nfv_sol001_vnfd_types.yaml",
|
||||||
|
def_dir + "helloworld3_types.yaml"],
|
||||||
|
"topology_template": {
|
||||||
|
"node_templates": {
|
||||||
|
"VNF": {
|
||||||
|
"type": "company.provider.VNF",
|
||||||
|
"properties": {
|
||||||
|
"flavour_description": "A simple flavour"}},
|
||||||
|
"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": 3}}}},
|
||||||
|
"policies": [
|
||||||
|
{
|
||||||
|
"scaling_aspects": {
|
||||||
|
"type": "tosca.policies.nfv.ScalingAspects",
|
||||||
|
"properties": {
|
||||||
|
"aspects": {
|
||||||
|
"vdu1_aspect": {
|
||||||
|
"name": "vdu1_aspect",
|
||||||
|
"description": "vdu1 scaling aspect",
|
||||||
|
"max_scale_level": 2,
|
||||||
|
"step_deltas": ["delta_1"]}}}}},
|
||||||
|
{
|
||||||
|
"vdu1_initial_delta": {
|
||||||
|
"type": "tosca.policies.nfv.VduInitialDelta",
|
||||||
|
"properties": {
|
||||||
|
"initial_delta": {
|
||||||
|
"number_of_instances": 0}},
|
||||||
|
"targets": ["VDU1"]}},
|
||||||
|
{
|
||||||
|
"vdu1_scaling_aspect_deltas": {
|
||||||
|
"type": "tosca.policies.nfv.VduScalingAspectDeltas",
|
||||||
|
"properties": {
|
||||||
|
"aspect": "vdu1_aspect",
|
||||||
|
"deltas": {
|
||||||
|
"delta_1": {
|
||||||
|
"number_of_instances": 1}}},
|
||||||
|
"targets": ["VDU1"]}},
|
||||||
|
{
|
||||||
|
"instantiation_levels": {
|
||||||
|
"type": "tosca.policies.nfv.InstantiationLevels",
|
||||||
|
"properties": {
|
||||||
|
"levels": {
|
||||||
|
"instantiation_level_1": {
|
||||||
|
"description": "Smallest size",
|
||||||
|
"scale_info": {
|
||||||
|
"vdu1_aspect": {
|
||||||
|
"scale_level": 0}}},
|
||||||
|
"instantiation_level_2": {
|
||||||
|
"description": "Largest size",
|
||||||
|
"scale_info": {
|
||||||
|
"vdu1_aspect": {
|
||||||
|
"scale_level": 2}}}
|
||||||
|
},
|
||||||
|
"default_level": "instantiation_level_1"}}},
|
||||||
|
{
|
||||||
|
"vdu1_instantiation_levels": {
|
||||||
|
"type": "tosca.policies.nfv.VduInstantiationLevels",
|
||||||
|
"properties": {
|
||||||
|
"levels": {
|
||||||
|
"instantiation_level_1": {
|
||||||
|
"number_of_instances": 0},
|
||||||
|
"instantiation_level_2": {
|
||||||
|
"number_of_instances": 2}}},
|
||||||
|
"targets": ["VDU1"]}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vnfd_dict
|
||||||
|
|
||||||
|
|
||||||
class InjectContext(wsgi.Middleware):
|
class InjectContext(wsgi.Middleware):
|
||||||
"""Add a 'tacker.context' to WSGI environ."""
|
"""Add a 'tacker.context' to WSGI environ."""
|
||||||
|
|
||||||
@@ -1294,13 +1419,12 @@ def fake_vnf_lcm_op_occs():
|
|||||||
}
|
}
|
||||||
changed_info_obj = objects.VnfInfoModifications(**changed_info)
|
changed_info_obj = objects.VnfInfoModifications(**changed_info)
|
||||||
|
|
||||||
|
dt = datetime.datetime(1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||||
vnf_lcm_op_occs = {
|
vnf_lcm_op_occs = {
|
||||||
'id': constants.UUID,
|
'id': constants.UUID,
|
||||||
'operation_state': 'COMPLETED',
|
'operation_state': 'COMPLETED',
|
||||||
'state_entered_time': datetime.datetime(1900, 1, 1, 1, 1, 1,
|
'state_entered_time': dt,
|
||||||
tzinfo=iso8601.UTC),
|
'start_time': dt,
|
||||||
'start_time': datetime.datetime(1900, 1, 1, 1, 1, 1,
|
|
||||||
tzinfo=iso8601.UTC),
|
|
||||||
'vnf_instance_id': constants.UUID,
|
'vnf_instance_id': constants.UUID,
|
||||||
'operation': 'MODIFY_INFO',
|
'operation': 'MODIFY_INFO',
|
||||||
'is_automatic_invocation': False,
|
'is_automatic_invocation': False,
|
||||||
|
@@ -69,6 +69,8 @@ class FakeVNFMPlugin(mock.Mock):
|
|||||||
self.vnf3_vnfd_id = 'e4015e9f-1ef2-49fb-adb6-070791ad3c45'
|
self.vnf3_vnfd_id = 'e4015e9f-1ef2-49fb-adb6-070791ad3c45'
|
||||||
self.vnf3_vnf_id = '7168062e-9fa1-4203-8cb7-f5c99ff3ee1b'
|
self.vnf3_vnf_id = '7168062e-9fa1-4203-8cb7-f5c99ff3ee1b'
|
||||||
self.vnf3_update_vnf_id = '10f66bc5-b2f1-45b7-a7cd-6dd6ad0017f5'
|
self.vnf3_update_vnf_id = '10f66bc5-b2f1-45b7-a7cd-6dd6ad0017f5'
|
||||||
|
self.vnf_for_cnf_vnfd_id = 'e889e4fe-52fe-437d-b1e1-a690dc95e3f8'
|
||||||
|
self.vnf_for_cnf_vnf_id = '436aaa6e-2db6-4d6e-a3fc-e728b2f0ac56'
|
||||||
|
|
||||||
self.cp11_id = 'd18c8bae-898a-4932-bff8-d5eac981a9c9'
|
self.cp11_id = 'd18c8bae-898a-4932-bff8-d5eac981a9c9'
|
||||||
self.cp11_update_id = 'a18c8bae-898a-4932-bff8-d5eac981a9b8'
|
self.cp11_update_id = 'a18c8bae-898a-4932-bff8-d5eac981a9b8'
|
||||||
@@ -110,6 +112,8 @@ class FakeVNFMPlugin(mock.Mock):
|
|||||||
return self.get_dummy_vnf_error()
|
return self.get_dummy_vnf_error()
|
||||||
elif self.vnf3_vnf_id in args:
|
elif self.vnf3_vnf_id in args:
|
||||||
return self.get_dummy_vnf_not_error()
|
return self.get_dummy_vnf_not_error()
|
||||||
|
elif self.vnf_for_cnf_vnf_id in args:
|
||||||
|
return fakes.vnf_dict_cnf()
|
||||||
else:
|
else:
|
||||||
return self.get_dummy_vnf_active()
|
return self.get_dummy_vnf_active()
|
||||||
|
|
||||||
@@ -2369,6 +2373,7 @@ class TestController(base.TestCase):
|
|||||||
return_value={'VNFM': FakeVNFMPlugin()})
|
return_value={'VNFM': FakeVNFMPlugin()})
|
||||||
@mock.patch.object(objects.VnfLcmOpOcc, "create")
|
@mock.patch.object(objects.VnfLcmOpOcc, "create")
|
||||||
@mock.patch.object(objects.ScaleVnfRequest, "obj_from_primitive")
|
@mock.patch.object(objects.ScaleVnfRequest, "obj_from_primitive")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "save")
|
||||||
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
||||||
@mock.patch.object(tacker.db.vnfm.vnfm_db.VNFMPluginDb, "get_vnf")
|
@mock.patch.object(tacker.db.vnfm.vnfm_db.VNFMPluginDb, "get_vnf")
|
||||||
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "scale")
|
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "scale")
|
||||||
@@ -2379,13 +2384,18 @@ class TestController(base.TestCase):
|
|||||||
mock_scale,
|
mock_scale,
|
||||||
mock_get_vnf,
|
mock_get_vnf,
|
||||||
mock_vnf_instance_get_by_id,
|
mock_vnf_instance_get_by_id,
|
||||||
|
mock_vnf_instance_save,
|
||||||
mock_obj_from_primitive,
|
mock_obj_from_primitive,
|
||||||
mock_create,
|
mock_create,
|
||||||
mock_get_service_plugins):
|
mock_get_service_plugins):
|
||||||
|
|
||||||
mock_get_vnf.return_value = fakes._get_vnf()
|
mock_get_vnf.return_value = fakes._get_vnf()
|
||||||
|
vim_connection_info = objects.VimConnectionInfo(
|
||||||
|
vim_type="openstack")
|
||||||
|
update = {'vim_connection_info': [vim_connection_info]}
|
||||||
mock_vnf_instance_get_by_id.return_value = fakes.return_vnf_instance(
|
mock_vnf_instance_get_by_id.return_value = fakes.return_vnf_instance(
|
||||||
fields.VnfInstanceState.INSTANTIATED, scale_status="scale_status")
|
fields.VnfInstanceState.INSTANTIATED, scale_status="scale_status",
|
||||||
|
**update)
|
||||||
mock_obj_from_primitive.return_value = fakes.scale_request_make(
|
mock_obj_from_primitive.return_value = fakes.scale_request_make(
|
||||||
"SCALE_IN", 1)
|
"SCALE_IN", 1)
|
||||||
mock_create.return_value = 200
|
mock_create.return_value = 200
|
||||||
@@ -2410,6 +2420,7 @@ class TestController(base.TestCase):
|
|||||||
return_value={'VNFM': FakeVNFMPlugin()})
|
return_value={'VNFM': FakeVNFMPlugin()})
|
||||||
@mock.patch.object(objects.VnfLcmOpOcc, "create")
|
@mock.patch.object(objects.VnfLcmOpOcc, "create")
|
||||||
@mock.patch.object(objects.ScaleVnfRequest, "obj_from_primitive")
|
@mock.patch.object(objects.ScaleVnfRequest, "obj_from_primitive")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "save")
|
||||||
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
||||||
@mock.patch.object(tacker.db.vnfm.vnfm_db.VNFMPluginDb, "get_vnf")
|
@mock.patch.object(tacker.db.vnfm.vnfm_db.VNFMPluginDb, "get_vnf")
|
||||||
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "scale")
|
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "scale")
|
||||||
@@ -2420,13 +2431,18 @@ class TestController(base.TestCase):
|
|||||||
mock_scale,
|
mock_scale,
|
||||||
mock_get_vnf,
|
mock_get_vnf,
|
||||||
mock_vnf_instance_get_by_id,
|
mock_vnf_instance_get_by_id,
|
||||||
|
mock_vnf_instance_save,
|
||||||
mock_obj_from_primitive,
|
mock_obj_from_primitive,
|
||||||
mock_create,
|
mock_create,
|
||||||
mock_get_service_plugins):
|
mock_get_service_plugins):
|
||||||
|
|
||||||
mock_get_vnf.return_value = fakes._get_vnf()
|
mock_get_vnf.return_value = fakes._get_vnf()
|
||||||
|
vim_connection_info = objects.VimConnectionInfo(
|
||||||
|
vim_type="openstack")
|
||||||
|
update = {'vim_connection_info': [vim_connection_info]}
|
||||||
mock_vnf_instance_get_by_id.return_value = fakes.return_vnf_instance(
|
mock_vnf_instance_get_by_id.return_value = fakes.return_vnf_instance(
|
||||||
fields.VnfInstanceState.INSTANTIATED, scale_status="scale_status")
|
fields.VnfInstanceState.INSTANTIATED, scale_status="scale_status",
|
||||||
|
**update)
|
||||||
mock_obj_from_primitive.return_value = fakes.scale_request_make(
|
mock_obj_from_primitive.return_value = fakes.scale_request_make(
|
||||||
"SCALE_OUT", 1)
|
"SCALE_OUT", 1)
|
||||||
mock_create.return_value = 200
|
mock_create.return_value = 200
|
||||||
@@ -2451,6 +2467,7 @@ class TestController(base.TestCase):
|
|||||||
return_value={'VNFM': FakeVNFMPlugin()})
|
return_value={'VNFM': FakeVNFMPlugin()})
|
||||||
@mock.patch.object(objects.VnfLcmOpOcc, "create")
|
@mock.patch.object(objects.VnfLcmOpOcc, "create")
|
||||||
@mock.patch.object(objects.ScaleVnfRequest, "obj_from_primitive")
|
@mock.patch.object(objects.ScaleVnfRequest, "obj_from_primitive")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "save")
|
||||||
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
||||||
@mock.patch.object(tacker.db.vnfm.vnfm_db.VNFMPluginDb, "get_vnf")
|
@mock.patch.object(tacker.db.vnfm.vnfm_db.VNFMPluginDb, "get_vnf")
|
||||||
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "scale")
|
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "scale")
|
||||||
@@ -2459,13 +2476,19 @@ class TestController(base.TestCase):
|
|||||||
mock_scale,
|
mock_scale,
|
||||||
mock_get_vnf,
|
mock_get_vnf,
|
||||||
mock_vnf_instance_get_by_id,
|
mock_vnf_instance_get_by_id,
|
||||||
|
mock_vnf_instance_save,
|
||||||
mock_obj_from_primitive,
|
mock_obj_from_primitive,
|
||||||
mock_create,
|
mock_create,
|
||||||
mock_get_service_plugins):
|
mock_get_service_plugins):
|
||||||
|
|
||||||
mock_get_vnf.return_value = fakes._get_vnf()
|
mock_get_vnf.return_value = fakes._get_vnf()
|
||||||
|
vim_connection_info = objects.VimConnectionInfo(
|
||||||
|
vim_type="openstack")
|
||||||
|
update = {'vim_connection_info': [vim_connection_info]}
|
||||||
mock_vnf_instance_get_by_id.return_value = fakes.return_vnf_instance(
|
mock_vnf_instance_get_by_id.return_value = fakes.return_vnf_instance(
|
||||||
fields.VnfInstanceState.INSTANTIATED, scale_status="scale_status")
|
fields.VnfInstanceState.INSTANTIATED, scale_status="scale_status",
|
||||||
|
**update)
|
||||||
|
|
||||||
mock_obj_from_primitive.return_value = fakes.scale_request_make(
|
mock_obj_from_primitive.return_value = fakes.scale_request_make(
|
||||||
"SCALE_IN", 4)
|
"SCALE_IN", 4)
|
||||||
mock_create.return_value = 200
|
mock_create.return_value = 200
|
||||||
@@ -2505,8 +2528,12 @@ class TestController(base.TestCase):
|
|||||||
mock_get_service_plugins):
|
mock_get_service_plugins):
|
||||||
|
|
||||||
mock_get_vnf.return_value = fakes._get_vnf()
|
mock_get_vnf.return_value = fakes._get_vnf()
|
||||||
|
vim_connection_info = objects.VimConnectionInfo(
|
||||||
|
vim_type="openstack")
|
||||||
|
update = {'vim_connection_info': [vim_connection_info]}
|
||||||
mock_vnf_instance_get_by_id.return_value = fakes.return_vnf_instance(
|
mock_vnf_instance_get_by_id.return_value = fakes.return_vnf_instance(
|
||||||
fields.VnfInstanceState.INSTANTIATED, scale_status="scale_status")
|
fields.VnfInstanceState.INSTANTIATED, scale_status="scale_status",
|
||||||
|
**update)
|
||||||
mock_obj_from_primitive.return_value = fakes.scale_request_make(
|
mock_obj_from_primitive.return_value = fakes.scale_request_make(
|
||||||
"SCALE_OUT", 4)
|
"SCALE_OUT", 4)
|
||||||
mock_create.return_value = 200
|
mock_create.return_value = 200
|
||||||
@@ -2533,6 +2560,7 @@ class TestController(base.TestCase):
|
|||||||
return_value={'VNFM': FakeVNFMPlugin()})
|
return_value={'VNFM': FakeVNFMPlugin()})
|
||||||
@mock.patch.object(objects.ScaleVnfRequest, "obj_from_primitive")
|
@mock.patch.object(objects.ScaleVnfRequest, "obj_from_primitive")
|
||||||
@mock.patch.object(controller.VnfLcmController, "_get_rollback_vnf")
|
@mock.patch.object(controller.VnfLcmController, "_get_rollback_vnf")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "save")
|
||||||
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
||||||
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "send_notification")
|
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "send_notification")
|
||||||
@mock.patch.object(objects.VnfLcmOpOcc, "create")
|
@mock.patch.object(objects.VnfLcmOpOcc, "create")
|
||||||
@@ -2541,6 +2569,7 @@ class TestController(base.TestCase):
|
|||||||
mock_create,
|
mock_create,
|
||||||
mock_send_notification,
|
mock_send_notification,
|
||||||
mock_vnf_instance,
|
mock_vnf_instance,
|
||||||
|
mock_vnf_instance_save,
|
||||||
mock_get_vnf,
|
mock_get_vnf,
|
||||||
mock_obj_from_primitive,
|
mock_obj_from_primitive,
|
||||||
get_service_plugins):
|
get_service_plugins):
|
||||||
@@ -2556,9 +2585,13 @@ class TestController(base.TestCase):
|
|||||||
"SCALE_IN", 1)
|
"SCALE_IN", 1)
|
||||||
mock_get_vnf.return_value = vnf_obj
|
mock_get_vnf.return_value = vnf_obj
|
||||||
|
|
||||||
|
vim_connection_info = objects.VimConnectionInfo(
|
||||||
|
vim_type="openstack")
|
||||||
|
update = {'vim_connection_info': [vim_connection_info]}
|
||||||
vnf_instance = fakes.return_vnf_instance(
|
vnf_instance = fakes.return_vnf_instance(
|
||||||
fields.VnfInstanceState.INSTANTIATED,
|
fields.VnfInstanceState.INSTANTIATED,
|
||||||
scale_status="scale_status")
|
scale_status="scale_status",
|
||||||
|
**update)
|
||||||
|
|
||||||
vnf_instance.instantiated_vnf_info.instance_id =\
|
vnf_instance.instantiated_vnf_info.instance_id =\
|
||||||
uuidsentinel.instance_id
|
uuidsentinel.instance_id
|
||||||
@@ -2571,9 +2604,10 @@ class TestController(base.TestCase):
|
|||||||
|
|
||||||
vnf_info = fakes._get_vnf()
|
vnf_info = fakes._get_vnf()
|
||||||
vnf_instance = fakes.return_vnf_instance(
|
vnf_instance = fakes.return_vnf_instance(
|
||||||
fields.VnfInstanceState.INSTANTIATED, scale_status="scale_status")
|
fields.VnfInstanceState.INSTANTIATED, scale_status="scale_status",
|
||||||
|
**update)
|
||||||
self.controller._scale(self.context,
|
self.controller._scale(self.context,
|
||||||
vnf_info, vnf_instance, body)
|
vnf_instance, vnf_info, body)
|
||||||
|
|
||||||
mock_send_notification.assert_called_once()
|
mock_send_notification.assert_called_once()
|
||||||
self.assertEqual(mock_send_notification.call_args[0][1].get(
|
self.assertEqual(mock_send_notification.call_args[0][1].get(
|
||||||
@@ -2592,6 +2626,228 @@ class TestController(base.TestCase):
|
|||||||
self.assertEqual(mock_send_notification.call_args[0][1].get(
|
self.assertEqual(mock_send_notification.call_args[0][1].get(
|
||||||
'isAutomaticInvocation'), 'False')
|
'isAutomaticInvocation'), 'False')
|
||||||
|
|
||||||
|
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||||
|
return_value={'VNFM': FakeVNFMPlugin()})
|
||||||
|
@mock.patch.object(objects.VnfPackageVnfd, "get_by_id")
|
||||||
|
@mock.patch.object(objects.VnfLcmOpOcc, "create")
|
||||||
|
@mock.patch.object(objects.ScaleVnfRequest, "obj_from_primitive")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "save")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
||||||
|
@mock.patch.object(tacker.db.vnfm.vnfm_db.VNFMPluginDb, "get_vnf")
|
||||||
|
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "scale")
|
||||||
|
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "send_notification")
|
||||||
|
def test_scale_in_cnf(
|
||||||
|
self,
|
||||||
|
mock_send_notification,
|
||||||
|
mock_scale,
|
||||||
|
mock_get_vnf,
|
||||||
|
mock_vnf_instance_get_by_id,
|
||||||
|
mock_vnf_instance_save,
|
||||||
|
mock_obj_from_primitive,
|
||||||
|
mock_create,
|
||||||
|
mock_vnf_package_vnfd_get_by_id,
|
||||||
|
mock_get_service_plugins):
|
||||||
|
|
||||||
|
mock_get_vnf.return_value = fakes.vnf_dict_cnf()
|
||||||
|
vim_connection_info = objects.VimConnectionInfo(
|
||||||
|
vim_type="kubernetes")
|
||||||
|
update = {'vim_connection_info': [vim_connection_info]}
|
||||||
|
scale_status = objects.ScaleInfo(
|
||||||
|
aspect_id='vdu1_aspect', scale_level=1)
|
||||||
|
mock_vnf_instance_get_by_id.return_value = fakes.return_vnf_instance(
|
||||||
|
fields.VnfInstanceState.INSTANTIATED, scale_status=scale_status,
|
||||||
|
**update)
|
||||||
|
mock_vnf_package_vnfd_get_by_id.return_value = \
|
||||||
|
fakes.return_vnf_package_vnfd()
|
||||||
|
scale_request = fakes.scale_request_make("SCALE_IN", 1)
|
||||||
|
scale_request.aspect_id = "vdu1_aspect"
|
||||||
|
mock_obj_from_primitive.return_value = scale_request
|
||||||
|
mock_create.return_value = 200
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"type": "SCALE_IN",
|
||||||
|
"aspectId": "vdu1_aspect",
|
||||||
|
"numberOfSteps": 1,
|
||||||
|
"additionalParams": {}}
|
||||||
|
req = fake_request.HTTPRequest.blank(
|
||||||
|
'/vnf_instances/%s/scale' %
|
||||||
|
FakeVNFMPlugin().vnf_for_cnf_vnf_id)
|
||||||
|
req.body = jsonutils.dump_as_bytes(body)
|
||||||
|
req.headers['Content-Type'] = 'application/json'
|
||||||
|
req.method = 'POST'
|
||||||
|
resp = req.get_response(self.app)
|
||||||
|
self.assertEqual(http_client.ACCEPTED, resp.status_code)
|
||||||
|
mock_scale.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||||
|
return_value={'VNFM': FakeVNFMPlugin()})
|
||||||
|
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
|
||||||
|
@mock.patch.object(objects.VnfPackageVnfd, "get_by_id")
|
||||||
|
@mock.patch.object(objects.VnfLcmOpOcc, "create")
|
||||||
|
@mock.patch.object(objects.ScaleVnfRequest, "obj_from_primitive")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "save")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
||||||
|
@mock.patch.object(tacker.db.vnfm.vnfm_db.VNFMPluginDb, "get_vnf")
|
||||||
|
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "scale")
|
||||||
|
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "send_notification")
|
||||||
|
def test_scale_out_cnf(
|
||||||
|
self,
|
||||||
|
mock_send_notification,
|
||||||
|
mock_scale,
|
||||||
|
mock_get_vnf,
|
||||||
|
mock_vnf_instance_get_by_id,
|
||||||
|
mock_vnf_instance_save,
|
||||||
|
mock_obj_from_primitive,
|
||||||
|
mock_create,
|
||||||
|
mock_vnf_package_vnfd_get_by_id,
|
||||||
|
mock_vnfd_dict,
|
||||||
|
mock_get_service_plugins):
|
||||||
|
|
||||||
|
vnf_info = fakes.vnf_dict_cnf()
|
||||||
|
mock_get_vnf.return_value = vnf_info
|
||||||
|
vim_connection_info = objects.VimConnectionInfo(
|
||||||
|
vim_type="kubernetes")
|
||||||
|
update = {'vim_connection_info': [vim_connection_info]}
|
||||||
|
scale_status = objects.ScaleInfo(
|
||||||
|
aspect_id='vdu1_aspect', scale_level=1)
|
||||||
|
mock_vnf_instance_get_by_id.return_value = fakes.return_vnf_instance(
|
||||||
|
fields.VnfInstanceState.INSTANTIATED, scale_status=scale_status,
|
||||||
|
**update)
|
||||||
|
mock_vnf_package_vnfd_get_by_id.return_value = \
|
||||||
|
fakes.return_vnf_package_vnfd()
|
||||||
|
mock_vnfd_dict.return_value = fakes.vnfd_dict_cnf()
|
||||||
|
scale_request = fakes.scale_request_make("SCALE_OUT", 1)
|
||||||
|
scale_request.aspect_id = "vdu1_aspect"
|
||||||
|
mock_obj_from_primitive.return_value = scale_request
|
||||||
|
mock_create.return_value = 200
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"type": "SCALE_OUT",
|
||||||
|
"aspectId": "vdu1_aspect",
|
||||||
|
"numberOfSteps": 1,
|
||||||
|
"additionalParams": {}}
|
||||||
|
req = fake_request.HTTPRequest.blank(
|
||||||
|
'/vnf_instances/%s/scale' %
|
||||||
|
FakeVNFMPlugin().vnf_for_cnf_vnf_id)
|
||||||
|
req.body = jsonutils.dump_as_bytes(body)
|
||||||
|
req.headers['Content-Type'] = 'application/json'
|
||||||
|
req.method = 'POST'
|
||||||
|
resp = req.get_response(self.app)
|
||||||
|
self.assertEqual(http_client.ACCEPTED, resp.status_code)
|
||||||
|
mock_scale.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||||
|
return_value={'VNFM': FakeVNFMPlugin()})
|
||||||
|
@mock.patch.object(objects.VnfLcmOpOcc, "create")
|
||||||
|
@mock.patch.object(objects.ScaleVnfRequest, "obj_from_primitive")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "save")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
||||||
|
@mock.patch.object(tacker.db.vnfm.vnfm_db.VNFMPluginDb, "get_vnf")
|
||||||
|
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "scale")
|
||||||
|
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "send_notification")
|
||||||
|
def test_scale_in_cnf_error_is_reverse(
|
||||||
|
self,
|
||||||
|
mock_send_notification,
|
||||||
|
mock_scale,
|
||||||
|
mock_get_vnf,
|
||||||
|
mock_vnf_instance_get_by_id,
|
||||||
|
mock_vnf_instance_save,
|
||||||
|
mock_obj_from_primitive,
|
||||||
|
mock_create,
|
||||||
|
mock_get_service_plugins):
|
||||||
|
|
||||||
|
mock_get_vnf.return_value = fakes.vnf_dict_cnf()
|
||||||
|
vim_connection_info = objects.VimConnectionInfo(
|
||||||
|
vim_type="kubernetes")
|
||||||
|
update = {'vim_connection_info': [vim_connection_info]}
|
||||||
|
scale_status = objects.ScaleInfo(
|
||||||
|
aspect_id='vdu1_aspect', scale_level=1)
|
||||||
|
mock_vnf_instance_get_by_id.return_value = fakes.return_vnf_instance(
|
||||||
|
fields.VnfInstanceState.INSTANTIATED, scale_status=scale_status,
|
||||||
|
**update)
|
||||||
|
scale_request = fakes.scale_request_make("SCALE_IN", 1)
|
||||||
|
scale_request.aspect_id = "vdu1_aspect"
|
||||||
|
scale_request.additional_params = {"is_reverse": "True"}
|
||||||
|
mock_obj_from_primitive.return_value = scale_request
|
||||||
|
mock_create.return_value = 200
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"type": "SCALE_IN",
|
||||||
|
"aspectId": "vdu1_aspect",
|
||||||
|
"numberOfSteps": 1,
|
||||||
|
"additionalParams": {
|
||||||
|
"is_reverse": "True"}}
|
||||||
|
req = fake_request.HTTPRequest.blank(
|
||||||
|
'/vnf_instances/%s/scale' %
|
||||||
|
FakeVNFMPlugin().vnf_for_cnf_vnf_id)
|
||||||
|
req.body = jsonutils.dump_as_bytes(body)
|
||||||
|
req.headers['Content-Type'] = 'application/json'
|
||||||
|
req.method = 'POST'
|
||||||
|
resp = req.get_response(self.app)
|
||||||
|
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
|
||||||
|
mock_scale.assert_not_called()
|
||||||
|
|
||||||
|
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||||
|
return_value={'VNFM': FakeVNFMPlugin()})
|
||||||
|
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
|
||||||
|
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
|
||||||
|
@mock.patch.object(objects.VnfPackage, "get_by_id")
|
||||||
|
@mock.patch.object(objects.VnfLcmOpOcc, "create")
|
||||||
|
@mock.patch.object(objects.ScaleVnfRequest, "obj_from_primitive")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "save")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
||||||
|
@mock.patch.object(tacker.db.vnfm.vnfm_db.VNFMPluginDb, "get_vnf")
|
||||||
|
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "scale")
|
||||||
|
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "send_notification")
|
||||||
|
def test_scale_out_cnf_err_over_max_scale_level(
|
||||||
|
self,
|
||||||
|
mock_send_notification,
|
||||||
|
mock_scale,
|
||||||
|
mock_get_vnf,
|
||||||
|
mock_vnf_instance_get_by_id,
|
||||||
|
mock_vnf_instance_save,
|
||||||
|
mock_obj_from_primitive,
|
||||||
|
mock_create,
|
||||||
|
mock_vnf_package_get_by_id,
|
||||||
|
mock_vnf_package_vnfd_get_by_id,
|
||||||
|
mock_vnfd_dict,
|
||||||
|
mock_get_service_plugins):
|
||||||
|
|
||||||
|
vnf_info = fakes.vnf_dict_cnf()
|
||||||
|
mock_get_vnf.return_value = vnf_info
|
||||||
|
vim_connection_info = objects.VimConnectionInfo(
|
||||||
|
vim_type="kubernetes")
|
||||||
|
update = {'vim_connection_info': [vim_connection_info]}
|
||||||
|
scale_status = objects.ScaleInfo(
|
||||||
|
aspect_id='vdu1_aspect', scale_level=1)
|
||||||
|
mock_vnf_instance_get_by_id.return_value = fakes.return_vnf_instance(
|
||||||
|
fields.VnfInstanceState.INSTANTIATED, scale_status=scale_status,
|
||||||
|
**update)
|
||||||
|
mock_vnf_package_vnfd_get_by_id.return_value = \
|
||||||
|
fakes.return_vnf_package_vnfd()
|
||||||
|
mock_vnf_package_get_by_id.return_value = \
|
||||||
|
fakes.return_vnf_package_with_deployment_flavour()
|
||||||
|
mock_vnfd_dict.return_value = fakes.vnfd_dict_cnf()
|
||||||
|
scale_request = fakes.scale_request_make("SCALE_OUT", 3)
|
||||||
|
scale_request.aspect_id = "vdu1_aspect"
|
||||||
|
mock_obj_from_primitive.return_value = scale_request
|
||||||
|
mock_create.return_value = 200
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"type": "SCALE_OUT",
|
||||||
|
"aspectId": "vdu1_aspect",
|
||||||
|
"numberOfSteps": 3,
|
||||||
|
"additionalParams": {}}
|
||||||
|
req = fake_request.HTTPRequest.blank(
|
||||||
|
'/vnf_instances/%s/scale' %
|
||||||
|
FakeVNFMPlugin().vnf_for_cnf_vnf_id)
|
||||||
|
req.body = jsonutils.dump_as_bytes(body)
|
||||||
|
req.headers['Content-Type'] = 'application/json'
|
||||||
|
req.method = 'POST'
|
||||||
|
resp = req.get_response(self.app)
|
||||||
|
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
|
||||||
|
mock_scale.assert_not_called()
|
||||||
|
|
||||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||||
return_value={'VNFM': FakeVNFMPlugin()})
|
return_value={'VNFM': FakeVNFMPlugin()})
|
||||||
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
|
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
|
||||||
|
@@ -411,7 +411,10 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
|||||||
test_utils.copy_csar_files(fake_csar, "vnflcm4")
|
test_utils.copy_csar_files(fake_csar, "vnflcm4")
|
||||||
self._mock_vnf_manager(fail_method_name='create_wait')
|
self._mock_vnf_manager(fail_method_name='create_wait')
|
||||||
driver = vnflcm_driver.VnfLcmDriver()
|
driver = vnflcm_driver.VnfLcmDriver()
|
||||||
vnf_dict = {"vnfd": {"attributes": {}}, "attributes": {}}
|
scale_status = objects.ScaleInfo(aspect_id='SP1', scale_level=0)
|
||||||
|
vnf_dict = {"vnfd": {"attributes": {}},
|
||||||
|
"attributes": {"scaling_group_names": {"SP1": "G1"}},
|
||||||
|
"scale_status": [scale_status]}
|
||||||
error = self.assertRaises(exceptions.VnfInstantiationWaitFailed,
|
error = self.assertRaises(exceptions.VnfInstantiationWaitFailed,
|
||||||
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)
|
||||||
@@ -750,7 +753,9 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
|||||||
uuidsentinel.instance_id
|
uuidsentinel.instance_id
|
||||||
self._mock_vnf_manager()
|
self._mock_vnf_manager()
|
||||||
driver = vnflcm_driver.VnfLcmDriver()
|
driver = vnflcm_driver.VnfLcmDriver()
|
||||||
vnf_dict = {"attributes": {}}
|
scale_status = objects.ScaleInfo(aspect_id='SP1', scale_level=0)
|
||||||
|
vnf_dict = {"attributes": {"scaling_group_names": {"SP1": "G1"}},
|
||||||
|
"scale_status": [scale_status]}
|
||||||
mock_make_final_vnf_dict.return_value = {}
|
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)
|
||||||
@@ -1062,9 +1067,9 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
|||||||
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
|
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
|
||||||
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
|
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
|
||||||
'\"initialLevel\": 0, \"default\": 0 }}}'
|
'\"initialLevel\": 0, \"default\": 0 }}}'
|
||||||
scale_vnf_request = fakes.scale_request("SCALE_IN", 1, "True")
|
scale_vnf_request = fakes.scale_request("SCALE_IN", "SP1", 1, "True")
|
||||||
vim_connection_info = vim_connection.VimConnectionInfo(
|
vim_connection_info = vim_connection.VimConnectionInfo(
|
||||||
vim_type="fake_type")
|
vim_type="openstack")
|
||||||
scale_name_list = ["fake"]
|
scale_name_list = ["fake"]
|
||||||
grp_id = "fake_id"
|
grp_id = "fake_id"
|
||||||
driver = vnflcm_driver.VnfLcmDriver()
|
driver = vnflcm_driver.VnfLcmDriver()
|
||||||
@@ -1089,9 +1094,9 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
|||||||
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
|
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
|
||||||
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
|
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
|
||||||
'\"initialLevel\": 0, \"default\": 0 }}}'
|
'\"initialLevel\": 0, \"default\": 0 }}}'
|
||||||
scale_vnf_request = fakes.scale_request("SCALE_IN", 1, "False")
|
scale_vnf_request = fakes.scale_request("SCALE_IN", "SP1", 1, "False")
|
||||||
vim_connection_info = vim_connection.VimConnectionInfo(
|
vim_connection_info = vim_connection.VimConnectionInfo(
|
||||||
vim_type="fake_type")
|
vim_type="openstack")
|
||||||
scale_name_list = ["fake"]
|
scale_name_list = ["fake"]
|
||||||
grp_id = "fake_id"
|
grp_id = "fake_id"
|
||||||
with open(vnf_info["attributes"]["heat_template"], "r") as f:
|
with open(vnf_info["attributes"]["heat_template"], "r") as f:
|
||||||
@@ -1119,9 +1124,9 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
|||||||
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
|
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
|
||||||
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
|
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
|
||||||
'\"initialLevel\": 0, \"default\": 0 }}}'
|
'\"initialLevel\": 0, \"default\": 0 }}}'
|
||||||
scale_vnf_request = fakes.scale_request("SCALE_OUT", 1, "False")
|
scale_vnf_request = fakes.scale_request("SCALE_OUT", "SP1", 1, "False")
|
||||||
vim_connection_info = vim_connection.VimConnectionInfo(
|
vim_connection_info = vim_connection.VimConnectionInfo(
|
||||||
vim_type="fake_type")
|
vim_type="openstack")
|
||||||
scale_name_list = ["fake"]
|
scale_name_list = ["fake"]
|
||||||
grp_id = "fake_id"
|
grp_id = "fake_id"
|
||||||
with open(vnf_info["attributes"]["heat_template"], "r") as f:
|
with open(vnf_info["attributes"]["heat_template"], "r") as f:
|
||||||
@@ -1149,9 +1154,9 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
|||||||
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
|
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
|
||||||
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
|
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
|
||||||
'\"initialLevel\": 0, \"default\": 1 }}}'
|
'\"initialLevel\": 0, \"default\": 1 }}}'
|
||||||
scale_vnf_request = fakes.scale_request("SCALE_OUT", 1, "False")
|
scale_vnf_request = fakes.scale_request("SCALE_OUT", "SP1", 1, "False")
|
||||||
vim_connection_info = vim_connection.VimConnectionInfo(
|
vim_connection_info = vim_connection.VimConnectionInfo(
|
||||||
vim_type="fake_type")
|
vim_type="openstack")
|
||||||
scale_name_list = ["fake"]
|
scale_name_list = ["fake"]
|
||||||
grp_id = "fake_id"
|
grp_id = "fake_id"
|
||||||
with open(vnf_info["attributes"]["heat_template"], "r") as f:
|
with open(vnf_info["attributes"]["heat_template"], "r") as f:
|
||||||
@@ -1161,6 +1166,108 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
|||||||
driver.scale(self.context, vnf_info, scale_vnf_request,
|
driver.scale(self.context, vnf_info, scale_vnf_request,
|
||||||
vim_connection_info, scale_name_list, grp_id)
|
vim_connection_info, scale_name_list, grp_id)
|
||||||
|
|
||||||
|
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||||
|
return_value={'VNFM': FakeVNFMPlugin()})
|
||||||
|
@mock.patch.object(VnfLcmDriver,
|
||||||
|
'_init_mgmt_driver_hash')
|
||||||
|
@mock.patch.object(yaml, "safe_load")
|
||||||
|
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
|
||||||
|
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
|
||||||
|
@mock.patch.object(vim_client.VimClient, "get_vim")
|
||||||
|
@mock.patch.object(objects.VnfLcmOpOcc, "save")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
||||||
|
@mock.patch.object(driver_manager.DriverManager, "invoke")
|
||||||
|
def test_scale_in_cnf(self, mock_invoke, mock_vnf_instance_get_by_id,
|
||||||
|
mock_lcm_save, mock_vim, mock_vnf_package_vnfd,
|
||||||
|
mock_vnfd_dict, mock_yaml_safe_load, mock_init_hash,
|
||||||
|
mock_get_service_plugins):
|
||||||
|
mock_init_hash.return_value = {
|
||||||
|
"vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859"
|
||||||
|
"b18d663b127100eb72b19eecd7ed51"
|
||||||
|
}
|
||||||
|
vnf_info = fakes.vnf_dict_cnf()
|
||||||
|
vnf_info['vnf_lcm_op_occ'] = fakes.vnflcm_scale_in_cnf()
|
||||||
|
vnf_info['scale_level'] = 1
|
||||||
|
vnf_info['after_scale_level'] = 0
|
||||||
|
vnf_info['notification'] = {}
|
||||||
|
scale_vnf_request = fakes.scale_request(
|
||||||
|
"SCALE_IN", "vdu1_aspect", 1, "False")
|
||||||
|
vim_connection_info = vim_connection.VimConnectionInfo(
|
||||||
|
vim_type="kubernetes")
|
||||||
|
update = {'vim_connection_info': [vim_connection_info]}
|
||||||
|
scale_status = objects.ScaleInfo(
|
||||||
|
aspect_id='vdu1_aspect', scale_level=1)
|
||||||
|
vnf_instance = fakes.return_vnf_instance(
|
||||||
|
fields.VnfInstanceState.INSTANTIATED, scale_status=scale_status,
|
||||||
|
**update)
|
||||||
|
mock_vnfd_dict.return_value = fakes.vnfd_dict_cnf()
|
||||||
|
mock_yaml_safe_load.return_value = fakes.vnfd_dict_cnf()
|
||||||
|
mock_invoke.side_effect = [
|
||||||
|
# Kubernetes.get_scale_in_ids called in _scale_vnf_pre()
|
||||||
|
[[], [], None, None],
|
||||||
|
# Kubernetes.scale called in scale()
|
||||||
|
None,
|
||||||
|
# Kubernetes.scale_wait called in scale()
|
||||||
|
None,
|
||||||
|
# scale_resource_update called in _scale_resource_update()
|
||||||
|
None]
|
||||||
|
mock_vnf_package_vnfd.return_value = fakes.return_vnf_package_vnfd()
|
||||||
|
driver = vnflcm_driver.VnfLcmDriver()
|
||||||
|
vim_obj = {'vim_id': uuidsentinel.vim_id,
|
||||||
|
'vim_name': 'fake_vim',
|
||||||
|
'vim_type': 'kubernetes',
|
||||||
|
'vim_auth': {
|
||||||
|
'auth_url': 'http://localhost:8443',
|
||||||
|
'password': 'test_pw',
|
||||||
|
'username': 'test_user',
|
||||||
|
'project_name': 'test_project'}}
|
||||||
|
self.vim_client.get_vim.return_value = vim_obj
|
||||||
|
driver.scale_vnf(self.context, vnf_info, vnf_instance,
|
||||||
|
scale_vnf_request)
|
||||||
|
|
||||||
|
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||||
|
return_value={'VNFM': FakeVNFMPlugin()})
|
||||||
|
@mock.patch.object(yaml, "safe_load")
|
||||||
|
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
|
||||||
|
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
|
||||||
|
@mock.patch.object(objects.VnfLcmOpOcc, "save")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
||||||
|
@mock.patch.object(driver_manager.DriverManager, "invoke")
|
||||||
|
def test_scale_out_cnf(self, mock_invoke, mock_vnf_instance_get_by_id,
|
||||||
|
mock_lcm_save, mock_vnf_package_vnfd, mock_vnfd_dict,
|
||||||
|
mock_yaml_safe_load, mock_get_service_plugins):
|
||||||
|
vnf_info = fakes.vnf_dict_cnf()
|
||||||
|
vnf_info['vnf_lcm_op_occ'] = fakes.vnflcm_scale_out_cnf()
|
||||||
|
vnf_info['scale_level'] = 0
|
||||||
|
vnf_info['after_scale_level'] = 1
|
||||||
|
vnf_info['notification'] = {}
|
||||||
|
scale_vnf_request = fakes.scale_request(
|
||||||
|
"SCALE_OUT", "vdu1_aspect", 1, "False")
|
||||||
|
vim_connection_info = vim_connection.VimConnectionInfo(
|
||||||
|
vim_type="kubernetes")
|
||||||
|
update = {'vim_connection_info': [vim_connection_info]}
|
||||||
|
scale_status = objects.ScaleInfo(
|
||||||
|
aspect_id='vdu1_aspect', scale_level=1)
|
||||||
|
vnf_instance = fakes.return_vnf_instance(
|
||||||
|
fields.VnfInstanceState.INSTANTIATED, scale_status=scale_status,
|
||||||
|
**update)
|
||||||
|
mock_vnf_instance_get_by_id.return_value = vnf_instance
|
||||||
|
mock_vnf_package_vnfd.return_value = fakes.return_vnf_package_vnfd()
|
||||||
|
mock_vnfd_dict.return_value = fakes.vnfd_dict_cnf()
|
||||||
|
mock_yaml_safe_load.return_value = fakes.vnfd_dict_cnf()
|
||||||
|
driver = vnflcm_driver.VnfLcmDriver()
|
||||||
|
vim_obj = {'vim_id': uuidsentinel.vim_id,
|
||||||
|
'vim_name': 'fake_vim',
|
||||||
|
'vim_type': 'kubernetes',
|
||||||
|
'vim_auth': {
|
||||||
|
'auth_url': 'http://localhost:8443',
|
||||||
|
'password': 'test_pw',
|
||||||
|
'username': 'test_user',
|
||||||
|
'project_name': 'test_project'}}
|
||||||
|
self.vim_client.get_vim.return_value = vim_obj
|
||||||
|
driver.scale_vnf(self.context, vnf_info, vnf_instance,
|
||||||
|
scale_vnf_request)
|
||||||
|
|
||||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||||
return_value={'VNFM': FakeVNFMPlugin()})
|
return_value={'VNFM': FakeVNFMPlugin()})
|
||||||
@mock.patch.object(VnfLcmDriver,
|
@mock.patch.object(VnfLcmDriver,
|
||||||
@@ -2068,3 +2175,69 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
|||||||
self.assertEqual(1, mock_scale.call_count)
|
self.assertEqual(1, mock_scale.call_count)
|
||||||
self.assertEqual(1, mock_wait.call_count)
|
self.assertEqual(1, mock_wait.call_count)
|
||||||
self.assertEqual(2, mock_scale_resource.call_count)
|
self.assertEqual(2, mock_scale_resource.call_count)
|
||||||
|
|
||||||
|
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||||
|
return_value={'VNFM': FakeVNFMPlugin()})
|
||||||
|
@mock.patch.object(VnfLcmDriver,
|
||||||
|
'_init_mgmt_driver_hash')
|
||||||
|
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
|
||||||
|
@mock.patch.object(yaml, "safe_load")
|
||||||
|
@mock.patch.object(objects.VnfLcmOpOcc, "save")
|
||||||
|
@mock.patch.object(VNFLcmRPCAPI, "send_notification")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "save")
|
||||||
|
@mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback_pre")
|
||||||
|
@mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback")
|
||||||
|
def test_rollback_vnf_scale_cnf(
|
||||||
|
self,
|
||||||
|
mock_update,
|
||||||
|
mock_up,
|
||||||
|
mock_insta_save,
|
||||||
|
mock_notification,
|
||||||
|
mock_lcm_save,
|
||||||
|
mock_yaml_safe_load,
|
||||||
|
mock_vnfd_dict,
|
||||||
|
mock_init_hash,
|
||||||
|
mock_get_service_plugins):
|
||||||
|
mock_init_hash.return_value = {
|
||||||
|
"vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859"
|
||||||
|
"b18d663b127100eb72b19eecd7ed51"
|
||||||
|
}
|
||||||
|
vim_connection_info = vim_connection.VimConnectionInfo(
|
||||||
|
vim_type="kubernetes")
|
||||||
|
update = {'vim_connection_info': [vim_connection_info]}
|
||||||
|
vnf_instance = fakes.return_vnf_instance(
|
||||||
|
fields.VnfInstanceState.INSTANTIATED, **update)
|
||||||
|
|
||||||
|
vnf_instance.instantiated_vnf_info.instance_id =\
|
||||||
|
uuidsentinel.instance_id
|
||||||
|
vnf_instance.instantiated_vnf_info.scale_status = []
|
||||||
|
vnf_instance.instantiated_vnf_info.scale_status.append(
|
||||||
|
objects.ScaleInfo(aspect_id='vdu1_aspect', scale_level=0))
|
||||||
|
vnf_lcm_op_occs = fakes.vnflcm_rollback()
|
||||||
|
vnf_lcm_op_occs.operation_params = \
|
||||||
|
'{"type": "SCALE_OUT", "aspect_id": "vdu1_aspect"}'
|
||||||
|
vnf_info = fakes.vnf_dict_cnf()
|
||||||
|
vnf_info['vnf_lcm_op_occ'] = vnf_lcm_op_occs
|
||||||
|
vnf_info['scale_level'] = 1
|
||||||
|
mock_vnfd_dict.return_value = fakes.vnfd_dict_cnf()
|
||||||
|
operation_params = jsonutils.loads(vnf_lcm_op_occs.operation_params)
|
||||||
|
mock_yaml_safe_load.return_value = fakes.vnfd_dict_cnf()
|
||||||
|
vim_obj = {'vim_id': uuidsentinel.vim_id,
|
||||||
|
'vim_name': 'fake_vim',
|
||||||
|
'vim_type': 'kubernetes',
|
||||||
|
'vim_auth': {
|
||||||
|
'auth_url': 'http://localhost:8443',
|
||||||
|
'password': 'test_pw',
|
||||||
|
'username': 'test_user',
|
||||||
|
'project_name': 'test_project'}}
|
||||||
|
self.vim_client.get_vim.return_value = vim_obj
|
||||||
|
|
||||||
|
self._mock_vnf_manager()
|
||||||
|
driver = vnflcm_driver.VnfLcmDriver()
|
||||||
|
|
||||||
|
driver.rollback_vnf(
|
||||||
|
self.context,
|
||||||
|
vnf_info,
|
||||||
|
vnf_instance,
|
||||||
|
operation_params)
|
||||||
|
self.assertEqual(1, mock_lcm_save.call_count)
|
||||||
|
@@ -15,6 +15,8 @@
|
|||||||
|
|
||||||
|
|
||||||
from kubernetes import client
|
from kubernetes import client
|
||||||
|
from tacker.db.db_sqlalchemy import models
|
||||||
|
from tacker.tests import uuidsentinel
|
||||||
|
|
||||||
CREATE_K8S_FALSE_VALUE = None
|
CREATE_K8S_FALSE_VALUE = None
|
||||||
|
|
||||||
@@ -982,3 +984,44 @@ def fake_pod_list():
|
|||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_scale_policy(type, aspect_id='vdu1', delta_num=1):
|
||||||
|
policy = dict()
|
||||||
|
policy['vnf_instance_id'] = uuidsentinel.vnf_instance_id
|
||||||
|
policy['action'] = type
|
||||||
|
policy['name'] = aspect_id
|
||||||
|
policy['delta_num'] = delta_num
|
||||||
|
policy['vdu_defs'] = {
|
||||||
|
'VDU1': {
|
||||||
|
'type': 'tosca.nodes.nfv.Vdu.Compute',
|
||||||
|
'properties': {
|
||||||
|
'name': 'fake_name',
|
||||||
|
'description': 'test description',
|
||||||
|
'vdu_profile': {
|
||||||
|
'min_number_of_instances': 1,
|
||||||
|
'max_number_of_instances': 3}}}}
|
||||||
|
|
||||||
|
return policy
|
||||||
|
|
||||||
|
|
||||||
|
def get_vnf_resource_list(kind, name='fake_name'):
|
||||||
|
vnf_resource = models.VnfResource()
|
||||||
|
vnf_resource.vnf_instance_id = uuidsentinel.vnf_instance_id
|
||||||
|
vnf_resource.resource_name = \
|
||||||
|
_("fake_namespace,{name}").format(name=name)
|
||||||
|
vnf_resource.resource_type = \
|
||||||
|
_("v1,{kind}").format(kind=kind)
|
||||||
|
return [vnf_resource]
|
||||||
|
|
||||||
|
|
||||||
|
def get_fake_pod_info(kind, name='fake_name', pod_status='Running'):
|
||||||
|
if kind == 'Deployment':
|
||||||
|
pod_name = _('{name}-1234567890-abcde').format(name=name)
|
||||||
|
elif kind == 'ReplicaSet':
|
||||||
|
pod_name = _('{name}-12345').format(name=name)
|
||||||
|
elif kind == 'StatefulSet':
|
||||||
|
pod_name = _('{name}-1').format(name=name)
|
||||||
|
return client.V1Pod(
|
||||||
|
metadata=client.V1ObjectMeta(name=pod_name),
|
||||||
|
status=client.V1PodStatus(phase=pod_status))
|
||||||
|
@@ -30,6 +30,7 @@ from tacker.objects import vnf_package_vnfd
|
|||||||
from tacker.objects import vnf_resources as vnf_resource_obj
|
from tacker.objects import vnf_resources as vnf_resource_obj
|
||||||
from tacker.tests.unit import base
|
from tacker.tests.unit import base
|
||||||
from tacker.tests.unit.db import utils
|
from tacker.tests.unit.db import utils
|
||||||
|
from tacker.tests.unit.vnflcm import fakes as vnflcm_fakes
|
||||||
from tacker.tests.unit.vnfm.infra_drivers.kubernetes import fakes
|
from tacker.tests.unit.vnfm.infra_drivers.kubernetes import fakes
|
||||||
from tacker.tests.unit.vnfm.infra_drivers.openstack.fixture_data import \
|
from tacker.tests.unit.vnfm.infra_drivers.openstack.fixture_data import \
|
||||||
fixture_data_utils as fd_utils
|
fixture_data_utils as fd_utils
|
||||||
@@ -1799,3 +1800,490 @@ class TestKubernetes(base.TestCase):
|
|||||||
"{'namespace': 'test', 'name': " +
|
"{'namespace': 'test', 'name': " +
|
||||||
"'curry-test001', 'apiVersion': 'apps/v1', " +
|
"'curry-test001', 'apiVersion': 'apps/v1', " +
|
||||||
"'kind': 'Deployment', 'status': 'Creating'}")
|
"'kind': 'Deployment', 'status': 'Creating'}")
|
||||||
|
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'patch_namespaced_deployment_scale')
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_deployment_scale')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_in_deployment(self, mock_vnf_resource_list,
|
||||||
|
mock_read_namespaced_deployment_scale,
|
||||||
|
mock_patch_namespaced_deployment_scale):
|
||||||
|
policy = fakes.get_scale_policy(type='in')
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='Deployment')
|
||||||
|
mock_read_namespaced_deployment_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=2),
|
||||||
|
status=client.V1ScaleStatus(replicas=2))
|
||||||
|
mock_patch_namespaced_deployment_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=1),
|
||||||
|
status=client.V1ScaleStatus(replicas=1))
|
||||||
|
self.kubernetes.scale(context=self.context, plugin=None,
|
||||||
|
auth_attr=utils.get_vim_auth_obj(),
|
||||||
|
policy=policy,
|
||||||
|
region_name=None)
|
||||||
|
mock_read_namespaced_deployment_scale.assert_called_once()
|
||||||
|
mock_patch_namespaced_deployment_scale.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'patch_namespaced_stateful_set_scale')
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_stateful_set_scale')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_in_stateful_set(self, mock_vnf_resource_list,
|
||||||
|
mock_read_namespaced_stateful_set_scale,
|
||||||
|
mock_patch_namespaced_stateful_set_scale):
|
||||||
|
policy = fakes.get_scale_policy(type='in')
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='StatefulSet')
|
||||||
|
mock_read_namespaced_stateful_set_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=2),
|
||||||
|
status=client.V1ScaleStatus(replicas=2))
|
||||||
|
mock_patch_namespaced_stateful_set_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=1),
|
||||||
|
status=client.V1ScaleStatus(replicas=1))
|
||||||
|
self.kubernetes.scale(context=self.context, plugin=None,
|
||||||
|
auth_attr=utils.get_vim_auth_obj(),
|
||||||
|
policy=policy,
|
||||||
|
region_name=None)
|
||||||
|
mock_read_namespaced_stateful_set_scale.assert_called_once()
|
||||||
|
mock_patch_namespaced_stateful_set_scale.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'patch_namespaced_replica_set_scale')
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_replica_set_scale')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_in_replica_set(self, mock_vnf_resource_list,
|
||||||
|
mock_read_namespaced_replica_set_scale,
|
||||||
|
mock_patch_namespaced_replica_set_scale):
|
||||||
|
policy = fakes.get_scale_policy(type='in')
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='ReplicaSet')
|
||||||
|
mock_read_namespaced_replica_set_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=2),
|
||||||
|
status=client.V1ScaleStatus(replicas=2))
|
||||||
|
mock_patch_namespaced_replica_set_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=1),
|
||||||
|
status=client.V1ScaleStatus(replicas=1))
|
||||||
|
self.kubernetes.scale(context=self.context, plugin=None,
|
||||||
|
auth_attr=utils.get_vim_auth_obj(),
|
||||||
|
policy=policy,
|
||||||
|
region_name=None)
|
||||||
|
mock_read_namespaced_replica_set_scale.assert_called_once()
|
||||||
|
mock_patch_namespaced_replica_set_scale.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'patch_namespaced_deployment_scale')
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_deployment_scale')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_out(self, mock_vnf_resource_list,
|
||||||
|
mock_read_namespaced_deployment_scale,
|
||||||
|
mock_patch_namespaced_deployment_scale):
|
||||||
|
policy = fakes.get_scale_policy(type='out')
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='Deployment')
|
||||||
|
mock_read_namespaced_deployment_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=1),
|
||||||
|
status=client.V1ScaleStatus(replicas=1))
|
||||||
|
mock_patch_namespaced_deployment_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=2),
|
||||||
|
status=client.V1ScaleStatus(replicas=2))
|
||||||
|
self.kubernetes.scale(context=self.context, plugin=None,
|
||||||
|
auth_attr=utils.get_vim_auth_obj(),
|
||||||
|
policy=policy,
|
||||||
|
region_name=None)
|
||||||
|
mock_read_namespaced_deployment_scale.assert_called_once()
|
||||||
|
mock_patch_namespaced_deployment_scale.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_target_not_found(self, mock_vnf_resource_list):
|
||||||
|
policy = fakes.get_scale_policy(type='in')
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='Depoyment', name='other_name')
|
||||||
|
self.assertRaises(vnfm.CNFScaleFailed,
|
||||||
|
self.kubernetes.scale,
|
||||||
|
self.context, None,
|
||||||
|
utils.get_vim_auth_obj(), policy, None)
|
||||||
|
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_out_of_target_kind(self, mock_vnf_resource_list):
|
||||||
|
policy = fakes.get_scale_policy(type='in')
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='Pod')
|
||||||
|
self.assertRaises(vnfm.CNFScaleFailed,
|
||||||
|
self.kubernetes.scale,
|
||||||
|
self.context, None,
|
||||||
|
utils.get_vim_auth_obj(), policy, None)
|
||||||
|
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_deployment_scale')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_in_less_than_min_replicas(self, mock_vnf_resource_list,
|
||||||
|
mock_read_namespaced_deployment_scale):
|
||||||
|
policy = fakes.get_scale_policy(type='in')
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='Deployment')
|
||||||
|
mock_read_namespaced_deployment_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=1),
|
||||||
|
status=client.V1ScaleStatus(replicas=1))
|
||||||
|
self.assertRaises(vnfm.CNFScaleFailed,
|
||||||
|
self.kubernetes.scale,
|
||||||
|
self.context, None,
|
||||||
|
utils.get_vim_auth_obj(), policy, None)
|
||||||
|
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_deployment_scale')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_out_over_max_replicas(self, mock_vnf_resource_list,
|
||||||
|
mock_read_namespaced_deployment_scale):
|
||||||
|
policy = fakes.get_scale_policy(type='out')
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='Deployment')
|
||||||
|
mock_read_namespaced_deployment_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=3),
|
||||||
|
status=client.V1ScaleStatus(replicas=3))
|
||||||
|
self.assertRaises(vnfm.CNFScaleFailed,
|
||||||
|
self.kubernetes.scale,
|
||||||
|
self.context, None,
|
||||||
|
utils.get_vim_auth_obj(), policy, None)
|
||||||
|
|
||||||
|
def _test_scale_legacy(self, scale_type,
|
||||||
|
current_replicas, after_replicas,
|
||||||
|
mock_vnf_resource_list,
|
||||||
|
mock_read_namespaced_deployment,
|
||||||
|
mock_patch_namespaced_deployment_scale,
|
||||||
|
mock_read_namespaced_horizontal_pod_autoscaler):
|
||||||
|
policy = fakes.get_scale_policy(type=scale_type, aspect_id='SP1')
|
||||||
|
policy['instance_id'] = "fake_namespace,fake_name"
|
||||||
|
mock_vnf_resource_list.return_value = []
|
||||||
|
mock_read_namespaced_deployment.return_value = \
|
||||||
|
client.V1Deployment(
|
||||||
|
spec=client.V1ScaleSpec(replicas=current_replicas),
|
||||||
|
status=client.V1DeploymentStatus(replicas=current_replicas),
|
||||||
|
metadata=client.V1ObjectMeta(labels={'scaling_name': 'SP1'}))
|
||||||
|
mock_read_namespaced_horizontal_pod_autoscaler.return_value = \
|
||||||
|
client.V1HorizontalPodAutoscaler(
|
||||||
|
spec=client.V1HorizontalPodAutoscalerSpec(
|
||||||
|
min_replicas=1, max_replicas=3,
|
||||||
|
scale_target_ref=client.V1CrossVersionObjectReference(
|
||||||
|
kind='Deployment', name='fake_name')))
|
||||||
|
mock_patch_namespaced_deployment_scale.return_value = \
|
||||||
|
client.V1Scale(
|
||||||
|
spec=client.V1ScaleSpec(replicas=after_replicas),
|
||||||
|
status=client.V1ScaleStatus(replicas=after_replicas))
|
||||||
|
self.kubernetes.scale(context=self.context, plugin=None,
|
||||||
|
auth_attr=utils.get_vim_auth_obj(),
|
||||||
|
policy=policy,
|
||||||
|
region_name=None)
|
||||||
|
|
||||||
|
@mock.patch.object(client.AutoscalingV1Api,
|
||||||
|
'read_namespaced_horizontal_pod_autoscaler')
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'patch_namespaced_deployment_scale')
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_deployment')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_legacy_in(self, mock_vnf_resource_list,
|
||||||
|
mock_read_namespaced_deployment,
|
||||||
|
mock_patch_namespaced_deployment_scale,
|
||||||
|
mock_read_namespaced_horizontal_pod_autoscaler):
|
||||||
|
self._test_scale_legacy('in', 2, 1,
|
||||||
|
mock_vnf_resource_list,
|
||||||
|
mock_read_namespaced_deployment,
|
||||||
|
mock_patch_namespaced_deployment_scale,
|
||||||
|
mock_read_namespaced_horizontal_pod_autoscaler)
|
||||||
|
mock_read_namespaced_deployment.assert_called_once()
|
||||||
|
mock_read_namespaced_horizontal_pod_autoscaler.assert_called_once()
|
||||||
|
mock_patch_namespaced_deployment_scale.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch.object(client.AutoscalingV1Api,
|
||||||
|
'read_namespaced_horizontal_pod_autoscaler')
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'patch_namespaced_deployment_scale')
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_deployment')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_legacy_out(self, mock_vnf_resource_list,
|
||||||
|
mock_read_namespaced_deployment,
|
||||||
|
mock_patch_namespaced_deployment_scale,
|
||||||
|
mock_read_namespaced_horizontal_pod_autoscaler):
|
||||||
|
self._test_scale_legacy('out', 2, 3,
|
||||||
|
mock_vnf_resource_list,
|
||||||
|
mock_read_namespaced_deployment,
|
||||||
|
mock_patch_namespaced_deployment_scale,
|
||||||
|
mock_read_namespaced_horizontal_pod_autoscaler)
|
||||||
|
mock_read_namespaced_deployment.assert_called_once()
|
||||||
|
mock_read_namespaced_horizontal_pod_autoscaler.assert_called_once()
|
||||||
|
mock_patch_namespaced_deployment_scale.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch.object(client.AutoscalingV1Api,
|
||||||
|
'read_namespaced_horizontal_pod_autoscaler')
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'patch_namespaced_deployment_scale')
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_deployment')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_legacy_in_less_than_min(self, mock_vnf_resource_list,
|
||||||
|
mock_read_namespaced_deployment,
|
||||||
|
mock_patch_namespaced_deployment_scale,
|
||||||
|
mock_read_namespaced_horizontal_pod_autoscaler):
|
||||||
|
self._test_scale_legacy('in', 1, 1,
|
||||||
|
mock_vnf_resource_list,
|
||||||
|
mock_read_namespaced_deployment,
|
||||||
|
mock_patch_namespaced_deployment_scale,
|
||||||
|
mock_read_namespaced_horizontal_pod_autoscaler)
|
||||||
|
mock_read_namespaced_deployment.assert_called_once()
|
||||||
|
mock_read_namespaced_horizontal_pod_autoscaler.assert_called_once()
|
||||||
|
mock_patch_namespaced_deployment_scale.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch.object(client.AutoscalingV1Api,
|
||||||
|
'read_namespaced_horizontal_pod_autoscaler')
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'patch_namespaced_deployment_scale')
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_deployment')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_legacy_out_over_max(self, mock_vnf_resource_list,
|
||||||
|
mock_read_namespaced_deployment,
|
||||||
|
mock_patch_namespaced_deployment_scale,
|
||||||
|
mock_read_namespaced_horizontal_pod_autoscaler):
|
||||||
|
self._test_scale_legacy('out', 3, 3,
|
||||||
|
mock_vnf_resource_list,
|
||||||
|
mock_read_namespaced_deployment,
|
||||||
|
mock_patch_namespaced_deployment_scale,
|
||||||
|
mock_read_namespaced_horizontal_pod_autoscaler)
|
||||||
|
mock_read_namespaced_deployment.assert_called_once()
|
||||||
|
mock_read_namespaced_horizontal_pod_autoscaler.assert_called_once()
|
||||||
|
mock_patch_namespaced_deployment_scale.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_deployment_scale')
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'list_namespaced_pod')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_wait_deployment(self, mock_vnf_resource_list,
|
||||||
|
mock_list_namespaced_pod,
|
||||||
|
mock_read_namespaced_deployment_scale):
|
||||||
|
policy = fakes.get_scale_policy(type='out')
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='Deployment')
|
||||||
|
mock_list_namespaced_pod.return_value = \
|
||||||
|
client.V1PodList(items=[
|
||||||
|
fakes.get_fake_pod_info(kind='Deployment')])
|
||||||
|
mock_read_namespaced_deployment_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=1),
|
||||||
|
status=client.V1ScaleStatus(replicas=1))
|
||||||
|
self.kubernetes.scale_wait(context=self.context, plugin=None,
|
||||||
|
auth_attr=utils.get_vim_auth_obj(),
|
||||||
|
policy=policy,
|
||||||
|
region_name=None,
|
||||||
|
last_event_id=None)
|
||||||
|
mock_list_namespaced_pod.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_stateful_set_scale')
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'list_namespaced_pod')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_wait_stateful_set(self, mock_vnf_resource_list,
|
||||||
|
mock_list_namespaced_pod,
|
||||||
|
mock_read_namespaced_stateful_set_scale):
|
||||||
|
policy = fakes.get_scale_policy(type='out')
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='StatefulSet')
|
||||||
|
mock_list_namespaced_pod.return_value = \
|
||||||
|
client.V1PodList(items=[
|
||||||
|
fakes.get_fake_pod_info(kind='StatefulSet')])
|
||||||
|
mock_read_namespaced_stateful_set_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=1),
|
||||||
|
status=client.V1ScaleStatus(replicas=1))
|
||||||
|
self.kubernetes.scale_wait(context=self.context, plugin=None,
|
||||||
|
auth_attr=utils.get_vim_auth_obj(),
|
||||||
|
policy=policy,
|
||||||
|
region_name=None,
|
||||||
|
last_event_id=None)
|
||||||
|
mock_list_namespaced_pod.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_replica_set_scale')
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'list_namespaced_pod')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_wait_replica_set(self, mock_vnf_resource_list,
|
||||||
|
mock_list_namespaced_pod,
|
||||||
|
mock_read_namespaced_replica_set_scale):
|
||||||
|
policy = fakes.get_scale_policy(type='out')
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='ReplicaSet')
|
||||||
|
mock_list_namespaced_pod.return_value = \
|
||||||
|
client.V1PodList(items=[
|
||||||
|
fakes.get_fake_pod_info(kind='ReplicaSet')])
|
||||||
|
mock_read_namespaced_replica_set_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=1),
|
||||||
|
status=client.V1ScaleStatus(replicas=1))
|
||||||
|
self.kubernetes.scale_wait(context=self.context, plugin=None,
|
||||||
|
auth_attr=utils.get_vim_auth_obj(),
|
||||||
|
policy=policy,
|
||||||
|
region_name=None,
|
||||||
|
last_event_id=None)
|
||||||
|
mock_list_namespaced_pod.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_wait_target_not_found(self, mock_vnf_resource_list):
|
||||||
|
policy = fakes.get_scale_policy(type='out')
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='Depoyment', name='other_name')
|
||||||
|
self.assertRaises(vnfm.CNFScaleWaitFailed,
|
||||||
|
self.kubernetes.scale_wait,
|
||||||
|
self.context, None,
|
||||||
|
utils.get_vim_auth_obj(), policy, None, None)
|
||||||
|
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_deployment_scale')
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'list_namespaced_pod')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_wait_retry_over(self, mock_vnf_resource_list,
|
||||||
|
mock_list_namespaced_pod,
|
||||||
|
mock_read_namespaced_deployment_scale):
|
||||||
|
policy = fakes.get_scale_policy(type='out')
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='Deployment')
|
||||||
|
mock_list_namespaced_pod.return_value = \
|
||||||
|
client.V1PodList(items=[
|
||||||
|
fakes.get_fake_pod_info(
|
||||||
|
kind='Deployment', pod_status='Pending')])
|
||||||
|
mock_read_namespaced_deployment_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=2),
|
||||||
|
status=client.V1ScaleStatus(replicas=2))
|
||||||
|
self.assertRaises(vnfm.CNFScaleWaitFailed,
|
||||||
|
self.kubernetes.scale_wait,
|
||||||
|
self.context, None,
|
||||||
|
utils.get_vim_auth_obj(), policy, None, None)
|
||||||
|
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_deployment_scale')
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'list_namespaced_pod')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_wait_status_unknown(self, mock_vnf_resource_list,
|
||||||
|
mock_list_namespaced_pod,
|
||||||
|
mock_read_namespaced_deployment_scale):
|
||||||
|
policy = fakes.get_scale_policy(type='out')
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='Deployment')
|
||||||
|
mock_list_namespaced_pod.return_value = \
|
||||||
|
client.V1PodList(items=[
|
||||||
|
fakes.get_fake_pod_info(
|
||||||
|
kind='Deployment', pod_status='Unknown')])
|
||||||
|
mock_read_namespaced_deployment_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=2),
|
||||||
|
status=client.V1ScaleStatus(replicas=2))
|
||||||
|
self.assertRaises(vnfm.CNFScaleWaitFailed,
|
||||||
|
self.kubernetes.scale_wait,
|
||||||
|
self.context, None,
|
||||||
|
utils.get_vim_auth_obj(), policy, None, None)
|
||||||
|
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'list_namespaced_pod')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_wait_legacy(self, mock_vnf_resource_list,
|
||||||
|
mock_list_namespaced_pod):
|
||||||
|
policy = fakes.get_scale_policy(type='out', aspect_id='SP1')
|
||||||
|
policy['instance_id'] = "fake_namespace,fake_name"
|
||||||
|
mock_vnf_resource_list.return_value = []
|
||||||
|
mock_list_namespaced_pod.return_value = \
|
||||||
|
client.V1PodList(items=[
|
||||||
|
fakes.get_fake_pod_info(
|
||||||
|
kind='Deployment', pod_status='Running')])
|
||||||
|
self.kubernetes.scale_wait(context=self.context, plugin=None,
|
||||||
|
auth_attr=utils.get_vim_auth_obj(),
|
||||||
|
policy=policy,
|
||||||
|
region_name=None,
|
||||||
|
last_event_id=None)
|
||||||
|
mock_list_namespaced_pod.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'list_namespaced_pod')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_wait_legacy_retry_over(self, mock_vnf_resource_list,
|
||||||
|
mock_list_namespaced_pod):
|
||||||
|
policy = fakes.get_scale_policy(type='out', aspect_id='SP1')
|
||||||
|
policy['instance_id'] = "fake_namespace,fake_name"
|
||||||
|
mock_vnf_resource_list.return_value = []
|
||||||
|
mock_list_namespaced_pod.return_value = \
|
||||||
|
client.V1PodList(items=[
|
||||||
|
fakes.get_fake_pod_info(
|
||||||
|
kind='Deployment', pod_status='Pending')])
|
||||||
|
self.assertRaises(vnfm.VNFCreateWaitFailed,
|
||||||
|
self.kubernetes.scale_wait,
|
||||||
|
self.context, None,
|
||||||
|
utils.get_vim_auth_obj(), policy, None, None)
|
||||||
|
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'list_namespaced_pod')
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
def test_scale_wait_legacy_status_unknown(self, mock_vnf_resource_list,
|
||||||
|
mock_list_namespaced_pod):
|
||||||
|
policy = fakes.get_scale_policy(type='out', aspect_id='SP1')
|
||||||
|
policy['instance_id'] = "fake_namespace,fake_name"
|
||||||
|
mock_vnf_resource_list.return_value = []
|
||||||
|
mock_list_namespaced_pod.return_value = \
|
||||||
|
client.V1PodList(items=[
|
||||||
|
fakes.get_fake_pod_info(
|
||||||
|
kind='Deployment', pod_status='Unknown')])
|
||||||
|
self.assertRaises(vnfm.VNFCreateWaitFailed,
|
||||||
|
self.kubernetes.scale_wait,
|
||||||
|
self.context, None,
|
||||||
|
utils.get_vim_auth_obj(), policy, None, None)
|
||||||
|
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'patch_namespaced_deployment_scale')
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_deployment_scale')
|
||||||
|
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
|
||||||
|
@mock.patch.object(objects.VnfPackageVnfd, "get_by_id")
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
||||||
|
def test_scale_in_reverse(self, mock_vnf_instance_get_by_id,
|
||||||
|
mock_vnf_resource_list,
|
||||||
|
mock_vnf_package_vnfd_get_by_id,
|
||||||
|
mock_vnfd_dict,
|
||||||
|
mock_read_namespaced_deployment_scale,
|
||||||
|
mock_patch_namespaced_deployment_scale):
|
||||||
|
vnf_info = vnflcm_fakes.vnf_dict_cnf()
|
||||||
|
vnf_info['vnf_lcm_op_occ'] = vnflcm_fakes.vnflcm_scale_out_cnf()
|
||||||
|
scale_vnf_request = vnflcm_fakes.scale_request(
|
||||||
|
"SCALE_OUT", "vdu1_aspect", 1, "False")
|
||||||
|
scale_status = objects.ScaleInfo(
|
||||||
|
aspect_id='vdu1_aspect', scale_level=1)
|
||||||
|
mock_vnf_instance_get_by_id.return_value = \
|
||||||
|
vnflcm_fakes.return_vnf_instance(
|
||||||
|
fields.VnfInstanceState.INSTANTIATED,
|
||||||
|
scale_status=scale_status)
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='Deployment', name='vdu1')
|
||||||
|
mock_vnf_package_vnfd_get_by_id.return_value = \
|
||||||
|
vnflcm_fakes.return_vnf_package_vnfd()
|
||||||
|
mock_vnfd_dict.return_value = vnflcm_fakes.vnfd_dict_cnf()
|
||||||
|
mock_read_namespaced_deployment_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=2),
|
||||||
|
status=client.V1ScaleStatus(replicas=2))
|
||||||
|
mock_patch_namespaced_deployment_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=1),
|
||||||
|
status=client.V1ScaleStatus(replicas=1))
|
||||||
|
self.kubernetes.scale_in_reverse(context=self.context, plugin=None,
|
||||||
|
auth_attr=utils.get_vim_auth_obj(),
|
||||||
|
vnf_info=vnf_info,
|
||||||
|
scale_vnf_request=scale_vnf_request,
|
||||||
|
region_name=None,
|
||||||
|
scale_name_list=None,
|
||||||
|
grp_id=None)
|
||||||
|
mock_read_namespaced_deployment_scale.assert_called_once()
|
||||||
|
mock_patch_namespaced_deployment_scale.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch.object(client.AppsV1Api, 'read_namespaced_deployment_scale')
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'list_namespaced_pod')
|
||||||
|
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
|
||||||
|
@mock.patch.object(objects.VnfPackageVnfd, "get_by_id")
|
||||||
|
@mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
|
||||||
|
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
||||||
|
def test_scale_update_wait(self, mock_vnf_instance_get_by_id,
|
||||||
|
mock_vnf_resource_list,
|
||||||
|
mock_vnf_package_vnfd_get_by_id,
|
||||||
|
mock_vnfd_dict,
|
||||||
|
mock_list_namespaced_pod,
|
||||||
|
mock_read_namespaced_deployment_scale):
|
||||||
|
vnf_info = vnflcm_fakes.vnf_dict_cnf()
|
||||||
|
vnf_info['vnf_lcm_op_occ'] = vnflcm_fakes.vnflcm_scale_out_cnf()
|
||||||
|
scale_status = objects.ScaleInfo(
|
||||||
|
aspect_id='vdu1_aspect', scale_level=1)
|
||||||
|
mock_vnf_instance_get_by_id.return_value = \
|
||||||
|
vnflcm_fakes.return_vnf_instance(
|
||||||
|
fields.VnfInstanceState.INSTANTIATED,
|
||||||
|
scale_status=scale_status)
|
||||||
|
mock_vnf_resource_list.return_value = \
|
||||||
|
fakes.get_vnf_resource_list(kind='Deployment', name='vdu1')
|
||||||
|
mock_vnf_package_vnfd_get_by_id.return_value = \
|
||||||
|
vnflcm_fakes.return_vnf_package_vnfd()
|
||||||
|
mock_vnfd_dict.return_value = vnflcm_fakes.vnfd_dict_cnf()
|
||||||
|
mock_list_namespaced_pod.return_value = \
|
||||||
|
client.V1PodList(items=[
|
||||||
|
fakes.get_fake_pod_info(kind='Deployment', name='vdu1')])
|
||||||
|
mock_read_namespaced_deployment_scale.return_value = \
|
||||||
|
client.V1Scale(spec=client.V1ScaleSpec(replicas=1),
|
||||||
|
status=client.V1ScaleStatus(replicas=1))
|
||||||
|
self.kubernetes.scale_update_wait(context=self.context, plugin=None,
|
||||||
|
auth_attr=utils.get_vim_auth_obj(),
|
||||||
|
vnf_info=vnf_info,
|
||||||
|
region_name=None)
|
||||||
|
mock_list_namespaced_pod.assert_called_once()
|
||||||
|
@@ -700,12 +700,15 @@ def convert_inst_req_info(heat_dict, inst_req_info, tosca):
|
|||||||
aspect_id_dict = {}
|
aspect_id_dict = {}
|
||||||
# { vduId: initialDelta }
|
# { vduId: initialDelta }
|
||||||
vdu_delta_dict = {}
|
vdu_delta_dict = {}
|
||||||
|
# { aspectId: maxScaleLevel }
|
||||||
|
aspect_max_level_dict = {}
|
||||||
|
|
||||||
tosca_policies = tosca.topology_template.policies
|
tosca_policies = tosca.topology_template.policies
|
||||||
default_inst_level_id = _extract_policy_info(
|
default_inst_level_id = _extract_policy_info(
|
||||||
tosca_policies, inst_level_dict,
|
tosca_policies, inst_level_dict,
|
||||||
aspect_delta_dict, aspect_id_dict,
|
aspect_delta_dict, aspect_id_dict,
|
||||||
aspect_vdu_dict, vdu_delta_dict)
|
aspect_vdu_dict, vdu_delta_dict,
|
||||||
|
aspect_max_level_dict)
|
||||||
|
|
||||||
if inst_level_id is not None:
|
if inst_level_id is not None:
|
||||||
# Case which instLevelId is defined.
|
# Case which instLevelId is defined.
|
||||||
@@ -848,7 +851,8 @@ def _convert_ext_mng_vl(heat_dict, vl_name, vl_id):
|
|||||||
|
|
||||||
def _extract_policy_info(tosca_policies, inst_level_dict,
|
def _extract_policy_info(tosca_policies, inst_level_dict,
|
||||||
aspect_delta_dict, aspect_id_dict,
|
aspect_delta_dict, aspect_id_dict,
|
||||||
aspect_vdu_dict, vdu_delta_dict):
|
aspect_vdu_dict, vdu_delta_dict,
|
||||||
|
aspect_max_level_dict):
|
||||||
default_inst_level_id = None
|
default_inst_level_id = None
|
||||||
if tosca_policies:
|
if tosca_policies:
|
||||||
for p in tosca_policies:
|
for p in tosca_policies:
|
||||||
@@ -885,7 +889,8 @@ def _extract_policy_info(tosca_policies, inst_level_dict,
|
|||||||
delta_names = aspect_val['step_deltas']
|
delta_names = aspect_val['step_deltas']
|
||||||
delta_name = delta_names[0]
|
delta_name = delta_names[0]
|
||||||
aspect_id_dict[aspect_id] = delta_name
|
aspect_id_dict[aspect_id] = delta_name
|
||||||
|
aspect_max_level_dict[aspect_id] = \
|
||||||
|
aspect_val['max_scale_level']
|
||||||
elif p.type == ETSI_INITIAL_DELTA:
|
elif p.type == ETSI_INITIAL_DELTA:
|
||||||
vdus = p.targets
|
vdus = p.targets
|
||||||
initial_delta = \
|
initial_delta = \
|
||||||
|
@@ -994,6 +994,7 @@ def _convert_desired_capacity(inst_level_id, vnfd_dict, vdu):
|
|||||||
inst_level_dict = {}
|
inst_level_dict = {}
|
||||||
aspect_id_dict = {}
|
aspect_id_dict = {}
|
||||||
vdu_delta_dict = {}
|
vdu_delta_dict = {}
|
||||||
|
aspect_max_level_dict = {}
|
||||||
desired_capacity = 1
|
desired_capacity = 1
|
||||||
|
|
||||||
tosca = tosca_template.ToscaTemplate(parsed_params={}, a_file=False,
|
tosca = tosca_template.ToscaTemplate(parsed_params={}, a_file=False,
|
||||||
@@ -1002,7 +1003,8 @@ def _convert_desired_capacity(inst_level_id, vnfd_dict, vdu):
|
|||||||
default_inst_level_id = toscautils._extract_policy_info(
|
default_inst_level_id = toscautils._extract_policy_info(
|
||||||
tosca_policies, inst_level_dict,
|
tosca_policies, inst_level_dict,
|
||||||
aspect_delta_dict, aspect_id_dict,
|
aspect_delta_dict, aspect_id_dict,
|
||||||
aspect_vdu_dict, vdu_delta_dict)
|
aspect_vdu_dict, vdu_delta_dict,
|
||||||
|
aspect_max_level_dict)
|
||||||
|
|
||||||
if vdu_delta_dict.get(vdu) is None:
|
if vdu_delta_dict.get(vdu) is None:
|
||||||
return desired_capacity
|
return desired_capacity
|
||||||
@@ -1064,3 +1066,89 @@ def get_base_nest_hot_dict(context, flavour_id, vnfd_id):
|
|||||||
LOG.debug("Loaded base hot: %s", base_hot_dict)
|
LOG.debug("Loaded base hot: %s", base_hot_dict)
|
||||||
LOG.debug("Loaded nested_hot_dict: %s", nested_hot_dict)
|
LOG.debug("Loaded nested_hot_dict: %s", nested_hot_dict)
|
||||||
return base_hot_dict, nested_hot_dict
|
return base_hot_dict, nested_hot_dict
|
||||||
|
|
||||||
|
|
||||||
|
def get_extract_policy_infos(tosca):
|
||||||
|
aspect_delta_dict = {}
|
||||||
|
aspect_vdu_dict = {}
|
||||||
|
inst_level_dict = {}
|
||||||
|
aspect_id_dict = {}
|
||||||
|
vdu_delta_dict = {}
|
||||||
|
aspect_max_level_dict = {}
|
||||||
|
|
||||||
|
tosca_policies = tosca.topology_template.policies
|
||||||
|
default_inst_level_id = toscautils._extract_policy_info(
|
||||||
|
tosca_policies, inst_level_dict,
|
||||||
|
aspect_delta_dict, aspect_id_dict,
|
||||||
|
aspect_vdu_dict, vdu_delta_dict,
|
||||||
|
aspect_max_level_dict)
|
||||||
|
|
||||||
|
extract_policy_infos = dict()
|
||||||
|
extract_policy_infos['inst_level_dict'] = inst_level_dict
|
||||||
|
extract_policy_infos['aspect_delta_dict'] = aspect_delta_dict
|
||||||
|
extract_policy_infos['aspect_id_dict'] = aspect_id_dict
|
||||||
|
extract_policy_infos['aspect_vdu_dict'] = aspect_vdu_dict
|
||||||
|
extract_policy_infos['vdu_delta_dict'] = vdu_delta_dict
|
||||||
|
extract_policy_infos['aspect_max_level_dict'] = aspect_max_level_dict
|
||||||
|
extract_policy_infos['default_inst_level_id'] = default_inst_level_id
|
||||||
|
|
||||||
|
return extract_policy_infos
|
||||||
|
|
||||||
|
|
||||||
|
def get_scale_delta_num(extract_policy_infos, aspect_id):
|
||||||
|
delta_num = 1
|
||||||
|
|
||||||
|
if extract_policy_infos['aspect_id_dict'] is None:
|
||||||
|
return delta_num
|
||||||
|
delta_id = extract_policy_infos['aspect_id_dict'].get(aspect_id)
|
||||||
|
if delta_id is None:
|
||||||
|
return delta_num
|
||||||
|
delta_num = \
|
||||||
|
extract_policy_infos['aspect_delta_dict'].get(aspect_id).get(delta_id)
|
||||||
|
|
||||||
|
return delta_num
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_scale_status(context, vnf_instance, vnfd_dict):
|
||||||
|
default_scale_status = None
|
||||||
|
|
||||||
|
vnfd_dict = _get_vnfd_dict(context,
|
||||||
|
vnf_instance.vnfd_id,
|
||||||
|
vnf_instance.instantiated_vnf_info.flavour_id)
|
||||||
|
tosca = tosca_template.ToscaTemplate(parsed_params={}, a_file=False,
|
||||||
|
yaml_dict_tpl=vnfd_dict)
|
||||||
|
extract_policy_infos = get_extract_policy_infos(tosca)
|
||||||
|
|
||||||
|
if extract_policy_infos['inst_level_dict'] is None:
|
||||||
|
return default_scale_status
|
||||||
|
default_inst_level_id = extract_policy_infos['default_inst_level_id']
|
||||||
|
default_al_dict = \
|
||||||
|
extract_policy_infos['inst_level_dict'].get(default_inst_level_id)
|
||||||
|
if default_al_dict is None:
|
||||||
|
return default_scale_status
|
||||||
|
default_scale_status = []
|
||||||
|
for aspect_id, level_num in default_al_dict.items():
|
||||||
|
default_scale_status.append(
|
||||||
|
objects.ScaleInfo(
|
||||||
|
aspect_id=aspect_id,
|
||||||
|
scale_level=level_num))
|
||||||
|
|
||||||
|
return default_scale_status
|
||||||
|
|
||||||
|
|
||||||
|
def get_target_vdu_def_dict(extract_policy_infos, aspect_id, tosca):
|
||||||
|
vdu_def_dict = {}
|
||||||
|
|
||||||
|
tosca_node_tpls = tosca.topology_template.nodetemplates
|
||||||
|
|
||||||
|
if extract_policy_infos['aspect_vdu_dict'] is None:
|
||||||
|
return vdu_def_dict
|
||||||
|
vdus = extract_policy_infos['aspect_vdu_dict'].get(aspect_id)
|
||||||
|
if vdus is None:
|
||||||
|
return vdu_def_dict
|
||||||
|
for nt in tosca_node_tpls:
|
||||||
|
for node_name, node_value in nt.templates.items():
|
||||||
|
if node_name in vdus:
|
||||||
|
vdu_def_dict[node_name] = node_value
|
||||||
|
|
||||||
|
return vdu_def_dict
|
||||||
|
@@ -29,6 +29,7 @@ from oslo_log import log as logging
|
|||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
from oslo_utils import encodeutils
|
from oslo_utils import encodeutils
|
||||||
from oslo_utils import excutils
|
from oslo_utils import excutils
|
||||||
|
from toscaparser import tosca_template
|
||||||
|
|
||||||
from tacker.common import driver_manager
|
from tacker.common import driver_manager
|
||||||
from tacker.common import exceptions
|
from tacker.common import exceptions
|
||||||
@@ -92,6 +93,7 @@ def revert_to_error_scale(function):
|
|||||||
"instance %(id)s. Error: %(error)s",
|
"instance %(id)s. Error: %(error)s",
|
||||||
{"id": vnf_instance.id, "error": e})
|
{"id": vnf_instance.id, "error": e})
|
||||||
try:
|
try:
|
||||||
|
vnf_instance.task_state = None
|
||||||
self._vnf_instance_update(context, vnf_instance)
|
self._vnf_instance_update(context, vnf_instance)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.warning("Failed to revert instantiation info for vnf "
|
LOG.warning("Failed to revert instantiation info for vnf "
|
||||||
@@ -386,6 +388,15 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
|
|||||||
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']
|
||||||
|
elif vnf_instance.instantiated_vnf_info:
|
||||||
|
default_scale_status = vnflcm_utils.\
|
||||||
|
get_default_scale_status(
|
||||||
|
context=context,
|
||||||
|
vnf_instance=vnf_instance,
|
||||||
|
vnfd_dict=vnfd_dict)
|
||||||
|
if default_scale_status is not None:
|
||||||
|
vnf_instance.instantiated_vnf_info.scale_status = \
|
||||||
|
default_scale_status
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._vnf_manager.invoke(
|
self._vnf_manager.invoke(
|
||||||
@@ -815,16 +826,21 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
|
|||||||
vnf_lcm_op_occ.error_point = 7
|
vnf_lcm_op_occ.error_point = 7
|
||||||
vnf_instance.instantiated_vnf_info.scale_level =\
|
vnf_instance.instantiated_vnf_info.scale_level =\
|
||||||
vnf_info['after_scale_level']
|
vnf_info['after_scale_level']
|
||||||
scaleGroupDict = \
|
if vim_connection_info.vim_type != 'kubernetes':
|
||||||
jsonutils.loads(vnf_info['attributes']['scale_group'])
|
# NOTE(ueha): The logic of Scale for OpenStack VIM is widely hard
|
||||||
(scaleGroupDict
|
# coded with `vnf_info`. This dependency is to be refactored in
|
||||||
['scaleGroupDict'][scale_vnf_request.aspect_id]['default']) =\
|
# future.
|
||||||
vnf_info['res_num']
|
scaleGroupDict = \
|
||||||
vnf_info['attributes']['scale_group'] =\
|
jsonutils.loads(vnf_info['attributes']['scale_group'])
|
||||||
jsonutils.dump_as_bytes(scaleGroupDict)
|
(scaleGroupDict
|
||||||
|
['scaleGroupDict'][scale_vnf_request.aspect_id]['default']) =\
|
||||||
|
vnf_info['res_num']
|
||||||
|
vnf_info['attributes']['scale_group'] =\
|
||||||
|
jsonutils.dump_as_bytes(scaleGroupDict)
|
||||||
vnf_lcm_op_occ = vnf_info['vnf_lcm_op_occ']
|
vnf_lcm_op_occ = vnf_info['vnf_lcm_op_occ']
|
||||||
vnf_lcm_op_occ.operation_state = 'COMPLETED'
|
vnf_lcm_op_occ.operation_state = 'COMPLETED'
|
||||||
vnf_lcm_op_occ.resource_changes = resource_changes
|
vnf_lcm_op_occ.resource_changes = resource_changes
|
||||||
|
vnf_instance.task_state = None
|
||||||
self._vnfm_plugin._update_vnf_scaling(context, vnf_info,
|
self._vnfm_plugin._update_vnf_scaling(context, vnf_info,
|
||||||
'PENDING_' + scale_vnf_request.type,
|
'PENDING_' + scale_vnf_request.type,
|
||||||
'ACTIVE',
|
'ACTIVE',
|
||||||
@@ -1084,10 +1100,33 @@ 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'))
|
||||||
scale_json = vnf_info['attributes']['scale_group']
|
default = None
|
||||||
scaleGroupDict = jsonutils.loads(scale_json)
|
if vim_connection_info.vim_type == 'kubernetes':
|
||||||
key_aspect = scale_vnf_request.aspect_id
|
policy['vnf_instance_id'] = \
|
||||||
default = scaleGroupDict['scaleGroupDict'][key_aspect]['default']
|
vnf_info['vnf_lcm_op_occ'].get('vnf_instance_id')
|
||||||
|
vnf_instance = objects.VnfInstance.get_by_id(context,
|
||||||
|
policy['vnf_instance_id'])
|
||||||
|
vnfd_dict = vnflcm_utils._get_vnfd_dict(context,
|
||||||
|
vnf_instance.vnfd_id,
|
||||||
|
vnf_instance.instantiated_vnf_info.flavour_id)
|
||||||
|
tosca = tosca_template.ToscaTemplate(
|
||||||
|
parsed_params={}, a_file=False, yaml_dict_tpl=vnfd_dict)
|
||||||
|
extract_policy_infos = vnflcm_utils.get_extract_policy_infos(tosca)
|
||||||
|
policy['vdu_defs'] = vnflcm_utils.get_target_vdu_def_dict(
|
||||||
|
extract_policy_infos=extract_policy_infos,
|
||||||
|
aspect_id=scale_vnf_request.aspect_id,
|
||||||
|
tosca=tosca)
|
||||||
|
policy['delta_num'] = vnflcm_utils.get_scale_delta_num(
|
||||||
|
extract_policy_infos=extract_policy_infos,
|
||||||
|
aspect_id=scale_vnf_request.aspect_id)
|
||||||
|
else:
|
||||||
|
# NOTE(ueha): The logic of Scale for OpenStack VIM is widely hard
|
||||||
|
# coded with `vnf_info`. This dependency is to be refactored in
|
||||||
|
# future.
|
||||||
|
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
|
if (scale_vnf_request.type == 'SCALE_IN' and
|
||||||
scale_vnf_request.additional_params['is_reverse'] == 'True'):
|
scale_vnf_request.additional_params['is_reverse'] == 'True'):
|
||||||
self._vnf_manager.invoke(
|
self._vnf_manager.invoke(
|
||||||
@@ -1132,26 +1171,32 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
|
|||||||
region_name=vim_connection_info.access_info.get('region_name')
|
region_name=vim_connection_info.access_info.get('region_name')
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
heat_template = vnf_info['attributes']['heat_template']
|
cooldown = None
|
||||||
policy_in_name = scale_vnf_request.aspect_id + '_scale_in'
|
if vim_connection_info.vim_type != 'kubernetes':
|
||||||
policy_out_name = scale_vnf_request.aspect_id + '_scale_out'
|
# NOTE(ueha): The logic of Scale for OpenStack VIM is widely
|
||||||
|
# hard coded with `vnf_info`. This dependency is to be
|
||||||
|
# refactored in future.
|
||||||
|
heat_template = vnf_info['attributes']['heat_template']
|
||||||
|
policy_in_name = scale_vnf_request.aspect_id + '_scale_in'
|
||||||
|
policy_out_name = scale_vnf_request.aspect_id + '_scale_out'
|
||||||
|
|
||||||
heat_resource = yaml.safe_load(heat_template)
|
heat_resource = yaml.safe_load(heat_template)
|
||||||
if scale_vnf_request.type == 'SCALE_IN':
|
if scale_vnf_request.type == 'SCALE_IN':
|
||||||
policy['action'] = 'in'
|
policy['action'] = 'in'
|
||||||
policy_temp = heat_resource['resources'][policy_in_name]
|
policy_temp = heat_resource['resources'][policy_in_name]
|
||||||
policy_prop = policy_temp['properties']
|
policy_prop = policy_temp['properties']
|
||||||
cooldown = policy_prop.get('cooldown')
|
cooldown = policy_prop.get('cooldown')
|
||||||
policy_name = policy_in_name
|
policy_name = policy_in_name
|
||||||
else:
|
else:
|
||||||
policy['action'] = 'out'
|
policy['action'] = 'out'
|
||||||
policy_temp = heat_resource['resources'][policy_out_name]
|
policy_temp = heat_resource['resources'][policy_out_name]
|
||||||
policy_prop = policy_temp['properties']
|
policy_prop = policy_temp['properties']
|
||||||
cooldown = policy_prop.get('cooldown')
|
cooldown = policy_prop.get('cooldown')
|
||||||
policy_name = policy_out_name
|
policy_name = policy_out_name
|
||||||
|
|
||||||
|
policy_temp = heat_resource['resources'][policy_name]
|
||||||
|
policy_prop = policy_temp['properties']
|
||||||
|
|
||||||
policy_temp = heat_resource['resources'][policy_name]
|
|
||||||
policy_prop = policy_temp['properties']
|
|
||||||
for i in range(scale_vnf_request.number_of_steps):
|
for i in range(scale_vnf_request.number_of_steps):
|
||||||
last_event_id = self._vnf_manager.invoke(
|
last_event_id = self._vnf_manager.invoke(
|
||||||
vim_connection_info.vim_type,
|
vim_connection_info.vim_type,
|
||||||
@@ -1290,11 +1335,15 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
|
|||||||
grp_id = None
|
grp_id = None
|
||||||
self._update_vnf_rollback_pre(context, vnf_info)
|
self._update_vnf_rollback_pre(context, vnf_info)
|
||||||
if vnf_lcm_op_occs.operation == 'SCALE':
|
if vnf_lcm_op_occs.operation == 'SCALE':
|
||||||
scaleGroupDict = jsonutils.loads(
|
if vim_connection_info.vim_type != 'kubernetes':
|
||||||
vnf_info['attributes']['scale_group'])
|
# NOTE(ueha): The logic of Scale for OpenStack VIM is widely
|
||||||
cap_size = scaleGroupDict['scaleGroupDict'][operation_params
|
# hard coded with `vnf_info`. This dependency is to be
|
||||||
['aspect_id']]['default']
|
# refactored in future.
|
||||||
vnf_info['res_num'] = cap_size
|
scaleGroupDict = jsonutils.loads(
|
||||||
|
vnf_info['attributes']['scale_group'])
|
||||||
|
cap_size = scaleGroupDict['scaleGroupDict'][operation_params
|
||||||
|
['aspect_id']]['default']
|
||||||
|
vnf_info['res_num'] = cap_size
|
||||||
scale_vnf_request = objects.ScaleVnfRequest.obj_from_primitive(
|
scale_vnf_request = objects.ScaleVnfRequest.obj_from_primitive(
|
||||||
operation_params, context=context)
|
operation_params, context=context)
|
||||||
for scale in vnf_instance.instantiated_vnf_info.scale_status:
|
for scale in vnf_instance.instantiated_vnf_info.scale_status:
|
||||||
@@ -1403,6 +1452,7 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
|
|||||||
status = 'ACTIVE'
|
status = 'ACTIVE'
|
||||||
else:
|
else:
|
||||||
status = 'INACTIVE'
|
status = 'INACTIVE'
|
||||||
|
vnf_instance.task_state = None
|
||||||
self._vnfm_plugin._update_vnf_rollback(context, vnf_info,
|
self._vnfm_plugin._update_vnf_rollback(context, vnf_info,
|
||||||
'ERROR',
|
'ERROR',
|
||||||
status,
|
status,
|
||||||
|
@@ -23,6 +23,7 @@ from kubernetes import client
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
|
from toscaparser import tosca_template
|
||||||
|
|
||||||
from tacker._i18n import _
|
from tacker._i18n import _
|
||||||
from tacker.common.container import kubernetes_utils
|
from tacker.common.container import kubernetes_utils
|
||||||
@@ -34,6 +35,7 @@ from tacker import objects
|
|||||||
from tacker.objects import vnf_package as vnf_package_obj
|
from tacker.objects import vnf_package as vnf_package_obj
|
||||||
from tacker.objects import vnf_package_vnfd as vnfd_obj
|
from tacker.objects import vnf_package_vnfd as vnfd_obj
|
||||||
from tacker.objects import vnf_resources as vnf_resource_obj
|
from tacker.objects import vnf_resources as vnf_resource_obj
|
||||||
|
from tacker.vnflcm import utils as vnflcm_utils
|
||||||
from tacker.vnfm.infra_drivers import abstract_driver
|
from tacker.vnfm.infra_drivers import abstract_driver
|
||||||
from tacker.vnfm.infra_drivers.kubernetes.k8s import translate_outputs
|
from tacker.vnfm.infra_drivers.kubernetes.k8s import translate_outputs
|
||||||
from tacker.vnfm.infra_drivers.kubernetes import translate_template
|
from tacker.vnfm.infra_drivers.kubernetes import translate_template
|
||||||
@@ -1019,6 +1021,96 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
finally:
|
finally:
|
||||||
self.clean_authenticate_vim(auth_cred, file_descriptor)
|
self.clean_authenticate_vim(auth_cred, file_descriptor)
|
||||||
|
|
||||||
|
def _scale_legacy(self, policy, auth_cred):
|
||||||
|
LOG.debug("VNF are scaled by updating instance of deployment")
|
||||||
|
|
||||||
|
app_v1_api_client = self.kubernetes.get_app_v1_api_client(
|
||||||
|
auth=auth_cred)
|
||||||
|
scaling_api_client = self.kubernetes.get_scaling_api_client(
|
||||||
|
auth=auth_cred)
|
||||||
|
deployment_names = policy['instance_id'].split(COMMA_CHARACTER)
|
||||||
|
policy_name = policy['name']
|
||||||
|
policy_action = policy['action']
|
||||||
|
|
||||||
|
for i in range(0, len(deployment_names), 2):
|
||||||
|
namespace = deployment_names[i]
|
||||||
|
deployment_name = deployment_names[i + 1]
|
||||||
|
deployment_info = app_v1_api_client.\
|
||||||
|
read_namespaced_deployment(namespace=namespace,
|
||||||
|
name=deployment_name)
|
||||||
|
scaling_info = scaling_api_client.\
|
||||||
|
read_namespaced_horizontal_pod_autoscaler(
|
||||||
|
namespace=namespace,
|
||||||
|
name=deployment_name)
|
||||||
|
|
||||||
|
replicas = deployment_info.status.replicas
|
||||||
|
scale_replicas = replicas
|
||||||
|
vnf_scaling_name = deployment_info.metadata.labels.\
|
||||||
|
get("scaling_name")
|
||||||
|
if vnf_scaling_name == policy_name:
|
||||||
|
if policy_action == 'out':
|
||||||
|
scale_replicas = replicas + 1
|
||||||
|
elif policy_action == 'in':
|
||||||
|
scale_replicas = replicas - 1
|
||||||
|
|
||||||
|
min_replicas = scaling_info.spec.min_replicas
|
||||||
|
max_replicas = scaling_info.spec.max_replicas
|
||||||
|
if (scale_replicas < min_replicas) or \
|
||||||
|
(scale_replicas > max_replicas):
|
||||||
|
LOG.debug("Scaling replicas is out of range. The number of"
|
||||||
|
" replicas keeps %(number)s replicas",
|
||||||
|
{'number': replicas})
|
||||||
|
scale_replicas = replicas
|
||||||
|
deployment_info.spec.replicas = scale_replicas
|
||||||
|
app_v1_api_client.patch_namespaced_deployment_scale(
|
||||||
|
namespace=namespace,
|
||||||
|
name=deployment_name,
|
||||||
|
body=deployment_info)
|
||||||
|
|
||||||
|
def _call_read_scale_api(self, app_v1_api_client, namespace, name, kind):
|
||||||
|
"""select kubernetes read scale api and call"""
|
||||||
|
def convert(name):
|
||||||
|
name_with_underscores = re.sub(
|
||||||
|
'(.)([A-Z][a-z]+)', r'\1_\2', name)
|
||||||
|
return re.sub('([a-z0-9])([A-Z])', r'\1_\2',
|
||||||
|
name_with_underscores).lower()
|
||||||
|
snake_case_kind = convert(kind)
|
||||||
|
try:
|
||||||
|
read_scale_api = eval('app_v1_api_client.'
|
||||||
|
'read_namespaced_%s_scale' % snake_case_kind)
|
||||||
|
response = read_scale_api(name=name, namespace=namespace)
|
||||||
|
except Exception as e:
|
||||||
|
error_reason = _("Failed the request to read a scale information."
|
||||||
|
" namespace: {namespace}, name: {name},"
|
||||||
|
" kind: {kind}, Reason: {exception}").format(
|
||||||
|
namespace=namespace, name=name, kind=kind, exception=e)
|
||||||
|
raise vnfm.CNFScaleFailed(reason=error_reason)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
def _call_patch_scale_api(self, app_v1_api_client, namespace, name,
|
||||||
|
kind, body):
|
||||||
|
"""select kubernetes patch scale api and call"""
|
||||||
|
def convert(name):
|
||||||
|
name_with_underscores = re.sub(
|
||||||
|
'(.)([A-Z][a-z]+)', r'\1_\2', name)
|
||||||
|
return re.sub('([a-z0-9])([A-Z])', r'\1_\2',
|
||||||
|
name_with_underscores).lower()
|
||||||
|
snake_case_kind = convert(kind)
|
||||||
|
try:
|
||||||
|
patch_scale_api = eval('app_v1_api_client.'
|
||||||
|
'patch_namespaced_%s_scale' % snake_case_kind)
|
||||||
|
response = patch_scale_api(name=name, namespace=namespace,
|
||||||
|
body=body)
|
||||||
|
except Exception as e:
|
||||||
|
error_reason = _("Failed the request to update a scale information"
|
||||||
|
". namespace: {namespace}, name: {name},"
|
||||||
|
" kind: {kind}, Reason: {exception}").format(
|
||||||
|
namespace=namespace, name=name, kind=kind, exception=e)
|
||||||
|
raise vnfm.CNFScaleFailed(reason=error_reason)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
@log.log
|
@log.log
|
||||||
def scale(self, context, plugin, auth_attr, policy, region_name):
|
def scale(self, context, plugin, auth_attr, policy, region_name):
|
||||||
"""Scale function
|
"""Scale function
|
||||||
@@ -1027,58 +1119,154 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
The min_replicas and max_replicas is limited by the number of replicas
|
The min_replicas and max_replicas is limited by the number of replicas
|
||||||
of policy scaling when user define VNF descriptor.
|
of policy scaling when user define VNF descriptor.
|
||||||
"""
|
"""
|
||||||
LOG.debug("VNF are scaled by updating instance of deployment")
|
|
||||||
# initialize Kubernetes APIs
|
# initialize Kubernetes APIs
|
||||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
||||||
|
vnf_resources = objects.VnfResourceList.get_by_vnf_instance_id(
|
||||||
|
context, policy['vnf_instance_id'])
|
||||||
try:
|
try:
|
||||||
app_v1_api_client = self.kubernetes.get_app_v1_api_client(
|
if not vnf_resources:
|
||||||
auth=auth_cred)
|
# execute legacy scale method
|
||||||
scaling_api_client = self.kubernetes.get_scaling_api_client(
|
self._scale_legacy(policy, auth_cred)
|
||||||
auth=auth_cred)
|
else:
|
||||||
deployment_names = policy['instance_id'].split(COMMA_CHARACTER)
|
app_v1_api_client = self.kubernetes.get_app_v1_api_client(
|
||||||
policy_name = policy['name']
|
auth=auth_cred)
|
||||||
policy_action = policy['action']
|
aspect_id = policy['name']
|
||||||
|
vdu_defs = policy['vdu_defs']
|
||||||
|
is_found = False
|
||||||
|
error_reason = None
|
||||||
|
for vnf_resource in vnf_resources:
|
||||||
|
# The resource that matches the following is the resource
|
||||||
|
# to be scaled:
|
||||||
|
# The `name` of the resource stored in vnf_resource (the
|
||||||
|
# name defined in `metadata.name` of Kubernetes object
|
||||||
|
# file) matches the value of `properties.name` of VDU
|
||||||
|
# defined in VNFD.
|
||||||
|
name = vnf_resource.resource_name.\
|
||||||
|
split(COMMA_CHARACTER)[1]
|
||||||
|
for vdu_id, vdu_def in vdu_defs.items():
|
||||||
|
vdu_properties = vdu_def.get('properties')
|
||||||
|
if name == vdu_properties.get('name'):
|
||||||
|
namespace = vnf_resource.resource_name.\
|
||||||
|
split(COMMA_CHARACTER)[0]
|
||||||
|
kind = vnf_resource.resource_type.\
|
||||||
|
split(COMMA_CHARACTER)[1]
|
||||||
|
is_found = True
|
||||||
|
break
|
||||||
|
if is_found:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
error_reason = _(
|
||||||
|
"Target VnfResource for aspectId"
|
||||||
|
" {aspect_id} is not found in DB").format(
|
||||||
|
aspect_id=aspect_id)
|
||||||
|
raise vnfm.CNFScaleFailed(reason=error_reason)
|
||||||
|
|
||||||
for i in range(0, len(deployment_names), 2):
|
target_kinds = ["Deployment", "ReplicaSet", "StatefulSet"]
|
||||||
namespace = deployment_names[i]
|
if kind not in target_kinds:
|
||||||
deployment_name = deployment_names[i + 1]
|
error_reason = _(
|
||||||
deployment_info = app_v1_api_client.\
|
"Target kind {kind} is out of scale target").\
|
||||||
read_namespaced_deployment(namespace=namespace,
|
format(kind=kind)
|
||||||
name=deployment_name)
|
raise vnfm.CNFScaleFailed(reason=error_reason)
|
||||||
scaling_info = scaling_api_client.\
|
|
||||||
read_namespaced_horizontal_pod_autoscaler(
|
|
||||||
namespace=namespace,
|
|
||||||
name=deployment_name)
|
|
||||||
|
|
||||||
replicas = deployment_info.status.replicas
|
scale_info = self._call_read_scale_api(
|
||||||
scale_replicas = replicas
|
app_v1_api_client=app_v1_api_client,
|
||||||
vnf_scaling_name = deployment_info.metadata.labels.\
|
namespace=namespace,
|
||||||
get("scaling_name")
|
name=name,
|
||||||
if vnf_scaling_name == policy_name:
|
kind=kind)
|
||||||
if policy_action == 'out':
|
|
||||||
scale_replicas = replicas + 1
|
|
||||||
elif policy_action == 'in':
|
|
||||||
scale_replicas = replicas - 1
|
|
||||||
|
|
||||||
min_replicas = scaling_info.spec.min_replicas
|
current_replicas = scale_info.status.replicas
|
||||||
max_replicas = scaling_info.spec.max_replicas
|
vdu_profile = vdu_properties.get('vdu_profile')
|
||||||
|
if policy['action'] == 'out':
|
||||||
|
scale_replicas = current_replicas + policy['delta_num']
|
||||||
|
elif policy['action'] == 'in':
|
||||||
|
scale_replicas = current_replicas - policy['delta_num']
|
||||||
|
|
||||||
|
max_replicas = vdu_profile.get('max_number_of_instances')
|
||||||
|
min_replicas = vdu_profile.get('min_number_of_instances')
|
||||||
if (scale_replicas < min_replicas) or \
|
if (scale_replicas < min_replicas) or \
|
||||||
(scale_replicas > max_replicas):
|
(scale_replicas > max_replicas):
|
||||||
LOG.debug("Scaling replicas is out of range. The number of"
|
error_reason = _(
|
||||||
" replicas keeps %(number)s replicas",
|
"The number of target replicas after"
|
||||||
{'number': replicas})
|
" scaling [{after_replicas}] is out of range").\
|
||||||
scale_replicas = replicas
|
format(
|
||||||
deployment_info.spec.replicas = scale_replicas
|
after_replicas=scale_replicas)
|
||||||
app_v1_api_client.patch_namespaced_deployment_scale(
|
raise vnfm.CNFScaleFailed(reason=error_reason)
|
||||||
|
|
||||||
|
scale_info.spec.replicas = scale_replicas
|
||||||
|
self._call_patch_scale_api(
|
||||||
|
app_v1_api_client=app_v1_api_client,
|
||||||
namespace=namespace,
|
namespace=namespace,
|
||||||
name=deployment_name,
|
name=name,
|
||||||
body=deployment_info)
|
kind=kind,
|
||||||
|
body=scale_info)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error('Scaling VNF got an error due to %s', e)
|
LOG.error('Scaling VNF got an error due to %s', e)
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
self.clean_authenticate_vim(auth_cred, file_descriptor)
|
self.clean_authenticate_vim(auth_cred, file_descriptor)
|
||||||
|
|
||||||
|
def _scale_wait_legacy(self, policy, auth_cred):
|
||||||
|
core_v1_api_client = self.kubernetes.get_core_v1_api_client(
|
||||||
|
auth=auth_cred)
|
||||||
|
deployment_info = policy['instance_id'].split(",")
|
||||||
|
|
||||||
|
pods_information = self._get_pods_information(
|
||||||
|
core_v1_api_client=core_v1_api_client,
|
||||||
|
deployment_info=deployment_info)
|
||||||
|
status = self._get_pod_status(pods_information)
|
||||||
|
|
||||||
|
stack_retries = self.STACK_RETRIES
|
||||||
|
error_reason = None
|
||||||
|
while status == 'Pending' and stack_retries > 0:
|
||||||
|
time.sleep(self.STACK_RETRY_WAIT)
|
||||||
|
|
||||||
|
pods_information = self._get_pods_information(
|
||||||
|
core_v1_api_client=core_v1_api_client,
|
||||||
|
deployment_info=deployment_info)
|
||||||
|
status = self._get_pod_status(pods_information)
|
||||||
|
|
||||||
|
# LOG.debug('status: %s', status)
|
||||||
|
stack_retries = stack_retries - 1
|
||||||
|
|
||||||
|
LOG.debug('VNF initializing status: %(service_name)s %(status)s',
|
||||||
|
{'service_name': str(deployment_info), 'status': status})
|
||||||
|
|
||||||
|
if stack_retries == 0 and status != 'Running':
|
||||||
|
error_reason = _("Resource creation is not completed within"
|
||||||
|
" {wait} seconds as creation of stack {stack}"
|
||||||
|
" is not completed").format(
|
||||||
|
wait=(self.STACK_RETRIES *
|
||||||
|
self.STACK_RETRY_WAIT),
|
||||||
|
stack=policy['instance_id'])
|
||||||
|
LOG.error("VNF Creation failed: %(reason)s",
|
||||||
|
{'reason': error_reason})
|
||||||
|
raise vnfm.VNFCreateWaitFailed(reason=error_reason)
|
||||||
|
|
||||||
|
elif stack_retries != 0 and status != 'Running':
|
||||||
|
raise vnfm.VNFCreateWaitFailed(reason=error_reason)
|
||||||
|
|
||||||
|
def _is_match_pod_naming_rule(self, rsc_kind, rsc_name, pod_name):
|
||||||
|
match_result = None
|
||||||
|
if rsc_kind == 'Deployment':
|
||||||
|
# Expected example: name-012789abef-019az
|
||||||
|
match_result = re.match(
|
||||||
|
rsc_name + '-([0-9a-f]{10})-([0-9a-z]{5})+$',
|
||||||
|
pod_name)
|
||||||
|
elif rsc_kind == 'ReplicaSet':
|
||||||
|
# Expected example: name-019az
|
||||||
|
match_result = re.match(
|
||||||
|
rsc_name + '-([0-9a-z]{5})+$',
|
||||||
|
pod_name)
|
||||||
|
elif rsc_kind == 'StatefulSet':
|
||||||
|
# Expected example: name-0
|
||||||
|
match_result = re.match(
|
||||||
|
rsc_name + '-[0-9]+$',
|
||||||
|
pod_name)
|
||||||
|
if match_result:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def scale_wait(self, context, plugin, auth_attr, policy, region_name,
|
def scale_wait(self, context, plugin, auth_attr, policy, region_name,
|
||||||
last_event_id):
|
last_event_id):
|
||||||
"""Scale wait function
|
"""Scale wait function
|
||||||
@@ -1088,47 +1276,87 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
"""
|
"""
|
||||||
# initialize Kubernetes APIs
|
# initialize Kubernetes APIs
|
||||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
||||||
|
vnf_resources = objects.VnfResourceList.get_by_vnf_instance_id(
|
||||||
|
context, policy['vnf_instance_id'])
|
||||||
try:
|
try:
|
||||||
core_v1_api_client = self.kubernetes.get_core_v1_api_client(
|
if not vnf_resources:
|
||||||
auth=auth_cred)
|
# execute legacy scale_wait method
|
||||||
deployment_info = policy['instance_id'].split(",")
|
self._scale_wait_legacy(policy, auth_cred)
|
||||||
|
else:
|
||||||
|
core_v1_api_client = self.kubernetes.get_core_v1_api_client(
|
||||||
|
auth=auth_cred)
|
||||||
|
app_v1_api_client = self.kubernetes.get_app_v1_api_client(
|
||||||
|
auth=auth_cred)
|
||||||
|
aspect_id = policy['name']
|
||||||
|
vdu_defs = policy['vdu_defs']
|
||||||
|
is_found = False
|
||||||
|
error_reason = None
|
||||||
|
for vnf_resource in vnf_resources:
|
||||||
|
name = vnf_resource.resource_name.\
|
||||||
|
split(COMMA_CHARACTER)[1]
|
||||||
|
for vdu_id, vdu_def in vdu_defs.items():
|
||||||
|
vdu_properties = vdu_def.get('properties')
|
||||||
|
if name == vdu_properties.get('name'):
|
||||||
|
namespace = vnf_resource.resource_name.\
|
||||||
|
split(COMMA_CHARACTER)[0]
|
||||||
|
kind = vnf_resource.resource_type.\
|
||||||
|
split(COMMA_CHARACTER)[1]
|
||||||
|
is_found = True
|
||||||
|
break
|
||||||
|
if is_found:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
error_reason = _(
|
||||||
|
"Target VnfResource for aspectId {aspect_id}"
|
||||||
|
" is not found in DB").format(
|
||||||
|
aspect_id=aspect_id)
|
||||||
|
raise vnfm.CNFScaleWaitFailed(reason=error_reason)
|
||||||
|
|
||||||
pods_information = self._get_pods_information(
|
scale_info = self._call_read_scale_api(
|
||||||
core_v1_api_client=core_v1_api_client,
|
app_v1_api_client=app_v1_api_client,
|
||||||
deployment_info=deployment_info)
|
namespace=namespace,
|
||||||
status = self._get_pod_status(pods_information)
|
name=name,
|
||||||
|
kind=kind)
|
||||||
|
status = 'Pending'
|
||||||
|
stack_retries = self.STACK_RETRIES
|
||||||
|
error_reason = None
|
||||||
|
while status == 'Pending' and stack_retries > 0:
|
||||||
|
pods_information = list()
|
||||||
|
respone = core_v1_api_client.list_namespaced_pod(
|
||||||
|
namespace=namespace)
|
||||||
|
for pod in respone.items:
|
||||||
|
match_result = self._is_match_pod_naming_rule(
|
||||||
|
kind, name, pod.metadata.name)
|
||||||
|
if match_result:
|
||||||
|
pods_information.append(pod)
|
||||||
|
|
||||||
stack_retries = self.STACK_RETRIES
|
status = self._get_pod_status(pods_information)
|
||||||
error_reason = None
|
if status == 'Running' and \
|
||||||
while status == 'Pending' and stack_retries > 0:
|
scale_info.spec.replicas != len(pods_information):
|
||||||
time.sleep(self.STACK_RETRY_WAIT)
|
status = 'Pending'
|
||||||
|
|
||||||
pods_information = self._get_pods_information(
|
if status == 'Pending':
|
||||||
core_v1_api_client=core_v1_api_client,
|
stack_retries = stack_retries - 1
|
||||||
deployment_info=deployment_info)
|
time.sleep(self.STACK_RETRY_WAIT)
|
||||||
status = self._get_pod_status(pods_information)
|
elif status == 'Unknown':
|
||||||
|
error_reason = _(
|
||||||
|
"CNF Scale failed caused by the Pod status"
|
||||||
|
" is Unknown")
|
||||||
|
raise vnfm.CNFScaleWaitFailed(reason=error_reason)
|
||||||
|
|
||||||
# LOG.debug('status: %s', status)
|
if stack_retries == 0 and status != 'Running':
|
||||||
stack_retries = stack_retries - 1
|
error_reason = _(
|
||||||
|
"CNF Scale failed to complete within"
|
||||||
LOG.debug('VNF initializing status: %(service_name)s %(status)s',
|
" {wait} seconds while waiting for the aspect_id"
|
||||||
{'service_name': str(deployment_info), 'status': status})
|
" {aspect_id} to be scaled").format(
|
||||||
|
wait=(self.STACK_RETRIES *
|
||||||
if stack_retries == 0 and status != 'Running':
|
self.STACK_RETRY_WAIT),
|
||||||
error_reason = _("Resource creation is not completed within"
|
aspect_id=aspect_id)
|
||||||
" {wait} seconds as creation of stack {stack}"
|
LOG.error("CNF Scale failed: %(reason)s",
|
||||||
" is not completed").format(
|
{'reason': error_reason})
|
||||||
wait=(self.STACK_RETRIES *
|
raise vnfm.CNFScaleWaitFailed(reason=error_reason)
|
||||||
self.STACK_RETRY_WAIT),
|
|
||||||
stack=policy['instance_id'])
|
|
||||||
LOG.error("VNF Creation failed: %(reason)s",
|
|
||||||
{'reason': error_reason})
|
|
||||||
raise vnfm.VNFCreateWaitFailed(reason=error_reason)
|
|
||||||
|
|
||||||
elif stack_retries != 0 and status != 'Running':
|
|
||||||
raise vnfm.VNFCreateWaitFailed(reason=error_reason)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error('Scaling wait VNF got an error due to %s', e)
|
LOG.error('Scaling wait CNF got an error due to %s', e)
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
self.clean_authenticate_vim(auth_cred, file_descriptor)
|
self.clean_authenticate_vim(auth_cred, file_descriptor)
|
||||||
@@ -1315,7 +1543,8 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
vnf_dict,
|
vnf_dict,
|
||||||
auth_attr,
|
auth_attr,
|
||||||
region_name):
|
region_name):
|
||||||
pass
|
return_id_list = []
|
||||||
|
return return_id_list
|
||||||
|
|
||||||
def get_scale_in_ids(self,
|
def get_scale_in_ids(self,
|
||||||
plugin,
|
plugin,
|
||||||
@@ -1325,7 +1554,11 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
auth_attr,
|
auth_attr,
|
||||||
region_name,
|
region_name,
|
||||||
number_of_steps):
|
number_of_steps):
|
||||||
pass
|
return_id_list = []
|
||||||
|
return_name_list = []
|
||||||
|
return_grp_id = None
|
||||||
|
return_res_num = None
|
||||||
|
return return_id_list, return_name_list, return_grp_id, return_res_num
|
||||||
|
|
||||||
def scale_resource_update(self, context, vnf_instance,
|
def scale_resource_update(self, context, vnf_instance,
|
||||||
scale_vnf_request,
|
scale_vnf_request,
|
||||||
@@ -1341,7 +1574,33 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
region_name,
|
region_name,
|
||||||
scale_name_list,
|
scale_name_list,
|
||||||
grp_id):
|
grp_id):
|
||||||
pass
|
# NOTE(ueha): The `is_reverse` option is not supported in kubernetes
|
||||||
|
# VIM, and returns an error response to the user if `is_reverse` is
|
||||||
|
# true. However, since this method is called in the sequence of
|
||||||
|
# rollback operation, implementation is required.
|
||||||
|
vnf_instance_id = vnf_info['vnf_lcm_op_occ'].vnf_instance_id
|
||||||
|
aspect_id = scale_vnf_request.aspect_id
|
||||||
|
vnf_instance = objects.VnfInstance.get_by_id(context, vnf_instance_id)
|
||||||
|
vnfd_dict = vnflcm_utils._get_vnfd_dict(context,
|
||||||
|
vnf_instance.vnfd_id,
|
||||||
|
vnf_instance.instantiated_vnf_info.flavour_id)
|
||||||
|
tosca = tosca_template.ToscaTemplate(parsed_params={}, a_file=False,
|
||||||
|
yaml_dict_tpl=vnfd_dict)
|
||||||
|
extract_policy_infos = vnflcm_utils.get_extract_policy_infos(tosca)
|
||||||
|
|
||||||
|
policy = dict()
|
||||||
|
policy['name'] = aspect_id
|
||||||
|
policy['action'] = 'in'
|
||||||
|
policy['vnf_instance_id'] = vnf_instance_id
|
||||||
|
policy['vdu_defs'] = vnflcm_utils.get_target_vdu_def_dict(
|
||||||
|
extract_policy_infos=extract_policy_infos,
|
||||||
|
aspect_id=scale_vnf_request.aspect_id,
|
||||||
|
tosca=tosca)
|
||||||
|
policy['delta_num'] = vnflcm_utils.get_scale_delta_num(
|
||||||
|
extract_policy_infos=extract_policy_infos,
|
||||||
|
aspect_id=scale_vnf_request.aspect_id)
|
||||||
|
|
||||||
|
self.scale(context, plugin, auth_attr, policy, region_name)
|
||||||
|
|
||||||
def scale_out_initial(self,
|
def scale_out_initial(self,
|
||||||
context,
|
context,
|
||||||
@@ -1358,7 +1617,30 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
auth_attr,
|
auth_attr,
|
||||||
vnf_info,
|
vnf_info,
|
||||||
region_name):
|
region_name):
|
||||||
pass
|
lcm_op_occ = vnf_info.get('vnf_lcm_op_occ')
|
||||||
|
vnf_instance_id = lcm_op_occ.get('vnf_instance_id')
|
||||||
|
operation_params = jsonutils.loads(lcm_op_occ.get('operation_params'))
|
||||||
|
scale_vnf_request = objects.ScaleVnfRequest.obj_from_primitive(
|
||||||
|
operation_params, context=context)
|
||||||
|
aspect_id = scale_vnf_request.aspect_id
|
||||||
|
vnf_instance = objects.VnfInstance.get_by_id(context, vnf_instance_id)
|
||||||
|
vnfd_dict = vnflcm_utils._get_vnfd_dict(context,
|
||||||
|
vnf_instance.vnfd_id,
|
||||||
|
vnf_instance.instantiated_vnf_info.flavour_id)
|
||||||
|
tosca = tosca_template.ToscaTemplate(parsed_params={}, a_file=False,
|
||||||
|
yaml_dict_tpl=vnfd_dict)
|
||||||
|
extract_policy_infos = vnflcm_utils.get_extract_policy_infos(tosca)
|
||||||
|
|
||||||
|
policy = dict()
|
||||||
|
policy['name'] = aspect_id
|
||||||
|
policy['vnf_instance_id'] = lcm_op_occ.get('vnf_instance_id')
|
||||||
|
policy['vdu_defs'] = vnflcm_utils.get_target_vdu_def_dict(
|
||||||
|
extract_policy_infos=extract_policy_infos,
|
||||||
|
aspect_id=scale_vnf_request.aspect_id,
|
||||||
|
tosca=tosca)
|
||||||
|
|
||||||
|
self.scale_wait(context, plugin, auth_attr, policy,
|
||||||
|
region_name, None)
|
||||||
|
|
||||||
def get_cinder_list(self,
|
def get_cinder_list(self,
|
||||||
vnf_info):
|
vnf_info):
|
||||||
@@ -1380,4 +1662,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
aspect_id,
|
aspect_id,
|
||||||
auth_attr,
|
auth_attr,
|
||||||
region_name):
|
region_name):
|
||||||
pass
|
return_id_list = []
|
||||||
|
return_name_list = []
|
||||||
|
return_grp_id = None
|
||||||
|
return return_id_list, return_name_list, return_grp_id
|
||||||
|
Reference in New Issue
Block a user