CNF v2 API enhance and refactor
This patch enables the following CNF v2 APIs. * scale VNF task * heal VNF task * Rollback operation task for the following APIs. - instantiate VNF task - scale (SCALE_OUT) VNF task This patch also refactors k8s infra_driver to make this enhance, future enhance and maintanace easy. There are changes about the specification of additionalParams in ChangeCurrentVnfPkgRequest, although function of change_vnfpkg API is not changed basically. Documentation about this enhance will be provided by anther patch. It will include about specification change of change_vnfpkg too. Implement: blueprint support-nfv-solv3-scale-vnf Implement: blueprint support-nfv-solv3-heal-vnf Implement: blueprint support-nfv-solv3-error-handling Change-Id: Ifb0fc16c3fb67299763bceb4cc69e36e163531c0
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Support CNF Heal/Scale/Rollback operation in v2 LCM API.
|
||||||
|
This feature provides the following CNF operations in
|
||||||
|
v2 LCM API based on ETSI NFV specifications and
|
||||||
|
instantiatiationLevel parameter to determine
|
||||||
|
the initial number of Pods.
|
||||||
|
* Scale VNF task
|
||||||
|
* Heal VNF task
|
||||||
|
* Rollback operation task (instantiate and scale-out)
|
||||||
@@ -338,3 +338,21 @@ class MalformedRequestBody(SolHttpError400):
|
|||||||
|
|
||||||
class InvalidPagingMarker(SolHttpError400):
|
class InvalidPagingMarker(SolHttpError400):
|
||||||
message = _("Paging marker value %(marker)s is invalid.")
|
message = _("Paging marker value %(marker)s is invalid.")
|
||||||
|
|
||||||
|
|
||||||
|
class K8sOperationFailed(SolHttpError422):
|
||||||
|
# title and detail are set in the code from kubernetes operation
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class K8sOperaitionTimeout(SolHttpError422):
|
||||||
|
message = _("Kubernetes operation did not complete within"
|
||||||
|
" the timeout period.")
|
||||||
|
|
||||||
|
|
||||||
|
class K8sResourceNotFound(SolHttpError404):
|
||||||
|
message = _("Kubernetes resource %(rsc_name)s is not found.")
|
||||||
|
|
||||||
|
|
||||||
|
class K8sInvalidManifestFound(SolHttpError400):
|
||||||
|
message = _("Invalid manifest found.")
|
||||||
|
|||||||
@@ -16,12 +16,9 @@
|
|||||||
import os
|
import os
|
||||||
import pickle
|
import pickle
|
||||||
import subprocess
|
import subprocess
|
||||||
from urllib.parse import urlparse
|
|
||||||
import urllib.request as urllib2
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
import yaml
|
|
||||||
|
|
||||||
from tacker.sol_refactored.common import config
|
from tacker.sol_refactored.common import config
|
||||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||||
@@ -396,6 +393,17 @@ class VnfLcmDriverV2(object):
|
|||||||
|
|
||||||
inst.vimConnectionInfo = vim_infos
|
inst.vimConnectionInfo = vim_infos
|
||||||
|
|
||||||
|
# vimType dependent parameter check
|
||||||
|
# NOTE: controller cannot check because vim_info is not identified
|
||||||
|
# to here, although it is better to check in controller.
|
||||||
|
if lcmocc.operationState == v2fields.LcmOperationStateType.STARTING:
|
||||||
|
vim_info = inst_utils.select_vim_info(vim_infos)
|
||||||
|
if (vim_info.vimType == "kubernetes" and
|
||||||
|
not req.get('additionalParams', {}).get(
|
||||||
|
'lcm-kubernetes-def-files')):
|
||||||
|
raise sol_ex.SolValidationError(
|
||||||
|
detail="'lcm-kubernetes-def-files' must be specified")
|
||||||
|
|
||||||
def instantiate_process(self, context, lcmocc, inst, grant_req,
|
def instantiate_process(self, context, lcmocc, inst, grant_req,
|
||||||
grant, vnfd):
|
grant, vnfd):
|
||||||
req = lcmocc.operationParams
|
req = lcmocc.operationParams
|
||||||
@@ -406,7 +414,7 @@ class VnfLcmDriverV2(object):
|
|||||||
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
||||||
driver = openstack.Openstack()
|
driver = openstack.Openstack()
|
||||||
driver.instantiate(req, inst, grant_req, grant, vnfd)
|
driver.instantiate(req, inst, grant_req, grant, vnfd)
|
||||||
elif vim_info.vimType == 'kubernetes': # k8s
|
elif vim_info.vimType == 'kubernetes':
|
||||||
driver = kubernetes.Kubernetes()
|
driver = kubernetes.Kubernetes()
|
||||||
driver.instantiate(req, inst, grant_req, grant, vnfd)
|
driver.instantiate(req, inst, grant_req, grant, vnfd)
|
||||||
else:
|
else:
|
||||||
@@ -422,8 +430,11 @@ class VnfLcmDriverV2(object):
|
|||||||
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
||||||
driver = openstack.Openstack()
|
driver = openstack.Openstack()
|
||||||
driver.instantiate_rollback(req, inst, grant_req, grant, vnfd)
|
driver.instantiate_rollback(req, inst, grant_req, grant, vnfd)
|
||||||
|
elif vim_info.vimType == 'kubernetes':
|
||||||
|
driver = kubernetes.Kubernetes()
|
||||||
|
driver.instantiate_rollback(req, inst, grant_req, grant, vnfd)
|
||||||
else:
|
else:
|
||||||
# only support openstack at the moment
|
# should not occur
|
||||||
raise sol_ex.SolException(sol_detail='not support vim type')
|
raise sol_ex.SolException(sol_detail='not support vim type')
|
||||||
|
|
||||||
def _make_res_def_for_remove_vnfcs(self, inst_info, inst_vnfcs,
|
def _make_res_def_for_remove_vnfcs(self, inst_info, inst_vnfcs,
|
||||||
@@ -561,7 +572,7 @@ class VnfLcmDriverV2(object):
|
|||||||
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
||||||
driver = openstack.Openstack()
|
driver = openstack.Openstack()
|
||||||
driver.terminate(req, inst, grant_req, grant, vnfd)
|
driver.terminate(req, inst, grant_req, grant, vnfd)
|
||||||
elif vim_info.vimType == 'kubernetes': # k8s
|
elif vim_info.vimType == 'kubernetes':
|
||||||
driver = kubernetes.Kubernetes()
|
driver = kubernetes.Kubernetes()
|
||||||
driver.terminate(req, inst, grant_req, grant, vnfd)
|
driver.terminate(req, inst, grant_req, grant, vnfd)
|
||||||
else:
|
else:
|
||||||
@@ -674,8 +685,11 @@ class VnfLcmDriverV2(object):
|
|||||||
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
||||||
driver = openstack.Openstack()
|
driver = openstack.Openstack()
|
||||||
driver.scale(req, inst, grant_req, grant, vnfd)
|
driver.scale(req, inst, grant_req, grant, vnfd)
|
||||||
|
elif vim_info.vimType == 'kubernetes':
|
||||||
|
driver = kubernetes.Kubernetes()
|
||||||
|
driver.scale(req, inst, grant_req, grant, vnfd)
|
||||||
else:
|
else:
|
||||||
# only support openstack at the moment
|
# should not occur
|
||||||
raise sol_ex.SolException(sol_detail='not support vim type')
|
raise sol_ex.SolException(sol_detail='not support vim type')
|
||||||
|
|
||||||
def scale_rollback(self, context, lcmocc, inst, grant_req,
|
def scale_rollback(self, context, lcmocc, inst, grant_req,
|
||||||
@@ -688,8 +702,11 @@ class VnfLcmDriverV2(object):
|
|||||||
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
||||||
driver = openstack.Openstack()
|
driver = openstack.Openstack()
|
||||||
driver.scale_rollback(req, inst, grant_req, grant, vnfd)
|
driver.scale_rollback(req, inst, grant_req, grant, vnfd)
|
||||||
|
elif vim_info.vimType == 'kubernetes':
|
||||||
|
driver = kubernetes.Kubernetes()
|
||||||
|
driver.scale_rollback(req, inst, grant_req, grant, vnfd)
|
||||||
else:
|
else:
|
||||||
# only support openstack at the moment
|
# should not occur
|
||||||
raise sol_ex.SolException(sol_detail='not support vim type')
|
raise sol_ex.SolException(sol_detail='not support vim type')
|
||||||
|
|
||||||
def _modify_from_vnfd_prop(self, inst, vnfd_prop, attr):
|
def _modify_from_vnfd_prop(self, inst, vnfd_prop, attr):
|
||||||
@@ -867,8 +884,11 @@ class VnfLcmDriverV2(object):
|
|||||||
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
||||||
driver = openstack.Openstack()
|
driver = openstack.Openstack()
|
||||||
driver.heal(req, inst, grant_req, grant, vnfd)
|
driver.heal(req, inst, grant_req, grant, vnfd)
|
||||||
|
elif vim_info.vimType == 'kubernetes':
|
||||||
|
driver = kubernetes.Kubernetes()
|
||||||
|
driver.heal(req, inst, grant_req, grant, vnfd)
|
||||||
else:
|
else:
|
||||||
# only support openstack at the moment
|
# should not occur
|
||||||
raise sol_ex.SolException(sol_detail='not support vim type')
|
raise sol_ex.SolException(sol_detail='not support vim type')
|
||||||
|
|
||||||
def change_ext_conn_grant(self, grant_req, req, inst, vnfd):
|
def change_ext_conn_grant(self, grant_req, req, inst, vnfd):
|
||||||
@@ -992,14 +1012,11 @@ class VnfLcmDriverV2(object):
|
|||||||
if not inst_info.obj_attr_is_set('vnfcResourceInfo'):
|
if not inst_info.obj_attr_is_set('vnfcResourceInfo'):
|
||||||
return
|
return
|
||||||
|
|
||||||
if req.additionalParams.get('vdu_params'):
|
vdu_ids = [vdu_param['vdu_id']
|
||||||
vdu_ids = [vdu_param['vdu_id']
|
for vdu_param in req.additionalParams['vdu_params']]
|
||||||
for vdu_param in req.additionalParams['vdu_params']]
|
inst_vnfcs = [inst_vnfc
|
||||||
inst_vnfcs = [inst_vnfc
|
for inst_vnfc in inst_info.vnfcResourceInfo
|
||||||
for inst_vnfc in inst_info.vnfcResourceInfo
|
if inst_vnfc.vduId in vdu_ids]
|
||||||
if inst_vnfc.vduId in vdu_ids]
|
|
||||||
else:
|
|
||||||
inst_vnfcs = inst_info.vnfcResourceInfo
|
|
||||||
|
|
||||||
add_reses = []
|
add_reses = []
|
||||||
rm_reses = []
|
rm_reses = []
|
||||||
@@ -1025,70 +1042,6 @@ class VnfLcmDriverV2(object):
|
|||||||
if rm_reses:
|
if rm_reses:
|
||||||
grant_req.removeResources = rm_reses
|
grant_req.removeResources = rm_reses
|
||||||
|
|
||||||
def _pre_check_for_change_vnfpkg(self, context, req, inst, vnfd):
|
|
||||||
def _get_file_content(file_path):
|
|
||||||
if ((urlparse(file_path).scheme == 'file') or
|
|
||||||
(bool(urlparse(file_path).scheme) and
|
|
||||||
bool(urlparse(file_path).netloc))):
|
|
||||||
with urllib2.urlopen(file_path) as file_object:
|
|
||||||
file_content = file_object.read()
|
|
||||||
else:
|
|
||||||
with open(file_path, 'rb') as file_object:
|
|
||||||
file_content = file_object.read()
|
|
||||||
return file_content
|
|
||||||
|
|
||||||
vnf_artifact_files = vnfd.get_vnf_artifact_files()
|
|
||||||
if req.additionalParams.get('lcm-kubernetes-def-files') is None:
|
|
||||||
target_k8s_files = inst.metadata.get('lcm-kubernetes-def-files')
|
|
||||||
else:
|
|
||||||
target_k8s_files = []
|
|
||||||
new_file_paths = req.additionalParams.get(
|
|
||||||
'lcm-kubernetes-def-files')
|
|
||||||
old_vnfd = self.nfvo_client.get_vnfd(
|
|
||||||
context=context, vnfd_id=inst.vnfdId, all_contents=False)
|
|
||||||
old_file_paths = inst.metadata.get('lcm-kubernetes-def-files')
|
|
||||||
|
|
||||||
for new_file_path in new_file_paths:
|
|
||||||
new_file_infos = [
|
|
||||||
{"kind": content.get('kind'),
|
|
||||||
"name": content.get('metadata', {}).get('name', '')}
|
|
||||||
for content in list(yaml.safe_load_all(
|
|
||||||
_get_file_content(os.path.join(
|
|
||||||
vnfd.csar_dir, new_file_path))))]
|
|
||||||
for old_file_path in old_file_paths:
|
|
||||||
find_flag = False
|
|
||||||
old_file_infos = [
|
|
||||||
{"kind": content.get('kind'),
|
|
||||||
"name": content.get('metadata', {}).get('name', '')}
|
|
||||||
for content in list(yaml.safe_load_all(
|
|
||||||
_get_file_content(os.path.join(
|
|
||||||
old_vnfd.csar_dir, old_file_path))))]
|
|
||||||
resources = [info for info in old_file_infos
|
|
||||||
if info in new_file_infos]
|
|
||||||
if len(resources) != 0:
|
|
||||||
if len(resources) != len(old_file_infos):
|
|
||||||
raise sol_ex.UnmatchedFileException(
|
|
||||||
new_file_path=new_file_path)
|
|
||||||
if 'Deployment' not in [res.get(
|
|
||||||
'kind') for res in resources]:
|
|
||||||
raise sol_ex.UnSupportedKindException(
|
|
||||||
new_file_path=new_file_path)
|
|
||||||
old_file_paths.remove(old_file_path)
|
|
||||||
target_k8s_files.append(new_file_path)
|
|
||||||
find_flag = True
|
|
||||||
break
|
|
||||||
continue
|
|
||||||
if not find_flag:
|
|
||||||
raise sol_ex.NotFoundUpdateFileException(
|
|
||||||
new_file_path=new_file_path)
|
|
||||||
|
|
||||||
target_k8s_files.extend(old_file_paths)
|
|
||||||
if set(target_k8s_files).difference(set(vnf_artifact_files)):
|
|
||||||
diff_files = ','.join(list(set(
|
|
||||||
target_k8s_files).difference(set(vnf_artifact_files))))
|
|
||||||
raise sol_ex.CnfDefinitionNotFound(diff_files=diff_files)
|
|
||||||
return target_k8s_files
|
|
||||||
|
|
||||||
def change_vnfpkg_process(
|
def change_vnfpkg_process(
|
||||||
self, context, lcmocc, inst, grant_req, grant, vnfd):
|
self, context, lcmocc, inst, grant_req, grant, vnfd):
|
||||||
req = lcmocc.operationParams
|
req = lcmocc.operationParams
|
||||||
@@ -1099,14 +1052,9 @@ class VnfLcmDriverV2(object):
|
|||||||
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
if vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3':
|
||||||
driver = openstack.Openstack()
|
driver = openstack.Openstack()
|
||||||
driver.change_vnfpkg(req, inst, grant_req, grant, vnfd)
|
driver.change_vnfpkg(req, inst, grant_req, grant, vnfd)
|
||||||
elif vim_info.vimType == 'kubernetes': # k8s
|
elif vim_info.vimType == 'kubernetes':
|
||||||
target_k8s_files = self._pre_check_for_change_vnfpkg(
|
|
||||||
context, req, inst, vnfd)
|
|
||||||
update_req = req.obj_clone()
|
|
||||||
update_req.additionalParams[
|
|
||||||
'lcm-kubernetes-def-files'] = target_k8s_files
|
|
||||||
driver = kubernetes.Kubernetes()
|
driver = kubernetes.Kubernetes()
|
||||||
driver.change_vnfpkg(update_req, inst, grant_req, grant, vnfd)
|
driver.change_vnfpkg(req, inst, grant_req, grant, vnfd)
|
||||||
else:
|
else:
|
||||||
# should not occur
|
# should not occur
|
||||||
raise sol_ex.SolException(sol_detail='not support vim type')
|
raise sol_ex.SolException(sol_detail='not support vim type')
|
||||||
@@ -1121,10 +1069,10 @@ class VnfLcmDriverV2(object):
|
|||||||
driver = openstack.Openstack()
|
driver = openstack.Openstack()
|
||||||
driver.change_vnfpkg_rollback(
|
driver.change_vnfpkg_rollback(
|
||||||
req, inst, grant_req, grant, vnfd, lcmocc)
|
req, inst, grant_req, grant, vnfd, lcmocc)
|
||||||
elif vim_info.vimType == 'kubernetes': # k8s
|
elif vim_info.vimType == 'kubernetes':
|
||||||
driver = kubernetes.Kubernetes()
|
driver = kubernetes.Kubernetes()
|
||||||
driver.change_vnfpkg_rollback(
|
driver.change_vnfpkg_rollback(
|
||||||
req, inst, grant_req, grant, vnfd, lcmocc)
|
req, inst, grant_req, grant, vnfd)
|
||||||
else:
|
else:
|
||||||
# should not occur
|
# should not occur
|
||||||
raise sol_ex.SolException(sol_detail='not support vim type')
|
raise sol_ex.SolException(sol_detail='not support vim type')
|
||||||
|
|||||||
@@ -447,14 +447,16 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
|
|||||||
raise sol_ex.NotSupportUpgradeType(upgrade_type=upgrade_type)
|
raise sol_ex.NotSupportUpgradeType(upgrade_type=upgrade_type)
|
||||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||||
vdu_params = additional_params.get('vdu_params')
|
vdu_params = additional_params.get('vdu_params')
|
||||||
if (vim_info.vimType == 'ETSINFV.OPENSTACK_KEYSTONE.V_3' and
|
if vdu_params is None:
|
||||||
vdu_params is None):
|
|
||||||
raise sol_ex.SolValidationError(
|
raise sol_ex.SolValidationError(
|
||||||
detail="'vdu_params' must exist in additionalParams")
|
detail="'vdu_params' must exist in additionalParams")
|
||||||
if vdu_params:
|
self._check_vdu_params(inst, vdu_params, vim_info.vimType,
|
||||||
self._check_vdu_params(inst, vdu_params, vim_info.vimType,
|
additional_params.get('lcm-operation-coordinate-new-vnf'),
|
||||||
additional_params.get('lcm-operation-coordinate-new-vnf'),
|
additional_params.get('lcm-operation-coordinate-old-vnf'))
|
||||||
additional_params.get('lcm-operation-coordinate-old-vnf'))
|
if (vim_info.vimType == "kubernetes" and
|
||||||
|
not additional_params.get('lcm-kubernetes-def-files')):
|
||||||
|
raise sol_ex.SolValidationError(
|
||||||
|
detail="'lcm-kubernetes-def-files' must be specified")
|
||||||
|
|
||||||
lcmocc = self._new_lcmocc(id, v2fields.LcmOperationType.CHANGE_VNFPKG,
|
lcmocc = self._new_lcmocc(id, v2fields.LcmOperationType.CHANGE_VNFPKG,
|
||||||
body)
|
body)
|
||||||
@@ -609,9 +611,6 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
|
|||||||
inst = inst_utils.get_inst(context, lcmocc.vnfInstanceId)
|
inst = inst_utils.get_inst(context, lcmocc.vnfInstanceId)
|
||||||
vim_infos = inst.vimConnectionInfo
|
vim_infos = inst.vimConnectionInfo
|
||||||
vim_info = inst_utils.select_vim_info(vim_infos)
|
vim_info = inst_utils.select_vim_info(vim_infos)
|
||||||
if lcmocc.operation != 'CHANGE_VNFPKG' and (
|
|
||||||
vim_info.vimType == 'kubernetes'):
|
|
||||||
raise sol_ex.NotSupportOperationType
|
|
||||||
|
|
||||||
self.conductor_rpc.retry_lcm_op(context, lcmocc.id)
|
self.conductor_rpc.retry_lcm_op(context, lcmocc.id)
|
||||||
|
|
||||||
@@ -641,9 +640,6 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
|
|||||||
inst = inst_utils.get_inst(context, lcmocc.vnfInstanceId)
|
inst = inst_utils.get_inst(context, lcmocc.vnfInstanceId)
|
||||||
vim_infos = inst.vimConnectionInfo
|
vim_infos = inst.vimConnectionInfo
|
||||||
vim_info = inst_utils.select_vim_info(vim_infos)
|
vim_info = inst_utils.select_vim_info(vim_infos)
|
||||||
if lcmocc.operation != 'CHANGE_VNFPKG' and (
|
|
||||||
vim_info.vimType == 'kubernetes'):
|
|
||||||
raise sol_ex.NotSupportOperationType
|
|
||||||
|
|
||||||
self.conductor_rpc.rollback_lcm_op(context, lcmocc.id)
|
self.conductor_rpc.rollback_lcm_op(context, lcmocc.id)
|
||||||
|
|
||||||
|
|||||||
@@ -13,17 +13,14 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from kubernetes import client
|
||||||
import os
|
|
||||||
import pickle
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import uuidutils
|
from oslo_service import loopingcall
|
||||||
|
|
||||||
from tacker.sol_refactored.common import config
|
from tacker.sol_refactored.common import config
|
||||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||||
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
||||||
|
from tacker.sol_refactored.infra_drivers.kubernetes import kubernetes_resource
|
||||||
from tacker.sol_refactored.infra_drivers.kubernetes import kubernetes_utils
|
from tacker.sol_refactored.infra_drivers.kubernetes import kubernetes_utils
|
||||||
from tacker.sol_refactored import objects
|
from tacker.sol_refactored import objects
|
||||||
|
|
||||||
@@ -31,6 +28,10 @@ from tacker.sol_refactored import objects
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONF = config.CONF
|
CONF = config.CONF
|
||||||
|
CHECK_INTERVAL = 10
|
||||||
|
|
||||||
|
TARGET_KIND = {"Pod", "Deployment", "DaemonSet", "StatefulSet", "ReplicaSet"}
|
||||||
|
SCALABLE_KIND = {"Deployment", "ReplicaSet", "StatefulSet"}
|
||||||
|
|
||||||
|
|
||||||
class Kubernetes(object):
|
class Kubernetes(object):
|
||||||
@@ -39,351 +40,435 @@ class Kubernetes(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def instantiate(self, req, inst, grant_req, grant, vnfd):
|
def instantiate(self, req, inst, grant_req, grant, vnfd):
|
||||||
# pre instantiate cnf
|
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||||
target_k8s_files = req.additionalParams.get(
|
with kubernetes_utils.AuthContextManager(vim_info) as acm:
|
||||||
'lcm-kubernetes-def-files')
|
k8s_api_client = acm.init_k8s_api_client()
|
||||||
vnf_artifact_files = vnfd.get_vnf_artifact_files()
|
self._instantiate(req, inst, grant_req, grant, vnfd,
|
||||||
|
k8s_api_client)
|
||||||
|
|
||||||
if vnf_artifact_files is None or set(target_k8s_files).difference(
|
def _instantiate(self, req, inst, grant_req, grant, vnfd, k8s_api_client):
|
||||||
set(vnf_artifact_files)):
|
target_k8s_files = req.additionalParams['lcm-kubernetes-def-files']
|
||||||
if vnf_artifact_files:
|
|
||||||
diff_files = ','.join(list(set(
|
k8s_reses, namespace = self._setup_k8s_reses(
|
||||||
target_k8s_files).difference(set(vnf_artifact_files))))
|
vnfd, target_k8s_files, k8s_api_client,
|
||||||
else:
|
req.additionalParams.get('namespace'))
|
||||||
diff_files = ','.join(target_k8s_files)
|
|
||||||
|
vdus_num = self._get_vdus_num_from_grant_req_res_defs(
|
||||||
|
grant_req.addResources)
|
||||||
|
vdu_reses = self._select_vdu_reses(vnfd, req.flavourId, k8s_reses)
|
||||||
|
|
||||||
|
for vdu_name, vdu_res in vdu_reses.items():
|
||||||
|
if vdu_name not in vdus_num:
|
||||||
|
LOG.debug(f'resource name {vdu_res.name} in the kubernetes'
|
||||||
|
f' manifest does not match the VNFD.')
|
||||||
|
continue
|
||||||
|
|
||||||
|
if vdu_res.kind in SCALABLE_KIND:
|
||||||
|
vdu_res.body['spec']['replicas'] = vdus_num[vdu_name]
|
||||||
|
|
||||||
|
# deploy k8s resources
|
||||||
|
for k8s_res in k8s_reses:
|
||||||
|
if not k8s_res.is_exists():
|
||||||
|
k8s_res.create()
|
||||||
|
|
||||||
|
# wait k8s resource create complete
|
||||||
|
self._wait_k8s_reses_ready(k8s_reses)
|
||||||
|
|
||||||
|
# make instantiated info
|
||||||
|
self._init_instantiated_vnf_info(
|
||||||
|
inst, req.flavourId, target_k8s_files, vdu_reses, namespace)
|
||||||
|
self._update_vnfc_info(inst, k8s_api_client)
|
||||||
|
|
||||||
|
def _setup_k8s_reses(self, vnfd, target_k8s_files, k8s_api_client,
|
||||||
|
namespace):
|
||||||
|
# NOTE: this check should be done in STARTING phase.
|
||||||
|
vnf_artifact_files = vnfd.get_vnf_artifact_files()
|
||||||
|
diff_files = set(target_k8s_files) - set(vnf_artifact_files)
|
||||||
|
if diff_files:
|
||||||
|
diff_files = ','.join(list(diff_files))
|
||||||
raise sol_ex.CnfDefinitionNotFound(diff_files=diff_files)
|
raise sol_ex.CnfDefinitionNotFound(diff_files=diff_files)
|
||||||
|
|
||||||
# get k8s content from yaml file
|
# get k8s content from yaml file
|
||||||
k8s_resources, namespace = kubernetes_utils.get_k8s_json_file(
|
return kubernetes_utils.get_k8s_reses_from_json_files(
|
||||||
req, inst, target_k8s_files, vnfd, 'INSTANTIATE')
|
target_k8s_files, vnfd, k8s_api_client, namespace)
|
||||||
|
|
||||||
# sort k8s resource
|
def instantiate_rollback(self, req, inst, grant_req, grant, vnfd):
|
||||||
sorted_k8s_reses = kubernetes_utils.sort_k8s_resource(
|
|
||||||
k8s_resources, 'INSTANTIATE')
|
|
||||||
|
|
||||||
# deploy k8s resources with sorted resources
|
|
||||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||||
# This is Context Manager for creation and deletion
|
with kubernetes_utils.AuthContextManager(vim_info) as acm:
|
||||||
# of CA certificate temp file
|
k8s_api_client = acm.init_k8s_api_client()
|
||||||
with kubernetes_utils.CaCertFileContextManager(
|
self._instantiate_rollback(req, inst, grant_req, grant, vnfd,
|
||||||
vim_info.interfaceInfo.get('ssl_ca_cert')) as ca_cert_cm:
|
k8s_api_client)
|
||||||
|
|
||||||
# add an item ca_cert_file:file_path into vim_info.interfaceInfo,
|
def _instantiate_rollback(self, req, inst, grant_req, grant, vnfd,
|
||||||
# and will be deleted in KubernetesClient
|
k8s_api_client):
|
||||||
vim_info.interfaceInfo['ca_cert_file'] = ca_cert_cm.file_path
|
target_k8s_files = req.additionalParams['lcm-kubernetes-def-files']
|
||||||
|
|
||||||
k8s_client = kubernetes_utils.KubernetesClient(vim_info)
|
try:
|
||||||
created_k8s_reses = k8s_client.create_k8s_resource(
|
k8s_reses, _ = self._setup_k8s_reses(
|
||||||
sorted_k8s_reses, namespace)
|
vnfd, target_k8s_files, k8s_api_client,
|
||||||
|
req.additionalParams.get('namespace'))
|
||||||
|
except sol_ex.SolException:
|
||||||
|
# it means it failed in a basic check and it failes always.
|
||||||
|
# nothing to do since instantiate failed in it too.
|
||||||
|
return
|
||||||
|
k8s_reses.reverse()
|
||||||
|
|
||||||
# wait k8s resource create complete
|
# delete k8s resources
|
||||||
k8s_client.wait_k8s_res_create(created_k8s_reses)
|
body = client.V1DeleteOptions(propagation_policy='Foreground')
|
||||||
|
self._delete_k8s_resource(k8s_reses, body)
|
||||||
|
|
||||||
# make instantiated info
|
# wait k8s resource delete complete
|
||||||
all_pods = k8s_client.list_namespaced_pods(namespace)
|
self._wait_k8s_reses_deleted(k8s_reses)
|
||||||
self._make_cnf_instantiated_info(
|
|
||||||
req, inst, vnfd, namespace, created_k8s_reses, all_pods)
|
def _delete_k8s_resource(self, k8s_reses, body):
|
||||||
|
for k8s_res in k8s_reses:
|
||||||
|
if k8s_res.is_exists():
|
||||||
|
k8s_res.delete(body)
|
||||||
|
|
||||||
def terminate(self, req, inst, grant_req, grant, vnfd):
|
def terminate(self, req, inst, grant_req, grant, vnfd):
|
||||||
target_k8s_files = inst.metadata.get('lcm-kubernetes-def-files')
|
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||||
|
with kubernetes_utils.AuthContextManager(vim_info) as acm:
|
||||||
|
k8s_api_client = acm.init_k8s_api_client()
|
||||||
|
self._terminate(req, inst, grant_req, grant, vnfd,
|
||||||
|
k8s_api_client)
|
||||||
|
|
||||||
|
def _terminate(self, req, inst, grant_req, grant, vnfd, k8s_api_client):
|
||||||
|
target_k8s_files = inst.instantiatedVnfInfo.metadata[
|
||||||
|
'lcm-kubernetes-def-files']
|
||||||
|
|
||||||
# get k8s content from yaml file
|
# get k8s content from yaml file
|
||||||
k8s_resources, namespace = kubernetes_utils.get_k8s_json_file(
|
namespace = inst.instantiatedVnfInfo.metadata['namespace']
|
||||||
req, inst, target_k8s_files, vnfd, 'TERMINATE')
|
k8s_reses, _ = kubernetes_utils.get_k8s_reses_from_json_files(
|
||||||
|
target_k8s_files, vnfd, k8s_api_client, namespace)
|
||||||
|
k8s_reses.reverse()
|
||||||
|
|
||||||
# sort k8s resource
|
# delete k8s resources
|
||||||
sorted_k8s_reses = kubernetes_utils.sort_k8s_resource(
|
timeout = 0
|
||||||
k8s_resources, 'TERMINATE')
|
if req.terminationType == 'GRACEFUL':
|
||||||
|
timeout = CONF.v2_vnfm.default_graceful_termination_timeout
|
||||||
|
if req.obj_attr_is_set('gracefulTerminationTimeout'):
|
||||||
|
timeout = req.gracefulTerminationTimeout
|
||||||
|
|
||||||
# delete k8s resources with sorted resources
|
body = client.V1DeleteOptions(propagation_policy='Foreground',
|
||||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
grace_period_seconds=timeout)
|
||||||
# This is Context Manager for creation and deletion
|
self._delete_k8s_resource(k8s_reses, body)
|
||||||
# of CA certificate temp file
|
|
||||||
with kubernetes_utils.CaCertFileContextManager(
|
|
||||||
vim_info.interfaceInfo.get('ssl_ca_cert')) as ca_cert_cm:
|
|
||||||
|
|
||||||
# add an item ca_cert_file:file_path into vim_info.interfaceInfo,
|
# wait k8s resource delete complete
|
||||||
# and will be deleted in KubernetesClient
|
self._wait_k8s_reses_deleted(k8s_reses)
|
||||||
vim_info.interfaceInfo['ca_cert_file'] = ca_cert_cm.file_path
|
|
||||||
|
|
||||||
k8s_client = kubernetes_utils.KubernetesClient(vim_info)
|
def _change_vnfpkg_rolling_update(
|
||||||
k8s_client.delete_k8s_resource(req, sorted_k8s_reses, namespace)
|
self, inst, grant_req, grant, vnfd, k8s_api_client,
|
||||||
|
namespace, old_pods_names):
|
||||||
|
|
||||||
# wait k8s resource delete complete
|
vdus_num = self._get_vdus_num_from_grant_req_res_defs(
|
||||||
k8s_client.wait_k8s_res_delete(sorted_k8s_reses, namespace)
|
grant_req.addResources)
|
||||||
|
vdu_reses = []
|
||||||
|
for vdu_name, vdu_num in vdus_num.items():
|
||||||
|
vdu_res = self._get_vdu_res(inst, k8s_api_client, vdu_name)
|
||||||
|
vdu_res.body['spec']['replicas'] = vdu_num
|
||||||
|
vdu_reses.append(vdu_res)
|
||||||
|
|
||||||
|
# apply new deployment
|
||||||
|
for vdu_res in vdu_reses:
|
||||||
|
vdu_res.patch()
|
||||||
|
|
||||||
|
# wait k8s resource update complete
|
||||||
|
self._wait_k8s_reses_updated(
|
||||||
|
vdu_reses, k8s_api_client, namespace, old_pods_names)
|
||||||
|
|
||||||
|
# update cnf instantiated info
|
||||||
|
self._update_vnfc_info(inst, k8s_api_client)
|
||||||
|
|
||||||
def change_vnfpkg(self, req, inst, grant_req, grant, vnfd):
|
def change_vnfpkg(self, req, inst, grant_req, grant, vnfd):
|
||||||
if req.additionalParams.get('upgrade_type') == 'RollingUpdate':
|
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||||
# get deployment name from vnfd
|
with kubernetes_utils.AuthContextManager(vim_info) as acm:
|
||||||
deployment_names, namespace = (
|
k8s_api_client = acm.init_k8s_api_client()
|
||||||
self._get_update_deployment_names_and_namespace(
|
self._change_vnfpkg(req, inst, grant_req, grant, vnfd,
|
||||||
vnfd, req, inst))
|
k8s_api_client)
|
||||||
|
|
||||||
# check deployment exists in kubernetes
|
def _change_vnfpkg(self, req, inst, grant_req, grant, vnfd,
|
||||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
k8s_api_client):
|
||||||
# This is Context Manager for creation and deletion
|
if req.additionalParams['upgrade_type'] == 'RollingUpdate':
|
||||||
# of CA certificate temp file
|
target_k8s_files = req.additionalParams[
|
||||||
with kubernetes_utils.CaCertFileContextManager(
|
'lcm-kubernetes-def-files']
|
||||||
vim_info.interfaceInfo.get('ssl_ca_cert')) as ca_cert_cm:
|
namespace = inst.instantiatedVnfInfo.metadata['namespace']
|
||||||
|
|
||||||
# add an item ca_cert_file:file_path
|
target_vdus = {res_def.resourceTemplateId
|
||||||
# into vim_info.interfaceInfo,
|
for res_def in grant_req.addResources
|
||||||
# and will be deleted in KubernetesClient
|
if res_def.type == 'COMPUTE'}
|
||||||
vim_info.interfaceInfo['ca_cert_file'] = ca_cert_cm.file_path
|
old_pods_names = {vnfc.computeResource.resourceId
|
||||||
|
for vnfc in inst.instantiatedVnfInfo.vnfcResourceInfo
|
||||||
|
if vnfc.vduId in target_vdus}
|
||||||
|
|
||||||
k8s_client = kubernetes_utils.KubernetesClient(vim_info)
|
k8s_reses, _ = self._setup_k8s_reses(
|
||||||
k8s_client.check_deployment_exist(deployment_names, namespace)
|
vnfd, target_k8s_files, k8s_api_client, namespace)
|
||||||
|
|
||||||
# get new deployment body
|
vdu_reses = self._select_vdu_reses(
|
||||||
new_deploy_reses = kubernetes_utils.get_new_deployment_body(
|
vnfd, inst.instantiatedVnfInfo.flavourId, k8s_reses)
|
||||||
req, inst, vnfd, deployment_names,
|
|
||||||
operation='CHANGE_VNFPKG')
|
|
||||||
|
|
||||||
# apply new deployment
|
self._init_instantiated_vnf_info(
|
||||||
k8s_client.update_k8s_resource(new_deploy_reses, namespace)
|
inst, inst.instantiatedVnfInfo.flavourId, target_k8s_files,
|
||||||
|
vdu_reses, namespace)
|
||||||
# wait k8s resource update complete
|
|
||||||
old_pods_names = [vnfc.computeResource.resourceId for vnfc in
|
|
||||||
inst.instantiatedVnfInfo.vnfcResourceInfo]
|
|
||||||
try:
|
|
||||||
k8s_client.wait_k8s_res_update(
|
|
||||||
new_deploy_reses, namespace, old_pods_names)
|
|
||||||
except sol_ex.UpdateK8SResourceFailed as ex:
|
|
||||||
self._update_cnf_instantiated_info(
|
|
||||||
inst, deployment_names,
|
|
||||||
k8s_client.list_namespaced_pods(
|
|
||||||
namespace=namespace))
|
|
||||||
raise ex
|
|
||||||
|
|
||||||
# execute coordinate vnf script
|
|
||||||
try:
|
|
||||||
self._execute_coordinate_vnf_script(
|
|
||||||
req, inst, grant_req, grant, vnfd, 'CHANGE_VNFPKG',
|
|
||||||
namespace, new_deploy_reses)
|
|
||||||
except sol_ex.CoordinateVNFExecutionFailed as ex:
|
|
||||||
self._update_cnf_instantiated_info(
|
|
||||||
inst, deployment_names,
|
|
||||||
k8s_client.list_namespaced_pods(
|
|
||||||
namespace=namespace))
|
|
||||||
raise ex
|
|
||||||
|
|
||||||
# update cnf instantiated info
|
|
||||||
all_pods = k8s_client.list_namespaced_pods(namespace)
|
|
||||||
self._update_cnf_instantiated_info(
|
|
||||||
inst, deployment_names, all_pods)
|
|
||||||
|
|
||||||
|
self._change_vnfpkg_rolling_update(
|
||||||
|
inst, grant_req, grant, vnfd, k8s_api_client, namespace,
|
||||||
|
old_pods_names)
|
||||||
else:
|
else:
|
||||||
# TODO(YiFeng): Blue-Green type will be supported in next version.
|
# not reach here
|
||||||
raise sol_ex.SolException(sol_detail='not support update type')
|
pass
|
||||||
|
|
||||||
inst.vnfdId = req.vnfdId
|
inst.vnfdId = req.vnfdId
|
||||||
if set(req.additionalParams.get(
|
|
||||||
'lcm-kubernetes-def-files')).difference(set(
|
|
||||||
inst.metadata.get('lcm-kubernetes-def-files'))):
|
|
||||||
inst.metadata['lcm-kubernetes-def-files'] = (
|
|
||||||
req.additionalParams.get('lcm-kubernetes-def-files'))
|
|
||||||
|
|
||||||
def change_vnfpkg_rollback(
|
def change_vnfpkg_rollback(self, req, inst, grant_req, grant, vnfd):
|
||||||
self, req, inst, grant_req, grant, vnfd, lcmocc):
|
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||||
if not lcmocc.obj_attr_is_set('resourceChanges'):
|
with kubernetes_utils.AuthContextManager(vim_info) as acm:
|
||||||
return
|
k8s_api_client = acm.init_k8s_api_client()
|
||||||
if req.additionalParams.get('upgrade_type') == 'RollingUpdate':
|
self._change_vnfpkg_rollback(req, inst, grant_req, grant, vnfd,
|
||||||
deployment_names = list({
|
k8s_api_client)
|
||||||
affected_vnfc.metadata['Deployment']['name'] for affected_vnfc
|
|
||||||
in lcmocc.resourceChanges.affectedVnfcs if
|
|
||||||
affected_vnfc.changeType == 'ADDED'})
|
|
||||||
namespace = inst.metadata.get('namespace')
|
|
||||||
|
|
||||||
old_deploy_reses = kubernetes_utils.get_new_deployment_body(
|
def _change_vnfpkg_rollback(self, req, inst, grant_req, grant, vnfd,
|
||||||
req, inst, vnfd, deployment_names,
|
k8s_api_client):
|
||||||
operation='CHANGE_VNFPKG_ROLLBACK')
|
if req.additionalParams['upgrade_type'] == 'RollingUpdate':
|
||||||
|
namespace = inst.instantiatedVnfInfo.metadata['namespace']
|
||||||
|
|
||||||
# apply old deployment
|
original_pods = {vnfc.computeResource.resourceId for vnfc in
|
||||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
inst.instantiatedVnfInfo.vnfcResourceInfo}
|
||||||
# This is Context Manager for creation and deletion
|
all_pods = kubernetes_utils.list_namespaced_pods(
|
||||||
# of CA certificate temp file
|
k8s_api_client, namespace)
|
||||||
with kubernetes_utils.CaCertFileContextManager(
|
current_pods = {pod.metadata.name for pod in all_pods}
|
||||||
vim_info.interfaceInfo.get('ssl_ca_cert')) as ca_cert_cm:
|
old_pods_names = current_pods - original_pods
|
||||||
|
|
||||||
# add an item ca_cert_file:file_path
|
|
||||||
# into vim_info.interfaceInfo,
|
|
||||||
# and will be deleted in KubernetesClient
|
|
||||||
vim_info.interfaceInfo['ca_cert_file'] = ca_cert_cm.file_path
|
|
||||||
|
|
||||||
k8s_client = kubernetes_utils.KubernetesClient(vim_info)
|
|
||||||
k8s_client.update_k8s_resource(old_deploy_reses, namespace)
|
|
||||||
|
|
||||||
# wait k8s resource update complete
|
|
||||||
old_pods_names = [vnfc.computeResource.resourceId for vnfc in
|
|
||||||
inst.instantiatedVnfInfo.vnfcResourceInfo]
|
|
||||||
try:
|
|
||||||
k8s_client.wait_k8s_res_update(
|
|
||||||
old_deploy_reses, namespace, old_pods_names)
|
|
||||||
except sol_ex.UpdateK8SResourceFailed as ex:
|
|
||||||
raise ex
|
|
||||||
|
|
||||||
# execute coordinate vnf script
|
|
||||||
try:
|
|
||||||
self._execute_coordinate_vnf_script(
|
|
||||||
req, inst, grant_req, grant, vnfd,
|
|
||||||
'CHANGE_VNFPKG_ROLLBACK',
|
|
||||||
namespace, old_deploy_reses)
|
|
||||||
except sol_ex.CoordinateVNFExecutionFailed as ex:
|
|
||||||
raise ex
|
|
||||||
|
|
||||||
# update cnf instantiated info
|
|
||||||
all_pods = k8s_client.list_namespaced_pods(namespace)
|
|
||||||
self._update_cnf_instantiated_info(
|
|
||||||
inst, deployment_names, all_pods)
|
|
||||||
|
|
||||||
|
self._change_vnfpkg_rolling_update(
|
||||||
|
inst, grant_req, grant, vnfd, k8s_api_client, namespace,
|
||||||
|
old_pods_names)
|
||||||
else:
|
else:
|
||||||
# TODO(YiFeng): Blue-Green type will be supported in next version.
|
# not reach here
|
||||||
raise sol_ex.SolException(sol_detail='not support update type')
|
pass
|
||||||
|
|
||||||
def _get_update_deployment_names_and_namespace(self, vnfd, req, inst):
|
def heal(self, req, inst, grant_req, grant, vnfd):
|
||||||
vdu_nodes = vnfd.get_vdu_nodes(
|
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||||
flavour_id=inst.instantiatedVnfInfo.flavourId)
|
with kubernetes_utils.AuthContextManager(vim_info) as acm:
|
||||||
|
k8s_api_client = acm.init_k8s_api_client()
|
||||||
|
self._heal(req, inst, grant_req, grant, vnfd, k8s_api_client)
|
||||||
|
|
||||||
if req.additionalParams.get('vdu_params'):
|
def _heal(self, req, inst, grant_req, grant, vnfd, k8s_api_client):
|
||||||
target_vdus = [vdu_param.get('vdu_id') for vdu_param
|
namespace = inst.instantiatedVnfInfo.metadata['namespace']
|
||||||
in req.additionalParams.get('vdu_params')]
|
|
||||||
if None in target_vdus:
|
|
||||||
raise sol_ex.MissingParameterException
|
|
||||||
else:
|
|
||||||
target_vdus = [inst_vnc.vduId for inst_vnc in
|
|
||||||
inst.instantiatedVnfInfo.vnfcResourceInfo]
|
|
||||||
|
|
||||||
deployment_names = [value.get('properties', {}).get('name')
|
# get heal Pod name
|
||||||
for name, value in vdu_nodes.items()
|
vnfc_res_ids = [res_def.resource.resourceId
|
||||||
if name in target_vdus]
|
for res_def in grant_req.removeResources
|
||||||
namespace = inst.metadata.get('namespace')
|
if res_def.type == 'COMPUTE']
|
||||||
|
|
||||||
return deployment_names, namespace
|
target_vnfcs = [vnfc
|
||||||
|
for vnfc in inst.instantiatedVnfInfo.vnfcResourceInfo
|
||||||
|
if vnfc.computeResource.resourceId in vnfc_res_ids]
|
||||||
|
|
||||||
def _make_cnf_instantiated_info(
|
# check running Pod
|
||||||
self, req, inst, vnfd, namespace, created_k8s_reses, all_pods):
|
all_pods = kubernetes_utils.list_namespaced_pods(
|
||||||
flavour_id = req.flavourId
|
k8s_api_client, namespace)
|
||||||
target_kinds = {"Pod", "Deployment", "DaemonSet",
|
current_pods_name = [pod.metadata.name for pod in all_pods]
|
||||||
"StatefulSet", "ReplicaSet"}
|
|
||||||
|
|
||||||
|
old_pods_names = set()
|
||||||
|
vdu_reses = {}
|
||||||
|
for vnfc in target_vnfcs:
|
||||||
|
if vnfc.id not in current_pods_name:
|
||||||
|
# may happen when retry or auto healing
|
||||||
|
msg = f'heal target pod {vnfc.id} is not in the running pod.'
|
||||||
|
LOG.error(msg)
|
||||||
|
continue
|
||||||
|
if vnfc.vduId in vdu_reses:
|
||||||
|
res = vdu_reses[vnfc.vduId]
|
||||||
|
else:
|
||||||
|
res = self._get_vdu_res(inst, k8s_api_client, vnfc.vduId)
|
||||||
|
vdu_reses[vnfc.vduId] = res
|
||||||
|
res.delete_pod(vnfc.id)
|
||||||
|
old_pods_names.add(vnfc.id)
|
||||||
|
|
||||||
|
# wait k8s resource create complete
|
||||||
|
if old_pods_names:
|
||||||
|
self._wait_k8s_reses_updated(list(vdu_reses.values()),
|
||||||
|
k8s_api_client, namespace, old_pods_names)
|
||||||
|
|
||||||
|
# make instantiated info
|
||||||
|
self._update_vnfc_info(inst, k8s_api_client)
|
||||||
|
|
||||||
|
def _scale_k8s_resource(self, inst, vdus_num, k8s_api_client):
|
||||||
|
namespace = inst.instantiatedVnfInfo.metadata['namespace']
|
||||||
|
|
||||||
|
vdu_reses = []
|
||||||
|
for vdu_name, vdu_num in vdus_num.items():
|
||||||
|
vdu_res = self._get_vdu_res(inst, k8s_api_client, vdu_name)
|
||||||
|
if vdu_res.kind not in SCALABLE_KIND:
|
||||||
|
LOG.error(f'scale vdu {vdu_name}'
|
||||||
|
f' is not scalable resource')
|
||||||
|
continue
|
||||||
|
vdu_res.scale(vdu_num)
|
||||||
|
vdu_reses.append(vdu_res)
|
||||||
|
|
||||||
|
# wait k8s resource create complete
|
||||||
|
self._wait_k8s_reses_updated(vdu_reses, k8s_api_client,
|
||||||
|
namespace, old_pods_names=set())
|
||||||
|
|
||||||
|
# make instantiated info
|
||||||
|
self._update_vnfc_info(inst, k8s_api_client)
|
||||||
|
|
||||||
|
def scale(self, req, inst, grant_req, grant, vnfd):
|
||||||
|
|
||||||
|
if req.type == 'SCALE_OUT':
|
||||||
|
vdus_num = self._get_vdus_num_from_grant_req_res_defs(
|
||||||
|
grant_req.addResources)
|
||||||
|
for vdu_name, vdu_num in vdus_num.items():
|
||||||
|
vdus_num[vdu_name] = (self._get_current_vdu_num(inst, vdu_name)
|
||||||
|
+ vdu_num)
|
||||||
|
elif req.type == 'SCALE_IN':
|
||||||
|
vdus_num = self._get_vdus_num_from_grant_req_res_defs(
|
||||||
|
grant_req.removeResources)
|
||||||
|
for vdu_name, vdu_num in vdus_num.items():
|
||||||
|
vdus_num[vdu_name] = (self._get_current_vdu_num(inst, vdu_name)
|
||||||
|
- vdu_num)
|
||||||
|
|
||||||
|
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||||
|
with kubernetes_utils.AuthContextManager(vim_info) as acm:
|
||||||
|
k8s_api_client = acm.init_k8s_api_client()
|
||||||
|
self._scale_k8s_resource(inst, vdus_num, k8s_api_client)
|
||||||
|
|
||||||
|
def scale_rollback(self, req, inst, grant_req, grant, vnfd):
|
||||||
|
|
||||||
|
vdus_num = self._get_vdus_num_from_grant_req_res_defs(
|
||||||
|
grant_req.addResources)
|
||||||
|
for vdu_name, _ in vdus_num.items():
|
||||||
|
vdus_num[vdu_name] = self._get_current_vdu_num(inst, vdu_name)
|
||||||
|
|
||||||
|
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||||
|
with kubernetes_utils.AuthContextManager(vim_info) as acm:
|
||||||
|
k8s_api_client = acm.init_k8s_api_client()
|
||||||
|
self._scale_k8s_resource(inst, vdus_num, k8s_api_client)
|
||||||
|
|
||||||
|
def _get_vdus_num_from_grant_req_res_defs(self, res_defs):
|
||||||
|
vdus_num = {}
|
||||||
|
for res_def in res_defs:
|
||||||
|
if res_def.type == 'COMPUTE':
|
||||||
|
vdus_num.setdefault(res_def.resourceTemplateId, 0)
|
||||||
|
vdus_num[res_def.resourceTemplateId] += 1
|
||||||
|
return vdus_num
|
||||||
|
|
||||||
|
def _get_current_vdu_num(self, inst, vdu):
|
||||||
|
num = 0
|
||||||
|
for vnfc in inst.instantiatedVnfInfo.vnfcResourceInfo:
|
||||||
|
if vnfc.vduId == vdu:
|
||||||
|
num += 1
|
||||||
|
return num
|
||||||
|
|
||||||
|
def _select_vdu_reses(self, vnfd, flavour_id, k8s_reses):
|
||||||
vdu_nodes = vnfd.get_vdu_nodes(flavour_id)
|
vdu_nodes = vnfd.get_vdu_nodes(flavour_id)
|
||||||
vdu_ids = {value.get('properties').get('name'): key
|
vdu_ids = {value.get('properties').get('name'): key
|
||||||
for key, value in vdu_nodes.items()}
|
for key, value in vdu_nodes.items()}
|
||||||
|
return {vdu_ids[res.name]: res
|
||||||
|
for res in k8s_reses
|
||||||
|
if res.kind in TARGET_KIND and res.name in vdu_ids}
|
||||||
|
|
||||||
vnfc_resources = []
|
def _init_instantiated_vnf_info(self, inst, flavour_id, def_files,
|
||||||
for k8s_res in created_k8s_reses:
|
vdu_reses, namespace):
|
||||||
if k8s_res.get('kind', '') not in target_kinds:
|
metadata = {
|
||||||
continue
|
'namespace': namespace,
|
||||||
for pod in all_pods:
|
'lcm-kubernetes-def-files': def_files,
|
||||||
pod_name = pod.metadata.name
|
'vdu_reses': {vdu_name: vdu_res.body
|
||||||
match_result = kubernetes_utils.is_match_pod_naming_rule(
|
for vdu_name, vdu_res in vdu_reses.items()}
|
||||||
k8s_res.get('kind', ''), k8s_res.get('name', ''),
|
}
|
||||||
pod_name)
|
inst.instantiatedVnfInfo = objects.VnfInstanceV2_InstantiatedVnfInfo(
|
||||||
if match_result:
|
|
||||||
metadata = {}
|
|
||||||
metadata[k8s_res.get('kind')] = k8s_res.get('metadata')
|
|
||||||
if k8s_res.get('kind') != 'Pod':
|
|
||||||
metadata['Pod'] = pod.metadata.to_dict()
|
|
||||||
vnfc_resource = objects.VnfcResourceInfoV2(
|
|
||||||
id=uuidutils.generate_uuid(),
|
|
||||||
vduId=vdu_ids.get(k8s_res.get('name', '')),
|
|
||||||
computeResource=objects.ResourceHandle(
|
|
||||||
resourceId=pod_name,
|
|
||||||
vimLevelResourceType=k8s_res.get('kind')
|
|
||||||
),
|
|
||||||
metadata=metadata
|
|
||||||
)
|
|
||||||
vnfc_resources.append(vnfc_resource)
|
|
||||||
|
|
||||||
inst_vnf_info = objects.VnfInstanceV2_InstantiatedVnfInfo(
|
|
||||||
flavourId=flavour_id,
|
flavourId=flavour_id,
|
||||||
vnfState='STARTED',
|
vnfState='STARTED',
|
||||||
|
metadata=metadata
|
||||||
)
|
)
|
||||||
|
|
||||||
if vnfc_resources:
|
def _get_vdu_res(self, inst, k8s_api_client, vdu):
|
||||||
inst_vnf_info.vnfcResourceInfo = vnfc_resources
|
# must be found
|
||||||
# make vnfcInfo
|
res = inst.instantiatedVnfInfo.metadata['vdu_reses'][vdu]
|
||||||
# NOTE: vnfcInfo only exists in SOL002
|
cls = getattr(kubernetes_resource, res['kind'])
|
||||||
inst_vnf_info.vnfcInfo = [
|
return cls(k8s_api_client, res)
|
||||||
objects.VnfcInfoV2(
|
|
||||||
id=f'{vnfc_res_info.vduId}-{vnfc_res_info.id}',
|
|
||||||
vduId=vnfc_res_info.vduId,
|
|
||||||
vnfcResourceInfoId=vnfc_res_info.id,
|
|
||||||
vnfcState='STARTED'
|
|
||||||
)
|
|
||||||
for vnfc_res_info in vnfc_resources
|
|
||||||
]
|
|
||||||
|
|
||||||
inst.instantiatedVnfInfo = inst_vnf_info
|
def _update_vnfc_info(self, inst, k8s_api_client):
|
||||||
inst.metadata = {"namespace": namespace if namespace else None}
|
all_pods = kubernetes_utils.list_namespaced_pods(
|
||||||
inst.metadata['lcm-kubernetes-def-files'] = req.additionalParams.get(
|
k8s_api_client, inst.instantiatedVnfInfo.metadata['namespace'])
|
||||||
'lcm-kubernetes-def-files')
|
vnfc_resources = []
|
||||||
|
for pod in all_pods:
|
||||||
def _execute_coordinate_vnf_script(
|
pod_name = pod.metadata.name
|
||||||
self, req, inst, grant_req, grant, vnfd,
|
for vdu_name, vdu_res in (
|
||||||
operation, namespace, new_deploy_reses):
|
inst.instantiatedVnfInfo.metadata['vdu_reses'].items()):
|
||||||
coordinate_vnf = None
|
if kubernetes_utils.is_match_pod_naming_rule(
|
||||||
if req.obj_attr_is_set('additionalParams'):
|
vdu_res['kind'], vdu_res['metadata']['name'],
|
||||||
if operation == 'CHANGE_VNFPKG':
|
pod_name):
|
||||||
coordinate_vnf = req.additionalParams.get(
|
vnfc_resources.append(objects.VnfcResourceInfoV2(
|
||||||
'lcm-operation-coordinate-new-vnf')
|
id=pod_name,
|
||||||
else:
|
vduId=vdu_name,
|
||||||
coordinate_vnf = req.additionalParams.get(
|
|
||||||
'lcm-operation-coordinate-old-vnf')
|
|
||||||
|
|
||||||
if coordinate_vnf:
|
|
||||||
tmp_csar_dir = vnfd.make_tmp_csar_dir()
|
|
||||||
script_dict = {
|
|
||||||
"request": req.to_dict(),
|
|
||||||
"vnf_instance": inst.to_dict(),
|
|
||||||
"grant_request": grant_req.to_dict(),
|
|
||||||
"grant_response": grant.to_dict(),
|
|
||||||
"tmp_csar_dir": tmp_csar_dir,
|
|
||||||
"k8s_info": {
|
|
||||||
"namespace": namespace,
|
|
||||||
"new_deploy_reses": new_deploy_reses
|
|
||||||
}
|
|
||||||
}
|
|
||||||
script_path = os.path.join(tmp_csar_dir, coordinate_vnf)
|
|
||||||
out = subprocess.run(["python3", script_path],
|
|
||||||
input=pickle.dumps(script_dict),
|
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
||||||
if out.returncode != 0:
|
|
||||||
LOG.error(out)
|
|
||||||
raise sol_ex.CoordinateVNFExecutionFailed
|
|
||||||
|
|
||||||
def _update_cnf_instantiated_info(self, inst, deployment_names, all_pods):
|
|
||||||
error_resource = None
|
|
||||||
for vnfc in inst.instantiatedVnfInfo.vnfcResourceInfo:
|
|
||||||
if (vnfc.computeResource.vimLevelResourceType == 'Deployment'
|
|
||||||
) and (vnfc.metadata.get('Deployment').get(
|
|
||||||
'name') in deployment_names):
|
|
||||||
pods_info = [pod for pod in all_pods if
|
|
||||||
kubernetes_utils.is_match_pod_naming_rule(
|
|
||||||
'Deployment',
|
|
||||||
vnfc.metadata.get('Deployment').get('name'),
|
|
||||||
pod.metadata.name)]
|
|
||||||
if 'Pending' in [pod.status.phase for pod in pods_info] or (
|
|
||||||
'Unknown' in [pod.status.phase for pod in pods_info]):
|
|
||||||
pod_name = [pod.metadata.name for pod in pods_info
|
|
||||||
if pod.status.phase in [
|
|
||||||
'Pending', 'Unknown']][0]
|
|
||||||
error_resource = objects.VnfcResourceInfoV2(
|
|
||||||
id=uuidutils.generate_uuid(),
|
|
||||||
vduId=vnfc.vduId,
|
|
||||||
computeResource=objects.ResourceHandle(
|
computeResource=objects.ResourceHandle(
|
||||||
resourceId=pod_name,
|
resourceId=pod_name,
|
||||||
vimLevelResourceType='Deployment'
|
vimLevelResourceType=vdu_res['kind']
|
||||||
),
|
),
|
||||||
metadata={'Deployment': vnfc.metadata.get(
|
# lcmocc_utils.update_lcmocc assumes its existence
|
||||||
'Deployment')}
|
metadata={}
|
||||||
)
|
))
|
||||||
continue
|
|
||||||
pod_info = pods_info.pop(-1)
|
|
||||||
vnfc.id = uuidutils.generate_uuid()
|
|
||||||
vnfc.computeResource.resourceId = pod_info.metadata.name
|
|
||||||
vnfc.metadata['Pod'] = pod_info.metadata.to_dict()
|
|
||||||
all_pods.remove(pod_info)
|
|
||||||
|
|
||||||
if error_resource:
|
inst.instantiatedVnfInfo.vnfcResourceInfo = vnfc_resources
|
||||||
inst.instantiatedVnfInfo.vnfcResourceInfo.append(error_resource)
|
|
||||||
|
# make vnfcInfo
|
||||||
|
# NOTE: vnfcInfo only exists in SOL002
|
||||||
|
inst.instantiatedVnfInfo.vnfcInfo = [
|
||||||
|
objects.VnfcInfoV2(
|
||||||
|
id=f'{vnfc_res_info.vduId}-{vnfc_res_info.id}',
|
||||||
|
vduId=vnfc_res_info.vduId,
|
||||||
|
vnfcResourceInfoId=vnfc_res_info.id,
|
||||||
|
vnfcState='STARTED'
|
||||||
|
)
|
||||||
|
for vnfc_res_info in vnfc_resources
|
||||||
|
]
|
||||||
|
|
||||||
|
def _check_status(self, check_func, *args):
|
||||||
|
timer = loopingcall.FixedIntervalWithTimeoutLoopingCall(
|
||||||
|
check_func, *args)
|
||||||
|
try:
|
||||||
|
timer.start(interval=CHECK_INTERVAL,
|
||||||
|
timeout=CONF.v2_vnfm.kubernetes_vim_rsc_wait_timeout).wait()
|
||||||
|
except loopingcall.LoopingCallTimeOut:
|
||||||
|
raise sol_ex.K8sOperaitionTimeout()
|
||||||
|
|
||||||
|
def _wait_k8s_reses_ready(self, k8s_reses):
|
||||||
|
def _check_ready(check_reses):
|
||||||
|
ok_reses = {res for res in check_reses if res.is_ready()}
|
||||||
|
check_reses -= ok_reses
|
||||||
|
if not check_reses:
|
||||||
|
raise loopingcall.LoopingCallDone()
|
||||||
|
|
||||||
|
check_reses = set(k8s_reses)
|
||||||
|
self._check_status(_check_ready, check_reses)
|
||||||
|
|
||||||
|
def _wait_k8s_reses_deleted(self, k8s_reses):
|
||||||
|
def _check_deleted(check_reses):
|
||||||
|
ok_reses = {res for res in check_reses if not res.is_exists()}
|
||||||
|
check_reses -= ok_reses
|
||||||
|
if not check_reses:
|
||||||
|
raise loopingcall.LoopingCallDone()
|
||||||
|
|
||||||
|
check_reses = set(k8s_reses)
|
||||||
|
self._check_status(_check_deleted, check_reses)
|
||||||
|
|
||||||
|
def _wait_k8s_reses_updated(self, k8s_reses, k8s_api_client, namespace,
|
||||||
|
old_pods_names):
|
||||||
|
def _check_update(check_reses, k8s_api_client, namespace,
|
||||||
|
old_pods_names):
|
||||||
|
ok_reses = set()
|
||||||
|
all_pods = kubernetes_utils.list_namespaced_pods(
|
||||||
|
k8s_api_client, namespace)
|
||||||
|
for res in check_reses:
|
||||||
|
pods_info = [pod for pod in all_pods
|
||||||
|
if kubernetes_utils.is_match_pod_naming_rule(
|
||||||
|
res.kind, res.name, pod.metadata.name)]
|
||||||
|
if res.is_update(pods_info, old_pods_names):
|
||||||
|
ok_reses.add(res)
|
||||||
|
check_reses -= ok_reses
|
||||||
|
if not check_reses:
|
||||||
|
raise loopingcall.LoopingCallDone()
|
||||||
|
|
||||||
|
check_reses = set(k8s_reses)
|
||||||
|
self._check_status(_check_update, check_reses, k8s_api_client,
|
||||||
|
namespace, old_pods_names)
|
||||||
|
|||||||
@@ -0,0 +1,586 @@
|
|||||||
|
# Copyright (C) 2022 Nippon Telegraph and Telephone Corporation
|
||||||
|
# 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 inspect
|
||||||
|
import ipaddress
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
|
||||||
|
from kubernetes import client
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from tacker.sol_refactored.common import config
|
||||||
|
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
CHECK_INTERVAL = 10
|
||||||
|
|
||||||
|
|
||||||
|
def convert(name):
|
||||||
|
return re.sub('([A-Z])', lambda x: '_' + x.group(1).lower(), name)
|
||||||
|
|
||||||
|
|
||||||
|
class CommonResource:
|
||||||
|
# Default API Class
|
||||||
|
api_class = client.CoreV1Api
|
||||||
|
|
||||||
|
def __init__(self, k8s_api_client, k8s_res):
|
||||||
|
self.k8s_api_client = k8s_api_client
|
||||||
|
self.k8s_client = self.api_class(api_client=self.k8s_api_client)
|
||||||
|
self.kind = k8s_res['kind']
|
||||||
|
self.namespace = k8s_res.get('metadata', {}).get('namespace')
|
||||||
|
self.name = k8s_res.get('metadata', {}).get('name')
|
||||||
|
self.metadata = k8s_res.get('metadata', {})
|
||||||
|
self.body = k8s_res
|
||||||
|
|
||||||
|
def create(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def delete(self, body):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def is_exists(self):
|
||||||
|
try:
|
||||||
|
return self.read() is not None
|
||||||
|
except sol_ex.K8sResourceNotFound:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class NamespacedResource(CommonResource):
|
||||||
|
|
||||||
|
def create(self):
|
||||||
|
method = getattr(self.k8s_client,
|
||||||
|
'create_namespaced' + convert(self.__class__.__name__))
|
||||||
|
try:
|
||||||
|
method(namespace=self.namespace, body=self.body)
|
||||||
|
except Exception as ex:
|
||||||
|
operation = inspect.currentframe().f_code.co_name
|
||||||
|
sol_title = "%s failed" % operation
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
method = getattr(self.k8s_client,
|
||||||
|
'read_namespaced' + convert(self.__class__.__name__))
|
||||||
|
try:
|
||||||
|
return method(namespace=self.namespace, name=self.name)
|
||||||
|
except Exception as ex:
|
||||||
|
if isinstance(ex, client.ApiException) and ex.status == 404:
|
||||||
|
raise sol_ex.K8sResourceNotFound(rsc_name=self.name)
|
||||||
|
else:
|
||||||
|
operation = inspect.currentframe().f_code.co_name
|
||||||
|
sol_title = "%s failed" % operation
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
|
||||||
|
def delete(self, body):
|
||||||
|
method = getattr(self.k8s_client,
|
||||||
|
'delete_namespaced' + convert(self.__class__.__name__))
|
||||||
|
try:
|
||||||
|
method(namespace=self.namespace, name=self.name, body=body)
|
||||||
|
except Exception as ex:
|
||||||
|
operation = inspect.currentframe().f_code.co_name
|
||||||
|
sol_title = "%s failed" % operation
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
method = getattr(self.k8s_client,
|
||||||
|
'patch_namespaced' + convert(self.__class__.__name__))
|
||||||
|
try:
|
||||||
|
method(namespace=self.namespace, name=self.name, body=self.body)
|
||||||
|
except Exception as ex:
|
||||||
|
operation = inspect.currentframe().f_code.co_name
|
||||||
|
sol_title = "%s failed" % operation
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
|
||||||
|
def scale(self, scale_replicas):
|
||||||
|
body = {'spec': {'replicas': scale_replicas}}
|
||||||
|
method = getattr(self.k8s_client,
|
||||||
|
'patch_namespaced' + convert(self.__class__.__name__) + '_scale')
|
||||||
|
try:
|
||||||
|
method(namespace=self.namespace, name=self.name, body=body)
|
||||||
|
self.body['spec']['replicas'] = scale_replicas
|
||||||
|
except Exception as ex:
|
||||||
|
operation = inspect.currentframe().f_code.co_name
|
||||||
|
sol_title = "%s failed" % operation
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
|
||||||
|
def delete_pod(self, pod_name):
|
||||||
|
body = client.V1DeleteOptions(propagation_policy='Foreground')
|
||||||
|
v1 = client.CoreV1Api(api_client=self.k8s_api_client)
|
||||||
|
try:
|
||||||
|
v1.delete_namespaced_pod(namespace=self.namespace,
|
||||||
|
name=pod_name, body=body)
|
||||||
|
except Exception as ex:
|
||||||
|
operation = inspect.currentframe().f_code.co_name
|
||||||
|
sol_title = "%s failed" % operation
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
|
||||||
|
|
||||||
|
class ClusterResource(CommonResource):
|
||||||
|
|
||||||
|
def create(self):
|
||||||
|
method = getattr(self.k8s_client,
|
||||||
|
'create' + convert(self.__class__.__name__))
|
||||||
|
try:
|
||||||
|
method(body=self.body)
|
||||||
|
except Exception as ex:
|
||||||
|
operation = inspect.currentframe().f_code.co_name
|
||||||
|
sol_title = "%s failed" % operation
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
method = getattr(self.k8s_client,
|
||||||
|
'read' + convert(self.__class__.__name__))
|
||||||
|
try:
|
||||||
|
return method(name=self.name)
|
||||||
|
except Exception as ex:
|
||||||
|
if isinstance(ex, client.ApiException) and ex.status == 404:
|
||||||
|
raise sol_ex.K8sResourceNotFound(rsc_name=self.name)
|
||||||
|
else:
|
||||||
|
operation = inspect.currentframe().f_code.co_name
|
||||||
|
sol_title = "%s failed" % operation
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
|
||||||
|
def delete(self, body):
|
||||||
|
method = getattr(self.k8s_client,
|
||||||
|
'delete' + convert(self.__class__.__name__))
|
||||||
|
try:
|
||||||
|
method(name=self.name, body=body)
|
||||||
|
except Exception as ex:
|
||||||
|
operation = inspect.currentframe().f_code.co_name
|
||||||
|
sol_title = "%s failed" % operation
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
|
||||||
|
def patch(self):
|
||||||
|
method = getattr(self.k8s_client,
|
||||||
|
'patch' + convert(self.__class__.__name__))
|
||||||
|
try:
|
||||||
|
method(namespace=self.namespace, name=self.name, body=self.body)
|
||||||
|
except Exception as ex:
|
||||||
|
operation = inspect.currentframe().f_code.co_name
|
||||||
|
sol_title = "%s failed" % operation
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticationResource(CommonResource):
|
||||||
|
def create(self):
|
||||||
|
method = getattr(self.k8s_client,
|
||||||
|
'create' + convert(self.__class__.__name__))
|
||||||
|
try:
|
||||||
|
method(body=self.body)
|
||||||
|
except Exception as ex:
|
||||||
|
operation = inspect.currentframe().f_code.co_name
|
||||||
|
sol_title = "%s failed" % operation
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentStatus(CommonResource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigMap(NamespacedResource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Container(CommonResource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class LimitRange(NamespacedResource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Namespace(ClusterResource):
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
namespace_info = self.read()
|
||||||
|
return (namespace_info.status.phase and
|
||||||
|
namespace_info.status.phase == 'Active')
|
||||||
|
|
||||||
|
|
||||||
|
class Node(ClusterResource):
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
node_info = self.read()
|
||||||
|
for condition in node_info.status.conditions:
|
||||||
|
if condition.type == 'Ready' and condition.status == 'True':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class PersistentVolume(ClusterResource):
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
volume_info = self.read()
|
||||||
|
return (volume_info.status.phase and
|
||||||
|
volume_info.status.phase in ['Available', 'Bound'])
|
||||||
|
|
||||||
|
|
||||||
|
class PersistentVolumeClaim(NamespacedResource):
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
claim_info = self.read()
|
||||||
|
return claim_info.status.phase and claim_info.status.phase == 'Bound'
|
||||||
|
|
||||||
|
|
||||||
|
class Pod(NamespacedResource):
|
||||||
|
|
||||||
|
def delete_pod(self, pod_name):
|
||||||
|
# Get Pod information before deletition
|
||||||
|
pod_info = self.read()
|
||||||
|
body = client.V1DeleteOptions(propagation_policy='Foreground')
|
||||||
|
self.delete(body=body)
|
||||||
|
|
||||||
|
timeout = CONF.v2_vnfm.kubernetes_vim_rsc_wait_timeout
|
||||||
|
max_check_count = (timeout / CHECK_INTERVAL)
|
||||||
|
check_count = 0
|
||||||
|
while (check_count < max_check_count):
|
||||||
|
if not self.is_exists():
|
||||||
|
break
|
||||||
|
check_count += 1
|
||||||
|
time.sleep(CHECK_INTERVAL)
|
||||||
|
else:
|
||||||
|
raise sol_ex.K8sOperaitionTimeout()
|
||||||
|
|
||||||
|
create_info = client.V1Pod(metadata=self.metadata, spec=pod_info.spec)
|
||||||
|
self.k8s_client.create_namespaced_pod(
|
||||||
|
namespace=self.namespace, body=create_info)
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
pod_info = self.read()
|
||||||
|
return pod_info.status.phase and pod_info.status.phase == 'Running'
|
||||||
|
|
||||||
|
def is_update(self, pods_info, old_pods_names):
|
||||||
|
return self.is_ready()
|
||||||
|
|
||||||
|
|
||||||
|
class PodTemplate(NamespacedResource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceQuota(NamespacedResource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Secret(NamespacedResource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Service(NamespacedResource):
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
|
||||||
|
def _check_is_ip(ip_addr):
|
||||||
|
try:
|
||||||
|
ipaddress.ip_address(ip_addr)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
service_info = self.read()
|
||||||
|
if service_info.spec.cluster_ip in ['', None] or _check_is_ip(
|
||||||
|
service_info.spec.cluster_ip):
|
||||||
|
try:
|
||||||
|
endpoint_info = self.k8s_client.read_namespaced_endpoints(
|
||||||
|
namespace=self.namespace, name=self.name)
|
||||||
|
if endpoint_info:
|
||||||
|
return True
|
||||||
|
except Exception as ex:
|
||||||
|
sol_title = "Read Endpoint failed"
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceAccount(NamespacedResource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Volume(CommonResource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ControllerRevision(NamespacedResource):
|
||||||
|
api_class = client.AppsV1Api
|
||||||
|
|
||||||
|
def delete(self, body):
|
||||||
|
body = client.V1DeleteOptions(
|
||||||
|
propagation_policy='Background')
|
||||||
|
try:
|
||||||
|
self.k8s_client.delete_namespaced_controller_revision(
|
||||||
|
namespace=self.namespace, name=self.name, body=body)
|
||||||
|
except Exception as ex:
|
||||||
|
operation = inspect.currentframe().f_code.co_name
|
||||||
|
sol_title = "%s failed" % operation
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
|
||||||
|
|
||||||
|
class DaemonSet(NamespacedResource):
|
||||||
|
api_class = client.AppsV1Api
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
daemonset_info = self.read()
|
||||||
|
return (daemonset_info.status.desired_number_scheduled and
|
||||||
|
(daemonset_info.status.desired_number_scheduled ==
|
||||||
|
daemonset_info.status.number_ready))
|
||||||
|
|
||||||
|
def is_update(self, pods_info, old_pods_names):
|
||||||
|
daemonset_info = self.read()
|
||||||
|
replicas = daemonset_info.status.desired_number_scheduled
|
||||||
|
|
||||||
|
for pod_info in pods_info:
|
||||||
|
if (pod_info.status.phase != 'Running' or
|
||||||
|
pod_info.metadata.name in old_pods_names):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return len(pods_info) == replicas
|
||||||
|
|
||||||
|
|
||||||
|
class Deployment(NamespacedResource):
|
||||||
|
api_class = client.AppsV1Api
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
deployment_info = self.read()
|
||||||
|
return (deployment_info.status.replicas and
|
||||||
|
(deployment_info.status.replicas ==
|
||||||
|
deployment_info.status.ready_replicas))
|
||||||
|
|
||||||
|
def is_update(self, pods_info, old_pods_names):
|
||||||
|
deployment_info = self.read()
|
||||||
|
replicas = deployment_info.spec.replicas
|
||||||
|
|
||||||
|
for pod_info in pods_info:
|
||||||
|
if (pod_info.status.phase != 'Running' or
|
||||||
|
pod_info.metadata.name in old_pods_names):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return len(pods_info) == replicas
|
||||||
|
|
||||||
|
|
||||||
|
class ReplicaSet(NamespacedResource):
|
||||||
|
api_class = client.AppsV1Api
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
replicaset_info = self.read()
|
||||||
|
return (replicaset_info.status.replicas and
|
||||||
|
(replicaset_info.status.replicas ==
|
||||||
|
replicaset_info.status.ready_replicas))
|
||||||
|
|
||||||
|
def is_update(self, pods_info, old_pods_names):
|
||||||
|
replicaset_info = self.read()
|
||||||
|
replicas = replicaset_info.spec.replicas
|
||||||
|
|
||||||
|
for pod_info in pods_info:
|
||||||
|
if (pod_info.status.phase != 'Running' or
|
||||||
|
pod_info.metadata.name in old_pods_names):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return len(pods_info) == replicas
|
||||||
|
|
||||||
|
|
||||||
|
class StatefulSet(NamespacedResource):
|
||||||
|
api_class = client.AppsV1Api
|
||||||
|
|
||||||
|
def delete(self, body):
|
||||||
|
pvcs_for_delete = []
|
||||||
|
try:
|
||||||
|
resp_read_sfs = self.read()
|
||||||
|
sfs_spec = resp_read_sfs.spec
|
||||||
|
volume_claim_templates = sfs_spec.volume_claim_templates
|
||||||
|
|
||||||
|
v1 = client.CoreV1Api(api_client=self.k8s_api_client)
|
||||||
|
resps_pvc = v1.list_namespaced_persistent_volume_claim(
|
||||||
|
namespace=self.namespace)
|
||||||
|
pvcs = resps_pvc.items
|
||||||
|
for volume_claim_template in volume_claim_templates:
|
||||||
|
pvc_template_metadata = volume_claim_template.metadata
|
||||||
|
match_pattern = '-'.join(
|
||||||
|
[pvc_template_metadata.name, self.name, ""])
|
||||||
|
|
||||||
|
for pvc in pvcs:
|
||||||
|
pvc_metadata = pvc.metadata
|
||||||
|
pvc_name = pvc_metadata.name
|
||||||
|
match_result = re.match(
|
||||||
|
match_pattern + '[0-9]+$', pvc_name)
|
||||||
|
if match_result is not None:
|
||||||
|
pvcs_for_delete.append(pvc_name)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.k8s_client.delete_namespaced_stateful_set(
|
||||||
|
namespace=self.namespace, name=self.name, body=body)
|
||||||
|
|
||||||
|
for delete_pvc_name in pvcs_for_delete:
|
||||||
|
try:
|
||||||
|
v1 = client.CoreV1Api(api_client=self.k8s_api_client)
|
||||||
|
v1.delete_namespaced_persistent_volume_claim(
|
||||||
|
name=delete_pvc_name, namespace=self.namespace,
|
||||||
|
body=body)
|
||||||
|
except Exception as ex:
|
||||||
|
operation = inspect.currentframe().f_code.co_name
|
||||||
|
sol_title = "%s failed" % operation
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
except Exception as ex:
|
||||||
|
operation = inspect.currentframe().f_code.co_name
|
||||||
|
sol_title = "%s failed" % operation
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
statefulset_info = self.read()
|
||||||
|
replicas = statefulset_info.status.replicas
|
||||||
|
if replicas == statefulset_info.status.ready_replicas:
|
||||||
|
for i in range(0, statefulset_info.spec.replicas):
|
||||||
|
volume_claim_templates = (
|
||||||
|
statefulset_info.spec.volume_claim_templates)
|
||||||
|
for volume_claim_template in volume_claim_templates:
|
||||||
|
pvc_name = "-".join(
|
||||||
|
[volume_claim_template.metadata.name,
|
||||||
|
self.name, str(i)])
|
||||||
|
v1 = client.CoreV1Api(api_client=self.k8s_api_client)
|
||||||
|
persistent_volume_claim = (
|
||||||
|
v1.read_namespaced_persistent_volume_claim(
|
||||||
|
namespace=self.namespace, name=pvc_name))
|
||||||
|
if persistent_volume_claim.status.phase != 'Bound':
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_update(self, pods_info, old_pods_names):
|
||||||
|
statefulset_info = self.read()
|
||||||
|
replicas = statefulset_info.spec.replicas
|
||||||
|
|
||||||
|
for pod_info in pods_info:
|
||||||
|
if pod_info.status.phase != 'Running':
|
||||||
|
return False
|
||||||
|
|
||||||
|
return len(pods_info) == replicas
|
||||||
|
|
||||||
|
|
||||||
|
class HorizontalPodAutoscaler(NamespacedResource):
|
||||||
|
api_class = client.AutoscalingV1Api
|
||||||
|
|
||||||
|
|
||||||
|
class Job(NamespacedResource):
|
||||||
|
api_class = client.BatchV1Api
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
job_info = self.read()
|
||||||
|
return (job_info.spec.completions and
|
||||||
|
job_info.spec.completions == job_info.status.succeeded)
|
||||||
|
|
||||||
|
|
||||||
|
class APIService(ClusterResource):
|
||||||
|
api_class = client.ApiregistrationV1Api
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
api_service_info = self.read()
|
||||||
|
for condition in api_service_info.status.conditions:
|
||||||
|
if condition.type == 'Available':
|
||||||
|
if condition.status != 'True':
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class TokenReview(AuthenticationResource):
|
||||||
|
api_class = client.AuthenticationV1Api
|
||||||
|
|
||||||
|
|
||||||
|
class LocalSubjectAccessReview(AuthenticationResource):
|
||||||
|
api_class = client.AuthorizationV1Api
|
||||||
|
|
||||||
|
def create(self):
|
||||||
|
try:
|
||||||
|
self.k8s_client.create_namespaced_local_subject_access_review(
|
||||||
|
namespace=self.namespace, body=self.body)
|
||||||
|
except Exception as ex:
|
||||||
|
operation = inspect.currentframe().f_code.co_name
|
||||||
|
sol_title = "%s failed" % operation
|
||||||
|
raise sol_ex.K8sOperationFailed(sol_title=sol_title,
|
||||||
|
sol_detail=str(ex))
|
||||||
|
|
||||||
|
|
||||||
|
class SelfSubjectAccessReview(AuthenticationResource):
|
||||||
|
api_class = client.AuthorizationV1Api
|
||||||
|
|
||||||
|
|
||||||
|
class SelfSubjectRulesReview(AuthenticationResource):
|
||||||
|
api_class = client.AuthorizationV1Api
|
||||||
|
|
||||||
|
|
||||||
|
class SubjectAccessReview(AuthenticationResource):
|
||||||
|
api_class = client.AuthorizationV1Api
|
||||||
|
|
||||||
|
|
||||||
|
class Lease(NamespacedResource):
|
||||||
|
api_class = client.CoordinationV1Api
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkPolicy(NamespacedResource):
|
||||||
|
api_class = client.NetworkingV1Api
|
||||||
|
|
||||||
|
|
||||||
|
class ClusterRole(ClusterResource):
|
||||||
|
api_class = client.RbacAuthorizationV1Api
|
||||||
|
|
||||||
|
|
||||||
|
class ClusterRoleBinding(ClusterResource):
|
||||||
|
api_class = client.RbacAuthorizationV1Api
|
||||||
|
|
||||||
|
|
||||||
|
class Role(NamespacedResource):
|
||||||
|
api_class = client.RbacAuthorizationV1Api
|
||||||
|
|
||||||
|
|
||||||
|
class RoleBinding(NamespacedResource):
|
||||||
|
api_class = client.RbacAuthorizationV1Api
|
||||||
|
|
||||||
|
|
||||||
|
class PriorityClass(ClusterResource):
|
||||||
|
api_class = client.SchedulingV1Api
|
||||||
|
|
||||||
|
|
||||||
|
class StorageClass(ClusterResource):
|
||||||
|
api_class = client.StorageV1Api
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeAttachment(ClusterResource):
|
||||||
|
api_class = client.StorageV1Api
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
volume_info = self.read()
|
||||||
|
return volume_info.status.attached
|
||||||
@@ -13,28 +13,22 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import copy
|
|
||||||
import ipaddress
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
import urllib.request as urllib2
|
import urllib.request as urllib2
|
||||||
|
|
||||||
from kubernetes import client
|
from kubernetes import client
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_service import loopingcall
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from tacker.sol_refactored.common import config
|
|
||||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||||
from tacker.sol_refactored.objects.v2 import fields as v2fields
|
from tacker.sol_refactored.infra_drivers.kubernetes import kubernetes_resource
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
CONF = config.CONF
|
|
||||||
CHECK_INTERVAL = 10
|
|
||||||
SUPPORTED_NAMESPACE_KINDS = [
|
SUPPORTED_NAMESPACE_KINDS = [
|
||||||
"Pod",
|
"Pod",
|
||||||
"Binding",
|
"Binding",
|
||||||
@@ -59,478 +53,6 @@ SUPPORTED_NAMESPACE_KINDS = [
|
|||||||
"RoleBinding",
|
"RoleBinding",
|
||||||
"Role"
|
"Role"
|
||||||
]
|
]
|
||||||
RESOURCE_CREATION_ORDER = [
|
|
||||||
"StorageClass",
|
|
||||||
"PersistentVolume",
|
|
||||||
"PriorityClass",
|
|
||||||
"Namespace",
|
|
||||||
"LimitRange",
|
|
||||||
"ResourceQuota",
|
|
||||||
"HorizontalPodAutoscaler",
|
|
||||||
"NetworkPolicy",
|
|
||||||
"Service",
|
|
||||||
"Endpoints",
|
|
||||||
"PersistentVolumeClaim",
|
|
||||||
"ConfigMap",
|
|
||||||
"Secret",
|
|
||||||
"Pod",
|
|
||||||
"Binding",
|
|
||||||
"StatefulSet",
|
|
||||||
"Job",
|
|
||||||
"Deployment",
|
|
||||||
"DaemonSet",
|
|
||||||
]
|
|
||||||
STATUS_CHECK_RES = [
|
|
||||||
"Pod",
|
|
||||||
"Service",
|
|
||||||
"PersistentVolumeClaim",
|
|
||||||
"Namespace",
|
|
||||||
"Node",
|
|
||||||
"PersistentVolume",
|
|
||||||
"APIService",
|
|
||||||
"DaemonSet",
|
|
||||||
"Deployment",
|
|
||||||
"ReplicaSet",
|
|
||||||
"StatefulSet",
|
|
||||||
"Job",
|
|
||||||
"VolumeAttachment"
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class KubernetesClient(object):
|
|
||||||
|
|
||||||
def __init__(self, vim_info):
|
|
||||||
self.k8s_api_client = init_k8s_api_client(vim_info)
|
|
||||||
self.k8s_clients = get_k8s_clients(self.k8s_api_client)
|
|
||||||
|
|
||||||
def create_k8s_resource(self, sorted_k8s_reses, namespace):
|
|
||||||
created_k8s_reses = []
|
|
||||||
|
|
||||||
for k8s_res in sorted_k8s_reses:
|
|
||||||
kind = k8s_res.get('kind', '')
|
|
||||||
api_version = k8s_res.get('apiVersion', '')
|
|
||||||
name = k8s_res.get('metadata', {}).get('name', '')
|
|
||||||
metadata = k8s_res.get('metadata', {})
|
|
||||||
body = k8s_res
|
|
||||||
k8s_client = self.k8s_clients[api_version]
|
|
||||||
try:
|
|
||||||
if kind in SUPPORTED_NAMESPACE_KINDS:
|
|
||||||
k8s_method = getattr(
|
|
||||||
k8s_client, f"create_namespaced_{convert(kind)}")
|
|
||||||
k8s_method(namespace=namespace, body=body)
|
|
||||||
create_k8s_res = {
|
|
||||||
"api_version": api_version,
|
|
||||||
"namespace": namespace,
|
|
||||||
"kind": kind,
|
|
||||||
"name": name,
|
|
||||||
"metadata": metadata,
|
|
||||||
"status": "CREATE_IN_PROCESS"
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
k8s_method = getattr(
|
|
||||||
k8s_client, f"create_{convert(kind)}")
|
|
||||||
k8s_method(body=body)
|
|
||||||
create_k8s_res = {
|
|
||||||
"api_version": api_version,
|
|
||||||
"kind": kind,
|
|
||||||
"name": name,
|
|
||||||
"metadata": metadata,
|
|
||||||
"status": "CREATE_IN_PROCESS"
|
|
||||||
}
|
|
||||||
created_k8s_reses.append(create_k8s_res)
|
|
||||||
except Exception as ex:
|
|
||||||
LOG.error(ex)
|
|
||||||
raise sol_ex.ExecuteK8SResourceCreateApiFailed
|
|
||||||
return created_k8s_reses
|
|
||||||
|
|
||||||
def delete_k8s_resource(self, req, sorted_k8s_reses, namespace):
|
|
||||||
if req.terminationType:
|
|
||||||
if req.terminationType == 'GRACEFUL' and req.obj_attr_is_set(
|
|
||||||
'gracefulTerminationTimeout'):
|
|
||||||
body = client.V1DeleteOptions(
|
|
||||||
propagation_policy='Foreground',
|
|
||||||
grace_period_seconds=req.gracefulTerminationTimeout)
|
|
||||||
else:
|
|
||||||
body = client.V1DeleteOptions(
|
|
||||||
propagation_policy='Foreground',
|
|
||||||
grace_period_seconds=0)
|
|
||||||
|
|
||||||
for k8s_res in sorted_k8s_reses:
|
|
||||||
kind = k8s_res.get('kind', '')
|
|
||||||
api_version = k8s_res.get('apiVersion', '')
|
|
||||||
name = k8s_res.get('metadata', {}).get('name', '')
|
|
||||||
k8s_client = self.k8s_clients[api_version]
|
|
||||||
|
|
||||||
if kind == 'StatefulSet':
|
|
||||||
pvcs_for_delete = self._get_pvcs_for_delete(
|
|
||||||
sfs_name=name, namespace=namespace)
|
|
||||||
|
|
||||||
if kind == 'ControllerRevision':
|
|
||||||
body = client.V1DeleteOptions(
|
|
||||||
propagation_policy='Background')
|
|
||||||
try:
|
|
||||||
if kind in SUPPORTED_NAMESPACE_KINDS:
|
|
||||||
k8s_method = getattr(
|
|
||||||
k8s_client, f"delete_namespaced_{convert(kind)}")
|
|
||||||
k8s_method(name=name, namespace=namespace, body=body)
|
|
||||||
else:
|
|
||||||
k8s_method = getattr(
|
|
||||||
k8s_client, f"delete_{convert(kind)}")
|
|
||||||
k8s_method(name=name, body=body)
|
|
||||||
k8s_res.update(status='DELETE_IN_PROGRESS')
|
|
||||||
except Exception as ex:
|
|
||||||
k8s_res.update(status='DELETE_IN_PROGRESS')
|
|
||||||
LOG.debug(ex)
|
|
||||||
|
|
||||||
if kind == 'StatefulSet' and len(pvcs_for_delete) > 0:
|
|
||||||
for delete_pvc_name in pvcs_for_delete:
|
|
||||||
try:
|
|
||||||
self.k8s_clients[
|
|
||||||
'v1'].delete_namespaced_persistent_volume_claim(
|
|
||||||
name=delete_pvc_name, namespace=namespace,
|
|
||||||
body=body)
|
|
||||||
except Exception as ex:
|
|
||||||
LOG.debug(ex)
|
|
||||||
|
|
||||||
def update_k8s_resource(self, new_reses, namespace):
|
|
||||||
for k8s_res in new_reses:
|
|
||||||
kind = k8s_res.get('kind', '')
|
|
||||||
api_version = k8s_res.get('apiVersion', '')
|
|
||||||
name = k8s_res.get('metadata', {}).get('name', '')
|
|
||||||
k8s_client = self.k8s_clients[api_version]
|
|
||||||
k8s_method = getattr(
|
|
||||||
k8s_client, f"patch_namespaced_{convert(kind)}")
|
|
||||||
try:
|
|
||||||
k8s_method(name=name, namespace=namespace, body=k8s_res)
|
|
||||||
k8s_res.update(status='UPDATE_IN_PROCESS')
|
|
||||||
except Exception as e:
|
|
||||||
LOG.error(f'update resource failed. kind: {kind},'
|
|
||||||
f' name: {name}')
|
|
||||||
raise sol_ex.UpdateK8SResourceFailed from e
|
|
||||||
|
|
||||||
def list_namespaced_pods(self, namespace):
|
|
||||||
if namespace is None:
|
|
||||||
return None
|
|
||||||
return self.k8s_clients['v1'].list_namespaced_pod(
|
|
||||||
namespace=namespace).items
|
|
||||||
|
|
||||||
def check_deployment_exist(self, deployment_names, namespace):
|
|
||||||
for name in deployment_names:
|
|
||||||
try:
|
|
||||||
self.k8s_clients['apps/v1'].read_namespaced_deployment(
|
|
||||||
name=name, namespace=namespace)
|
|
||||||
except Exception as ex:
|
|
||||||
LOG.error(f'update deployment {name} does'
|
|
||||||
f' not exist in kubernetes cluster')
|
|
||||||
raise ex
|
|
||||||
|
|
||||||
def _get_pvcs_for_delete(self, sfs_name, namespace):
|
|
||||||
pvcs_for_delete = []
|
|
||||||
try:
|
|
||||||
resp_read_sfs = self.k8s_clients[
|
|
||||||
'apps/v1'].read_namespaced_stateful_set(sfs_name, namespace)
|
|
||||||
sfs_spec = resp_read_sfs.spec
|
|
||||||
volume_claim_templates = sfs_spec.volume_claim_templates
|
|
||||||
|
|
||||||
try:
|
|
||||||
resps_pvc = self.k8s_clients[
|
|
||||||
'v1'].list_namespaced_persistent_volume_claim(namespace)
|
|
||||||
pvcs = resps_pvc.items
|
|
||||||
for volume_claim_template in volume_claim_templates:
|
|
||||||
pvc_template_metadata = volume_claim_template.metadata
|
|
||||||
match_pattern = '-'.join(
|
|
||||||
[pvc_template_metadata.name, sfs_name, ""])
|
|
||||||
|
|
||||||
for pvc in pvcs:
|
|
||||||
pvc_metadata = pvc.metadata
|
|
||||||
pvc_name = pvc_metadata.name
|
|
||||||
match_result = re.match(
|
|
||||||
match_pattern + '[0-9]+$', pvc_name)
|
|
||||||
if match_result is not None:
|
|
||||||
pvcs_for_delete.append(pvc_name)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return pvcs_for_delete
|
|
||||||
|
|
||||||
def _wait_completion(self, k8s_reses, operation,
|
|
||||||
namespace=None, old_pods_names=None):
|
|
||||||
def _check_create_status():
|
|
||||||
for k8s_res in k8s_reses:
|
|
||||||
if k8s_res['status'] != 'CREATE_COMPLETE':
|
|
||||||
if k8s_res.get('kind') in STATUS_CHECK_RES:
|
|
||||||
res_check_method = getattr(
|
|
||||||
self, f"_check_status_"
|
|
||||||
f"{convert(k8s_res.get('kind'))}")
|
|
||||||
res_check_method(k8s_res)
|
|
||||||
else:
|
|
||||||
k8s_res.update(status='CREATE_COMPLETE')
|
|
||||||
statuses = {res['status'] for res in k8s_reses}
|
|
||||||
if len(statuses) == 1 and statuses.pop() == 'CREATE_COMPLETE':
|
|
||||||
raise loopingcall.LoopingCallDone()
|
|
||||||
if len(statuses) > 1 and (int(time.time()) - start_time > timeout):
|
|
||||||
raise sol_ex.CreateK8SResourceFailed
|
|
||||||
|
|
||||||
def _check_delete_status():
|
|
||||||
for k8s_res in k8s_reses:
|
|
||||||
kind = k8s_res.get('kind', '')
|
|
||||||
api_version = k8s_res.get('apiVersion', '')
|
|
||||||
name = k8s_res.get('metadata', {}).get('name', '')
|
|
||||||
k8s_client = self.k8s_clients[api_version]
|
|
||||||
if k8s_res['status'] != 'DELETE_COMPLETE':
|
|
||||||
try:
|
|
||||||
if kind in SUPPORTED_NAMESPACE_KINDS:
|
|
||||||
k8s_method = getattr(
|
|
||||||
k8s_client, f'read_namespaced_{convert(kind)}')
|
|
||||||
k8s_method(name=name, namespace=namespace)
|
|
||||||
else:
|
|
||||||
k8s_method = getattr(
|
|
||||||
k8s_client, f'read_{convert(kind)}')
|
|
||||||
k8s_method(name=name)
|
|
||||||
except Exception:
|
|
||||||
k8s_res.update(status='DELETE_COMPLETE')
|
|
||||||
statuses = {res['status'] for res in k8s_reses}
|
|
||||||
if len(statuses) == 1 and statuses.pop() == 'DELETE_COMPLETE':
|
|
||||||
raise loopingcall.LoopingCallDone()
|
|
||||||
if len(statuses) > 1 and (int(time.time()) - start_time > timeout):
|
|
||||||
raise sol_ex.DeleteK8SResourceFailed
|
|
||||||
|
|
||||||
def _check_update_status():
|
|
||||||
all_namespaced_pods = self.list_namespaced_pods(namespace)
|
|
||||||
for k8s_res in k8s_reses:
|
|
||||||
if k8s_res['status'] not in ['UPDATE_COMPLETE',
|
|
||||||
'UPDATE_FAILED']:
|
|
||||||
kind = k8s_res.get('kind', '')
|
|
||||||
api_version = k8s_res.get('apiVersion', '')
|
|
||||||
name = k8s_res.get('metadata', {}).get('name', '')
|
|
||||||
k8s_client = self.k8s_clients[api_version]
|
|
||||||
k8s_method = getattr(
|
|
||||||
k8s_client, f'read_namespaced_{convert(kind)}')
|
|
||||||
k8s_info = k8s_method(name=name, namespace=namespace)
|
|
||||||
replicas = k8s_info.spec.replicas
|
|
||||||
|
|
||||||
pods_info = [pod for pod in all_namespaced_pods if
|
|
||||||
is_match_pod_naming_rule(
|
|
||||||
kind, name, pod.metadata.name)]
|
|
||||||
pending_flag = False
|
|
||||||
unkown_flag = False
|
|
||||||
for pod_info in pods_info:
|
|
||||||
if pod_info.status.phase == 'Pending':
|
|
||||||
pending_flag = True
|
|
||||||
elif pod_info.status.phase == 'Unknown':
|
|
||||||
unkown_flag = True
|
|
||||||
|
|
||||||
if not pending_flag and not unkown_flag and len(
|
|
||||||
pods_info) == replicas and (
|
|
||||||
pods_info[0].metadata.name not in old_pods_names):
|
|
||||||
k8s_res.update(status='UPDATE_COMPLETE')
|
|
||||||
|
|
||||||
if unkown_flag:
|
|
||||||
k8s_res.update(status='UPDATE_FAILED')
|
|
||||||
|
|
||||||
statuses = {res['status'] for res in k8s_reses}
|
|
||||||
if len(statuses) == 1 and list(statuses)[0] == 'UPDATE_COMPLETE':
|
|
||||||
raise loopingcall.LoopingCallDone()
|
|
||||||
if (list(statuses)[0] == 'UPDATE_IN_PROCESS' and (int(
|
|
||||||
time.time()) - start_time > timeout)) or (
|
|
||||||
'UPDATE_FAILED' in statuses):
|
|
||||||
raise sol_ex.UpdateK8SResourceFailed
|
|
||||||
|
|
||||||
start_time = int(time.time())
|
|
||||||
timeout = CONF.v2_vnfm.kubernetes_vim_rsc_wait_timeout
|
|
||||||
|
|
||||||
if operation == v2fields.LcmOperationType.INSTANTIATE:
|
|
||||||
timer = loopingcall.FixedIntervalLoopingCall(_check_create_status)
|
|
||||||
elif operation == v2fields.LcmOperationType.TERMINATE:
|
|
||||||
timer = loopingcall.FixedIntervalLoopingCall(_check_delete_status)
|
|
||||||
else:
|
|
||||||
timer = loopingcall.FixedIntervalLoopingCall(_check_update_status)
|
|
||||||
timer.start(interval=CHECK_INTERVAL).wait()
|
|
||||||
|
|
||||||
def _check_status_pod(self, k8s_res):
|
|
||||||
pod = self.k8s_clients[k8s_res.get(
|
|
||||||
'api_version')].read_namespaced_pod(
|
|
||||||
namespace=k8s_res.get('namespace'),
|
|
||||||
name=k8s_res.get('name'))
|
|
||||||
|
|
||||||
if pod.status.phase and pod.status.phase == 'Running':
|
|
||||||
k8s_res.update(status='CREATE_COMPLETE')
|
|
||||||
|
|
||||||
def _check_status_stateful_set(self, k8s_res):
|
|
||||||
namespace = k8s_res.get('namespace')
|
|
||||||
name = k8s_res.get('name')
|
|
||||||
|
|
||||||
stateful_set = self.k8s_clients[k8s_res.get(
|
|
||||||
'api_version')].read_namespaced_stateful_set(
|
|
||||||
namespace=namespace, name=name)
|
|
||||||
pvc_statuses = []
|
|
||||||
replicas = stateful_set.status.replicas
|
|
||||||
if replicas and replicas == stateful_set.status.ready_replicas:
|
|
||||||
for i in range(0, stateful_set.spec.replicas):
|
|
||||||
volume_claim_templates = (
|
|
||||||
stateful_set.spec.volume_claim_templates)
|
|
||||||
for volume_claim_template in volume_claim_templates:
|
|
||||||
pvc_name = "-".join(
|
|
||||||
[volume_claim_template.metadata.name,
|
|
||||||
k8s_res.get('name'), str(i)])
|
|
||||||
persistent_volume_claim = (
|
|
||||||
self.k8s_clients[
|
|
||||||
'v1'].read_namespaced_persistent_volume_claim(
|
|
||||||
namespace=namespace, name=pvc_name))
|
|
||||||
pvc_statuses.append(persistent_volume_claim.status.phase)
|
|
||||||
if len(set(pvc_statuses)) == 1 and pvc_statuses[0] == 'Bound':
|
|
||||||
k8s_res.update(status='CREATE_COMPLETE')
|
|
||||||
|
|
||||||
def _check_status_service(self, k8s_res):
|
|
||||||
namespace = k8s_res.get('namespace')
|
|
||||||
name = k8s_res.get('name')
|
|
||||||
|
|
||||||
service = self.k8s_clients[k8s_res.get(
|
|
||||||
'api_version')].read_namespaced_service(
|
|
||||||
namespace=namespace, name=name)
|
|
||||||
status_flag = False
|
|
||||||
if service.spec.cluster_ip in ['', None] or check_is_ip(
|
|
||||||
service.spec.cluster_ip):
|
|
||||||
try:
|
|
||||||
endpoint = self.k8s_clients['v1'].read_namespaced_endpoints(
|
|
||||||
namespace=namespace, name=name)
|
|
||||||
if endpoint:
|
|
||||||
status_flag = True
|
|
||||||
except Exception as e:
|
|
||||||
raise sol_ex.ReadEndpointsFalse(
|
|
||||||
kind=k8s_res.get('kind')) from e
|
|
||||||
|
|
||||||
if status_flag:
|
|
||||||
k8s_res.update(status='CREATE_COMPLETE')
|
|
||||||
|
|
||||||
def _check_status_persistent_volume_claim(self, k8s_res):
|
|
||||||
namespace = k8s_res.get('namespace')
|
|
||||||
name = k8s_res.get('name')
|
|
||||||
|
|
||||||
claim = self.k8s_clients[k8s_res.get(
|
|
||||||
'api_version')].read_namespaced_persistent_volume_claim(
|
|
||||||
namespace=namespace, name=name)
|
|
||||||
|
|
||||||
if claim.status.phase and claim.status.phase == 'Bound':
|
|
||||||
k8s_res.update(status='CREATE_COMPLETE')
|
|
||||||
|
|
||||||
def _check_status_namespace(self, k8s_res):
|
|
||||||
name = k8s_res.get('name')
|
|
||||||
|
|
||||||
name_space = self.k8s_clients[k8s_res.get(
|
|
||||||
'api_version')].read_namespace(name=name)
|
|
||||||
if name_space.status.phase and name_space.status.phase == 'Active':
|
|
||||||
k8s_res.update(status='CREATE_COMPLETE')
|
|
||||||
|
|
||||||
def _check_status_node(self, k8s_res):
|
|
||||||
name = k8s_res.get('name')
|
|
||||||
|
|
||||||
node = self.k8s_clients[k8s_res.get(
|
|
||||||
'api_version')].read_node(name=name)
|
|
||||||
status_flag = False
|
|
||||||
for condition in node.status.conditions:
|
|
||||||
if condition.type == 'Ready':
|
|
||||||
if condition.status == 'True':
|
|
||||||
status_flag = True
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
if status_flag:
|
|
||||||
k8s_res.update(status='CREATE_COMPLETE')
|
|
||||||
|
|
||||||
def _check_status_persistent_volume(self, k8s_res):
|
|
||||||
name = k8s_res.get('name')
|
|
||||||
|
|
||||||
volume = self.k8s_clients[k8s_res.get(
|
|
||||||
'api_version')].read_persistent_volume(name=name)
|
|
||||||
if volume.status.phase and volume.status.phase in [
|
|
||||||
'Available', 'Bound']:
|
|
||||||
k8s_res.update(status='CREATE_COMPLETE')
|
|
||||||
|
|
||||||
def _check_status_api_service(self, k8s_res):
|
|
||||||
name = k8s_res.get('name')
|
|
||||||
|
|
||||||
api_service = self.k8s_clients[k8s_res.get(
|
|
||||||
'api_version')].read_api_service(name=name)
|
|
||||||
status_flag = False
|
|
||||||
for condition in api_service.status.conditions:
|
|
||||||
if condition.type == 'Available':
|
|
||||||
if condition.status == 'True':
|
|
||||||
status_flag = True
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
if status_flag:
|
|
||||||
k8s_res.update(status='CREATE_COMPLETE')
|
|
||||||
|
|
||||||
def _check_status_daemon_set(self, k8s_res):
|
|
||||||
namespace = k8s_res.get('namespace')
|
|
||||||
name = k8s_res.get('name')
|
|
||||||
|
|
||||||
daemon_set = self.k8s_clients[k8s_res.get(
|
|
||||||
'api_version')].read_namespaced_daemon_set(
|
|
||||||
namespace=namespace, name=name)
|
|
||||||
if daemon_set.status.desired_number_scheduled and (
|
|
||||||
daemon_set.status.desired_number_scheduled ==
|
|
||||||
daemon_set.status.number_ready):
|
|
||||||
k8s_res.update(status='CREATE_COMPLETE')
|
|
||||||
|
|
||||||
def _check_status_deployment(self, k8s_res):
|
|
||||||
namespace = k8s_res.get('namespace')
|
|
||||||
name = k8s_res.get('name')
|
|
||||||
|
|
||||||
deployment = self.k8s_clients[k8s_res.get(
|
|
||||||
'api_version')].read_namespaced_deployment(
|
|
||||||
namespace=namespace, name=name)
|
|
||||||
if deployment.status.replicas and (
|
|
||||||
deployment.status.replicas ==
|
|
||||||
deployment.status.ready_replicas):
|
|
||||||
k8s_res.update(status='CREATE_COMPLETE')
|
|
||||||
|
|
||||||
def _check_status_replica_set(self, k8s_res):
|
|
||||||
namespace = k8s_res.get('namespace')
|
|
||||||
name = k8s_res.get('name')
|
|
||||||
|
|
||||||
replica_set = self.k8s_clients[k8s_res.get(
|
|
||||||
'api_version')].read_namespaced_replica_set(
|
|
||||||
namespace=namespace, name=name)
|
|
||||||
if replica_set.status.replicas and (
|
|
||||||
replica_set.status.replicas ==
|
|
||||||
replica_set.status.ready_replicas):
|
|
||||||
k8s_res.update(status='CREATE_COMPLETE')
|
|
||||||
|
|
||||||
def _check_status_job(self, k8s_res):
|
|
||||||
namespace = k8s_res.get('namespace')
|
|
||||||
name = k8s_res.get('name')
|
|
||||||
|
|
||||||
job = self.k8s_clients[k8s_res.get(
|
|
||||||
'api_version')].read_namespaced_job(
|
|
||||||
namespace=namespace, name=name)
|
|
||||||
if job.spec.completions and (
|
|
||||||
job.spec.completions == job.status.succeeded):
|
|
||||||
k8s_res.update(status='CREATE_COMPLETE')
|
|
||||||
|
|
||||||
def _check_status_volume_attachment(self, k8s_res):
|
|
||||||
name = k8s_res.get('name')
|
|
||||||
|
|
||||||
volume = self.k8s_clients[k8s_res.get(
|
|
||||||
'api_version')].read_volume_attachment(name=name)
|
|
||||||
if volume.status.attached:
|
|
||||||
k8s_res.update(status='CREATE_COMPLETE')
|
|
||||||
|
|
||||||
def wait_k8s_res_create(self, created_k8s_reses):
|
|
||||||
self._wait_completion(created_k8s_reses, operation='INSTANTIATE')
|
|
||||||
|
|
||||||
def wait_k8s_res_delete(self, sorted_k8s_reses, namespace):
|
|
||||||
self._wait_completion(
|
|
||||||
sorted_k8s_reses, operation='TERMINATE', namespace=namespace)
|
|
||||||
|
|
||||||
def wait_k8s_res_update(self, new_k8s_reses, namespace,
|
|
||||||
old_pods_names=None):
|
|
||||||
self._wait_completion(
|
|
||||||
new_k8s_reses, operation='UPDATE', namespace=namespace,
|
|
||||||
old_pods_names=old_pods_names)
|
|
||||||
|
|
||||||
|
|
||||||
def is_match_pod_naming_rule(rsc_kind, rsc_name, pod_name):
|
def is_match_pod_naming_rule(rsc_kind, rsc_name, pod_name):
|
||||||
@@ -538,7 +60,7 @@ def is_match_pod_naming_rule(rsc_kind, rsc_name, pod_name):
|
|||||||
if rsc_kind == 'Pod':
|
if rsc_kind == 'Pod':
|
||||||
# Expected example: name
|
# Expected example: name
|
||||||
if rsc_name == pod_name:
|
if rsc_name == pod_name:
|
||||||
match_result = True
|
return True
|
||||||
elif rsc_kind == 'Deployment':
|
elif rsc_kind == 'Deployment':
|
||||||
# Expected example: name-012789abef-019az
|
# Expected example: name-012789abef-019az
|
||||||
# NOTE(horie): The naming rule of Pod in deployment is
|
# NOTE(horie): The naming rule of Pod in deployment is
|
||||||
@@ -547,106 +69,21 @@ def is_match_pod_naming_rule(rsc_kind, rsc_name, pod_name):
|
|||||||
# This may be from 1 to 10 caracters but not sure the lower limit
|
# This may be from 1 to 10 caracters but not sure the lower limit
|
||||||
# from the source code of Kubernetes.
|
# from the source code of Kubernetes.
|
||||||
match_result = re.match(
|
match_result = re.match(
|
||||||
rsc_name + '-([0-9a-f]{1,10})-([0-9a-z]{5})+$',
|
rsc_name + '-([0-9a-f]{1,10})-([0-9a-z]{5})+$', pod_name)
|
||||||
pod_name)
|
|
||||||
elif rsc_kind in ('ReplicaSet', 'DaemonSet'):
|
elif rsc_kind in ('ReplicaSet', 'DaemonSet'):
|
||||||
# Expected example: name-019az
|
# Expected example: name-019az
|
||||||
match_result = re.match(
|
match_result = re.match(rsc_name + '-([0-9a-z]{5})+$', pod_name)
|
||||||
rsc_name + '-([0-9a-z]{5})+$',
|
|
||||||
pod_name)
|
|
||||||
elif rsc_kind == 'StatefulSet':
|
elif rsc_kind == 'StatefulSet':
|
||||||
# Expected example: name-0
|
# Expected example: name-0
|
||||||
match_result = re.match(
|
match_result = re.match(rsc_name + '-[0-9]+$', pod_name)
|
||||||
rsc_name + '-[0-9]+$',
|
|
||||||
pod_name)
|
|
||||||
if match_result:
|
if match_result:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def check_is_ip(ip_addr):
|
def get_k8s_reses_from_json_files(target_k8s_files, vnfd, k8s_api_client,
|
||||||
try:
|
namespace):
|
||||||
ipaddress.ip_address(ip_addr)
|
|
||||||
return True
|
|
||||||
except ValueError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def convert(tmp_name):
|
|
||||||
name_with_underscores = re.sub(
|
|
||||||
'(.)([A-Z][a-z]+)', r'\1_\2', tmp_name)
|
|
||||||
return re.sub('([a-z0-9])([A-Z])', r'\1_\2',
|
|
||||||
name_with_underscores).lower()
|
|
||||||
|
|
||||||
|
|
||||||
def init_k8s_api_client(vim_info):
|
|
||||||
k8s_config = client.Configuration()
|
|
||||||
k8s_config.host = vim_info.interfaceInfo['endpoint']
|
|
||||||
|
|
||||||
ca_cert_file = (vim_info.interfaceInfo.pop('ca_cert_file')
|
|
||||||
if 'ca_cert_file' in vim_info.interfaceInfo else None)
|
|
||||||
|
|
||||||
if ('username' in vim_info.accessInfo and 'password'
|
|
||||||
in vim_info.accessInfo and vim_info.accessInfo.get(
|
|
||||||
'password') is not None):
|
|
||||||
k8s_config.username = vim_info.accessInfo['username']
|
|
||||||
k8s_config.password = vim_info.accessInfo['password']
|
|
||||||
basic_token = k8s_config.get_basic_auth_token()
|
|
||||||
k8s_config.api_key['authorization'] = basic_token
|
|
||||||
|
|
||||||
if 'bearer_token' in vim_info.accessInfo:
|
|
||||||
k8s_config.api_key_prefix['authorization'] = 'Bearer'
|
|
||||||
k8s_config.api_key['authorization'] = vim_info.accessInfo[
|
|
||||||
'bearer_token']
|
|
||||||
|
|
||||||
if 'ssl_ca_cert' in vim_info.interfaceInfo and ca_cert_file:
|
|
||||||
k8s_config.ssl_ca_cert = ca_cert_file
|
|
||||||
k8s_config.verify_ssl = True
|
|
||||||
else:
|
|
||||||
k8s_config.verify_ssl = False
|
|
||||||
|
|
||||||
return client.api_client.ApiClient(configuration=k8s_config)
|
|
||||||
|
|
||||||
|
|
||||||
def get_k8s_clients(k8s_api_client):
|
|
||||||
k8s_clients = {
|
|
||||||
"v1": client.CoreV1Api(api_client=k8s_api_client),
|
|
||||||
"apiregistration.k8s.io/v1":
|
|
||||||
client.ApiregistrationV1Api(api_client=k8s_api_client),
|
|
||||||
"apps/v1": client.AppsV1Api(api_client=k8s_api_client),
|
|
||||||
"authentication.k8s.io/v1":
|
|
||||||
client.AuthenticationV1Api(api_client=k8s_api_client),
|
|
||||||
"authorization.k8s.io/v1":
|
|
||||||
client.AuthorizationV1Api(api_client=k8s_api_client),
|
|
||||||
"autoscaling/v1": client.AutoscalingV1Api(
|
|
||||||
api_client=k8s_api_client),
|
|
||||||
"batch/v1": client.BatchV1Api(api_client=k8s_api_client),
|
|
||||||
"coordination.k8s.io/v1":
|
|
||||||
client.CoordinationV1Api(api_client=k8s_api_client),
|
|
||||||
"networking.k8s.io/v1":
|
|
||||||
client.NetworkingV1Api(api_client=k8s_api_client),
|
|
||||||
"rbac.authorization.k8s.io/v1":
|
|
||||||
client.RbacAuthorizationV1Api(api_client=k8s_api_client),
|
|
||||||
"scheduling.k8s.io/v1":
|
|
||||||
client.SchedulingV1Api(api_client=k8s_api_client),
|
|
||||||
"storage.k8s.io/v1":
|
|
||||||
client.StorageV1Api(api_client=k8s_api_client)
|
|
||||||
}
|
|
||||||
|
|
||||||
return k8s_clients
|
|
||||||
|
|
||||||
|
|
||||||
def get_k8s_json_file(req, inst, target_k8s_files, vnfd, operation):
|
|
||||||
|
|
||||||
def _update_k8s_resources(namespace):
|
|
||||||
for k8s_res in k8s_resources:
|
|
||||||
if (k8s_res.get('kind', '') in SUPPORTED_NAMESPACE_KINDS and
|
|
||||||
k8s_res.get('metadata') is None):
|
|
||||||
k8s_res.update(metadata={})
|
|
||||||
if k8s_res.get('kind', '') in SUPPORTED_NAMESPACE_KINDS:
|
|
||||||
k8s_res['metadata'].update(namespace=namespace)
|
|
||||||
|
|
||||||
k8s_resources = []
|
k8s_resources = []
|
||||||
|
|
||||||
for target_k8s_file in target_k8s_files:
|
for target_k8s_file in target_k8s_files:
|
||||||
@@ -662,93 +99,81 @@ def get_k8s_json_file(req, inst, target_k8s_files, vnfd, operation):
|
|||||||
|
|
||||||
k8s_resources.extend(list(yaml.safe_load_all(file_content)))
|
k8s_resources.extend(list(yaml.safe_load_all(file_content)))
|
||||||
|
|
||||||
# check namespace
|
|
||||||
if operation == v2fields.LcmOperationType.INSTANTIATE:
|
|
||||||
if req.additionalParams.get('namespace') is None:
|
|
||||||
_update_k8s_resources('default')
|
|
||||||
namespaces = {
|
|
||||||
k8s_res['metadata']['namespace'] for k8s_res in
|
|
||||||
k8s_resources if k8s_res.get('kind') in
|
|
||||||
SUPPORTED_NAMESPACE_KINDS}
|
|
||||||
if len(namespaces) > 1:
|
|
||||||
raise sol_ex.NamespaceNotUniform()
|
|
||||||
return k8s_resources, namespaces.pop() if namespaces else None
|
|
||||||
|
|
||||||
_update_k8s_resources(req.additionalParams.get('namespace'))
|
|
||||||
return k8s_resources, req.additionalParams.get('namespace')
|
|
||||||
|
|
||||||
return k8s_resources, inst.metadata.get('namespace')
|
|
||||||
|
|
||||||
|
|
||||||
def sort_k8s_resource(k8s_resources, operation):
|
|
||||||
pos = 0
|
|
||||||
sorted_k8s_reses = []
|
|
||||||
|
|
||||||
if operation == v2fields.LcmOperationType.INSTANTIATE:
|
|
||||||
sort_order = RESOURCE_CREATION_ORDER
|
|
||||||
else:
|
|
||||||
sort_order = list(reversed(RESOURCE_CREATION_ORDER))
|
|
||||||
|
|
||||||
copy_k8s_resources = copy.deepcopy(k8s_resources)
|
|
||||||
|
|
||||||
for kind in sort_order:
|
|
||||||
for res_index, res in enumerate(copy_k8s_resources):
|
|
||||||
if res.get('kind', '') == kind:
|
|
||||||
index = k8s_resources.index(res)
|
|
||||||
sorted_k8s_reses.append(k8s_resources.pop(index))
|
|
||||||
# Other kind (such as PodTemplate, Node, and so on) that are
|
|
||||||
# not present in `RESOURCE_CREATION_ORDER` are inserted in
|
|
||||||
# place of the Service kind and created/deleted in the same
|
|
||||||
# order as the Service kind.
|
|
||||||
if kind == 'Service':
|
|
||||||
pos = len(sorted_k8s_reses)
|
|
||||||
|
|
||||||
for k8s_res in k8s_resources:
|
for k8s_res in k8s_resources:
|
||||||
sorted_k8s_reses.insert(pos, k8s_res)
|
if not k8s_res.get('kind'):
|
||||||
|
raise sol_ex.K8sInvalidManifestFound()
|
||||||
|
if k8s_res['kind'] in SUPPORTED_NAMESPACE_KINDS:
|
||||||
|
k8s_res.setdefault('metadata', {})
|
||||||
|
if namespace is None:
|
||||||
|
k8s_res['metadata'].setdefault('namespace', 'default')
|
||||||
|
else:
|
||||||
|
k8s_res['metadata']['namespace'] = namespace
|
||||||
|
|
||||||
return sorted_k8s_reses
|
# check namespace
|
||||||
|
if namespace is None:
|
||||||
|
namespaces = {k8s_res['metadata']['namespace']
|
||||||
|
for k8s_res in k8s_resources
|
||||||
|
if k8s_res['kind'] in SUPPORTED_NAMESPACE_KINDS}
|
||||||
|
if len(namespaces) > 1:
|
||||||
|
raise sol_ex.NamespaceNotUniform()
|
||||||
|
namespace = namespaces.pop() if namespaces else 'default'
|
||||||
|
|
||||||
|
k8s_reses = []
|
||||||
|
for k8s_res in k8s_resources:
|
||||||
|
cls = getattr(kubernetes_resource, k8s_res['kind'])
|
||||||
|
k8s_reses.append(cls(k8s_api_client, k8s_res))
|
||||||
|
|
||||||
|
return k8s_reses, namespace
|
||||||
|
|
||||||
|
|
||||||
def get_new_deployment_body(
|
def list_namespaced_pods(k8s_api_client, namespace):
|
||||||
req, inst, vnfd, deployment_names, operation):
|
k8s_client = client.CoreV1Api(api_client=k8s_api_client)
|
||||||
if operation == v2fields.LcmOperationType.CHANGE_VNFPKG:
|
return k8s_client.list_namespaced_pod(namespace=namespace).items
|
||||||
target_k8s_files = req.additionalParams.get(
|
|
||||||
'lcm-kubernetes-def-files')
|
|
||||||
else:
|
|
||||||
target_k8s_files = inst.metadata.get('lcm-kubernetes-def-files')
|
|
||||||
|
|
||||||
new_k8s_resources, namespace = get_k8s_json_file(
|
|
||||||
req, inst, target_k8s_files, vnfd, operation)
|
|
||||||
|
|
||||||
new_deploy_reses = []
|
|
||||||
for k8s_res in new_k8s_resources:
|
|
||||||
if k8s_res.get('kind', '') == 'Deployment' and k8s_res.get(
|
|
||||||
'metadata', {}).get('name', '') in deployment_names:
|
|
||||||
k8s_res['metadata']['namespace'] = namespace
|
|
||||||
new_deploy_reses.append(k8s_res)
|
|
||||||
|
|
||||||
return new_deploy_reses
|
|
||||||
|
|
||||||
|
|
||||||
class CaCertFileContextManager:
|
class AuthContextManager:
|
||||||
def __init__(self, ca_cert_str):
|
def __init__(self, vim_info):
|
||||||
self._file_descriptor = None
|
self.vim_info = vim_info
|
||||||
self.file_path = None
|
self.ca_cert_file = None
|
||||||
self.ca_cert_str = ca_cert_str
|
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
if not self.ca_cert_str:
|
|
||||||
return self
|
|
||||||
self._file_descriptor, self.file_path = tempfile.mkstemp()
|
|
||||||
ca_cert = re.sub(r'\s', '\n', self.ca_cert_str)
|
|
||||||
ca_cert = re.sub(r'BEGIN\nCERT', r'BEGIN CERT', ca_cert)
|
|
||||||
ca_cert = re.sub(r'END\nCERT', r'END CERT', ca_cert)
|
|
||||||
# write ca cert file
|
|
||||||
os.write(self._file_descriptor, ca_cert.encode())
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, exc_traceback):
|
def __exit__(self, exc_type, exc_value, exc_traceback):
|
||||||
if not self.ca_cert_str:
|
if self.ca_cert_file:
|
||||||
return
|
os.remove(self.ca_cert_file)
|
||||||
os.close(self._file_descriptor)
|
|
||||||
os.remove(self.file_path)
|
def _create_ca_cert_file(self, ca_cert_str):
|
||||||
|
file_descriptor, self.ca_cert_file = tempfile.mkstemp()
|
||||||
|
ca_cert = re.sub(r'\s', '\n', ca_cert_str)
|
||||||
|
ca_cert = re.sub(r'BEGIN\nCERT', r'BEGIN CERT', ca_cert)
|
||||||
|
ca_cert = re.sub(r'END\nCERT', r'END CERT', ca_cert)
|
||||||
|
# write ca cert file
|
||||||
|
os.write(file_descriptor, ca_cert.encode())
|
||||||
|
os.close(file_descriptor)
|
||||||
|
|
||||||
|
def init_k8s_api_client(self):
|
||||||
|
k8s_config = client.Configuration()
|
||||||
|
k8s_config.host = self.vim_info.interfaceInfo['endpoint']
|
||||||
|
|
||||||
|
if ('username' in self.vim_info.accessInfo and
|
||||||
|
self.vim_info.accessInfo.get('password') is not None):
|
||||||
|
k8s_config.username = self.vim_info.accessInfo['username']
|
||||||
|
k8s_config.password = self.vim_info.accessInfo['password']
|
||||||
|
basic_token = k8s_config.get_basic_auth_token()
|
||||||
|
k8s_config.api_key['authorization'] = basic_token
|
||||||
|
|
||||||
|
if 'bearer_token' in self.vim_info.accessInfo:
|
||||||
|
k8s_config.api_key_prefix['authorization'] = 'Bearer'
|
||||||
|
k8s_config.api_key['authorization'] = self.vim_info.accessInfo[
|
||||||
|
'bearer_token']
|
||||||
|
|
||||||
|
if 'ssl_ca_cert' in self.vim_info.interfaceInfo:
|
||||||
|
self._create_ca_cert_file(
|
||||||
|
self.vim_info.interfaceInfo['ssl_ca_cert'])
|
||||||
|
k8s_config.ssl_ca_cert = self.ca_cert_file
|
||||||
|
k8s_config.verify_ssl = True
|
||||||
|
else:
|
||||||
|
k8s_config.verify_ssl = False
|
||||||
|
|
||||||
|
return client.api_client.ApiClient(configuration=k8s_config)
|
||||||
|
|||||||
@@ -84,6 +84,9 @@ class VnfInstanceV2_InstantiatedVnfInfo(base.TackerObject,
|
|||||||
'VirtualStorageResourceInfoV2', nullable=True),
|
'VirtualStorageResourceInfoV2', nullable=True),
|
||||||
# NOTE: vnfcInfo exists in SOL002 only.
|
# NOTE: vnfcInfo exists in SOL002 only.
|
||||||
'vnfcInfo': fields.ListOfObjectsField('VnfcInfoV2', nullable=True),
|
'vnfcInfo': fields.ListOfObjectsField('VnfcInfoV2', nullable=True),
|
||||||
|
# NOTE: metadata is not defined in SOL003. it is original
|
||||||
|
# definition of Tacker.
|
||||||
|
'metadata': fields.KeyValuePairsField(nullable=True),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -193,6 +193,16 @@ class BaseVnfLcmKubernetesV2Test(base.BaseTestCase):
|
|||||||
return self.tacker_client.do_request(
|
return self.tacker_client.do_request(
|
||||||
path, "POST", body=req_body, version="2.0.0")
|
path, "POST", body=req_body, version="2.0.0")
|
||||||
|
|
||||||
|
def scale_vnf_instance(self, inst_id, req_body):
|
||||||
|
path = f"/vnflcm/v2/vnf_instances/{inst_id}/scale"
|
||||||
|
return self.tacker_client.do_request(
|
||||||
|
path, "POST", body=req_body, version="2.0.0")
|
||||||
|
|
||||||
|
def heal_vnf_instance(self, inst_id, req_body):
|
||||||
|
path = f"/vnflcm/v2/vnf_instances/{inst_id}/heal"
|
||||||
|
return self.tacker_client.do_request(
|
||||||
|
path, "POST", body=req_body, version="2.0.0")
|
||||||
|
|
||||||
def change_vnfpkg(self, inst_id, req_body):
|
def change_vnfpkg(self, inst_id, req_body):
|
||||||
path = f"/vnflcm/v2/vnf_instances/{inst_id}/change_vnfpkg"
|
path = f"/vnflcm/v2/vnf_instances/{inst_id}/change_vnfpkg"
|
||||||
return self.tacker_client.do_request(
|
return self.tacker_client.do_request(
|
||||||
|
|||||||
@@ -138,6 +138,28 @@ def max_sample_terminate():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def max_sample_scale_out():
|
||||||
|
return {
|
||||||
|
"type": "SCALE_OUT",
|
||||||
|
"aspectId": "vdu3_aspect",
|
||||||
|
"numberOfSteps": 2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def max_sample_scale_in():
|
||||||
|
return {
|
||||||
|
"type": "SCALE_IN",
|
||||||
|
"aspectId": "vdu3_aspect",
|
||||||
|
"numberOfSteps": 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def max_sample_heal(vnfc_ids):
|
||||||
|
return {
|
||||||
|
"vnfcInstanceId": vnfc_ids
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def min_sample_instantiate(vim_id_1):
|
def min_sample_instantiate(vim_id_1):
|
||||||
vim_1 = {
|
vim_1 = {
|
||||||
"vimId": vim_id_1,
|
"vimId": vim_id_1,
|
||||||
@@ -166,14 +188,7 @@ def min_sample_terminate():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def change_vnfpkg_instantiate(auth_url, bearer_token):
|
def error_handling_instantiate(auth_url, bearer_token):
|
||||||
# All attributes are set.
|
|
||||||
# NOTE: All of the following cardinality attributes are set.
|
|
||||||
# In addition, 0..N or 1..N attributes are set to 2 or more.
|
|
||||||
# - 0..1 (1)
|
|
||||||
# - 0..N (2 or more)
|
|
||||||
# - 1
|
|
||||||
# - 1..N (2 or more)
|
|
||||||
vim_id_1 = uuidutils.generate_uuid()
|
vim_id_1 = uuidutils.generate_uuid()
|
||||||
vim_1 = {
|
vim_1 = {
|
||||||
"vimId": vim_id_1,
|
"vimId": vim_id_1,
|
||||||
@@ -191,44 +206,6 @@ def change_vnfpkg_instantiate(auth_url, bearer_token):
|
|||||||
"vimConnectionInfo": {
|
"vimConnectionInfo": {
|
||||||
"vim1": vim_1
|
"vim1": vim_1
|
||||||
},
|
},
|
||||||
"additionalParams": {
|
|
||||||
"lcm-kubernetes-def-files": [
|
|
||||||
"Files/kubernetes/deployment.yaml",
|
|
||||||
"Files/kubernetes/namespace.yaml"
|
|
||||||
],
|
|
||||||
"namespace": "curry"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def change_vnfpkg_all_params(vnfd_id):
|
|
||||||
return {
|
|
||||||
"vnfdId": vnfd_id,
|
|
||||||
"additionalParams": {
|
|
||||||
"upgrade_type": "RollingUpdate",
|
|
||||||
"lcm-operation-coordinate-old-vnf":
|
|
||||||
"Scripts/coordinate_old_vnf.py",
|
|
||||||
"lcm-operation-coordinate-new-vnf":
|
|
||||||
"Scripts/coordinate_new_vnf.py",
|
|
||||||
"lcm-kubernetes-def-files": [
|
|
||||||
"Files/new_kubernetes/new_deployment.yaml"],
|
|
||||||
"vdu_params": [{
|
|
||||||
"vdu_id": "VDU2"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def change_vnfpkg_instantiate_min(vim_id_1):
|
|
||||||
vim_1 = {
|
|
||||||
"vimId": vim_id_1,
|
|
||||||
"vimType": "kubernetes",
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
"flavourId": "simple",
|
|
||||||
"vimConnectionInfo": {
|
|
||||||
"vim1": vim_1,
|
|
||||||
},
|
|
||||||
"additionalParams": {
|
"additionalParams": {
|
||||||
"lcm-kubernetes-def-files": [
|
"lcm-kubernetes-def-files": [
|
||||||
"Files/kubernetes/deployment.yaml"
|
"Files/kubernetes/deployment.yaml"
|
||||||
@@ -237,33 +214,59 @@ def change_vnfpkg_instantiate_min(vim_id_1):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def change_vnfpkg_min(vnfd_id):
|
def error_handling_scale_out():
|
||||||
return {
|
return {
|
||||||
"vnfdId": vnfd_id,
|
"type": "SCALE_OUT",
|
||||||
|
"aspectId": "vdu2_aspect",
|
||||||
|
"numberOfSteps": 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def error_handling_terminate():
|
||||||
|
return {
|
||||||
|
"terminationType": "FORCEFUL"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def change_vnfpkg_instantiate(auth_url, bearer_token):
|
||||||
|
vim_id_1 = uuidutils.generate_uuid()
|
||||||
|
vim_1 = {
|
||||||
|
"vimId": vim_id_1,
|
||||||
|
"vimType": "kubernetes",
|
||||||
|
"interfaceInfo": {"endpoint": auth_url},
|
||||||
|
"accessInfo": {
|
||||||
|
"bearer_token": bearer_token,
|
||||||
|
"region": "RegionOne",
|
||||||
|
},
|
||||||
|
"extra": {"dummy-key": "dummy-val"}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"flavourId": "simple",
|
||||||
|
"vimConnectionInfo": {
|
||||||
|
"vim1": vim_1
|
||||||
|
},
|
||||||
"additionalParams": {
|
"additionalParams": {
|
||||||
"upgrade_type": "RollingUpdate",
|
"lcm-kubernetes-def-files": [
|
||||||
"lcm-operation-coordinate-old-vnf":
|
"Files/kubernetes/namespace.yaml",
|
||||||
"Scripts/coordinate_old_vnf.py",
|
"Files/kubernetes/deployment.yaml"
|
||||||
"lcm-operation-coordinate-new-vnf":
|
],
|
||||||
"Scripts/coordinate_new_vnf.py",
|
"namespace": "curry"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def change_vnfpkg_instantiate_error_handing(vim_id_1):
|
def change_vnfpkg(vnfd_id):
|
||||||
vim_1 = {
|
|
||||||
"vimId": vim_id_1,
|
|
||||||
"vimType": "kubernetes",
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
"flavourId": "simple",
|
"vnfdId": vnfd_id,
|
||||||
"vimConnectionInfo": {
|
|
||||||
"vim1": vim_1,
|
|
||||||
},
|
|
||||||
"additionalParams": {
|
"additionalParams": {
|
||||||
|
"upgrade_type": "RollingUpdate",
|
||||||
"lcm-kubernetes-def-files": [
|
"lcm-kubernetes-def-files": [
|
||||||
"Files/kubernetes/deployment_fail_test.yaml"
|
"Files/kubernetes/namespace.yaml",
|
||||||
]
|
"Files/new_kubernetes/new_deployment.yaml"],
|
||||||
|
"vdu_params": [{
|
||||||
|
"vdu_id": "VDU2"
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,11 +276,17 @@ def change_vnfpkg_error(vnfd_id):
|
|||||||
"vnfdId": vnfd_id,
|
"vnfdId": vnfd_id,
|
||||||
"additionalParams": {
|
"additionalParams": {
|
||||||
"upgrade_type": "RollingUpdate",
|
"upgrade_type": "RollingUpdate",
|
||||||
"lcm-operation-coordinate-old-vnf":
|
|
||||||
"Scripts/coordinate_old_vnf.py",
|
|
||||||
"lcm-operation-coordinate-new-vnf":
|
|
||||||
"Scripts/coordinate_new_vnf.py",
|
|
||||||
"lcm-kubernetes-def-files": [
|
"lcm-kubernetes-def-files": [
|
||||||
"Files/new_kubernetes/error_deployment.yaml"]
|
"Files/kubernetes/namespace.yaml",
|
||||||
|
"Files/new_kubernetes/not_exist_deployment.yaml"],
|
||||||
|
"vdu_params": [{
|
||||||
|
"vdu_id": "VDU2"
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def change_vnfpkg_terminate():
|
||||||
|
return {
|
||||||
|
"terminationType": "FORCEFUL"
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,6 +40,33 @@ topology_template:
|
|||||||
type: company.provider.VNF
|
type: company.provider.VNF
|
||||||
properties:
|
properties:
|
||||||
flavour_description: A simple flavour
|
flavour_description: A simple flavour
|
||||||
|
interfaces:
|
||||||
|
Vnflcm:
|
||||||
|
instantiate_start:
|
||||||
|
implementation: sample-script
|
||||||
|
instantiate_end:
|
||||||
|
implementation: sample-script
|
||||||
|
terminate_start:
|
||||||
|
implementation: sample-script
|
||||||
|
terminate_end:
|
||||||
|
implementation: sample-script
|
||||||
|
scale_start:
|
||||||
|
implementation: sample-script
|
||||||
|
scale_end:
|
||||||
|
implementation: sample-script
|
||||||
|
heal_start:
|
||||||
|
implementation: sample-script
|
||||||
|
heal_end:
|
||||||
|
implementation: sample-script
|
||||||
|
modify_information_start:
|
||||||
|
implementation: sample-script
|
||||||
|
modify_information_end:
|
||||||
|
implementation: sample-script
|
||||||
|
artifacts:
|
||||||
|
sample-script:
|
||||||
|
description: Sample script
|
||||||
|
type: tosca.artifacts.Implementation.Python
|
||||||
|
file: ../Scripts/sample_script.py
|
||||||
|
|
||||||
VDU1:
|
VDU1:
|
||||||
type: tosca.nodes.nfv.Vdu.Compute
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
@@ -63,11 +90,29 @@ topology_template:
|
|||||||
type: tosca.nodes.nfv.Vdu.Compute
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
properties:
|
properties:
|
||||||
name: vdu3
|
name: vdu3
|
||||||
description: VDU2 compute node
|
description: VDU3 compute node
|
||||||
vdu_profile:
|
vdu_profile:
|
||||||
min_number_of_instances: 1
|
min_number_of_instances: 1
|
||||||
max_number_of_instances: 3
|
max_number_of_instances: 3
|
||||||
|
|
||||||
|
VDU5:
|
||||||
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
|
properties:
|
||||||
|
name: vdu5
|
||||||
|
description: VDU5 compute node
|
||||||
|
vdu_profile:
|
||||||
|
min_number_of_instances: 1
|
||||||
|
max_number_of_instances: 3
|
||||||
|
|
||||||
|
VDU6:
|
||||||
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
|
properties:
|
||||||
|
name: vdu6
|
||||||
|
description: VDU6 compute node
|
||||||
|
vdu_profile:
|
||||||
|
min_number_of_instances: 1
|
||||||
|
max_number_of_instances: 1
|
||||||
|
|
||||||
policies:
|
policies:
|
||||||
- scaling_aspects:
|
- scaling_aspects:
|
||||||
type: tosca.policies.nfv.ScalingAspects
|
type: tosca.policies.nfv.ScalingAspects
|
||||||
@@ -85,12 +130,18 @@ topology_template:
|
|||||||
max_scale_level: 2
|
max_scale_level: 2
|
||||||
step_deltas:
|
step_deltas:
|
||||||
- delta_1
|
- delta_1
|
||||||
|
vdu5_aspect:
|
||||||
|
name: vdu5_aspect
|
||||||
|
description: vdu5 scaling aspect
|
||||||
|
max_scale_level: 2
|
||||||
|
step_deltas:
|
||||||
|
- delta_1
|
||||||
|
|
||||||
- VDU2_initial_delta:
|
- VDU2_initial_delta:
|
||||||
type: tosca.policies.nfv.VduInitialDelta
|
type: tosca.policies.nfv.VduInitialDelta
|
||||||
properties:
|
properties:
|
||||||
initial_delta:
|
initial_delta:
|
||||||
number_of_instances: 1
|
number_of_instances: 2
|
||||||
targets: [ VDU2 ]
|
targets: [ VDU2 ]
|
||||||
|
|
||||||
- VDU2_scaling_aspect_deltas:
|
- VDU2_scaling_aspect_deltas:
|
||||||
@@ -118,6 +169,22 @@ topology_template:
|
|||||||
number_of_instances: 1
|
number_of_instances: 1
|
||||||
targets: [ VDU3 ]
|
targets: [ VDU3 ]
|
||||||
|
|
||||||
|
- VDU5_initial_delta:
|
||||||
|
type: tosca.policies.nfv.VduInitialDelta
|
||||||
|
properties:
|
||||||
|
initial_delta:
|
||||||
|
number_of_instances: 1
|
||||||
|
targets: [ VDU5 ]
|
||||||
|
|
||||||
|
- VDU5_scaling_aspect_deltas:
|
||||||
|
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||||
|
properties:
|
||||||
|
aspect: vdu5_aspect
|
||||||
|
deltas:
|
||||||
|
delta_1:
|
||||||
|
number_of_instances: 1
|
||||||
|
targets: [ VDU5 ]
|
||||||
|
|
||||||
- instantiation_levels:
|
- instantiation_levels:
|
||||||
type: tosca.policies.nfv.InstantiationLevels
|
type: tosca.policies.nfv.InstantiationLevels
|
||||||
properties:
|
properties:
|
||||||
@@ -126,9 +193,11 @@ topology_template:
|
|||||||
description: Smallest size
|
description: Smallest size
|
||||||
scale_info:
|
scale_info:
|
||||||
vdu2_aspect:
|
vdu2_aspect:
|
||||||
scale_level: 0
|
scale_level: 1
|
||||||
vdu3_aspect:
|
vdu3_aspect:
|
||||||
scale_level: 0
|
scale_level: 0
|
||||||
|
vdu5_aspect:
|
||||||
|
scale_level: 0
|
||||||
instantiation_level_2:
|
instantiation_level_2:
|
||||||
description: Largest size
|
description: Largest size
|
||||||
scale_info:
|
scale_info:
|
||||||
@@ -136,6 +205,8 @@ topology_template:
|
|||||||
scale_level: 2
|
scale_level: 2
|
||||||
vdu3_aspect:
|
vdu3_aspect:
|
||||||
scale_level: 2
|
scale_level: 2
|
||||||
|
vdu5_aspect:
|
||||||
|
scale_level: 2
|
||||||
default_level: instantiation_level_1
|
default_level: instantiation_level_1
|
||||||
|
|
||||||
- VDU1_instantiation_levels:
|
- VDU1_instantiation_levels:
|
||||||
@@ -153,7 +224,7 @@ topology_template:
|
|||||||
properties:
|
properties:
|
||||||
levels:
|
levels:
|
||||||
instantiation_level_1:
|
instantiation_level_1:
|
||||||
number_of_instances: 1
|
number_of_instances: 2
|
||||||
instantiation_level_2:
|
instantiation_level_2:
|
||||||
number_of_instances: 3
|
number_of_instances: 3
|
||||||
targets: [ VDU2 ]
|
targets: [ VDU2 ]
|
||||||
@@ -167,3 +238,13 @@ topology_template:
|
|||||||
instantiation_level_2:
|
instantiation_level_2:
|
||||||
number_of_instances: 3
|
number_of_instances: 3
|
||||||
targets: [ VDU3 ]
|
targets: [ VDU3 ]
|
||||||
|
|
||||||
|
- VDU5_instantiation_levels:
|
||||||
|
type: tosca.policies.nfv.VduInstantiationLevels
|
||||||
|
properties:
|
||||||
|
levels:
|
||||||
|
instantiation_level_1:
|
||||||
|
number_of_instances: 1
|
||||||
|
instantiation_level_2:
|
||||||
|
number_of_instances: 3
|
||||||
|
targets: [ VDU5 ]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: vdu3
|
name: vdu2
|
||||||
namespace: default
|
namespace: default
|
||||||
spec:
|
spec:
|
||||||
replicas: 2
|
replicas: 2
|
||||||
@@ -20,9 +20,5 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
volumes:
|
|
||||||
- name: config
|
|
||||||
configMap:
|
|
||||||
name: nginx-app-original
|
|
||||||
strategy:
|
strategy:
|
||||||
type: RollingUpdate
|
type: RollingUpdate
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (C) 2022 Fujitsu
|
# Copyright (C) 2022 Nippon Telegraph and Telephone Corporation
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@@ -12,48 +12,50 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import functools
|
||||||
import os
|
import os
|
||||||
import pickle
|
import pickle
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
class FailScript(object):
|
||||||
CMD_TIMEOUT = 30
|
"""Define error method for each operation
|
||||||
SERVER_WAIT_COMPLETE_TIME = 60
|
|
||||||
SSH_CONNECT_RETRY_COUNT = 4
|
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
class SampleNewCoordinateVNFScript(object):
|
def instantiate_start(self):
|
||||||
|
if os.path.exists('/tmp/instantiate_start')
|
||||||
|
raise Exception('test instantiate_start error')
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, req, inst, grant_req, grant, csar_dir, k8s_info):
|
def __init__(self, req, inst, grant_req, grant, csar_dir):
|
||||||
self.req = req
|
self.req = req
|
||||||
self.inst = inst
|
self.inst = inst
|
||||||
self.grant_req = grant_req
|
self.grant_req = grant_req
|
||||||
self.grant = grant
|
self.grant = grant
|
||||||
self.csar_dir = csar_dir
|
self.csar_dir = csar_dir
|
||||||
self.k8s_info = k8s_info
|
|
||||||
|
|
||||||
def coordinate_vnf(self):
|
def _fail(self, method):
|
||||||
pass
|
if os.path.exists(f'/tmp/{method}'):
|
||||||
|
raise Exception(f'test {method} error')
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return functools.partial(self._fail, name)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
operation = "coordinate_vnf"
|
|
||||||
script_dict = pickle.load(sys.stdin.buffer)
|
script_dict = pickle.load(sys.stdin.buffer)
|
||||||
|
|
||||||
|
operation = script_dict['operation']
|
||||||
req = script_dict['request']
|
req = script_dict['request']
|
||||||
inst = script_dict['vnf_instance']
|
inst = script_dict['vnf_instance']
|
||||||
grant_req = script_dict['grant_request']
|
grant_req = script_dict['grant_request']
|
||||||
grant = script_dict['grant_response']
|
grant = script_dict['grant_response']
|
||||||
csar_dir = script_dict['tmp_csar_dir']
|
csar_dir = script_dict['tmp_csar_dir']
|
||||||
k8s_info = script_dict['k8s_info']
|
|
||||||
script = SampleNewCoordinateVNFScript(
|
script = FailScript(req, inst, grant_req, grant, csar_dir)
|
||||||
req, inst, grant_req, grant,
|
getattr(script, operation)()
|
||||||
csar_dir, k8s_info)
|
|
||||||
try:
|
|
||||||
getattr(script, operation)()
|
|
||||||
except Exception:
|
|
||||||
raise Exception
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -11,7 +11,7 @@ Hash: 30071afb22afcb0e54e03df3d22f0852994b4120ca85ac72e9c207c97a4755a8
|
|||||||
Name: Files/new_kubernetes/error_deployment.yaml
|
Name: Files/new_kubernetes/error_deployment.yaml
|
||||||
Content-Type: test-data
|
Content-Type: test-data
|
||||||
Algorithm: SHA-256
|
Algorithm: SHA-256
|
||||||
Hash: 1386a46e16e1c07aef97d9c1bb6ca7a6f2af99b314ecac42094634a59577a060
|
Hash: 1fad945911465b5c12fe20bc8350f17b417e5212544d442f2d56df15b8802d8d
|
||||||
|
|
||||||
Name: Files/new_kubernetes/new_deployment.yaml
|
Name: Files/new_kubernetes/new_deployment.yaml
|
||||||
Content-Type: test-data
|
Content-Type: test-data
|
||||||
|
|||||||
@@ -35,13 +35,18 @@ shutil.move(os.path.join(tmp_dir, zip_file_name), ".")
|
|||||||
shutil.rmtree(tmp_dir)
|
shutil.rmtree(tmp_dir)
|
||||||
|
|
||||||
# if you change_vnfpkg with all parameters
|
# if you change_vnfpkg with all parameters
|
||||||
change_vnfpkg_all_params = paramgen.change_vnfpkg_all_params(vnfd_id)
|
change_vnfpkg = paramgen.change_vnfpkg(vnfd_id)
|
||||||
|
|
||||||
# if you change_vnfpkg with no operational parameters
|
# if you change_vnfpkg with no operational parameters
|
||||||
change_vnfpkg_min = paramgen.change_vnfpkg_min(vnfd_id)
|
change_vnfpkg_error = paramgen.change_vnfpkg_error(vnfd_id)
|
||||||
|
|
||||||
with open("change_vnfpkg_all_params", "w", encoding='utf-8') as f:
|
change_vnfpkg_terminate = paramgen.change_vnfpkg_terminate()
|
||||||
f.write(json.dumps(change_vnfpkg_all_params, indent=2))
|
|
||||||
|
|
||||||
with open("change_vnfpkg_min", "w", encoding='utf-8') as f:
|
with open("change_vnfpkg", "w", encoding='utf-8') as f:
|
||||||
f.write(json.dumps(change_vnfpkg_min, indent=2))
|
f.write(json.dumps(change_vnfpkg, indent=2))
|
||||||
|
|
||||||
|
with open("change_vnfpkg_error", "w", encoding='utf-8') as f:
|
||||||
|
f.write(json.dumps(change_vnfpkg_error, indent=2))
|
||||||
|
|
||||||
|
with open("change_vnfpkg_terminate", "w", encoding='utf-8') as f:
|
||||||
|
f.write(json.dumps(change_vnfpkg_terminate, indent=2))
|
||||||
|
|||||||
@@ -40,6 +40,33 @@ topology_template:
|
|||||||
type: company.provider.VNF
|
type: company.provider.VNF
|
||||||
properties:
|
properties:
|
||||||
flavour_description: A simple flavour
|
flavour_description: A simple flavour
|
||||||
|
interfaces:
|
||||||
|
Vnflcm:
|
||||||
|
instantiate_start:
|
||||||
|
implementation: sample-script
|
||||||
|
instantiate_end:
|
||||||
|
implementation: sample-script
|
||||||
|
terminate_start:
|
||||||
|
implementation: sample-script
|
||||||
|
terminate_end:
|
||||||
|
implementation: sample-script
|
||||||
|
scale_start:
|
||||||
|
implementation: sample-script
|
||||||
|
scale_end:
|
||||||
|
implementation: sample-script
|
||||||
|
heal_start:
|
||||||
|
implementation: sample-script
|
||||||
|
heal_end:
|
||||||
|
implementation: sample-script
|
||||||
|
modify_information_start:
|
||||||
|
implementation: sample-script
|
||||||
|
modify_information_end:
|
||||||
|
implementation: sample-script
|
||||||
|
artifacts:
|
||||||
|
sample-script:
|
||||||
|
description: Sample script
|
||||||
|
type: tosca.artifacts.Implementation.Python
|
||||||
|
file: ../Scripts/sample_script.py
|
||||||
|
|
||||||
VDU1:
|
VDU1:
|
||||||
type: tosca.nodes.nfv.Vdu.Compute
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
@@ -56,7 +83,7 @@ topology_template:
|
|||||||
name: vdu2
|
name: vdu2
|
||||||
description: VDU2 compute node
|
description: VDU2 compute node
|
||||||
vdu_profile:
|
vdu_profile:
|
||||||
min_number_of_instances: 2
|
min_number_of_instances: 1
|
||||||
max_number_of_instances: 3
|
max_number_of_instances: 3
|
||||||
|
|
||||||
VDU3:
|
VDU3:
|
||||||
@@ -68,15 +95,6 @@ topology_template:
|
|||||||
min_number_of_instances: 1
|
min_number_of_instances: 1
|
||||||
max_number_of_instances: 3
|
max_number_of_instances: 3
|
||||||
|
|
||||||
VDU4:
|
|
||||||
type: tosca.nodes.nfv.Vdu.Compute
|
|
||||||
properties:
|
|
||||||
name: vdu4
|
|
||||||
description: VDU4 compute node
|
|
||||||
vdu_profile:
|
|
||||||
min_number_of_instances: 1
|
|
||||||
max_number_of_instances: 3
|
|
||||||
|
|
||||||
VDU5:
|
VDU5:
|
||||||
type: tosca.nodes.nfv.Vdu.Compute
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
properties:
|
properties:
|
||||||
@@ -93,7 +111,7 @@ topology_template:
|
|||||||
description: VDU6 compute node
|
description: VDU6 compute node
|
||||||
vdu_profile:
|
vdu_profile:
|
||||||
min_number_of_instances: 1
|
min_number_of_instances: 1
|
||||||
max_number_of_instances: 3
|
max_number_of_instances: 1
|
||||||
|
|
||||||
policies:
|
policies:
|
||||||
- scaling_aspects:
|
- scaling_aspects:
|
||||||
@@ -112,24 +130,12 @@ topology_template:
|
|||||||
max_scale_level: 2
|
max_scale_level: 2
|
||||||
step_deltas:
|
step_deltas:
|
||||||
- delta_1
|
- delta_1
|
||||||
vdu4_aspect:
|
|
||||||
name: vdu4_aspect
|
|
||||||
description: vdu4 scaling aspect
|
|
||||||
max_scale_level: 2
|
|
||||||
step_deltas:
|
|
||||||
- delta_1
|
|
||||||
vdu5_aspect:
|
vdu5_aspect:
|
||||||
name: vdu5_aspect
|
name: vdu5_aspect
|
||||||
description: vdu5 scaling aspect
|
description: vdu5 scaling aspect
|
||||||
max_scale_level: 2
|
max_scale_level: 2
|
||||||
step_deltas:
|
step_deltas:
|
||||||
- delta_1
|
- delta_1
|
||||||
vdu6_aspect:
|
|
||||||
name: vdu6_aspect
|
|
||||||
description: vdu6 scaling aspect
|
|
||||||
max_scale_level: 2
|
|
||||||
step_deltas:
|
|
||||||
- delta_1
|
|
||||||
|
|
||||||
- VDU2_initial_delta:
|
- VDU2_initial_delta:
|
||||||
type: tosca.policies.nfv.VduInitialDelta
|
type: tosca.policies.nfv.VduInitialDelta
|
||||||
@@ -163,22 +169,6 @@ topology_template:
|
|||||||
number_of_instances: 1
|
number_of_instances: 1
|
||||||
targets: [ VDU3 ]
|
targets: [ VDU3 ]
|
||||||
|
|
||||||
- VDU4_initial_delta:
|
|
||||||
type: tosca.policies.nfv.VduInitialDelta
|
|
||||||
properties:
|
|
||||||
initial_delta:
|
|
||||||
number_of_instances: 1
|
|
||||||
targets: [ VDU4 ]
|
|
||||||
|
|
||||||
- VDU4_scaling_aspect_deltas:
|
|
||||||
type: tosca.policies.nfv.VduScalingAspectDeltas
|
|
||||||
properties:
|
|
||||||
aspect: vdu4_aspect
|
|
||||||
deltas:
|
|
||||||
delta_1:
|
|
||||||
number_of_instances: 1
|
|
||||||
targets: [ VDU4 ]
|
|
||||||
|
|
||||||
- VDU5_initial_delta:
|
- VDU5_initial_delta:
|
||||||
type: tosca.policies.nfv.VduInitialDelta
|
type: tosca.policies.nfv.VduInitialDelta
|
||||||
properties:
|
properties:
|
||||||
@@ -195,22 +185,6 @@ topology_template:
|
|||||||
number_of_instances: 1
|
number_of_instances: 1
|
||||||
targets: [ VDU5 ]
|
targets: [ VDU5 ]
|
||||||
|
|
||||||
- VDU6_initial_delta:
|
|
||||||
type: tosca.policies.nfv.VduInitialDelta
|
|
||||||
properties:
|
|
||||||
initial_delta:
|
|
||||||
number_of_instances: 1
|
|
||||||
targets: [ VDU6 ]
|
|
||||||
|
|
||||||
- VDU6_scaling_aspect_deltas:
|
|
||||||
type: tosca.policies.nfv.VduScalingAspectDeltas
|
|
||||||
properties:
|
|
||||||
aspect: vdu6_aspect
|
|
||||||
deltas:
|
|
||||||
delta_1:
|
|
||||||
number_of_instances: 1
|
|
||||||
targets: [ VDU6 ]
|
|
||||||
|
|
||||||
- instantiation_levels:
|
- instantiation_levels:
|
||||||
type: tosca.policies.nfv.InstantiationLevels
|
type: tosca.policies.nfv.InstantiationLevels
|
||||||
properties:
|
properties:
|
||||||
@@ -219,15 +193,11 @@ topology_template:
|
|||||||
description: Smallest size
|
description: Smallest size
|
||||||
scale_info:
|
scale_info:
|
||||||
vdu2_aspect:
|
vdu2_aspect:
|
||||||
scale_level: 0
|
scale_level: 1
|
||||||
vdu3_aspect:
|
vdu3_aspect:
|
||||||
scale_level: 0
|
scale_level: 0
|
||||||
vdu4_aspect:
|
|
||||||
scale_level: 2
|
|
||||||
vdu5_aspect:
|
vdu5_aspect:
|
||||||
scale_level: 2
|
scale_level: 0
|
||||||
vdu6_aspect:
|
|
||||||
scale_level: 2
|
|
||||||
instantiation_level_2:
|
instantiation_level_2:
|
||||||
description: Largest size
|
description: Largest size
|
||||||
scale_info:
|
scale_info:
|
||||||
@@ -235,12 +205,8 @@ topology_template:
|
|||||||
scale_level: 2
|
scale_level: 2
|
||||||
vdu3_aspect:
|
vdu3_aspect:
|
||||||
scale_level: 2
|
scale_level: 2
|
||||||
vdu4_aspect:
|
|
||||||
scale_level: 2
|
|
||||||
vdu5_aspect:
|
vdu5_aspect:
|
||||||
scale_level: 2
|
scale_level: 2
|
||||||
vdu6_aspect:
|
|
||||||
scale_level: 2
|
|
||||||
default_level: instantiation_level_1
|
default_level: instantiation_level_1
|
||||||
|
|
||||||
- VDU1_instantiation_levels:
|
- VDU1_instantiation_levels:
|
||||||
@@ -273,16 +239,6 @@ topology_template:
|
|||||||
number_of_instances: 3
|
number_of_instances: 3
|
||||||
targets: [ VDU3 ]
|
targets: [ VDU3 ]
|
||||||
|
|
||||||
- VDU4_instantiation_levels:
|
|
||||||
type: tosca.policies.nfv.VduInstantiationLevels
|
|
||||||
properties:
|
|
||||||
levels:
|
|
||||||
instantiation_level_1:
|
|
||||||
number_of_instances: 1
|
|
||||||
instantiation_level_2:
|
|
||||||
number_of_instances: 3
|
|
||||||
targets: [ VDU4 ]
|
|
||||||
|
|
||||||
- VDU5_instantiation_levels:
|
- VDU5_instantiation_levels:
|
||||||
type: tosca.policies.nfv.VduInstantiationLevels
|
type: tosca.policies.nfv.VduInstantiationLevels
|
||||||
properties:
|
properties:
|
||||||
@@ -292,13 +248,3 @@ topology_template:
|
|||||||
instantiation_level_2:
|
instantiation_level_2:
|
||||||
number_of_instances: 3
|
number_of_instances: 3
|
||||||
targets: [ VDU5 ]
|
targets: [ VDU5 ]
|
||||||
|
|
||||||
- VDU6_instantiation_levels:
|
|
||||||
type: tosca.policies.nfv.VduInstantiationLevels
|
|
||||||
properties:
|
|
||||||
levels:
|
|
||||||
instantiation_level_1:
|
|
||||||
number_of_instances: 1
|
|
||||||
instantiation_level_2:
|
|
||||||
number_of_instances: 3
|
|
||||||
targets: [ VDU6 ]
|
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
# Copyright (C) 2022 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 pickle
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class SampleOldCoordinateVNFScript(object):
|
|
||||||
|
|
||||||
def __init__(self, req, inst, grant_req, grant, csar_dir, k8s_info):
|
|
||||||
self.req = req
|
|
||||||
self.inst = inst
|
|
||||||
self.grant_req = grant_req
|
|
||||||
self.grant = grant
|
|
||||||
self.csar_dir = csar_dir
|
|
||||||
self.k8s_info = k8s_info
|
|
||||||
|
|
||||||
def coordinate_vnf(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
operation = "coordinate_vnf"
|
|
||||||
script_dict = pickle.load(sys.stdin.buffer)
|
|
||||||
req = script_dict['request']
|
|
||||||
inst = script_dict['vnf_instance']
|
|
||||||
grant_req = script_dict['grant_request']
|
|
||||||
grant = script_dict['grant_response']
|
|
||||||
csar_dir = script_dict['tmp_csar_dir']
|
|
||||||
k8s_info = script_dict['k8s_info']
|
|
||||||
script = SampleOldCoordinateVNFScript(
|
|
||||||
req, inst, grant_req, grant,
|
|
||||||
csar_dir, k8s_info)
|
|
||||||
try:
|
|
||||||
getattr(script, operation)()
|
|
||||||
except Exception:
|
|
||||||
raise Exception
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
try:
|
|
||||||
main()
|
|
||||||
os._exit(0)
|
|
||||||
except Exception as ex:
|
|
||||||
sys.stderr.write(str(ex))
|
|
||||||
sys.stderr.flush()
|
|
||||||
os._exit(1)
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (C) 2022 Fujitsu
|
# Copyright (C) 2022 Nippon Telegraph and Telephone Corporation
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@@ -12,45 +12,50 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import functools
|
||||||
import os
|
import os
|
||||||
import pickle
|
import pickle
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
class FailScript(object):
|
||||||
|
"""Define error method for each operation
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
class SampleOldCoordinateVNFScript(object):
|
def instantiate_start(self):
|
||||||
|
if os.path.exists('/tmp/instantiate_start')
|
||||||
|
raise Exception('test instantiate_start error')
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, req, inst, grant_req, grant, csar_dir, k8s_info):
|
def __init__(self, req, inst, grant_req, grant, csar_dir):
|
||||||
self.req = req
|
self.req = req
|
||||||
self.inst = inst
|
self.inst = inst
|
||||||
self.grant_req = grant_req
|
self.grant_req = grant_req
|
||||||
self.grant = grant
|
self.grant = grant
|
||||||
self.csar_dir = csar_dir
|
self.csar_dir = csar_dir
|
||||||
self.k8s_info = k8s_info
|
|
||||||
|
|
||||||
def coordinate_vnf(self):
|
def _fail(self, method):
|
||||||
pass
|
if os.path.exists(f'/tmp/{method}'):
|
||||||
|
raise Exception(f'test {method} error')
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return functools.partial(self._fail, name)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
operation = "coordinate_vnf"
|
|
||||||
script_dict = pickle.load(sys.stdin.buffer)
|
script_dict = pickle.load(sys.stdin.buffer)
|
||||||
|
|
||||||
|
operation = script_dict['operation']
|
||||||
req = script_dict['request']
|
req = script_dict['request']
|
||||||
inst = script_dict['vnf_instance']
|
inst = script_dict['vnf_instance']
|
||||||
grant_req = script_dict['grant_request']
|
grant_req = script_dict['grant_request']
|
||||||
grant = script_dict['grant_response']
|
grant = script_dict['grant_response']
|
||||||
csar_dir = script_dict['tmp_csar_dir']
|
csar_dir = script_dict['tmp_csar_dir']
|
||||||
k8s_info = script_dict['k8s_info']
|
|
||||||
script = SampleOldCoordinateVNFScript(
|
script = FailScript(req, inst, grant_req, grant, csar_dir)
|
||||||
req, inst, grant_req, grant,
|
getattr(script, operation)()
|
||||||
csar_dir, k8s_info)
|
|
||||||
try:
|
|
||||||
getattr(script, operation)()
|
|
||||||
except Exception:
|
|
||||||
raise Exception
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -44,6 +44,9 @@ max_sample_instantiate = paramgen.max_sample_instantiate(
|
|||||||
auth_url, bearer_token)
|
auth_url, bearer_token)
|
||||||
|
|
||||||
max_sample_terminate = paramgen.max_sample_terminate()
|
max_sample_terminate = paramgen.max_sample_terminate()
|
||||||
|
max_sample_scale_out = paramgen.max_sample_scale_out()
|
||||||
|
max_sample_scale_in = paramgen.max_sample_scale_in()
|
||||||
|
max_sample_heal = paramgen.max_sample_heal(["replace real vnfc ids"])
|
||||||
|
|
||||||
# if you instantiate with only one resource
|
# if you instantiate with only one resource
|
||||||
# please change vim_id to your k8s's vim id
|
# please change vim_id to your k8s's vim id
|
||||||
@@ -55,6 +58,11 @@ min_sample_terminate = paramgen.min_sample_terminate()
|
|||||||
change_vnfpkg_instantiate = paramgen.change_vnfpkg_instantiate(
|
change_vnfpkg_instantiate = paramgen.change_vnfpkg_instantiate(
|
||||||
auth_url, bearer_token)
|
auth_url, bearer_token)
|
||||||
|
|
||||||
|
error_handling_instantiate = paramgen.error_handling_instantiate(
|
||||||
|
auth_url, bearer_token)
|
||||||
|
error_handling_scale_out = paramgen.error_handling_scale_out()
|
||||||
|
error_handling_terminate = paramgen.error_handling_terminate()
|
||||||
|
|
||||||
with open("create_req", "w", encoding='utf-8') as f:
|
with open("create_req", "w", encoding='utf-8') as f:
|
||||||
f.write(json.dumps(create_req, indent=2))
|
f.write(json.dumps(create_req, indent=2))
|
||||||
|
|
||||||
@@ -64,6 +72,15 @@ with open("max_sample_instantiate", "w", encoding='utf-8') as f:
|
|||||||
with open("max_sample_terminate", "w", encoding='utf-8') as f:
|
with open("max_sample_terminate", "w", encoding='utf-8') as f:
|
||||||
f.write(json.dumps(max_sample_terminate, indent=2))
|
f.write(json.dumps(max_sample_terminate, indent=2))
|
||||||
|
|
||||||
|
with open("max_sample_scale_out", "w", encoding='utf-8') as f:
|
||||||
|
f.write(json.dumps(max_sample_scale_out, indent=2))
|
||||||
|
|
||||||
|
with open("max_sample_scale_in", "w", encoding='utf-8') as f:
|
||||||
|
f.write(json.dumps(max_sample_scale_in, indent=2))
|
||||||
|
|
||||||
|
with open("max_sample_heal", "w", encoding='utf-8') as f:
|
||||||
|
f.write(json.dumps(max_sample_heal, indent=2))
|
||||||
|
|
||||||
with open("min_sample_instantiate", "w", encoding='utf-8') as f:
|
with open("min_sample_instantiate", "w", encoding='utf-8') as f:
|
||||||
f.write(json.dumps(min_sample_instantiate, indent=2))
|
f.write(json.dumps(min_sample_instantiate, indent=2))
|
||||||
|
|
||||||
@@ -72,3 +89,12 @@ with open("min_sample_terminate", "w", encoding='utf-8') as f:
|
|||||||
|
|
||||||
with open("change_vnfpkg_instantiate", "w", encoding='utf-8') as f:
|
with open("change_vnfpkg_instantiate", "w", encoding='utf-8') as f:
|
||||||
f.write(json.dumps(change_vnfpkg_instantiate, indent=2))
|
f.write(json.dumps(change_vnfpkg_instantiate, indent=2))
|
||||||
|
|
||||||
|
with open("error_handling_instantiate", "w", encoding='utf-8') as f:
|
||||||
|
f.write(json.dumps(error_handling_instantiate, indent=2))
|
||||||
|
|
||||||
|
with open("error_handling_scale_out", "w", encoding='utf-8') as f:
|
||||||
|
f.write(json.dumps(error_handling_scale_out, indent=2))
|
||||||
|
|
||||||
|
with open("error_handling_terminate", "w", encoding='utf-8') as f:
|
||||||
|
f.write(json.dumps(error_handling_terminate, indent=2))
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import ddt
|
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@@ -21,7 +20,6 @@ from tacker.tests.functional.sol_kubernetes_v2 import base_v2
|
|||||||
from tacker.tests.functional.sol_kubernetes_v2 import paramgen
|
from tacker.tests.functional.sol_kubernetes_v2 import paramgen
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
|
||||||
class VnfLcmKubernetesChangeVnfpkgTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
class VnfLcmKubernetesChangeVnfpkgTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -50,16 +48,8 @@ class VnfLcmKubernetesChangeVnfpkgTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(VnfLcmKubernetesChangeVnfpkgTest, self).setUp()
|
super(VnfLcmKubernetesChangeVnfpkgTest, self).setUp()
|
||||||
|
|
||||||
def test_change_vnfpkg_for_deployment_res_with_all_params(self):
|
def test_change_vnfpkg_for_deployment_res(self):
|
||||||
"""Test ChangeCurrentVNFPackage with all attributes set
|
"""Test ChangeCurrentVNFPackage
|
||||||
|
|
||||||
* About attributes:
|
|
||||||
All of the following cardinality attributes are set.
|
|
||||||
In addition, 0..N or 1..N attributes are set to 2 or more.
|
|
||||||
- 0..1 (1)
|
|
||||||
- 0..N (2 or more)
|
|
||||||
- 1
|
|
||||||
- 1..N (2 or more)
|
|
||||||
|
|
||||||
* About LCM operations:
|
* About LCM operations:
|
||||||
This test includes the following operations.
|
This test includes the following operations.
|
||||||
@@ -114,9 +104,6 @@ class VnfLcmKubernetesChangeVnfpkgTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
self.wait_lcmocc_complete(lcmocc_id)
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
# check vnfc_resource_info
|
|
||||||
# TODO()
|
|
||||||
|
|
||||||
# 3. Show VNF instance
|
# 3. Show VNF instance
|
||||||
additional_inst_attrs = [
|
additional_inst_attrs = [
|
||||||
'vimConnectionInfo',
|
'vimConnectionInfo',
|
||||||
@@ -128,19 +115,20 @@ class VnfLcmKubernetesChangeVnfpkgTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
self.check_resp_headers_in_get(resp)
|
self.check_resp_headers_in_get(resp)
|
||||||
self.check_resp_body(body, expected_inst_attrs)
|
self.check_resp_body(body, expected_inst_attrs)
|
||||||
|
|
||||||
vnfc_resource_infos = body['instantiatedVnfInfo'].get(
|
vnfc_resource_infos = body['instantiatedVnfInfo']['vnfcResourceInfo']
|
||||||
'vnfcResourceInfo')
|
before_resource_ids = {vnfc_info['computeResource']['resourceId']
|
||||||
before_resource_ids = [vnfc_info['computeResource']['resourceId']
|
for vnfc_info in vnfc_resource_infos}
|
||||||
for vnfc_info in vnfc_resource_infos]
|
self.assertEqual(2, len(before_resource_ids))
|
||||||
|
|
||||||
# 4. Change Current VNF Package
|
# 4. Change Current VNF Package
|
||||||
change_vnfpkg_req = paramgen.change_vnfpkg_all_params(self.vnfd_id_2)
|
change_vnfpkg_req = paramgen.change_vnfpkg(self.vnfd_id_2)
|
||||||
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
|
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
|
||||||
self.assertEqual(202, resp.status_code)
|
self.assertEqual(202, resp.status_code)
|
||||||
self.check_resp_headers_in_operation_task(resp)
|
self.check_resp_headers_in_operation_task(resp)
|
||||||
|
|
||||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
self.wait_lcmocc_complete(lcmocc_id)
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
# check usageState of VNF Package
|
# check usageState of VNF Package
|
||||||
usage_state = self.get_vnf_package(self.vnf_pkg_1).get('usageState')
|
usage_state = self.get_vnf_package(self.vnf_pkg_1).get('usageState')
|
||||||
@@ -160,14 +148,14 @@ class VnfLcmKubernetesChangeVnfpkgTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
self.check_resp_headers_in_get(resp)
|
self.check_resp_headers_in_get(resp)
|
||||||
self.check_resp_body(body, expected_inst_attrs)
|
self.check_resp_body(body, expected_inst_attrs)
|
||||||
|
|
||||||
vnfc_resource_infos = body['instantiatedVnfInfo'].get(
|
vnfc_resource_infos = body['instantiatedVnfInfo']['vnfcResourceInfo']
|
||||||
'vnfcResourceInfo')
|
after_resource_ids = {vnfc_info['computeResource']['resourceId']
|
||||||
after_resource_ids = [vnfc_info['computeResource']['resourceId']
|
for vnfc_info in vnfc_resource_infos}
|
||||||
for vnfc_info in vnfc_resource_infos]
|
self.assertEqual(2, len(after_resource_ids))
|
||||||
self.assertNotEqual(before_resource_ids, after_resource_ids)
|
self.assertNotEqual(before_resource_ids, after_resource_ids)
|
||||||
|
|
||||||
# 6. Terminate a VNF instance
|
# 6. Terminate a VNF instance
|
||||||
terminate_req = paramgen.max_sample_terminate()
|
terminate_req = paramgen.change_vnfpkg_terminate()
|
||||||
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||||
self.assertEqual(202, resp.status_code)
|
self.assertEqual(202, resp.status_code)
|
||||||
self.check_resp_headers_in_operation_task(resp)
|
self.check_resp_headers_in_operation_task(resp)
|
||||||
@@ -177,7 +165,7 @@ class VnfLcmKubernetesChangeVnfpkgTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
|
|
||||||
# wait a bit because there is a bit time lag between lcmocc DB
|
# wait a bit because there is a bit time lag between lcmocc DB
|
||||||
# update and terminate completion.
|
# update and terminate completion.
|
||||||
time.sleep(10)
|
time.sleep(3)
|
||||||
|
|
||||||
# 7. Delete a VNF instance
|
# 7. Delete a VNF instance
|
||||||
resp, body = self.delete_vnf_instance(inst_id)
|
resp, body = self.delete_vnf_instance(inst_id)
|
||||||
@@ -192,14 +180,8 @@ class VnfLcmKubernetesChangeVnfpkgTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
usage_state = self.get_vnf_package(self.vnf_pkg_2).get('usageState')
|
usage_state = self.get_vnf_package(self.vnf_pkg_2).get('usageState')
|
||||||
self.assertEqual('NOT_IN_USE', usage_state)
|
self.assertEqual('NOT_IN_USE', usage_state)
|
||||||
|
|
||||||
def test_change_vnfpkg_for_deployment_res_with_no_op_params(self):
|
def test_change_vnfpkg_failed_and_rollback(self):
|
||||||
"""Test ChangeCurrentVNFPackage with no optional attributes
|
"""Test LCM operations error handing
|
||||||
|
|
||||||
* About attributes:
|
|
||||||
Omit except for required attributes.
|
|
||||||
Only the following cardinality attributes are set.
|
|
||||||
- 1
|
|
||||||
- 1..N (1)
|
|
||||||
|
|
||||||
* About LCM operations:
|
* About LCM operations:
|
||||||
This test includes the following operations.
|
This test includes the following operations.
|
||||||
@@ -207,9 +189,10 @@ class VnfLcmKubernetesChangeVnfpkgTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
- 2. Instantiate a VNF instance
|
- 2. Instantiate a VNF instance
|
||||||
- 3. Show VNF instance
|
- 3. Show VNF instance
|
||||||
- 4. Change Current VNF Package
|
- 4. Change Current VNF Package
|
||||||
- 5. Show VNF instance
|
- 5. Rollback Change Current VNF Package
|
||||||
- 6. Terminate a VNF instance
|
- 6. Show VNF instance
|
||||||
- 7. Delete a VNF instance
|
- 7. Terminate a VNF instance
|
||||||
|
- 8. Delete a VNF instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 1. Create a new VNF instance resource
|
# 1. Create a new VNF instance resource
|
||||||
@@ -245,8 +228,8 @@ class VnfLcmKubernetesChangeVnfpkgTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
self.assertEqual('IN_USE', usage_state)
|
self.assertEqual('IN_USE', usage_state)
|
||||||
|
|
||||||
# 2. Instantiate a VNF instance
|
# 2. Instantiate a VNF instance
|
||||||
vim_id = self.get_k8s_vim_id()
|
instantiate_req = paramgen.change_vnfpkg_instantiate(
|
||||||
instantiate_req = paramgen.change_vnfpkg_instantiate_min(vim_id)
|
self.auth_url, self.bearer_token)
|
||||||
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
||||||
self.assertEqual(202, resp.status_code)
|
self.assertEqual(202, resp.status_code)
|
||||||
self.check_resp_headers_in_operation_task(resp)
|
self.check_resp_headers_in_operation_task(resp)
|
||||||
@@ -265,28 +248,26 @@ class VnfLcmKubernetesChangeVnfpkgTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
self.check_resp_headers_in_get(resp)
|
self.check_resp_headers_in_get(resp)
|
||||||
self.check_resp_body(body, expected_inst_attrs)
|
self.check_resp_body(body, expected_inst_attrs)
|
||||||
|
|
||||||
vnfc_resource_infos = body['instantiatedVnfInfo'].get(
|
vnfc_resource_infos = body['instantiatedVnfInfo']['vnfcResourceInfo']
|
||||||
'vnfcResourceInfo')
|
|
||||||
before_resource_ids = [vnfc_info['computeResource']['resourceId']
|
before_resource_ids = [vnfc_info['computeResource']['resourceId']
|
||||||
for vnfc_info in vnfc_resource_infos]
|
for vnfc_info in vnfc_resource_infos]
|
||||||
|
|
||||||
# 4. Change Current VNF Package
|
# 4. Change Current VNF Package (will fail)
|
||||||
change_vnfpkg_req = paramgen.change_vnfpkg_min(self.vnfd_id_2)
|
change_vnfpkg_req = paramgen.change_vnfpkg_error(self.vnfd_id_2)
|
||||||
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
|
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
|
||||||
self.assertEqual(202, resp.status_code)
|
self.assertEqual(202, resp.status_code)
|
||||||
self.check_resp_headers_in_operation_task(resp)
|
self.check_resp_headers_in_operation_task(resp)
|
||||||
|
|
||||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
self.wait_lcmocc_complete(lcmocc_id)
|
self.wait_lcmocc_failed_temp(lcmocc_id)
|
||||||
|
|
||||||
# check usageState of VNF Package
|
# 5. Rollback Change Current VNF Package operation
|
||||||
usage_state = self.get_vnf_package(self.vnf_pkg_1).get('usageState')
|
resp, body = self.rollback_lcmocc(lcmocc_id)
|
||||||
self.assertEqual('NOT_IN_USE', usage_state)
|
self.assertEqual(202, resp.status_code)
|
||||||
# check usageState of VNF Package
|
self.check_resp_headers_in_delete(resp)
|
||||||
usage_state = self.get_vnf_package(self.vnf_pkg_2).get('usageState')
|
self.wait_lcmocc_rolled_back(lcmocc_id)
|
||||||
self.assertEqual('IN_USE', usage_state)
|
|
||||||
|
|
||||||
# 5. Show VNF instance
|
# 6. Show VNF instance
|
||||||
additional_inst_attrs = [
|
additional_inst_attrs = [
|
||||||
'vimConnectionInfo',
|
'vimConnectionInfo',
|
||||||
'instantiatedVnfInfo'
|
'instantiatedVnfInfo'
|
||||||
@@ -297,14 +278,13 @@ class VnfLcmKubernetesChangeVnfpkgTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
self.check_resp_headers_in_get(resp)
|
self.check_resp_headers_in_get(resp)
|
||||||
self.check_resp_body(body, expected_inst_attrs)
|
self.check_resp_body(body, expected_inst_attrs)
|
||||||
|
|
||||||
vnfc_resource_infos = body['instantiatedVnfInfo'].get(
|
vnfc_resource_infos = body['instantiatedVnfInfo']['vnfcResourceInfo']
|
||||||
'vnfcResourceInfo')
|
|
||||||
after_resource_ids = [vnfc_info['computeResource']['resourceId']
|
after_resource_ids = [vnfc_info['computeResource']['resourceId']
|
||||||
for vnfc_info in vnfc_resource_infos]
|
for vnfc_info in vnfc_resource_infos]
|
||||||
self.assertNotEqual(before_resource_ids, after_resource_ids)
|
self.assertEqual(before_resource_ids, after_resource_ids)
|
||||||
|
|
||||||
# 6. Terminate a VNF instance
|
# 7. Terminate a VNF instance
|
||||||
terminate_req = paramgen.max_sample_terminate()
|
terminate_req = paramgen.change_vnfpkg_terminate()
|
||||||
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||||
self.assertEqual(202, resp.status_code)
|
self.assertEqual(202, resp.status_code)
|
||||||
self.check_resp_headers_in_operation_task(resp)
|
self.check_resp_headers_in_operation_task(resp)
|
||||||
@@ -314,9 +294,9 @@ class VnfLcmKubernetesChangeVnfpkgTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
|
|
||||||
# wait a bit because there is a bit time lag between lcmocc DB
|
# wait a bit because there is a bit time lag between lcmocc DB
|
||||||
# update and terminate completion.
|
# update and terminate completion.
|
||||||
time.sleep(10)
|
time.sleep(3)
|
||||||
|
|
||||||
# 7. Delete a VNF instance
|
# 8. Delete a VNF instance
|
||||||
resp, body = self.delete_vnf_instance(inst_id)
|
resp, body = self.delete_vnf_instance(inst_id)
|
||||||
self.assertEqual(204, resp.status_code)
|
self.assertEqual(204, resp.status_code)
|
||||||
self.check_resp_headers_in_delete(resp)
|
self.check_resp_headers_in_delete(resp)
|
||||||
|
|||||||
@@ -60,8 +60,14 @@ class VnfLcmKubernetesTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
- 1. Create a new VNF instance resource
|
- 1. Create a new VNF instance resource
|
||||||
- 2. Instantiate a VNF instance
|
- 2. Instantiate a VNF instance
|
||||||
- 3. Show VNF instance
|
- 3. Show VNF instance
|
||||||
- 4. Terminate a VNF instance
|
- 4. Scale out a VNF instance
|
||||||
- 5. Delete a VNF instance
|
- 5. Show VNF instance
|
||||||
|
- 6. Scale in a VNF instance
|
||||||
|
- 7. Show VNF instance
|
||||||
|
- 8. Heal in a VNF instance
|
||||||
|
- 9. Show VNF instance
|
||||||
|
- 10. Terminate a VNF instance
|
||||||
|
- 11. Delete a VNF instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 1. Create a new VNF instance resource
|
# 1. Create a new VNF instance resource
|
||||||
@@ -118,26 +124,99 @@ class VnfLcmKubernetesTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
self.check_resp_body(body, expected_inst_attrs)
|
self.check_resp_body(body, expected_inst_attrs)
|
||||||
|
|
||||||
# check vnfc_resource_info
|
# check vnfc_resource_info
|
||||||
vnfc_resource_infos = body['instantiatedVnfInfo'].get(
|
vnfc_resource_infos = body['instantiatedVnfInfo']['vnfcResourceInfo']
|
||||||
'vnfcResourceInfo')
|
vdu_nums = {'VDU1': 0, 'VDU2': 0, 'VDU3': 0, 'VDU5': 0, 'VDU6': 0}
|
||||||
for vnfc_info in vnfc_resource_infos:
|
for vnfc_info in vnfc_resource_infos:
|
||||||
if vnfc_info['vduId'] == 'VDU1':
|
if vnfc_info['vduId'] == 'VDU1':
|
||||||
self.assertEqual('Pod', vnfc_info[
|
self.assertEqual('Pod', vnfc_info[
|
||||||
'computeResource']['vimLevelResourceType'])
|
'computeResource']['vimLevelResourceType'])
|
||||||
|
vdu_nums['VDU1'] += 1
|
||||||
elif vnfc_info['vduId'] == 'VDU2':
|
elif vnfc_info['vduId'] == 'VDU2':
|
||||||
self.assertEqual('Deployment', vnfc_info[
|
self.assertEqual('Deployment', vnfc_info[
|
||||||
'computeResource']['vimLevelResourceType'])
|
'computeResource']['vimLevelResourceType'])
|
||||||
|
vdu_nums['VDU2'] += 1
|
||||||
elif vnfc_info['vduId'] == 'VDU3':
|
elif vnfc_info['vduId'] == 'VDU3':
|
||||||
self.assertEqual('ReplicaSet', vnfc_info[
|
self.assertEqual('ReplicaSet', vnfc_info[
|
||||||
'computeResource']['vimLevelResourceType'])
|
'computeResource']['vimLevelResourceType'])
|
||||||
|
vdu_nums['VDU3'] += 1
|
||||||
elif vnfc_info['vduId'] == 'VDU5':
|
elif vnfc_info['vduId'] == 'VDU5':
|
||||||
self.assertEqual('StatefulSet', vnfc_info[
|
self.assertEqual('StatefulSet', vnfc_info[
|
||||||
'computeResource']['vimLevelResourceType'])
|
'computeResource']['vimLevelResourceType'])
|
||||||
|
vdu_nums['VDU5'] += 1
|
||||||
elif vnfc_info['vduId'] == 'VDU6':
|
elif vnfc_info['vduId'] == 'VDU6':
|
||||||
self.assertEqual('DaemonSet', vnfc_info[
|
self.assertEqual('DaemonSet', vnfc_info[
|
||||||
'computeResource']['vimLevelResourceType'])
|
'computeResource']['vimLevelResourceType'])
|
||||||
|
vdu_nums['VDU6'] += 1
|
||||||
|
expected = {'VDU1': 1, 'VDU2': 2, 'VDU3': 1, 'VDU5': 1, 'VDU6': 1}
|
||||||
|
self.assertEqual(expected, vdu_nums)
|
||||||
|
|
||||||
# 4. Terminate a VNF instance
|
# 4. Scale out a VNF instance
|
||||||
|
scale_out_req = paramgen.max_sample_scale_out()
|
||||||
|
resp, body = self.scale_vnf_instance(inst_id, scale_out_req)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
self.check_resp_headers_in_operation_task(resp)
|
||||||
|
|
||||||
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
# 5. Show VNF instance
|
||||||
|
resp, body = self.show_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.check_resp_headers_in_get(resp)
|
||||||
|
|
||||||
|
# check vnfc_resource_info
|
||||||
|
vnfc_resource_infos = body['instantiatedVnfInfo']['vnfcResourceInfo']
|
||||||
|
vdu3_infos = [vnfc_info for vnfc_info in vnfc_resource_infos
|
||||||
|
if vnfc_info['vduId'] == 'VDU3']
|
||||||
|
self.assertEqual(3, len(vdu3_infos))
|
||||||
|
|
||||||
|
# 6. Scale in a VNF instance
|
||||||
|
scale_in_req = paramgen.max_sample_scale_in()
|
||||||
|
resp, body = self.scale_vnf_instance(inst_id, scale_in_req)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
self.check_resp_headers_in_operation_task(resp)
|
||||||
|
|
||||||
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
# 7. Show VNF instance
|
||||||
|
resp, body = self.show_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.check_resp_headers_in_get(resp)
|
||||||
|
|
||||||
|
# check vnfc_resource_info
|
||||||
|
vnfc_resource_infos = body['instantiatedVnfInfo']['vnfcResourceInfo']
|
||||||
|
vdu3_infos = [vnfc_info for vnfc_info in vnfc_resource_infos
|
||||||
|
if vnfc_info['vduId'] == 'VDU3']
|
||||||
|
self.assertEqual(2, len(vdu3_infos))
|
||||||
|
|
||||||
|
# 8. Heal a VNF instance
|
||||||
|
vnfc_infos = body['instantiatedVnfInfo']['vnfcInfo']
|
||||||
|
vdu2_ids = [vnfc_info['id'] for vnfc_info in vnfc_infos
|
||||||
|
if vnfc_info['vduId'] == 'VDU2']
|
||||||
|
target = [vdu2_ids[0]]
|
||||||
|
heal_req = paramgen.max_sample_heal(target)
|
||||||
|
resp, body = self.heal_vnf_instance(inst_id, heal_req)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
self.check_resp_headers_in_operation_task(resp)
|
||||||
|
|
||||||
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
# 9. Show VNF instance
|
||||||
|
resp, body = self.show_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.check_resp_headers_in_get(resp)
|
||||||
|
|
||||||
|
# check vnfc_resource_info
|
||||||
|
vnfc_infos = body['instantiatedVnfInfo']['vnfcInfo']
|
||||||
|
result_vdu2_ids = [vnfc_info['id'] for vnfc_info in vnfc_infos
|
||||||
|
if vnfc_info['vduId'] == 'VDU2']
|
||||||
|
self.assertEqual(2, len(result_vdu2_ids))
|
||||||
|
self.assertNotIn(vdu2_ids[0], result_vdu2_ids)
|
||||||
|
self.assertIn(vdu2_ids[1], result_vdu2_ids)
|
||||||
|
|
||||||
|
# 10. Terminate a VNF instance
|
||||||
terminate_req = paramgen.max_sample_terminate()
|
terminate_req = paramgen.max_sample_terminate()
|
||||||
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||||
self.assertEqual(202, resp.status_code)
|
self.assertEqual(202, resp.status_code)
|
||||||
@@ -148,9 +227,9 @@ class VnfLcmKubernetesTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
|
|
||||||
# wait a bit because there is a bit time lag between lcmocc DB
|
# wait a bit because there is a bit time lag between lcmocc DB
|
||||||
# update and terminate completion.
|
# update and terminate completion.
|
||||||
time.sleep(10)
|
time.sleep(3)
|
||||||
|
|
||||||
# 5. Delete a VNF instance
|
# 11. Delete a VNF instance
|
||||||
resp, body = self.delete_vnf_instance(inst_id)
|
resp, body = self.delete_vnf_instance(inst_id)
|
||||||
self.assertEqual(204, resp.status_code)
|
self.assertEqual(204, resp.status_code)
|
||||||
self.check_resp_headers_in_delete(resp)
|
self.check_resp_headers_in_delete(resp)
|
||||||
@@ -250,7 +329,7 @@ class VnfLcmKubernetesTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
|
|
||||||
# wait a bit because there is a bit time lag between lcmocc DB
|
# wait a bit because there is a bit time lag between lcmocc DB
|
||||||
# update and terminate completion.
|
# update and terminate completion.
|
||||||
time.sleep(10)
|
time.sleep(3)
|
||||||
|
|
||||||
# 5. Delete a VNF instance
|
# 5. Delete a VNF instance
|
||||||
resp, body = self.delete_vnf_instance(inst_id)
|
resp, body = self.delete_vnf_instance(inst_id)
|
||||||
@@ -264,3 +343,152 @@ class VnfLcmKubernetesTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|||||||
# check usageState of VNF Package
|
# check usageState of VNF Package
|
||||||
usage_state = self.get_vnf_package(self.vnf_pkg_1).get('usageState')
|
usage_state = self.get_vnf_package(self.vnf_pkg_1).get('usageState')
|
||||||
self.assertEqual('NOT_IN_USE', usage_state)
|
self.assertEqual('NOT_IN_USE', usage_state)
|
||||||
|
|
||||||
|
def _put_fail_file(self, operation):
|
||||||
|
with open(f'/tmp/{operation}', 'w'):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _rm_fail_file(self, operation):
|
||||||
|
os.remove(f'/tmp/{operation}')
|
||||||
|
|
||||||
|
def test_instantiate_rollback(self):
|
||||||
|
"""Test LCM operations with all attributes set
|
||||||
|
|
||||||
|
* About LCM operations:
|
||||||
|
This test includes the following operations.
|
||||||
|
- 1. Create a new VNF instance resource
|
||||||
|
- 2. Instantiate a VNF instance => FAILED_TEMP
|
||||||
|
- 3. Show VNF instance
|
||||||
|
- 4. Rollback instantiate
|
||||||
|
- 5. Show VNF instance
|
||||||
|
- 6. Delete a VNF instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 1. Create a new VNF instance resource
|
||||||
|
create_req = paramgen.test_instantiate_cnf_resources_create(
|
||||||
|
self.vnfd_id_1)
|
||||||
|
resp, body = self.create_vnf_instance(create_req)
|
||||||
|
self.assertEqual(201, resp.status_code)
|
||||||
|
self.check_resp_headers_in_create(resp)
|
||||||
|
inst_id = body['id']
|
||||||
|
|
||||||
|
# 2. Instantiate a VNF instance
|
||||||
|
self._put_fail_file('instantiate_end')
|
||||||
|
instantiate_req = paramgen.error_handling_instantiate(
|
||||||
|
self.auth_url, self.bearer_token)
|
||||||
|
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
self.check_resp_headers_in_operation_task(resp)
|
||||||
|
|
||||||
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
|
self.wait_lcmocc_failed_temp(lcmocc_id)
|
||||||
|
self._rm_fail_file('instantiate_end')
|
||||||
|
|
||||||
|
# 3. Show VNF instance
|
||||||
|
resp, body = self.show_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.check_resp_headers_in_get(resp)
|
||||||
|
self.assertEqual('NOT_INSTANTIATED', body['instantiationState'])
|
||||||
|
|
||||||
|
# 4. Rollback instantiate
|
||||||
|
resp, body = self.rollback_lcmocc(lcmocc_id)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
self.wait_lcmocc_rolled_back(lcmocc_id)
|
||||||
|
|
||||||
|
# 5. Show VNF instance
|
||||||
|
resp, body = self.show_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.check_resp_headers_in_get(resp)
|
||||||
|
self.assertEqual('NOT_INSTANTIATED', body['instantiationState'])
|
||||||
|
|
||||||
|
# 6. Delete a VNF instance
|
||||||
|
resp, body = self.delete_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
self.check_resp_headers_in_delete(resp)
|
||||||
|
|
||||||
|
def test_scale_out_rollback(self):
|
||||||
|
"""Test LCM operations with all attributes set
|
||||||
|
|
||||||
|
* About LCM operations:
|
||||||
|
This test includes the following operations.
|
||||||
|
- 1. Create a new VNF instance resource
|
||||||
|
- 2. Instantiate a VNF instance
|
||||||
|
- 3. Show VNF instance
|
||||||
|
- 4. Scale out ==> FAILED_TEMP
|
||||||
|
- 5. Rollback
|
||||||
|
- 5. Show VNF instance
|
||||||
|
- 6. Terminate a VNF instance
|
||||||
|
- 7. Delete a VNF instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 1. Create a new VNF instance resource
|
||||||
|
create_req = paramgen.test_instantiate_cnf_resources_create(
|
||||||
|
self.vnfd_id_1)
|
||||||
|
resp, body = self.create_vnf_instance(create_req)
|
||||||
|
self.assertEqual(201, resp.status_code)
|
||||||
|
self.check_resp_headers_in_create(resp)
|
||||||
|
inst_id = body['id']
|
||||||
|
|
||||||
|
# 2. Instantiate a VNF instance
|
||||||
|
instantiate_req = paramgen.error_handling_instantiate(
|
||||||
|
self.auth_url, self.bearer_token)
|
||||||
|
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
self.check_resp_headers_in_operation_task(resp)
|
||||||
|
|
||||||
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
# 3. Show VNF instance
|
||||||
|
resp, body = self.show_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.check_resp_headers_in_get(resp)
|
||||||
|
|
||||||
|
vnfc_resource_infos = body['instantiatedVnfInfo']['vnfcResourceInfo']
|
||||||
|
vdu2_ids_0 = {vnfc_info['id'] for vnfc_info in vnfc_resource_infos
|
||||||
|
if vnfc_info['vduId'] == 'VDU2'}
|
||||||
|
self.assertEqual(2, len(vdu2_ids_0))
|
||||||
|
|
||||||
|
# 4. Scale out a VNF instance
|
||||||
|
self._put_fail_file('scale_end')
|
||||||
|
scale_out_req = paramgen.error_handling_scale_out()
|
||||||
|
resp, body = self.scale_vnf_instance(inst_id, scale_out_req)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
self.check_resp_headers_in_operation_task(resp)
|
||||||
|
|
||||||
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
|
self.wait_lcmocc_failed_temp(lcmocc_id)
|
||||||
|
self._rm_fail_file('scale_end')
|
||||||
|
|
||||||
|
# 5. Rollback instantiate
|
||||||
|
resp, body = self.rollback_lcmocc(lcmocc_id)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
self.wait_lcmocc_rolled_back(lcmocc_id)
|
||||||
|
|
||||||
|
# 6. Show VNF instance
|
||||||
|
resp, body = self.show_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.check_resp_headers_in_get(resp)
|
||||||
|
|
||||||
|
vnfc_resource_infos = body['instantiatedVnfInfo']['vnfcResourceInfo']
|
||||||
|
vdu2_ids_1 = {vnfc_info['id'] for vnfc_info in vnfc_resource_infos
|
||||||
|
if vnfc_info['vduId'] == 'VDU2'}
|
||||||
|
self.assertEqual(vdu2_ids_0, vdu2_ids_1)
|
||||||
|
|
||||||
|
# 7. Terminate a VNF instance
|
||||||
|
terminate_req = paramgen.error_handling_terminate()
|
||||||
|
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
self.check_resp_headers_in_operation_task(resp)
|
||||||
|
|
||||||
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
# wait a bit because there is a bit time lag between lcmocc DB
|
||||||
|
# update and terminate completion.
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
# 8. Delete a VNF instance
|
||||||
|
resp, body = self.delete_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
self.check_resp_headers_in_delete(resp)
|
||||||
|
|||||||
@@ -1,474 +0,0 @@
|
|||||||
# Copyright (C) 2022 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 ddt
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from tacker.tests.functional.sol_kubernetes_v2 import base_v2
|
|
||||||
from tacker.tests.functional.sol_kubernetes_v2 import paramgen
|
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
|
||||||
class VnfLcmKubernetesErrorHandingTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
super(VnfLcmKubernetesErrorHandingTest, cls).setUpClass()
|
|
||||||
|
|
||||||
cur_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
test_instantiate_cnf_resources_path = os.path.join(
|
|
||||||
cur_dir, "samples/test_instantiate_cnf_resources")
|
|
||||||
cls.vnf_pkg_1, cls.vnfd_id_1 = cls.create_vnf_package(
|
|
||||||
test_instantiate_cnf_resources_path)
|
|
||||||
|
|
||||||
test_change_vnf_pkg_with_deployment_path = os.path.join(
|
|
||||||
cur_dir, "samples/test_change_vnf_pkg_with_deployment")
|
|
||||||
cls.vnf_pkg_2, cls.vnfd_id_2 = cls.create_vnf_package(
|
|
||||||
test_change_vnf_pkg_with_deployment_path)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
super(VnfLcmKubernetesErrorHandingTest, cls).tearDownClass()
|
|
||||||
|
|
||||||
cls.delete_vnf_package(cls.vnf_pkg_1)
|
|
||||||
cls.delete_vnf_package(cls.vnf_pkg_2)
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(VnfLcmKubernetesErrorHandingTest, self).setUp()
|
|
||||||
|
|
||||||
@unittest.skip("Until refactor CNF v2 API")
|
|
||||||
def test_change_vnfpkg_failed_in_update_wait_and_rollback(self):
|
|
||||||
"""Test LCM operations error handing
|
|
||||||
|
|
||||||
* About LCM operations:
|
|
||||||
This test includes the following operations.
|
|
||||||
- 1. Create a new VNF instance resource
|
|
||||||
- 2. Instantiate a VNF instance
|
|
||||||
- 3. Show VNF instance
|
|
||||||
- 4. Change Current VNF Package
|
|
||||||
- 5. Rollback Change Current VNF Package
|
|
||||||
- 6. Show VNF instance
|
|
||||||
- 7. Terminate a VNF instance
|
|
||||||
- 8. Delete a VNF instance
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 1. Create a new VNF instance resource
|
|
||||||
# NOTE: extensions and vnfConfigurableProperties are omitted
|
|
||||||
# because they are commented out in etsi_nfv_sol001.
|
|
||||||
expected_inst_attrs = [
|
|
||||||
'id',
|
|
||||||
'vnfInstanceName',
|
|
||||||
'vnfInstanceDescription',
|
|
||||||
'vnfdId',
|
|
||||||
'vnfProvider',
|
|
||||||
'vnfProductName',
|
|
||||||
'vnfSoftwareVersion',
|
|
||||||
'vnfdVersion',
|
|
||||||
# 'vnfConfigurableProperties', # omitted
|
|
||||||
# 'vimConnectionInfo', # omitted
|
|
||||||
'instantiationState',
|
|
||||||
# 'instantiatedVnfInfo', # omitted
|
|
||||||
'metadata',
|
|
||||||
# 'extensions', # omitted
|
|
||||||
'_links'
|
|
||||||
]
|
|
||||||
create_req = paramgen.test_instantiate_cnf_resources_create(
|
|
||||||
self.vnfd_id_1)
|
|
||||||
resp, body = self.create_vnf_instance(create_req)
|
|
||||||
self.assertEqual(201, resp.status_code)
|
|
||||||
self.check_resp_headers_in_create(resp)
|
|
||||||
self.check_resp_body(body, expected_inst_attrs)
|
|
||||||
inst_id = body['id']
|
|
||||||
|
|
||||||
# check usageState of VNF Package
|
|
||||||
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
|
|
||||||
self.assertEqual('IN_USE', usage_state)
|
|
||||||
|
|
||||||
# 2. Instantiate a VNF instance
|
|
||||||
vim_id = self.get_k8s_vim_id()
|
|
||||||
instantiate_req = paramgen.change_vnfpkg_instantiate_error_handing(
|
|
||||||
vim_id)
|
|
||||||
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
|
||||||
self.assertEqual(202, resp.status_code)
|
|
||||||
self.check_resp_headers_in_operation_task(resp)
|
|
||||||
|
|
||||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
|
||||||
self.wait_lcmocc_complete(lcmocc_id)
|
|
||||||
|
|
||||||
# 3. Show VNF instance
|
|
||||||
additional_inst_attrs = [
|
|
||||||
'vimConnectionInfo',
|
|
||||||
'instantiatedVnfInfo'
|
|
||||||
]
|
|
||||||
expected_inst_attrs.extend(additional_inst_attrs)
|
|
||||||
resp, body = self.show_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(200, resp.status_code)
|
|
||||||
self.check_resp_headers_in_get(resp)
|
|
||||||
self.check_resp_body(body, expected_inst_attrs)
|
|
||||||
|
|
||||||
vnfc_resource_infos = body['instantiatedVnfInfo'].get(
|
|
||||||
'vnfcResourceInfo')
|
|
||||||
before_resource_ids = [vnfc_info['computeResource']['resourceId']
|
|
||||||
for vnfc_info in vnfc_resource_infos]
|
|
||||||
|
|
||||||
# 4. Change Current VNF Package (will fail)
|
|
||||||
change_vnfpkg_req = paramgen.change_vnfpkg_error(self.vnfd_id_2)
|
|
||||||
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
|
|
||||||
self.assertEqual(202, resp.status_code)
|
|
||||||
self.check_resp_headers_in_operation_task(resp)
|
|
||||||
|
|
||||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
|
||||||
self.wait_lcmocc_failed_temp(lcmocc_id)
|
|
||||||
|
|
||||||
# 5. Rollback Change Current VNF Package operation
|
|
||||||
resp, body = self.rollback_lcmocc(lcmocc_id)
|
|
||||||
self.assertEqual(202, resp.status_code)
|
|
||||||
self.check_resp_headers_in_delete(resp)
|
|
||||||
self.wait_lcmocc_rolled_back(lcmocc_id)
|
|
||||||
|
|
||||||
# 6. Show VNF instance
|
|
||||||
additional_inst_attrs = [
|
|
||||||
'vimConnectionInfo',
|
|
||||||
'instantiatedVnfInfo'
|
|
||||||
]
|
|
||||||
expected_inst_attrs.extend(additional_inst_attrs)
|
|
||||||
resp, body = self.show_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(200, resp.status_code)
|
|
||||||
self.check_resp_headers_in_get(resp)
|
|
||||||
self.check_resp_body(body, expected_inst_attrs)
|
|
||||||
|
|
||||||
vnfc_resource_infos = body['instantiatedVnfInfo'].get(
|
|
||||||
'vnfcResourceInfo')
|
|
||||||
after_resource_ids = [vnfc_info['computeResource']['resourceId']
|
|
||||||
for vnfc_info in vnfc_resource_infos]
|
|
||||||
self.assertNotEqual(before_resource_ids, after_resource_ids)
|
|
||||||
|
|
||||||
# 7. Terminate a VNF instance
|
|
||||||
terminate_req = paramgen.max_sample_terminate()
|
|
||||||
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
|
||||||
self.assertEqual(202, resp.status_code)
|
|
||||||
self.check_resp_headers_in_operation_task(resp)
|
|
||||||
|
|
||||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
|
||||||
self.wait_lcmocc_complete(lcmocc_id)
|
|
||||||
|
|
||||||
# wait a bit because there is a bit time lag between lcmocc DB
|
|
||||||
# update and terminate completion.
|
|
||||||
time.sleep(10)
|
|
||||||
|
|
||||||
# 8. Delete a VNF instance
|
|
||||||
resp, body = self.delete_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(204, resp.status_code)
|
|
||||||
self.check_resp_headers_in_delete(resp)
|
|
||||||
|
|
||||||
# check deletion of VNF instance
|
|
||||||
resp, body = self.show_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(404, resp.status_code)
|
|
||||||
|
|
||||||
# check usageState of VNF Package
|
|
||||||
usage_state = self.get_vnf_package(self.vnf_pkg_2).get('usageState')
|
|
||||||
self.assertEqual('NOT_IN_USE', usage_state)
|
|
||||||
|
|
||||||
@unittest.skip("Until refactor CNF v2 API")
|
|
||||||
def test_change_vnfpkg_failed_and_retry(self):
|
|
||||||
"""Test LCM operations error handing
|
|
||||||
|
|
||||||
* About LCM operations:
|
|
||||||
This test includes the following operations.
|
|
||||||
- 1. Create a new VNF instance resource
|
|
||||||
- 2. Instantiate a VNF instance
|
|
||||||
- 3. Show VNF instance
|
|
||||||
- 4. Change Current VNF Package(will fail)
|
|
||||||
- 5. Retry Change Current VNF Package
|
|
||||||
- 6. Rollback Change Current VNF Package
|
|
||||||
- 7. Show VNF instance
|
|
||||||
- 8. Terminate a VNF instance
|
|
||||||
- 9. Delete a VNF instance
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 1. Create a new VNF instance resource
|
|
||||||
# NOTE: extensions and vnfConfigurableProperties are omitted
|
|
||||||
# because they are commented out in etsi_nfv_sol001.
|
|
||||||
expected_inst_attrs = [
|
|
||||||
'id',
|
|
||||||
'vnfInstanceName',
|
|
||||||
'vnfInstanceDescription',
|
|
||||||
'vnfdId',
|
|
||||||
'vnfProvider',
|
|
||||||
'vnfProductName',
|
|
||||||
'vnfSoftwareVersion',
|
|
||||||
'vnfdVersion',
|
|
||||||
# 'vnfConfigurableProperties', # omitted
|
|
||||||
# 'vimConnectionInfo', # omitted
|
|
||||||
'instantiationState',
|
|
||||||
# 'instantiatedVnfInfo', # omitted
|
|
||||||
'metadata',
|
|
||||||
# 'extensions', # omitted
|
|
||||||
'_links'
|
|
||||||
]
|
|
||||||
create_req = paramgen.test_instantiate_cnf_resources_create(
|
|
||||||
self.vnfd_id_1)
|
|
||||||
resp, body = self.create_vnf_instance(create_req)
|
|
||||||
self.assertEqual(201, resp.status_code)
|
|
||||||
self.check_resp_headers_in_create(resp)
|
|
||||||
self.check_resp_body(body, expected_inst_attrs)
|
|
||||||
inst_id = body['id']
|
|
||||||
|
|
||||||
# check usageState of VNF Package
|
|
||||||
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
|
|
||||||
self.assertEqual('IN_USE', usage_state)
|
|
||||||
|
|
||||||
# 2. Instantiate a VNF instance
|
|
||||||
vim_id = self.get_k8s_vim_id()
|
|
||||||
instantiate_req = paramgen.change_vnfpkg_instantiate_error_handing(
|
|
||||||
vim_id)
|
|
||||||
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
|
||||||
self.assertEqual(202, resp.status_code)
|
|
||||||
self.check_resp_headers_in_operation_task(resp)
|
|
||||||
|
|
||||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
|
||||||
self.wait_lcmocc_complete(lcmocc_id)
|
|
||||||
|
|
||||||
# 3. Show VNF instance
|
|
||||||
additional_inst_attrs = [
|
|
||||||
'vimConnectionInfo',
|
|
||||||
'instantiatedVnfInfo'
|
|
||||||
]
|
|
||||||
expected_inst_attrs.extend(additional_inst_attrs)
|
|
||||||
resp, body = self.show_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(200, resp.status_code)
|
|
||||||
self.check_resp_headers_in_get(resp)
|
|
||||||
self.check_resp_body(body, expected_inst_attrs)
|
|
||||||
|
|
||||||
vnfc_resource_infos = body['instantiatedVnfInfo'].get(
|
|
||||||
'vnfcResourceInfo')
|
|
||||||
before_resource_ids = [vnfc_info['computeResource']['resourceId']
|
|
||||||
for vnfc_info in vnfc_resource_infos]
|
|
||||||
|
|
||||||
# 4. Change Current VNF Package (will fail)
|
|
||||||
change_vnfpkg_req = paramgen.change_vnfpkg_error(self.vnfd_id_2)
|
|
||||||
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
|
|
||||||
self.assertEqual(202, resp.status_code)
|
|
||||||
self.check_resp_headers_in_operation_task(resp)
|
|
||||||
|
|
||||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
|
||||||
self.wait_lcmocc_failed_temp(lcmocc_id)
|
|
||||||
|
|
||||||
# 5. Retry Change Current VNF Package operation
|
|
||||||
resp, body = self.retry_lcmocc(lcmocc_id)
|
|
||||||
self.assertEqual(202, resp.status_code)
|
|
||||||
self.check_resp_headers_in_delete(resp)
|
|
||||||
self.wait_lcmocc_failed_temp(lcmocc_id)
|
|
||||||
|
|
||||||
# 6. Rollback Change Current VNF Package operation
|
|
||||||
resp, body = self.rollback_lcmocc(lcmocc_id)
|
|
||||||
self.assertEqual(202, resp.status_code)
|
|
||||||
self.check_resp_headers_in_delete(resp)
|
|
||||||
self.wait_lcmocc_rolled_back(lcmocc_id)
|
|
||||||
|
|
||||||
# 7. Show VNF instance
|
|
||||||
additional_inst_attrs = [
|
|
||||||
'vimConnectionInfo',
|
|
||||||
'instantiatedVnfInfo'
|
|
||||||
]
|
|
||||||
expected_inst_attrs.extend(additional_inst_attrs)
|
|
||||||
resp, body = self.show_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(200, resp.status_code)
|
|
||||||
self.check_resp_headers_in_get(resp)
|
|
||||||
self.check_resp_body(body, expected_inst_attrs)
|
|
||||||
|
|
||||||
vnfc_resource_infos = body['instantiatedVnfInfo'].get(
|
|
||||||
'vnfcResourceInfo')
|
|
||||||
after_resource_ids = [vnfc_info['computeResource']['resourceId']
|
|
||||||
for vnfc_info in vnfc_resource_infos]
|
|
||||||
self.assertNotEqual(before_resource_ids, after_resource_ids)
|
|
||||||
|
|
||||||
# 8. Terminate a VNF instance
|
|
||||||
terminate_req = paramgen.max_sample_terminate()
|
|
||||||
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
|
||||||
self.assertEqual(202, resp.status_code)
|
|
||||||
self.check_resp_headers_in_operation_task(resp)
|
|
||||||
|
|
||||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
|
||||||
self.wait_lcmocc_complete(lcmocc_id)
|
|
||||||
|
|
||||||
# wait a bit because there is a bit time lag between lcmocc DB
|
|
||||||
# update and terminate completion.
|
|
||||||
time.sleep(10)
|
|
||||||
|
|
||||||
# 9. Delete a VNF instance
|
|
||||||
resp, body = self.delete_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(204, resp.status_code)
|
|
||||||
self.check_resp_headers_in_delete(resp)
|
|
||||||
|
|
||||||
# check deletion of VNF instance
|
|
||||||
resp, body = self.show_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(404, resp.status_code)
|
|
||||||
|
|
||||||
# check usageState of VNF Package
|
|
||||||
usage_state = self.get_vnf_package(self.vnf_pkg_2).get('usageState')
|
|
||||||
self.assertEqual('NOT_IN_USE', usage_state)
|
|
||||||
|
|
||||||
def test_change_vnfpkg_failed_and_fail(self):
|
|
||||||
"""Test LCM operations error handing
|
|
||||||
|
|
||||||
* About LCM operations:
|
|
||||||
This test includes the following operations.
|
|
||||||
- 1. Create a new VNF instance resource
|
|
||||||
- 2. Instantiate a VNF instance
|
|
||||||
- 3. Show VNF instance
|
|
||||||
- 4. Change Current VNF Package
|
|
||||||
- 5. Fail Change Current VNF Package
|
|
||||||
- 6. Show VNF instance
|
|
||||||
- 7. Terminate VNF instance
|
|
||||||
- 8. Delete a VNF instance
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 1. Create a new VNF instance resource
|
|
||||||
# NOTE: extensions and vnfConfigurableProperties are omitted
|
|
||||||
# because they are commented out in etsi_nfv_sol001.
|
|
||||||
expected_inst_attrs = [
|
|
||||||
'id',
|
|
||||||
'vnfInstanceName',
|
|
||||||
'vnfInstanceDescription',
|
|
||||||
'vnfdId',
|
|
||||||
'vnfProvider',
|
|
||||||
'vnfProductName',
|
|
||||||
'vnfSoftwareVersion',
|
|
||||||
'vnfdVersion',
|
|
||||||
# 'vnfConfigurableProperties', # omitted
|
|
||||||
# 'vimConnectionInfo', # omitted
|
|
||||||
'instantiationState',
|
|
||||||
# 'instantiatedVnfInfo', # omitted
|
|
||||||
'metadata',
|
|
||||||
# 'extensions', # omitted
|
|
||||||
'_links'
|
|
||||||
]
|
|
||||||
create_req = paramgen.test_instantiate_cnf_resources_create(
|
|
||||||
self.vnfd_id_1)
|
|
||||||
resp, body = self.create_vnf_instance(create_req)
|
|
||||||
self.assertEqual(201, resp.status_code)
|
|
||||||
self.check_resp_headers_in_create(resp)
|
|
||||||
self.check_resp_body(body, expected_inst_attrs)
|
|
||||||
inst_id = body['id']
|
|
||||||
|
|
||||||
# check usageState of VNF Package
|
|
||||||
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
|
|
||||||
self.assertEqual('IN_USE', usage_state)
|
|
||||||
|
|
||||||
# 2. Instantiate a VNF instance
|
|
||||||
vim_id = self.get_k8s_vim_id()
|
|
||||||
instantiate_req = paramgen.change_vnfpkg_instantiate_error_handing(
|
|
||||||
vim_id)
|
|
||||||
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
|
||||||
self.assertEqual(202, resp.status_code)
|
|
||||||
self.check_resp_headers_in_operation_task(resp)
|
|
||||||
|
|
||||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
|
||||||
self.wait_lcmocc_complete(lcmocc_id)
|
|
||||||
|
|
||||||
# 3. Show VNF instance
|
|
||||||
additional_inst_attrs = [
|
|
||||||
'vimConnectionInfo',
|
|
||||||
'instantiatedVnfInfo'
|
|
||||||
]
|
|
||||||
expected_inst_attrs.extend(additional_inst_attrs)
|
|
||||||
resp, body = self.show_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(200, resp.status_code)
|
|
||||||
self.check_resp_headers_in_get(resp)
|
|
||||||
self.check_resp_body(body, expected_inst_attrs)
|
|
||||||
|
|
||||||
vnfc_resource_infos = body['instantiatedVnfInfo'].get(
|
|
||||||
'vnfcResourceInfo')
|
|
||||||
before_resource_ids = [vnfc_info['computeResource']['resourceId']
|
|
||||||
for vnfc_info in vnfc_resource_infos]
|
|
||||||
|
|
||||||
# 4. Change Current VNF Package (will fail)
|
|
||||||
change_vnfpkg_req = paramgen.change_vnfpkg_error(self.vnfd_id_2)
|
|
||||||
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
|
|
||||||
self.assertEqual(202, resp.status_code)
|
|
||||||
self.check_resp_headers_in_operation_task(resp)
|
|
||||||
|
|
||||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
|
||||||
self.wait_lcmocc_failed_temp(lcmocc_id)
|
|
||||||
|
|
||||||
# 5. Fail Change Current VNF Package operation
|
|
||||||
expected_inst_attrs_fail = [
|
|
||||||
'id',
|
|
||||||
'operationState',
|
|
||||||
'stateEnteredTime',
|
|
||||||
'startTime',
|
|
||||||
'vnfInstanceId',
|
|
||||||
'grantId',
|
|
||||||
'operation',
|
|
||||||
'isAutomaticInvocation',
|
|
||||||
'operationParams',
|
|
||||||
'isCancelPending',
|
|
||||||
'error',
|
|
||||||
'_links'
|
|
||||||
]
|
|
||||||
resp, body = self.fail_lcmocc(lcmocc_id)
|
|
||||||
self.assertEqual(200, resp.status_code)
|
|
||||||
self.check_resp_headers_in_get(resp)
|
|
||||||
self.check_resp_body(body, expected_inst_attrs_fail)
|
|
||||||
resp, body = self.show_lcmocc(lcmocc_id)
|
|
||||||
self.assertEqual(200, resp.status_code)
|
|
||||||
self.assertEqual('FAILED', body['operationState'])
|
|
||||||
|
|
||||||
# 6. Show VNF instance
|
|
||||||
additional_inst_attrs = [
|
|
||||||
'vimConnectionInfo',
|
|
||||||
'instantiatedVnfInfo'
|
|
||||||
]
|
|
||||||
expected_inst_attrs.extend(additional_inst_attrs)
|
|
||||||
resp, body = self.show_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(200, resp.status_code)
|
|
||||||
self.check_resp_headers_in_get(resp)
|
|
||||||
self.check_resp_body(body, expected_inst_attrs)
|
|
||||||
|
|
||||||
vnfc_resource_infos = body['instantiatedVnfInfo'].get(
|
|
||||||
'vnfcResourceInfo')
|
|
||||||
after_resource_ids = [vnfc_info['computeResource']['resourceId']
|
|
||||||
for vnfc_info in vnfc_resource_infos]
|
|
||||||
self.assertEqual(before_resource_ids, after_resource_ids)
|
|
||||||
|
|
||||||
# 7. Terminate a VNF instance
|
|
||||||
terminate_req = paramgen.max_sample_terminate()
|
|
||||||
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
|
||||||
self.assertEqual(202, resp.status_code)
|
|
||||||
self.check_resp_headers_in_operation_task(resp)
|
|
||||||
|
|
||||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
|
||||||
self.wait_lcmocc_complete(lcmocc_id)
|
|
||||||
|
|
||||||
# wait a bit because there is a bit time lag between lcmocc DB
|
|
||||||
# update and terminate completion.
|
|
||||||
time.sleep(10)
|
|
||||||
|
|
||||||
# 8. Delete a VNF instance
|
|
||||||
resp, body = self.delete_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(204, resp.status_code)
|
|
||||||
self.check_resp_headers_in_delete(resp)
|
|
||||||
|
|
||||||
# check deletion of VNF instance
|
|
||||||
resp, body = self.show_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(404, resp.status_code)
|
|
||||||
|
|
||||||
# check usageState of VNF Package
|
|
||||||
usage_state = self.get_vnf_package(self.vnf_pkg_2).get('usageState')
|
|
||||||
self.assertEqual('NOT_IN_USE', usage_state)
|
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user