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 uuidutils
|
||||
from sqlalchemy import exc as sqlexc
|
||||
from toscaparser import tosca_template
|
||||
|
||||
import ast
|
||||
import functools
|
||||
@@ -53,6 +54,8 @@ from tacker.objects import fields
|
||||
from tacker.objects import vnf_lcm_subscriptions as subscription_obj
|
||||
from tacker.plugins.common import constants
|
||||
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
|
||||
from tacker.vnfm import vim_client
|
||||
from tacker import wsgi
|
||||
@@ -977,7 +980,24 @@ class VnfLcmController(wsgi.Controller):
|
||||
return self._make_problem_detail(
|
||||
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)
|
||||
scale_vnf_request = objects.ScaleVnfRequest.obj_from_primitive(
|
||||
req_body, context=context)
|
||||
@@ -1005,18 +1025,34 @@ class VnfLcmController(wsgi.Controller):
|
||||
if not scale_vnf_request.additional_params.get('is_auto'):
|
||||
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 current_level == 0 or\
|
||||
current_level < scale_vnf_request.number_of_steps:
|
||||
return self._make_problem_detail(
|
||||
'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
|
||||
|
||||
elif scale_vnf_request.type == 'SCALE_OUT':
|
||||
scaleGroupDict = jsonutils.loads(
|
||||
vnf_info['attributes']['scale_group'])
|
||||
max_level = (scaleGroupDict['scaleGroupDict']
|
||||
[scale_vnf_request.aspect_id]['maxLevel'])
|
||||
if vim_type == "kubernetes":
|
||||
max_level = self._get_scale_max_level_from_vnfd(
|
||||
context=context,
|
||||
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
|
||||
if max_level < scale_level:
|
||||
return self._make_problem_detail(
|
||||
@@ -1043,6 +1079,9 @@ class VnfLcmController(wsgi.Controller):
|
||||
error_point=1)
|
||||
vnf_lcm_op_occ.create()
|
||||
|
||||
vnf_instance.task_state = fields.VnfInstanceTaskState.SCALING
|
||||
vnf_instance.save()
|
||||
|
||||
vnflcm_url = CONF.vnf_lcm.endpoint_url + \
|
||||
"/vnflcm/v1/vnf_lcm_op_occs/" + vnf_lcm_op_occs_id
|
||||
insta_url = CONF.vnf_lcm.endpoint_url + \
|
||||
@@ -1096,7 +1135,7 @@ class VnfLcmController(wsgi.Controller):
|
||||
if not vnf_instance.instantiated_vnf_info.scale_status:
|
||||
return self._make_problem_detail(
|
||||
'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:
|
||||
return self._make_problem_detail(
|
||||
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)
|
||||
vnfd_attribute.create()
|
||||
|
||||
break
|
||||
|
||||
@revert_upload_vnf_package
|
||||
def upload_vnf_package_content(self, context, vnf_package):
|
||||
vnf_package.onboarding_state = (
|
||||
|
@@ -109,6 +109,14 @@ class CNFCreateWaitFailed(exceptions.TackerException):
|
||||
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):
|
||||
message = _('service type %(service_type_id)s could not be found')
|
||||
|
||||
|
@@ -137,9 +137,10 @@ class VnfInstanceTaskState(BaseTackerEnum):
|
||||
INSTANTIATING = 'INSTANTIATING'
|
||||
HEALING = 'HEALING'
|
||||
TERMINATING = 'TERMINATING'
|
||||
SCALING = 'SCALING'
|
||||
ERROR = 'ERROR'
|
||||
|
||||
ALL = (INSTANTIATING, HEALING, TERMINATING, ERROR)
|
||||
ALL = (INSTANTIATING, HEALING, TERMINATING, SCALING, ERROR)
|
||||
|
||||
|
||||
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',
|
||||
'attributes': {}}
|
||||
|
||||
def _update_vnf_scaling(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
class TestNfvoPlugin(db_base.SqlTestCase):
|
||||
def setUp(self):
|
||||
|
@@ -184,8 +184,9 @@ def return_vnf_instance(
|
||||
}
|
||||
instantiated_vnf_info = get_instantiated_vnf_info
|
||||
|
||||
s_status = {"aspect_id": "SP1", "scale_level": 1}
|
||||
scale_status = objects.ScaleInfo(**s_status)
|
||||
if scale_status == "scale_status":
|
||||
s_status = {"aspect_id": "SP1", "scale_level": 1}
|
||||
scale_status = objects.ScaleInfo(**s_status)
|
||||
|
||||
instantiated_vnf_info.update(
|
||||
{"ext_cp_info": [],
|
||||
@@ -792,10 +793,10 @@ def _get_vnf(**updates):
|
||||
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 = {
|
||||
'type': type,
|
||||
'aspect_id': "SP1",
|
||||
'aspect_id': aspect_id,
|
||||
'number_of_steps': number_of_steps,
|
||||
'scale_level': 1,
|
||||
'additional_params': {"is_reverse": is_reverse},
|
||||
@@ -844,12 +845,41 @@ def vnf_scale():
|
||||
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(
|
||||
state_entered_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
start_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
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_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,
|
||||
operation='SCALE',
|
||||
operation_state='FAILED_TEMP',
|
||||
@@ -857,16 +887,14 @@ def vnflcm_rollback(error_point=7):
|
||||
operation_params='{"type": "SCALE_OUT", "aspect_id": "SP1"}',
|
||||
error_point=error_point,
|
||||
id=constants.UUID,
|
||||
created_at=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC))
|
||||
created_at=dt)
|
||||
|
||||
|
||||
def vnflcm_rollback_insta(error_point=7):
|
||||
dt = datetime.datetime(2000, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||
return objects.VnfLcmOpOcc(
|
||||
state_entered_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
start_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
state_entered_time=dt,
|
||||
start_time=dt,
|
||||
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
||||
operation='INSTANTIATE',
|
||||
operation_state='FAILED_TEMP',
|
||||
@@ -874,16 +902,14 @@ def vnflcm_rollback_insta(error_point=7):
|
||||
operation_params='{}',
|
||||
error_point=error_point,
|
||||
id=constants.UUID,
|
||||
created_at=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC))
|
||||
created_at=dt)
|
||||
|
||||
|
||||
def vnflcm_rollback_active():
|
||||
dt = datetime.datetime(2000, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||
return objects.VnfLcmOpOcc(
|
||||
state_entered_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
start_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
state_entered_time=dt,
|
||||
start_time=dt,
|
||||
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
||||
operation='SCALE',
|
||||
operation_state='ACTIVE',
|
||||
@@ -891,16 +917,14 @@ def vnflcm_rollback_active():
|
||||
operation_params='{"type": "SCALE_OUT", "aspect_id": "SP1"}',
|
||||
error_point=7,
|
||||
id=constants.UUID,
|
||||
created_at=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC))
|
||||
created_at=dt)
|
||||
|
||||
|
||||
def vnflcm_rollback_ope():
|
||||
dt = datetime.datetime(2000, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||
return objects.VnfLcmOpOcc(
|
||||
state_entered_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
start_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
state_entered_time=dt,
|
||||
start_time=dt,
|
||||
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
||||
operation='HEAL',
|
||||
operation_state='FAILED_TEMP',
|
||||
@@ -908,16 +932,14 @@ def vnflcm_rollback_ope():
|
||||
operation_params='{}',
|
||||
error_point=7,
|
||||
id=constants.UUID,
|
||||
created_at=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC))
|
||||
created_at=dt)
|
||||
|
||||
|
||||
def vnflcm_rollback_scale_in():
|
||||
dt = datetime.datetime(2000, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||
return objects.VnfLcmOpOcc(
|
||||
state_entered_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
start_time=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
state_entered_time=dt,
|
||||
start_time=dt,
|
||||
vnf_instance_id=uuidsentinel.vnf_instance_id,
|
||||
operation='SCALE',
|
||||
operation_state='FAILED_TEMP',
|
||||
@@ -925,8 +947,7 @@ def vnflcm_rollback_scale_in():
|
||||
operation_params='{"type": "SCALE_IN", "aspect_id": "SP1"}',
|
||||
error_point=7,
|
||||
id=constants.UUID,
|
||||
created_at=datetime.datetime(2000, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC))
|
||||
created_at=dt)
|
||||
|
||||
|
||||
def vnf_rollback():
|
||||
@@ -1090,6 +1111,110 @@ def 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):
|
||||
"""Add a 'tacker.context' to WSGI environ."""
|
||||
|
||||
@@ -1294,13 +1419,12 @@ def fake_vnf_lcm_op_occs():
|
||||
}
|
||||
changed_info_obj = objects.VnfInfoModifications(**changed_info)
|
||||
|
||||
dt = datetime.datetime(1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||
vnf_lcm_op_occs = {
|
||||
'id': constants.UUID,
|
||||
'operation_state': 'COMPLETED',
|
||||
'state_entered_time': datetime.datetime(1900, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
'start_time': datetime.datetime(1900, 1, 1, 1, 1, 1,
|
||||
tzinfo=iso8601.UTC),
|
||||
'state_entered_time': dt,
|
||||
'start_time': dt,
|
||||
'vnf_instance_id': constants.UUID,
|
||||
'operation': 'MODIFY_INFO',
|
||||
'is_automatic_invocation': False,
|
||||
|
@@ -69,6 +69,8 @@ class FakeVNFMPlugin(mock.Mock):
|
||||
self.vnf3_vnfd_id = 'e4015e9f-1ef2-49fb-adb6-070791ad3c45'
|
||||
self.vnf3_vnf_id = '7168062e-9fa1-4203-8cb7-f5c99ff3ee1b'
|
||||
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_update_id = 'a18c8bae-898a-4932-bff8-d5eac981a9b8'
|
||||
@@ -110,6 +112,8 @@ class FakeVNFMPlugin(mock.Mock):
|
||||
return self.get_dummy_vnf_error()
|
||||
elif self.vnf3_vnf_id in args:
|
||||
return self.get_dummy_vnf_not_error()
|
||||
elif self.vnf_for_cnf_vnf_id in args:
|
||||
return fakes.vnf_dict_cnf()
|
||||
else:
|
||||
return self.get_dummy_vnf_active()
|
||||
|
||||
@@ -2369,6 +2373,7 @@ class TestController(base.TestCase):
|
||||
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")
|
||||
@@ -2379,13 +2384,18 @@ class TestController(base.TestCase):
|
||||
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._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(
|
||||
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(
|
||||
"SCALE_IN", 1)
|
||||
mock_create.return_value = 200
|
||||
@@ -2410,6 +2420,7 @@ class TestController(base.TestCase):
|
||||
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")
|
||||
@@ -2420,13 +2431,18 @@ class TestController(base.TestCase):
|
||||
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._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(
|
||||
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(
|
||||
"SCALE_OUT", 1)
|
||||
mock_create.return_value = 200
|
||||
@@ -2451,6 +2467,7 @@ class TestController(base.TestCase):
|
||||
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")
|
||||
@@ -2459,13 +2476,19 @@ class TestController(base.TestCase):
|
||||
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._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(
|
||||
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(
|
||||
"SCALE_IN", 4)
|
||||
mock_create.return_value = 200
|
||||
@@ -2505,8 +2528,12 @@ class TestController(base.TestCase):
|
||||
mock_get_service_plugins):
|
||||
|
||||
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(
|
||||
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(
|
||||
"SCALE_OUT", 4)
|
||||
mock_create.return_value = 200
|
||||
@@ -2533,6 +2560,7 @@ class TestController(base.TestCase):
|
||||
return_value={'VNFM': FakeVNFMPlugin()})
|
||||
@mock.patch.object(objects.ScaleVnfRequest, "obj_from_primitive")
|
||||
@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(vnf_lcm_rpc.VNFLcmRPCAPI, "send_notification")
|
||||
@mock.patch.object(objects.VnfLcmOpOcc, "create")
|
||||
@@ -2541,6 +2569,7 @@ class TestController(base.TestCase):
|
||||
mock_create,
|
||||
mock_send_notification,
|
||||
mock_vnf_instance,
|
||||
mock_vnf_instance_save,
|
||||
mock_get_vnf,
|
||||
mock_obj_from_primitive,
|
||||
get_service_plugins):
|
||||
@@ -2556,9 +2585,13 @@ class TestController(base.TestCase):
|
||||
"SCALE_IN", 1)
|
||||
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(
|
||||
fields.VnfInstanceState.INSTANTIATED,
|
||||
scale_status="scale_status")
|
||||
scale_status="scale_status",
|
||||
**update)
|
||||
|
||||
vnf_instance.instantiated_vnf_info.instance_id =\
|
||||
uuidsentinel.instance_id
|
||||
@@ -2571,9 +2604,10 @@ class TestController(base.TestCase):
|
||||
|
||||
vnf_info = fakes._get_vnf()
|
||||
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,
|
||||
vnf_info, vnf_instance, body)
|
||||
vnf_instance, vnf_info, body)
|
||||
|
||||
mock_send_notification.assert_called_once()
|
||||
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(
|
||||
'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',
|
||||
return_value={'VNFM': FakeVNFMPlugin()})
|
||||
@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")
|
||||
self._mock_vnf_manager(fail_method_name='create_wait')
|
||||
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,
|
||||
driver.instantiate_vnf, self.context, vnf_instance_obj, vnf_dict,
|
||||
instantiate_vnf_req_obj)
|
||||
@@ -750,7 +753,9 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
||||
uuidsentinel.instance_id
|
||||
self._mock_vnf_manager()
|
||||
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 = {}
|
||||
driver.heal_vnf(self.context, vnf_instance, vnf_dict, heal_vnf_req)
|
||||
self.assertEqual(1, mock_save.call_count)
|
||||
@@ -1062,9 +1067,9 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
||||
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
|
||||
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
|
||||
'\"initialLevel\": 0, \"default\": 0 }}}'
|
||||
scale_vnf_request = fakes.scale_request("SCALE_IN", 1, "True")
|
||||
scale_vnf_request = fakes.scale_request("SCALE_IN", "SP1", 1, "True")
|
||||
vim_connection_info = vim_connection.VimConnectionInfo(
|
||||
vim_type="fake_type")
|
||||
vim_type="openstack")
|
||||
scale_name_list = ["fake"]
|
||||
grp_id = "fake_id"
|
||||
driver = vnflcm_driver.VnfLcmDriver()
|
||||
@@ -1089,9 +1094,9 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
||||
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
|
||||
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
|
||||
'\"initialLevel\": 0, \"default\": 0 }}}'
|
||||
scale_vnf_request = fakes.scale_request("SCALE_IN", 1, "False")
|
||||
scale_vnf_request = fakes.scale_request("SCALE_IN", "SP1", 1, "False")
|
||||
vim_connection_info = vim_connection.VimConnectionInfo(
|
||||
vim_type="fake_type")
|
||||
vim_type="openstack")
|
||||
scale_name_list = ["fake"]
|
||||
grp_id = "fake_id"
|
||||
with open(vnf_info["attributes"]["heat_template"], "r") as f:
|
||||
@@ -1119,9 +1124,9 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
||||
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
|
||||
'1, \"maxLevel\": 3, \"initialNum\": 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_type="fake_type")
|
||||
vim_type="openstack")
|
||||
scale_name_list = ["fake"]
|
||||
grp_id = "fake_id"
|
||||
with open(vnf_info["attributes"]["heat_template"], "r") as f:
|
||||
@@ -1149,9 +1154,9 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
||||
'{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \
|
||||
'1, \"maxLevel\": 3, \"initialNum\": 0, ' + \
|
||||
'\"initialLevel\": 0, \"default\": 1 }}}'
|
||||
scale_vnf_request = fakes.scale_request("SCALE_OUT", 1, "False")
|
||||
scale_vnf_request = fakes.scale_request("SCALE_OUT", "SP1", 1, "False")
|
||||
vim_connection_info = vim_connection.VimConnectionInfo(
|
||||
vim_type="fake_type")
|
||||
vim_type="openstack")
|
||||
scale_name_list = ["fake"]
|
||||
grp_id = "fake_id"
|
||||
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,
|
||||
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',
|
||||
return_value={'VNFM': FakeVNFMPlugin()})
|
||||
@mock.patch.object(VnfLcmDriver,
|
||||
@@ -2068,3 +2175,69 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
||||
self.assertEqual(1, mock_scale.call_count)
|
||||
self.assertEqual(1, mock_wait.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 tacker.db.db_sqlalchemy import models
|
||||
from tacker.tests import uuidsentinel
|
||||
|
||||
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.tests.unit import base
|
||||
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.openstack.fixture_data import \
|
||||
fixture_data_utils as fd_utils
|
||||
@@ -1799,3 +1800,490 @@ class TestKubernetes(base.TestCase):
|
||||
"{'namespace': 'test', 'name': " +
|
||||
"'curry-test001', 'apiVersion': 'apps/v1', " +
|
||||
"'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 = {}
|
||||
# { vduId: initialDelta }
|
||||
vdu_delta_dict = {}
|
||||
# { aspectId: maxScaleLevel }
|
||||
aspect_max_level_dict = {}
|
||||
|
||||
tosca_policies = tosca.topology_template.policies
|
||||
default_inst_level_id = _extract_policy_info(
|
||||
tosca_policies, inst_level_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:
|
||||
# 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,
|
||||
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
|
||||
if 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_name = delta_names[0]
|
||||
aspect_id_dict[aspect_id] = delta_name
|
||||
|
||||
aspect_max_level_dict[aspect_id] = \
|
||||
aspect_val['max_scale_level']
|
||||
elif p.type == ETSI_INITIAL_DELTA:
|
||||
vdus = p.targets
|
||||
initial_delta = \
|
||||
|
@@ -994,6 +994,7 @@ def _convert_desired_capacity(inst_level_id, vnfd_dict, vdu):
|
||||
inst_level_dict = {}
|
||||
aspect_id_dict = {}
|
||||
vdu_delta_dict = {}
|
||||
aspect_max_level_dict = {}
|
||||
desired_capacity = 1
|
||||
|
||||
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(
|
||||
tosca_policies, inst_level_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:
|
||||
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 nested_hot_dict: %s", 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_utils import encodeutils
|
||||
from oslo_utils import excutils
|
||||
from toscaparser import tosca_template
|
||||
|
||||
from tacker.common import driver_manager
|
||||
from tacker.common import exceptions
|
||||
@@ -92,6 +93,7 @@ def revert_to_error_scale(function):
|
||||
"instance %(id)s. Error: %(error)s",
|
||||
{"id": vnf_instance.id, "error": e})
|
||||
try:
|
||||
vnf_instance.task_state = None
|
||||
self._vnf_instance_update(context, vnf_instance)
|
||||
except Exception as e:
|
||||
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'):
|
||||
vnf_instance.instantiated_vnf_info.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:
|
||||
self._vnf_manager.invoke(
|
||||
@@ -815,16 +826,21 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
|
||||
vnf_lcm_op_occ.error_point = 7
|
||||
vnf_instance.instantiated_vnf_info.scale_level =\
|
||||
vnf_info['after_scale_level']
|
||||
scaleGroupDict = \
|
||||
jsonutils.loads(vnf_info['attributes']['scale_group'])
|
||||
(scaleGroupDict
|
||||
['scaleGroupDict'][scale_vnf_request.aspect_id]['default']) =\
|
||||
vnf_info['res_num']
|
||||
vnf_info['attributes']['scale_group'] =\
|
||||
jsonutils.dump_as_bytes(scaleGroupDict)
|
||||
if vim_connection_info.vim_type != 'kubernetes':
|
||||
# NOTE(ueha): The logic of Scale for OpenStack VIM is widely hard
|
||||
# coded with `vnf_info`. This dependency is to be refactored in
|
||||
# future.
|
||||
scaleGroupDict = \
|
||||
jsonutils.loads(vnf_info['attributes']['scale_group'])
|
||||
(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.operation_state = 'COMPLETED'
|
||||
vnf_lcm_op_occ.resource_changes = resource_changes
|
||||
vnf_instance.task_state = None
|
||||
self._vnfm_plugin._update_vnf_scaling(context, vnf_info,
|
||||
'PENDING_' + scale_vnf_request.type,
|
||||
'ACTIVE',
|
||||
@@ -1084,10 +1100,33 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
|
||||
LOG.debug(
|
||||
"is_reverse: %s",
|
||||
scale_vnf_request.additional_params.get('is_reverse'))
|
||||
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']
|
||||
default = None
|
||||
if vim_connection_info.vim_type == 'kubernetes':
|
||||
policy['vnf_instance_id'] = \
|
||||
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
|
||||
scale_vnf_request.additional_params['is_reverse'] == 'True'):
|
||||
self._vnf_manager.invoke(
|
||||
@@ -1132,26 +1171,32 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
|
||||
region_name=vim_connection_info.access_info.get('region_name')
|
||||
)
|
||||
else:
|
||||
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'
|
||||
cooldown = None
|
||||
if vim_connection_info.vim_type != 'kubernetes':
|
||||
# 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)
|
||||
if scale_vnf_request.type == 'SCALE_IN':
|
||||
policy['action'] = 'in'
|
||||
policy_temp = heat_resource['resources'][policy_in_name]
|
||||
policy_prop = policy_temp['properties']
|
||||
cooldown = policy_prop.get('cooldown')
|
||||
policy_name = policy_in_name
|
||||
else:
|
||||
policy['action'] = 'out'
|
||||
policy_temp = heat_resource['resources'][policy_out_name]
|
||||
policy_prop = policy_temp['properties']
|
||||
cooldown = policy_prop.get('cooldown')
|
||||
policy_name = policy_out_name
|
||||
heat_resource = yaml.safe_load(heat_template)
|
||||
if scale_vnf_request.type == 'SCALE_IN':
|
||||
policy['action'] = 'in'
|
||||
policy_temp = heat_resource['resources'][policy_in_name]
|
||||
policy_prop = policy_temp['properties']
|
||||
cooldown = policy_prop.get('cooldown')
|
||||
policy_name = policy_in_name
|
||||
else:
|
||||
policy['action'] = 'out'
|
||||
policy_temp = heat_resource['resources'][policy_out_name]
|
||||
policy_prop = policy_temp['properties']
|
||||
cooldown = policy_prop.get('cooldown')
|
||||
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):
|
||||
last_event_id = self._vnf_manager.invoke(
|
||||
vim_connection_info.vim_type,
|
||||
@@ -1290,11 +1335,15 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
|
||||
grp_id = None
|
||||
self._update_vnf_rollback_pre(context, vnf_info)
|
||||
if vnf_lcm_op_occs.operation == 'SCALE':
|
||||
scaleGroupDict = jsonutils.loads(
|
||||
vnf_info['attributes']['scale_group'])
|
||||
cap_size = scaleGroupDict['scaleGroupDict'][operation_params
|
||||
['aspect_id']]['default']
|
||||
vnf_info['res_num'] = cap_size
|
||||
if vim_connection_info.vim_type != 'kubernetes':
|
||||
# NOTE(ueha): The logic of Scale for OpenStack VIM is widely
|
||||
# hard coded with `vnf_info`. This dependency is to be
|
||||
# refactored in future.
|
||||
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(
|
||||
operation_params, context=context)
|
||||
for scale in vnf_instance.instantiated_vnf_info.scale_status:
|
||||
@@ -1403,6 +1452,7 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
|
||||
status = 'ACTIVE'
|
||||
else:
|
||||
status = 'INACTIVE'
|
||||
vnf_instance.task_state = None
|
||||
self._vnfm_plugin._update_vnf_rollback(context, vnf_info,
|
||||
'ERROR',
|
||||
status,
|
||||
|
@@ -23,6 +23,7 @@ from kubernetes import client
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
from toscaparser import tosca_template
|
||||
|
||||
from tacker._i18n import _
|
||||
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_vnfd as vnfd_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.kubernetes.k8s import translate_outputs
|
||||
from tacker.vnfm.infra_drivers.kubernetes import translate_template
|
||||
@@ -1019,6 +1021,96 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
finally:
|
||||
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
|
||||
def scale(self, context, plugin, auth_attr, policy, region_name):
|
||||
"""Scale function
|
||||
@@ -1027,58 +1119,154 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
The min_replicas and max_replicas is limited by the number of replicas
|
||||
of policy scaling when user define VNF descriptor.
|
||||
"""
|
||||
LOG.debug("VNF are scaled by updating instance of deployment")
|
||||
# initialize Kubernetes APIs
|
||||
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:
|
||||
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']
|
||||
if not vnf_resources:
|
||||
# execute legacy scale method
|
||||
self._scale_legacy(policy, auth_cred)
|
||||
else:
|
||||
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:
|
||||
# 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):
|
||||
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)
|
||||
target_kinds = ["Deployment", "ReplicaSet", "StatefulSet"]
|
||||
if kind not in target_kinds:
|
||||
error_reason = _(
|
||||
"Target kind {kind} is out of scale target").\
|
||||
format(kind=kind)
|
||||
raise vnfm.CNFScaleFailed(reason=error_reason)
|
||||
|
||||
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
|
||||
scale_info = self._call_read_scale_api(
|
||||
app_v1_api_client=app_v1_api_client,
|
||||
namespace=namespace,
|
||||
name=name,
|
||||
kind=kind)
|
||||
|
||||
min_replicas = scaling_info.spec.min_replicas
|
||||
max_replicas = scaling_info.spec.max_replicas
|
||||
current_replicas = scale_info.status.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 \
|
||||
(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(
|
||||
error_reason = _(
|
||||
"The number of target replicas after"
|
||||
" scaling [{after_replicas}] is out of range").\
|
||||
format(
|
||||
after_replicas=scale_replicas)
|
||||
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,
|
||||
name=deployment_name,
|
||||
body=deployment_info)
|
||||
name=name,
|
||||
kind=kind,
|
||||
body=scale_info)
|
||||
except Exception as e:
|
||||
LOG.error('Scaling VNF got an error due to %s', e)
|
||||
raise
|
||||
finally:
|
||||
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,
|
||||
last_event_id):
|
||||
"""Scale wait function
|
||||
@@ -1088,47 +1276,87 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
"""
|
||||
# initialize Kubernetes APIs
|
||||
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:
|
||||
core_v1_api_client = self.kubernetes.get_core_v1_api_client(
|
||||
auth=auth_cred)
|
||||
deployment_info = policy['instance_id'].split(",")
|
||||
if not vnf_resources:
|
||||
# execute legacy scale_wait method
|
||||
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(
|
||||
core_v1_api_client=core_v1_api_client,
|
||||
deployment_info=deployment_info)
|
||||
status = self._get_pod_status(pods_information)
|
||||
scale_info = self._call_read_scale_api(
|
||||
app_v1_api_client=app_v1_api_client,
|
||||
namespace=namespace,
|
||||
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
|
||||
error_reason = None
|
||||
while status == 'Pending' and stack_retries > 0:
|
||||
time.sleep(self.STACK_RETRY_WAIT)
|
||||
status = self._get_pod_status(pods_information)
|
||||
if status == 'Running' and \
|
||||
scale_info.spec.replicas != len(pods_information):
|
||||
status = 'Pending'
|
||||
|
||||
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)
|
||||
if status == 'Pending':
|
||||
stack_retries = stack_retries - 1
|
||||
time.sleep(self.STACK_RETRY_WAIT)
|
||||
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)
|
||||
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)
|
||||
if stack_retries == 0 and status != 'Running':
|
||||
error_reason = _(
|
||||
"CNF Scale failed to complete within"
|
||||
" {wait} seconds while waiting for the aspect_id"
|
||||
" {aspect_id} to be scaled").format(
|
||||
wait=(self.STACK_RETRIES *
|
||||
self.STACK_RETRY_WAIT),
|
||||
aspect_id=aspect_id)
|
||||
LOG.error("CNF Scale failed: %(reason)s",
|
||||
{'reason': error_reason})
|
||||
raise vnfm.CNFScaleWaitFailed(reason=error_reason)
|
||||
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
|
||||
finally:
|
||||
self.clean_authenticate_vim(auth_cred, file_descriptor)
|
||||
@@ -1315,7 +1543,8 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
vnf_dict,
|
||||
auth_attr,
|
||||
region_name):
|
||||
pass
|
||||
return_id_list = []
|
||||
return return_id_list
|
||||
|
||||
def get_scale_in_ids(self,
|
||||
plugin,
|
||||
@@ -1325,7 +1554,11 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
auth_attr,
|
||||
region_name,
|
||||
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,
|
||||
scale_vnf_request,
|
||||
@@ -1341,7 +1574,33 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
region_name,
|
||||
scale_name_list,
|
||||
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,
|
||||
context,
|
||||
@@ -1358,7 +1617,30 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
auth_attr,
|
||||
vnf_info,
|
||||
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,
|
||||
vnf_info):
|
||||
@@ -1380,4 +1662,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
aspect_id,
|
||||
auth_attr,
|
||||
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