Support CNF update with MgmtDriver
This patch supports MgmtDriver in the operation of modifying VNF. It provides a sample script MgmtDriver, when modifying CNF, If the ConfigMap and Secret are updated, the Pod and Deployment will also be updated (image only). Implements: blueprint container-update Change-Id: I1e7a1b03fef13f4c7a83488f6d2fdd7efc2e454b
This commit is contained in:
parent
17b03eb78f
commit
d219c49e11
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Support configuration changes to VNF instances by adding
|
||||||
|
``modify_start`` and ``modify_end`` to MgmtDriver for the Modify VNF
|
||||||
|
operation.
|
||||||
|
We provide the sample of MgmtDriver which changes the configuration
|
||||||
|
of ConfigMap and Secret in Kubernetes and changes the image parameters
|
||||||
|
in the Pod and Deployment manifests.
|
@ -188,3 +188,49 @@
|
|||||||
when:
|
when:
|
||||||
- inventory_hostname == 'controller-tacker'
|
- inventory_hostname == 'controller-tacker'
|
||||||
- kuryr_k8s_api_url is defined
|
- kuryr_k8s_api_url is defined
|
||||||
|
|
||||||
|
- block:
|
||||||
|
- name: Copy tools/test-setup-mgmt.sh
|
||||||
|
copy:
|
||||||
|
remote_src=True
|
||||||
|
src={{ devstack_base_dir }}/tacker/tools/test-setup-mgmt.sh
|
||||||
|
dest={{ zuul_work_dir }}/tools/test-setup-mgmt.sh
|
||||||
|
mode=0755
|
||||||
|
|
||||||
|
- name: Check if project's tools/test-setup-mgmt.sh exists
|
||||||
|
stat:
|
||||||
|
path: "{{ zuul_work_dir }}/tools/test-setup-mgmt.sh"
|
||||||
|
register: p
|
||||||
|
- fail:
|
||||||
|
msg: >
|
||||||
|
{{ zuul_work_dir }}/tools/test-setup-mgmt.sh doesn't exists
|
||||||
|
or it doesn't have execute permission.
|
||||||
|
when: p.stat.exists != True or p.stat.executable != True
|
||||||
|
|
||||||
|
- name: Get stackenv from devstack environment
|
||||||
|
slurp:
|
||||||
|
src: "{{ devstack_base_dir }}/devstack/.stackenv"
|
||||||
|
register: stackenv
|
||||||
|
|
||||||
|
- name: Set a keystone authentication uri
|
||||||
|
set_fact:
|
||||||
|
auth_uri: "{{
|
||||||
|
stackenv.content
|
||||||
|
| b64decode
|
||||||
|
| regex_replace('\n', ' ')
|
||||||
|
| regex_replace('^.*KEYSTONE_SERVICE_URI=([^ ]+).*$', '\\1')
|
||||||
|
}}"
|
||||||
|
when:
|
||||||
|
- p.stat.exists
|
||||||
|
|
||||||
|
- name: Run tools/test-setup-mgmt.sh
|
||||||
|
command: tools/test-setup-mgmt.sh
|
||||||
|
args:
|
||||||
|
chdir: "{{ zuul_work_dir }}"
|
||||||
|
when:
|
||||||
|
- p.stat.exists
|
||||||
|
- p.stat.executable
|
||||||
|
|
||||||
|
when:
|
||||||
|
- inventory_hostname == 'controller-tacker'
|
||||||
|
- kuryr_k8s_api_url is defined
|
||||||
|
@ -0,0 +1,399 @@
|
|||||||
|
# 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 copy
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
import urllib.request as urllib2
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from oslo_utils import encodeutils
|
||||||
|
from tacker.common.container import kubernetes_utils
|
||||||
|
from tacker.common import exceptions
|
||||||
|
from tacker.common import log
|
||||||
|
from tacker import objects
|
||||||
|
from tacker.vnflcm import utils as vnflcm_utils
|
||||||
|
from tacker.vnfm.infra_drivers.kubernetes.k8s import translate_outputs
|
||||||
|
from tacker.vnfm.infra_drivers.kubernetes.kubernetes_driver import Kubernetes
|
||||||
|
from tacker.vnfm.mgmt_drivers import vnflcm_abstract_driver
|
||||||
|
from toscaparser import tosca_template
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerUpdateMgmtDriver(vnflcm_abstract_driver.
|
||||||
|
VnflcmMgmtAbstractDriver):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_type(self):
|
||||||
|
return "mgmt-container-update"
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
return "mgmt-container-update"
|
||||||
|
|
||||||
|
def get_description(self):
|
||||||
|
return 'Tacker Container Update VNF Mgmt Driver'
|
||||||
|
|
||||||
|
def modify_information_start(self, context, vnf_instance,
|
||||||
|
modify_vnf_request=None, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _get_kind_and_name(self, file, vnf_package_path):
|
||||||
|
# kind_and_names ----> configmap_secrets list or resources list
|
||||||
|
kind_and_names = []
|
||||||
|
# Read the contents of the manifest file and get the name and kind
|
||||||
|
if ((urlparse(file).scheme == 'file') or
|
||||||
|
(bool(urlparse(file).scheme) and
|
||||||
|
bool(urlparse(file).netloc))):
|
||||||
|
file_content = urllib2.urlopen(file).read()
|
||||||
|
else:
|
||||||
|
manifest_file = os.path.join(vnf_package_path, file)
|
||||||
|
with open(manifest_file, 'r', encoding='utf-8') as f:
|
||||||
|
file_content = f.read()
|
||||||
|
file_contents = yaml.safe_load_all(file_content)
|
||||||
|
for file_content in file_contents:
|
||||||
|
kind = file_content.get('kind', '')
|
||||||
|
name = file_content.get('metadata', {}).get('name', '')
|
||||||
|
kind_and_names.append({'kind': kind, 'name': name})
|
||||||
|
return kind_and_names
|
||||||
|
|
||||||
|
def _initialize_k8s_client(self, auth_cred):
|
||||||
|
k8s_clients = (kubernetes_utils.KubernetesHTTPAPI().
|
||||||
|
get_k8s_client_dict(auth_cred))
|
||||||
|
return k8s_clients
|
||||||
|
|
||||||
|
def _get_k8s_objs(self, target_k8s_files, vnf_package_path, namespace,
|
||||||
|
k8s_clients):
|
||||||
|
transformer = translate_outputs.Transformer(
|
||||||
|
None, None, None, k8s_clients)
|
||||||
|
k8s_objs = transformer.get_k8s_objs_from_yaml(
|
||||||
|
target_k8s_files, vnf_package_path, namespace)
|
||||||
|
return k8s_objs
|
||||||
|
|
||||||
|
def _modify_container_img(self, old_containers, new_containers):
|
||||||
|
for old_container in old_containers:
|
||||||
|
for new_container in new_containers:
|
||||||
|
if (old_container.name == new_container.name and
|
||||||
|
old_container.image != new_container.image):
|
||||||
|
# Replace the old image with the new image
|
||||||
|
old_container.image = new_container.image
|
||||||
|
break
|
||||||
|
|
||||||
|
def _replace_api(self, k8s_clients, namespace, k8s_obj):
|
||||||
|
# get api
|
||||||
|
name = k8s_obj['object'].metadata.name
|
||||||
|
kind = k8s_obj['object'].kind
|
||||||
|
api_version = k8s_obj['object'].api_version
|
||||||
|
body = k8s_obj['object']
|
||||||
|
|
||||||
|
def convert(name):
|
||||||
|
name_with_underscores = re.sub(
|
||||||
|
'(.)([A-Z][a-z]+)', r'\1_\2', name)
|
||||||
|
return re.sub('([a-z0-9])([A-Z])', r'\1_\2',
|
||||||
|
name_with_underscores).lower()
|
||||||
|
|
||||||
|
snake_case_kind = convert(kind)
|
||||||
|
try:
|
||||||
|
replace_api = eval(f'k8s_clients["{api_version}"].'
|
||||||
|
f'replace_namespaced_{snake_case_kind}')
|
||||||
|
response = replace_api(name=name, namespace=namespace,
|
||||||
|
body=body)
|
||||||
|
except Exception as exp:
|
||||||
|
raise exceptions.MgmtDriverOtherError(
|
||||||
|
error_message=encodeutils.exception_to_unicode(exp))
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
def _replace_wait_k8s(self, kube_driver, k8s_pod_objs,
|
||||||
|
core_v1_api_client, vnf_instance):
|
||||||
|
try:
|
||||||
|
time.sleep(kube_driver.STACK_RETRY_WAIT)
|
||||||
|
status = 'Pending'
|
||||||
|
stack_retries = kube_driver.STACK_RETRIES
|
||||||
|
|
||||||
|
while status == 'Pending' and stack_retries > 0:
|
||||||
|
pods_information = []
|
||||||
|
for k8s_pod_obj in k8s_pod_objs:
|
||||||
|
kind = k8s_pod_obj['object'].kind
|
||||||
|
namespace = k8s_pod_obj.get('namespace')
|
||||||
|
if k8s_pod_obj['object'].metadata:
|
||||||
|
name = k8s_pod_obj['object'].metadata.name
|
||||||
|
else:
|
||||||
|
name = ''
|
||||||
|
|
||||||
|
response = core_v1_api_client.list_namespaced_pod(
|
||||||
|
namespace=namespace)
|
||||||
|
for pod in response.items:
|
||||||
|
match_result = kube_driver.is_match_pod_naming_rule(
|
||||||
|
kind, name, pod.metadata.name)
|
||||||
|
if match_result:
|
||||||
|
pods_information.append(pod)
|
||||||
|
status = kube_driver.get_pod_status(pods_information)
|
||||||
|
if status == 'Unknown':
|
||||||
|
wait = (kube_driver.STACK_RETRIES *
|
||||||
|
kube_driver.STACK_RETRY_WAIT)
|
||||||
|
error_reason = (
|
||||||
|
f"Resource creation is not completed within"
|
||||||
|
f" {wait} seconds as creation of CNF "
|
||||||
|
f"{vnf_instance.id} is not completed")
|
||||||
|
raise exceptions.MgmtDriverOtherError(
|
||||||
|
error_message=error_reason)
|
||||||
|
if status == 'Pending':
|
||||||
|
stack_retries = stack_retries - 1
|
||||||
|
time.sleep(kube_driver.STACK_RETRY_WAIT)
|
||||||
|
if stack_retries == 0 and status != 'Running':
|
||||||
|
LOG.error('It is time out, When modify cnf,'
|
||||||
|
'waiting for resource creation.')
|
||||||
|
wait = (kube_driver.STACK_RETRIES *
|
||||||
|
kube_driver.STACK_RETRY_WAIT)
|
||||||
|
error_reason = (f"Resource creation is not completed within"
|
||||||
|
f" {wait} seconds as creation of CNF "
|
||||||
|
f"{vnf_instance.id} is not completed")
|
||||||
|
raise exceptions.MgmtDriverOtherError(
|
||||||
|
error_message=error_reason)
|
||||||
|
return k8s_pod_objs
|
||||||
|
except Exception as exp:
|
||||||
|
raise exceptions.MgmtDriverOtherError(
|
||||||
|
error_message=encodeutils.exception_to_unicode(exp))
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def modify_information_end(self, context, vnf_instance,
|
||||||
|
modify_vnf_request=None, **kwargs):
|
||||||
|
kube_driver = Kubernetes()
|
||||||
|
# get old_vnf_package_path
|
||||||
|
old_vnf_package_path = kwargs['old_vnf_package_path']
|
||||||
|
|
||||||
|
# get configmap_secret_paths
|
||||||
|
configmap_secret_paths = kwargs['configmap_secret_paths']
|
||||||
|
# Get the path of the VNF Package according to vnf_instance.vnfd_id
|
||||||
|
new_vnf_package_path = vnflcm_utils.get_vnf_package_path(
|
||||||
|
context, vnf_instance.vnfd_id)
|
||||||
|
|
||||||
|
# Get the input parameters of instantiate
|
||||||
|
inst_vnf_info = vnf_instance.instantiated_vnf_info
|
||||||
|
|
||||||
|
# Make a deep copy of inst_vnf_info
|
||||||
|
new_inst_vnf_info = copy.deepcopy(inst_vnf_info)
|
||||||
|
|
||||||
|
# Get vim_connection_info from vnf_instance
|
||||||
|
vim_info = vnflcm_utils.get_vim(context,
|
||||||
|
vnf_instance.vim_connection_info)
|
||||||
|
vim_connection_info = objects.VimConnectionInfo.obj_from_primitive(
|
||||||
|
vim_info, context)
|
||||||
|
for configmap_secret_path in configmap_secret_paths:
|
||||||
|
# Read the contents of the manifest file and get name and kind
|
||||||
|
configmap_secrets = self._get_kind_and_name(configmap_secret_path,
|
||||||
|
new_vnf_package_path)
|
||||||
|
for index, manifest_path in enumerate(
|
||||||
|
inst_vnf_info.additional_params[
|
||||||
|
'lcm-kubernetes-def-files']):
|
||||||
|
# Read the contents of the manifest file and get name and kind
|
||||||
|
resources = self._get_kind_and_name(manifest_path,
|
||||||
|
old_vnf_package_path)
|
||||||
|
resource = [obj for obj in resources if
|
||||||
|
obj in configmap_secrets]
|
||||||
|
if resource:
|
||||||
|
if len(resource) == len(configmap_secrets):
|
||||||
|
# Update the manifest file path of ConfigMap/Secret
|
||||||
|
new_inst_vnf_info.additional_params[
|
||||||
|
'lcm-kubernetes-def-files'][
|
||||||
|
index] = configmap_secret_path
|
||||||
|
break
|
||||||
|
raise exceptions.MgmtDriverOtherError(
|
||||||
|
error_message='The number of resources in the '
|
||||||
|
'manifest file of the changed '
|
||||||
|
'ConfigMap/Secret is inconsistent '
|
||||||
|
'with the previous one.')
|
||||||
|
# Initialize k8s client api
|
||||||
|
auth_attr = vim_connection_info.access_info
|
||||||
|
auth_cred, _ = kube_driver.get_auth_creds(auth_attr)
|
||||||
|
k8s_clients = self._initialize_k8s_client(auth_cred)
|
||||||
|
|
||||||
|
# Get the namespace of this CNF
|
||||||
|
namespace = vnf_instance.vnf_metadata['namespace']
|
||||||
|
|
||||||
|
# Get old_k8s_objs
|
||||||
|
target_k8s_files = inst_vnf_info.additional_params[
|
||||||
|
'lcm-kubernetes-def-files']
|
||||||
|
old_k8s_objs = self._get_k8s_objs(target_k8s_files,
|
||||||
|
old_vnf_package_path, namespace,
|
||||||
|
k8s_clients)
|
||||||
|
|
||||||
|
# Get new_k8s_objs
|
||||||
|
target_k8s_files = new_inst_vnf_info.additional_params[
|
||||||
|
'lcm-kubernetes-def-files']
|
||||||
|
new_k8s_objs = self._get_k8s_objs(target_k8s_files,
|
||||||
|
new_vnf_package_path, namespace,
|
||||||
|
k8s_clients)
|
||||||
|
# Initialize k8s_pod_objs and k8s_config_objs
|
||||||
|
k8s_pod_objs = []
|
||||||
|
k8s_config_objs = []
|
||||||
|
|
||||||
|
for old_k8s_obj in old_k8s_objs:
|
||||||
|
old_k8s_obj_kind = old_k8s_obj['object'].kind
|
||||||
|
old_k8s_obj_name = old_k8s_obj['object'].metadata.name
|
||||||
|
if old_k8s_obj_kind in ['Pod', 'Deployment']:
|
||||||
|
for new_k8s_obj in new_k8s_objs:
|
||||||
|
# If the old and new k8s_obj have the same kind and name
|
||||||
|
new_k8s_obj_kind = new_k8s_obj['object'].kind
|
||||||
|
new_k8s_obj_name = new_k8s_obj['object'].metadata.name
|
||||||
|
if old_k8s_obj_kind == new_k8s_obj_kind and (
|
||||||
|
old_k8s_obj_name == new_k8s_obj_name):
|
||||||
|
# Call the read API
|
||||||
|
old_k8s_resource_info = (
|
||||||
|
kube_driver.select_k8s_obj_read_api(
|
||||||
|
k8s_client_dict=k8s_clients,
|
||||||
|
namespace=namespace,
|
||||||
|
name=old_k8s_obj_name,
|
||||||
|
kind=old_k8s_obj_kind,
|
||||||
|
api_version=old_k8s_obj['object'].api_version
|
||||||
|
))
|
||||||
|
# Assign old_k8s_resource_info to old_k8s_obj['object']
|
||||||
|
old_k8s_obj['object'] = old_k8s_resource_info
|
||||||
|
|
||||||
|
if old_k8s_obj_kind == 'Deployment':
|
||||||
|
old_containers = (old_k8s_obj['object'].spec
|
||||||
|
.template.spec.containers)
|
||||||
|
new_containers = (new_k8s_obj['object'].spec
|
||||||
|
.template.spec.containers)
|
||||||
|
elif old_k8s_obj_kind == 'Pod':
|
||||||
|
old_containers = (old_k8s_obj['object']
|
||||||
|
.spec.containers)
|
||||||
|
new_containers = (new_k8s_obj['object']
|
||||||
|
.spec.containers)
|
||||||
|
# Replace the old image with the new image
|
||||||
|
self._modify_container_img(old_containers,
|
||||||
|
new_containers)
|
||||||
|
break
|
||||||
|
|
||||||
|
# Append old_k8s_obj to k8s_pod_objs
|
||||||
|
k8s_pod_objs.append(old_k8s_obj)
|
||||||
|
elif old_k8s_obj_kind in ['ConfigMap', 'Secret']:
|
||||||
|
for new_k8s_obj in new_k8s_objs:
|
||||||
|
# If the old and new k8s_obj have the same kind and name
|
||||||
|
new_k8s_obj_kind = new_k8s_obj['object'].kind
|
||||||
|
new_k8s_obj_name = new_k8s_obj['object'].metadata.name
|
||||||
|
if old_k8s_obj_kind == new_k8s_obj_kind and (
|
||||||
|
old_k8s_obj_name == new_k8s_obj_name):
|
||||||
|
# Append old_k8s_obj to k8s_pod_objs
|
||||||
|
k8s_config_objs.append(new_k8s_obj)
|
||||||
|
break
|
||||||
|
for k8s_config_obj in k8s_config_objs:
|
||||||
|
# Call the replace API
|
||||||
|
self._replace_api(k8s_clients, namespace, k8s_config_obj)
|
||||||
|
for k8s_pod_obj in k8s_pod_objs:
|
||||||
|
# Call the replace API
|
||||||
|
self._replace_api(k8s_clients, namespace, k8s_pod_obj)
|
||||||
|
|
||||||
|
# _replace_wait_k8s
|
||||||
|
core_v1_api_client = k8s_clients['v1']
|
||||||
|
self._replace_wait_k8s(kube_driver, k8s_pod_objs, core_v1_api_client,
|
||||||
|
vnf_instance)
|
||||||
|
# Get all pod information under the specified namespace
|
||||||
|
pods = core_v1_api_client.list_namespaced_pod(namespace=namespace)
|
||||||
|
|
||||||
|
# get TOSCA node templates
|
||||||
|
vnfd_dict = vnflcm_utils.get_vnfd_dict(
|
||||||
|
context, vnf_instance.vnfd_id,
|
||||||
|
vnf_instance.instantiated_vnf_info.flavour_id)
|
||||||
|
tosca = tosca_template.ToscaTemplate(
|
||||||
|
parsed_params={}, a_file=False, yaml_dict_tpl=vnfd_dict)
|
||||||
|
tosca_node_tpls = tosca.topology_template.nodetemplates
|
||||||
|
# get vdu_names dict {vdu_id: vdu_name}
|
||||||
|
vdu_names = {}
|
||||||
|
for node_tpl in tosca_node_tpls:
|
||||||
|
for node_name, node_value in node_tpl.templates.items():
|
||||||
|
if node_value.get('type') == "tosca.nodes.nfv.Vdu.Compute":
|
||||||
|
vdu_id = node_name
|
||||||
|
vdu_name = node_value.get('properties').get('name')
|
||||||
|
vdu_names[vdu_id] = vdu_name
|
||||||
|
|
||||||
|
for vnfc_resource in new_inst_vnf_info.vnfc_resource_info:
|
||||||
|
for pod in pods.items:
|
||||||
|
# the name of the pod matches the resource_id in vnfc_resource
|
||||||
|
match_result = kube_driver.is_match_pod_naming_rule(
|
||||||
|
vnfc_resource.compute_resource.vim_level_resource_type,
|
||||||
|
vdu_names[vnfc_resource.vdu_id],
|
||||||
|
pod.metadata.name)
|
||||||
|
if match_result:
|
||||||
|
# Update the name of the pod in the resourceId
|
||||||
|
vnfc_resource.compute_resource.resource_id = (
|
||||||
|
pod.metadata.name)
|
||||||
|
# Delete the current pod from pods.items
|
||||||
|
pods.items.remove(pod)
|
||||||
|
break
|
||||||
|
# the ConfigMap/Secret file path in inst_vnf_info.additional_params
|
||||||
|
vnf_instance.instantiated_vnf_info.vnfc_resource_info = (
|
||||||
|
new_inst_vnf_info.vnfc_resource_info)
|
||||||
|
vnf_instance.instantiated_vnf_info.additional_params = (
|
||||||
|
new_inst_vnf_info.additional_params)
|
||||||
|
vnf_instance.save()
|
||||||
|
|
||||||
|
def instantiate_start(self, context, vnf_instance,
|
||||||
|
instantiate_vnf_request, grant,
|
||||||
|
grant_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def instantiate_end(self, context, vnf_instance,
|
||||||
|
instantiate_vnf_request, grant,
|
||||||
|
grant_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def terminate_start(self, context, vnf_instance,
|
||||||
|
terminate_vnf_request, grant,
|
||||||
|
grant_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def terminate_end(self, context, vnf_instance,
|
||||||
|
terminate_vnf_request, grant,
|
||||||
|
grant_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def scale_start(self, context, vnf_instance,
|
||||||
|
scale_vnf_request, grant,
|
||||||
|
grant_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def scale_end(self, context, vnf_instance,
|
||||||
|
scale_vnf_request, grant,
|
||||||
|
grant_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def heal_start(self, context, vnf_instance,
|
||||||
|
heal_vnf_request, grant,
|
||||||
|
grant_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def heal_end(self, context, vnf_instance,
|
||||||
|
heal_vnf_request, grant,
|
||||||
|
grant_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def change_external_connectivity_start(
|
||||||
|
self, context, vnf_instance,
|
||||||
|
change_ext_conn_request, grant,
|
||||||
|
grant_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def change_external_connectivity_end(
|
||||||
|
self, context, vnf_instance,
|
||||||
|
change_ext_conn_request, grant,
|
||||||
|
grant_request, **kwargs):
|
||||||
|
pass
|
@ -2776,6 +2776,14 @@ class KubernetesMgmtDriver(vnflcm_abstract_driver.VnflcmMgmtAbstractDriver):
|
|||||||
grant_request, **kwargs):
|
grant_request, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def modify_information_start(self, context, vnf_instance,
|
||||||
|
modify_vnf_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def modify_information_end(self, context, vnf_instance,
|
||||||
|
modify_vnf_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
def _check_envi(self, commander):
|
def _check_envi(self, commander):
|
||||||
ssh_command = 'cat /etc/os-release | grep "PRETTY_NAME=" | ' \
|
ssh_command = 'cat /etc/os-release | grep "PRETTY_NAME=" | ' \
|
||||||
'grep -c "Ubuntu 20.04"; arch | grep -c x86_64'
|
'grep -c "Ubuntu 20.04"; arch | grep -c x86_64'
|
||||||
|
@ -1528,3 +1528,13 @@ class KubesprayMgmtDriver(vnflcm_abstract_driver.VnflcmMgmtAbstractDriver):
|
|||||||
change_ext_conn_request, grant,
|
change_ext_conn_request, grant,
|
||||||
grant_request, **kwargs):
|
grant_request, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def modify_information_start(self, context, vnf_instance,
|
||||||
|
modify_vnf_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def modify_information_end(self, context, vnf_instance,
|
||||||
|
modify_vnf_request, **kwargs):
|
||||||
|
pass
|
||||||
|
@ -470,3 +470,11 @@ class PrivateRegistryMgmtDriver(
|
|||||||
change_ext_conn_request, grant,
|
change_ext_conn_request, grant,
|
||||||
grant_request, **kwargs):
|
grant_request, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def modify_information_start(self, context, vnf_instance,
|
||||||
|
modify_vnf_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def modify_information_end(self, context, vnf_instance,
|
||||||
|
modify_vnf_request, **kwargs):
|
||||||
|
pass
|
||||||
|
@ -2311,17 +2311,9 @@ class Conductor(manager.Manager, v2_hook.ConductorV2Hook):
|
|||||||
|
|
||||||
self.send_notification(context, notification_data)
|
self.send_notification(context, notification_data)
|
||||||
|
|
||||||
# update vnf_instances
|
# update vnf_instance
|
||||||
try:
|
state_entered_time = self.vnflcm_driver.modify_vnf(
|
||||||
ins_obj = objects.vnf_instance.VnfInstance(context=context)
|
context, vnf_lcm_opoccs, body_data, vnfd_pkg_data, vnfd_id)
|
||||||
result = ins_obj.update(
|
|
||||||
context,
|
|
||||||
vnf_lcm_opoccs,
|
|
||||||
body_data,
|
|
||||||
vnfd_pkg_data,
|
|
||||||
vnfd_id)
|
|
||||||
except Exception as msg:
|
|
||||||
raise Exception(str(msg))
|
|
||||||
|
|
||||||
# update lcm_op_occs
|
# update lcm_op_occs
|
||||||
if vnfd_pkg_data and len(vnfd_pkg_data) > 0:
|
if vnfd_pkg_data and len(vnfd_pkg_data) > 0:
|
||||||
@ -2340,7 +2332,7 @@ class Conductor(manager.Manager, v2_hook.ConductorV2Hook):
|
|||||||
now = timeutils.utcnow()
|
now = timeutils.utcnow()
|
||||||
lcm_op_obj.id = vnf_lcm_opoccs.get('id')
|
lcm_op_obj.id = vnf_lcm_opoccs.get('id')
|
||||||
lcm_op_obj.operation_state = fields.LcmOccsOperationState.COMPLETED
|
lcm_op_obj.operation_state = fields.LcmOccsOperationState.COMPLETED
|
||||||
lcm_op_obj.state_entered_time = result
|
lcm_op_obj.state_entered_time = state_entered_time
|
||||||
lcm_op_obj.updated_at = now
|
lcm_op_obj.updated_at = now
|
||||||
lcm_op_obj.changed_info = changed_info
|
lcm_op_obj.changed_info = changed_info
|
||||||
|
|
||||||
|
@ -0,0 +1,178 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||||
|
|
||||||
|
description: Sample VNF
|
||||||
|
|
||||||
|
imports:
|
||||||
|
- etsi_nfv_sol001_common_types.yaml
|
||||||
|
- etsi_nfv_sol001_vnfd_types.yaml
|
||||||
|
- sample_types.yaml
|
||||||
|
|
||||||
|
topology_template:
|
||||||
|
inputs:
|
||||||
|
descriptor_id:
|
||||||
|
type: string
|
||||||
|
descriptor_version:
|
||||||
|
type: string
|
||||||
|
provider:
|
||||||
|
type: string
|
||||||
|
product_name:
|
||||||
|
type: string
|
||||||
|
software_version:
|
||||||
|
type: string
|
||||||
|
vnfm_info:
|
||||||
|
type: list
|
||||||
|
entry_schema:
|
||||||
|
type: string
|
||||||
|
flavour_id:
|
||||||
|
type: string
|
||||||
|
flavour_description:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
substitution_mappings:
|
||||||
|
node_type: company.provider.VNF
|
||||||
|
properties:
|
||||||
|
flavour_id: simple
|
||||||
|
requirements:
|
||||||
|
virtual_link_external: []
|
||||||
|
|
||||||
|
node_templates:
|
||||||
|
VNF:
|
||||||
|
type: company.provider.VNF
|
||||||
|
properties:
|
||||||
|
flavour_description: A simple flavour
|
||||||
|
interfaces:
|
||||||
|
Vnflcm:
|
||||||
|
modify_information_start:
|
||||||
|
implementation: mgmt-container-update
|
||||||
|
modify_information_end:
|
||||||
|
implementation: mgmt-container-update
|
||||||
|
artifacts:
|
||||||
|
mgmt-container-update:
|
||||||
|
description: Management driver for container update
|
||||||
|
type: tosca.artifacts.Implementation.Python
|
||||||
|
file: Scripts/container_update_mgmt.py
|
||||||
|
|
||||||
|
VDU1:
|
||||||
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
|
properties:
|
||||||
|
name: vdu1
|
||||||
|
description: kubernetes controller resource as VDU
|
||||||
|
vdu_profile:
|
||||||
|
min_number_of_instances: 1
|
||||||
|
max_number_of_instances: 3
|
||||||
|
|
||||||
|
VDU2:
|
||||||
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
|
properties:
|
||||||
|
name: vdu2
|
||||||
|
description: kubernetes controller resource as VDU
|
||||||
|
vdu_profile:
|
||||||
|
min_number_of_instances: 1
|
||||||
|
max_number_of_instances: 3
|
||||||
|
|
||||||
|
VDU3:
|
||||||
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
|
properties:
|
||||||
|
name: env-test
|
||||||
|
description: kubernetes resource as VDU3
|
||||||
|
vdu_profile:
|
||||||
|
min_number_of_instances: 1
|
||||||
|
max_number_of_instances: 1
|
||||||
|
|
||||||
|
VDU4:
|
||||||
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
|
properties:
|
||||||
|
name: volume-test
|
||||||
|
description: kubernetes resource as VDU4
|
||||||
|
vdu_profile:
|
||||||
|
min_number_of_instances: 1
|
||||||
|
max_number_of_instances: 1
|
||||||
|
policies:
|
||||||
|
- scaling_aspects:
|
||||||
|
type: tosca.policies.nfv.ScalingAspects
|
||||||
|
properties:
|
||||||
|
aspects:
|
||||||
|
vdu1_aspect:
|
||||||
|
name: vdu1_aspect
|
||||||
|
description: vdu1 scaling aspect
|
||||||
|
max_scale_level: 2
|
||||||
|
step_deltas:
|
||||||
|
- delta_1
|
||||||
|
vdu2_aspect:
|
||||||
|
name: vdu2_aspect
|
||||||
|
description: vdu2 scaling aspect
|
||||||
|
max_scale_level: 2
|
||||||
|
step_deltas:
|
||||||
|
- delta_1
|
||||||
|
|
||||||
|
- instantiation_levels:
|
||||||
|
type: tosca.policies.nfv.InstantiationLevels
|
||||||
|
properties:
|
||||||
|
levels:
|
||||||
|
instantiation_level_1:
|
||||||
|
description: Smallest size
|
||||||
|
scale_info:
|
||||||
|
vdu1_aspect:
|
||||||
|
scale_level: 0
|
||||||
|
vdu2_aspect:
|
||||||
|
scale_level: 0
|
||||||
|
instantiation_level_2:
|
||||||
|
description: Largest size
|
||||||
|
scale_info:
|
||||||
|
vdu1_aspect:
|
||||||
|
scale_level: 2
|
||||||
|
vdu2_aspect:
|
||||||
|
scale_level: 2
|
||||||
|
default_level: instantiation_level_1
|
||||||
|
|
||||||
|
- vdu1_initial_delta:
|
||||||
|
type: tosca.policies.nfv.VduInitialDelta
|
||||||
|
properties:
|
||||||
|
initial_delta:
|
||||||
|
number_of_instances: 1
|
||||||
|
targets: [ VDU1 ]
|
||||||
|
|
||||||
|
- vdu1_scaling_aspect_deltas:
|
||||||
|
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||||
|
properties:
|
||||||
|
aspect: vdu1_aspect
|
||||||
|
deltas:
|
||||||
|
delta_1:
|
||||||
|
number_of_instances: 1
|
||||||
|
targets: [ VDU1 ]
|
||||||
|
|
||||||
|
- vdu1_instantiation_levels:
|
||||||
|
type: tosca.policies.nfv.VduInstantiationLevels
|
||||||
|
properties:
|
||||||
|
levels:
|
||||||
|
instantiation_level_1:
|
||||||
|
number_of_instances: 1
|
||||||
|
instantiation_level_2:
|
||||||
|
number_of_instances: 3
|
||||||
|
targets: [ VDU1 ]
|
||||||
|
|
||||||
|
- vdu2_initial_delta:
|
||||||
|
type: tosca.policies.nfv.VduInitialDelta
|
||||||
|
properties:
|
||||||
|
initial_delta:
|
||||||
|
number_of_instances: 1
|
||||||
|
targets: [ VDU2 ]
|
||||||
|
|
||||||
|
- vdu2_scaling_aspect_deltas:
|
||||||
|
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||||
|
properties:
|
||||||
|
aspect: vdu2_aspect
|
||||||
|
deltas:
|
||||||
|
delta_1:
|
||||||
|
number_of_instances: 1
|
||||||
|
targets: [ VDU2 ]
|
||||||
|
|
||||||
|
- vdu2_instantiation_levels:
|
||||||
|
type: tosca.policies.nfv.VduInstantiationLevels
|
||||||
|
properties:
|
||||||
|
levels:
|
||||||
|
instantiation_level_1:
|
||||||
|
number_of_instances: 1
|
||||||
|
instantiation_level_2:
|
||||||
|
number_of_instances: 3
|
||||||
|
targets: [ VDU2 ]
|
@ -0,0 +1,31 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||||
|
|
||||||
|
description: Sample VNF
|
||||||
|
|
||||||
|
imports:
|
||||||
|
- etsi_nfv_sol001_common_types.yaml
|
||||||
|
- etsi_nfv_sol001_vnfd_types.yaml
|
||||||
|
- sample_types.yaml
|
||||||
|
- sample_df_simple.yaml
|
||||||
|
|
||||||
|
topology_template:
|
||||||
|
inputs:
|
||||||
|
selected_flavour:
|
||||||
|
type: string
|
||||||
|
description: VNF deployment flavour selected by the consumer. It is provided in the API
|
||||||
|
|
||||||
|
node_templates:
|
||||||
|
VNF:
|
||||||
|
type: company.provider.VNF
|
||||||
|
properties:
|
||||||
|
flavour_id: { get_input: selected_flavour }
|
||||||
|
descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d70a8883
|
||||||
|
provider: Company
|
||||||
|
product_name: Sample VNF
|
||||||
|
software_version: '1.0'
|
||||||
|
descriptor_version: '1.0'
|
||||||
|
vnfm_info:
|
||||||
|
- Tacker
|
||||||
|
requirements:
|
||||||
|
#- virtual_link_external # mapped in lower-level templates
|
||||||
|
#- virtual_link_internal # mapped in lower-level templates
|
@ -0,0 +1,53 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||||
|
|
||||||
|
description: VNF type definition
|
||||||
|
|
||||||
|
imports:
|
||||||
|
- etsi_nfv_sol001_common_types.yaml
|
||||||
|
- etsi_nfv_sol001_vnfd_types.yaml
|
||||||
|
|
||||||
|
node_types:
|
||||||
|
company.provider.VNF:
|
||||||
|
derived_from: tosca.nodes.nfv.VNF
|
||||||
|
properties:
|
||||||
|
descriptor_id:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d70a8883 ] ]
|
||||||
|
default: b1bb0ce7-ebca-4fa7-95ed-4840d70a8883
|
||||||
|
descriptor_version:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ '1.0' ] ]
|
||||||
|
default: '1.0'
|
||||||
|
provider:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ 'Company' ] ]
|
||||||
|
default: 'Company'
|
||||||
|
product_name:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ 'Sample VNF' ] ]
|
||||||
|
default: 'Sample VNF'
|
||||||
|
software_version:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ '1.0' ] ]
|
||||||
|
default: '1.0'
|
||||||
|
vnfm_info:
|
||||||
|
type: list
|
||||||
|
entry_schema:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ Tacker ] ]
|
||||||
|
default: [ Tacker ]
|
||||||
|
flavour_id:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ simple ] ]
|
||||||
|
default: simple
|
||||||
|
flavour_description:
|
||||||
|
type: string
|
||||||
|
default: ""
|
||||||
|
requirements:
|
||||||
|
- virtual_link_external:
|
||||||
|
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||||
|
- virtual_link_internal:
|
||||||
|
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||||
|
interfaces:
|
||||||
|
Vnflcm:
|
||||||
|
type: tosca.interfaces.nfv.Vnflcm
|
@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: cm-data
|
||||||
|
data:
|
||||||
|
cmKey1.txt: |
|
||||||
|
configmap2 data2
|
||||||
|
foo2
|
||||||
|
bar2
|
@ -0,0 +1,39 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: vdu1
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: webserver
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: webserver
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: cirros
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
protocol: TCP
|
||||||
|
env:
|
||||||
|
- name: CMENV
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: cm-data
|
||||||
|
key: cmKey1.txt
|
||||||
|
- name: SECENV
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-data
|
||||||
|
key: password
|
||||||
|
envFrom:
|
||||||
|
- prefix: CM_
|
||||||
|
configMapRef:
|
||||||
|
name: cm-data
|
||||||
|
- prefix: SEC_
|
||||||
|
secretRef:
|
||||||
|
name: secret-data
|
@ -0,0 +1,26 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: env-test
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: tomcat
|
||||||
|
name: nginx
|
||||||
|
env:
|
||||||
|
- name: CMENV
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: cm-data
|
||||||
|
key: cmKey1.txt
|
||||||
|
- name: SECENV
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-data
|
||||||
|
key: password
|
||||||
|
envFrom:
|
||||||
|
- prefix: CM_
|
||||||
|
configMapRef:
|
||||||
|
name: cm-data
|
||||||
|
- prefix: SEC_
|
||||||
|
secretRef:
|
||||||
|
name: secret-data
|
@ -0,0 +1,8 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: volume-test
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: cirros
|
||||||
|
name: nginx
|
@ -0,0 +1,41 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: ReplicaSet
|
||||||
|
metadata:
|
||||||
|
name: vdu2
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: webserver
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: webserver
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: celebdor/kuryr-demo
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 180
|
||||||
|
protocol: TCP
|
||||||
|
volumeMounts:
|
||||||
|
- name: cm-volume
|
||||||
|
mountPath: /config
|
||||||
|
- name: sec-volume
|
||||||
|
mountPath: /etc/secrets
|
||||||
|
volumes:
|
||||||
|
- name: cm-volume
|
||||||
|
configMap:
|
||||||
|
name: cm-data
|
||||||
|
defaultMode: 0666
|
||||||
|
items:
|
||||||
|
- key: cmKey1.txt
|
||||||
|
path: cm/config.txt
|
||||||
|
- name: sec-volume
|
||||||
|
secret:
|
||||||
|
secretName: secret-data
|
||||||
|
defaultMode: 0600
|
||||||
|
items:
|
||||||
|
- key: secKey1.txt
|
||||||
|
path: creds/secret.txt
|
@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: secret-data
|
||||||
|
stringData:
|
||||||
|
password: 1mbb1G968fb1CUg2
|
||||||
|
secKey1.txt: |
|
||||||
|
secret2 data2
|
||||||
|
baz2
|
@ -0,0 +1,39 @@
|
|||||||
|
TOSCA-Meta-File-Version: 1.0
|
||||||
|
Created-by: dummy_user
|
||||||
|
CSAR-Version: 1.1
|
||||||
|
Entry-Definitions: Definitions/sample_top.vnfd.yaml
|
||||||
|
|
||||||
|
Name: Files/kubernetes/configmap_2.yaml
|
||||||
|
Content-Type: application/yaml
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: 6f34907da12cb5ad82b25d8e7dd6a7dd4219bd0cbad65e678b58b8466be220e0
|
||||||
|
|
||||||
|
Name: Files/kubernetes/deployment.yaml
|
||||||
|
Content-Type: application/yaml
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: 290debe315bb5b098f73d63049ba850cc5df3723447637bddc1cabe3d0e151d7
|
||||||
|
|
||||||
|
Name: Files/kubernetes/pod_env.yaml
|
||||||
|
Content-Type: application/yaml
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: b8674c55cd387fd2ab3d550bc622b314bed151d0afffe4f6bc44a553280d400c
|
||||||
|
|
||||||
|
Name: Files/kubernetes/pod_volume.yaml
|
||||||
|
Content-Type: application/yaml
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: e74cd59b3e91ca1a8fb103b13ef15965581639883cc6d8a531f959b4ae89e688
|
||||||
|
|
||||||
|
Name: Files/kubernetes/replicaset.yaml
|
||||||
|
Content-Type: application/yaml
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: 2d9e48ab5e1794bd9b220fcac75ec7f595d72e04ca287bab30a78748aa852c30
|
||||||
|
|
||||||
|
Name: Files/kubernetes/secret_2.yaml
|
||||||
|
Content-Type: application/yaml
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: f57cbb1822c57e523e5c6d48feea37ee1b1bbd976d720baf8491d47331249e11
|
||||||
|
|
||||||
|
Name: Scripts/container_update_mgmt.py
|
||||||
|
Content-Type: text/x-python
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: 9e272402dd0f6f6b39ffc750590eedef028ce22506eb7436a1a1fcfa76c66423
|
@ -0,0 +1,178 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||||
|
|
||||||
|
description: Sample VNF
|
||||||
|
|
||||||
|
imports:
|
||||||
|
- etsi_nfv_sol001_common_types.yaml
|
||||||
|
- etsi_nfv_sol001_vnfd_types.yaml
|
||||||
|
- sample_types.yaml
|
||||||
|
|
||||||
|
topology_template:
|
||||||
|
inputs:
|
||||||
|
descriptor_id:
|
||||||
|
type: string
|
||||||
|
descriptor_version:
|
||||||
|
type: string
|
||||||
|
provider:
|
||||||
|
type: string
|
||||||
|
product_name:
|
||||||
|
type: string
|
||||||
|
software_version:
|
||||||
|
type: string
|
||||||
|
vnfm_info:
|
||||||
|
type: list
|
||||||
|
entry_schema:
|
||||||
|
type: string
|
||||||
|
flavour_id:
|
||||||
|
type: string
|
||||||
|
flavour_description:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
substitution_mappings:
|
||||||
|
node_type: company.provider.VNF
|
||||||
|
properties:
|
||||||
|
flavour_id: simple
|
||||||
|
requirements:
|
||||||
|
virtual_link_external: []
|
||||||
|
|
||||||
|
node_templates:
|
||||||
|
VNF:
|
||||||
|
type: company.provider.VNF
|
||||||
|
properties:
|
||||||
|
flavour_description: A simple flavour
|
||||||
|
interfaces:
|
||||||
|
Vnflcm:
|
||||||
|
modify_information_start:
|
||||||
|
implementation: mgmt-container-update
|
||||||
|
modify_information_end:
|
||||||
|
implementation: mgmt-container-update
|
||||||
|
artifacts:
|
||||||
|
mgmt-container-update:
|
||||||
|
description: Management driver for container update
|
||||||
|
type: tosca.artifacts.Implementation.Python
|
||||||
|
file: Scripts/container_update_mgmt.py
|
||||||
|
|
||||||
|
VDU1:
|
||||||
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
|
properties:
|
||||||
|
name: vdu1
|
||||||
|
description: kubernetes controller resource as VDU
|
||||||
|
vdu_profile:
|
||||||
|
min_number_of_instances: 1
|
||||||
|
max_number_of_instances: 3
|
||||||
|
|
||||||
|
VDU2:
|
||||||
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
|
properties:
|
||||||
|
name: vdu2
|
||||||
|
description: kubernetes controller resource as VDU
|
||||||
|
vdu_profile:
|
||||||
|
min_number_of_instances: 1
|
||||||
|
max_number_of_instances: 3
|
||||||
|
|
||||||
|
VDU3:
|
||||||
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
|
properties:
|
||||||
|
name: env-test
|
||||||
|
description: kubernetes resource as VDU3
|
||||||
|
vdu_profile:
|
||||||
|
min_number_of_instances: 1
|
||||||
|
max_number_of_instances: 1
|
||||||
|
|
||||||
|
VDU4:
|
||||||
|
type: tosca.nodes.nfv.Vdu.Compute
|
||||||
|
properties:
|
||||||
|
name: volume-test
|
||||||
|
description: kubernetes resource as VDU4
|
||||||
|
vdu_profile:
|
||||||
|
min_number_of_instances: 1
|
||||||
|
max_number_of_instances: 1
|
||||||
|
policies:
|
||||||
|
- scaling_aspects:
|
||||||
|
type: tosca.policies.nfv.ScalingAspects
|
||||||
|
properties:
|
||||||
|
aspects:
|
||||||
|
vdu1_aspect:
|
||||||
|
name: vdu1_aspect
|
||||||
|
description: vdu1 scaling aspect
|
||||||
|
max_scale_level: 2
|
||||||
|
step_deltas:
|
||||||
|
- delta_1
|
||||||
|
vdu2_aspect:
|
||||||
|
name: vdu2_aspect
|
||||||
|
description: vdu2 scaling aspect
|
||||||
|
max_scale_level: 2
|
||||||
|
step_deltas:
|
||||||
|
- delta_1
|
||||||
|
|
||||||
|
- instantiation_levels:
|
||||||
|
type: tosca.policies.nfv.InstantiationLevels
|
||||||
|
properties:
|
||||||
|
levels:
|
||||||
|
instantiation_level_1:
|
||||||
|
description: Smallest size
|
||||||
|
scale_info:
|
||||||
|
vdu1_aspect:
|
||||||
|
scale_level: 0
|
||||||
|
vdu2_aspect:
|
||||||
|
scale_level: 0
|
||||||
|
instantiation_level_2:
|
||||||
|
description: Largest size
|
||||||
|
scale_info:
|
||||||
|
vdu1_aspect:
|
||||||
|
scale_level: 2
|
||||||
|
vdu2_aspect:
|
||||||
|
scale_level: 2
|
||||||
|
default_level: instantiation_level_1
|
||||||
|
|
||||||
|
- vdu1_initial_delta:
|
||||||
|
type: tosca.policies.nfv.VduInitialDelta
|
||||||
|
properties:
|
||||||
|
initial_delta:
|
||||||
|
number_of_instances: 1
|
||||||
|
targets: [ VDU1 ]
|
||||||
|
|
||||||
|
- vdu1_scaling_aspect_deltas:
|
||||||
|
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||||
|
properties:
|
||||||
|
aspect: vdu1_aspect
|
||||||
|
deltas:
|
||||||
|
delta_1:
|
||||||
|
number_of_instances: 1
|
||||||
|
targets: [ VDU1 ]
|
||||||
|
|
||||||
|
- vdu1_instantiation_levels:
|
||||||
|
type: tosca.policies.nfv.VduInstantiationLevels
|
||||||
|
properties:
|
||||||
|
levels:
|
||||||
|
instantiation_level_1:
|
||||||
|
number_of_instances: 1
|
||||||
|
instantiation_level_2:
|
||||||
|
number_of_instances: 3
|
||||||
|
targets: [ VDU1 ]
|
||||||
|
|
||||||
|
- vdu2_initial_delta:
|
||||||
|
type: tosca.policies.nfv.VduInitialDelta
|
||||||
|
properties:
|
||||||
|
initial_delta:
|
||||||
|
number_of_instances: 1
|
||||||
|
targets: [ VDU2 ]
|
||||||
|
|
||||||
|
- vdu2_scaling_aspect_deltas:
|
||||||
|
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||||
|
properties:
|
||||||
|
aspect: vdu2_aspect
|
||||||
|
deltas:
|
||||||
|
delta_1:
|
||||||
|
number_of_instances: 1
|
||||||
|
targets: [ VDU2 ]
|
||||||
|
|
||||||
|
- vdu2_instantiation_levels:
|
||||||
|
type: tosca.policies.nfv.VduInstantiationLevels
|
||||||
|
properties:
|
||||||
|
levels:
|
||||||
|
instantiation_level_1:
|
||||||
|
number_of_instances: 1
|
||||||
|
instantiation_level_2:
|
||||||
|
number_of_instances: 3
|
||||||
|
targets: [ VDU2 ]
|
@ -0,0 +1,31 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||||
|
|
||||||
|
description: Sample VNF
|
||||||
|
|
||||||
|
imports:
|
||||||
|
- etsi_nfv_sol001_common_types.yaml
|
||||||
|
- etsi_nfv_sol001_vnfd_types.yaml
|
||||||
|
- sample_types.yaml
|
||||||
|
- sample_df_simple.yaml
|
||||||
|
|
||||||
|
topology_template:
|
||||||
|
inputs:
|
||||||
|
selected_flavour:
|
||||||
|
type: string
|
||||||
|
description: VNF deployment flavour selected by the consumer. It is provided in the API
|
||||||
|
|
||||||
|
node_templates:
|
||||||
|
VNF:
|
||||||
|
type: company.provider.VNF
|
||||||
|
properties:
|
||||||
|
flavour_id: { get_input: selected_flavour }
|
||||||
|
descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d70a7774
|
||||||
|
provider: Company
|
||||||
|
product_name: Sample VNF
|
||||||
|
software_version: '1.0'
|
||||||
|
descriptor_version: '1.0'
|
||||||
|
vnfm_info:
|
||||||
|
- Tacker
|
||||||
|
requirements:
|
||||||
|
#- virtual_link_external # mapped in lower-level templates
|
||||||
|
#- virtual_link_internal # mapped in lower-level templates
|
@ -0,0 +1,53 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||||
|
|
||||||
|
description: VNF type definition
|
||||||
|
|
||||||
|
imports:
|
||||||
|
- etsi_nfv_sol001_common_types.yaml
|
||||||
|
- etsi_nfv_sol001_vnfd_types.yaml
|
||||||
|
|
||||||
|
node_types:
|
||||||
|
company.provider.VNF:
|
||||||
|
derived_from: tosca.nodes.nfv.VNF
|
||||||
|
properties:
|
||||||
|
descriptor_id:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d70a7774 ] ]
|
||||||
|
default: b1bb0ce7-ebca-4fa7-95ed-4840d70a7774
|
||||||
|
descriptor_version:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ '1.0' ] ]
|
||||||
|
default: '1.0'
|
||||||
|
provider:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ 'Company' ] ]
|
||||||
|
default: 'Company'
|
||||||
|
product_name:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ 'Sample VNF' ] ]
|
||||||
|
default: 'Sample VNF'
|
||||||
|
software_version:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ '1.0' ] ]
|
||||||
|
default: '1.0'
|
||||||
|
vnfm_info:
|
||||||
|
type: list
|
||||||
|
entry_schema:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ Tacker ] ]
|
||||||
|
default: [ Tacker ]
|
||||||
|
flavour_id:
|
||||||
|
type: string
|
||||||
|
constraints: [ valid_values: [ simple ] ]
|
||||||
|
default: simple
|
||||||
|
flavour_description:
|
||||||
|
type: string
|
||||||
|
default: ""
|
||||||
|
requirements:
|
||||||
|
- virtual_link_external:
|
||||||
|
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||||
|
- virtual_link_internal:
|
||||||
|
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||||
|
interfaces:
|
||||||
|
Vnflcm:
|
||||||
|
type: tosca.interfaces.nfv.Vnflcm
|
@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: cm-data
|
||||||
|
data:
|
||||||
|
cmKey1.txt: |
|
||||||
|
configmap data
|
||||||
|
foo
|
||||||
|
bar
|
@ -0,0 +1,39 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: vdu1
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: webserver
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: webserver
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
protocol: TCP
|
||||||
|
env:
|
||||||
|
- name: CMENV
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: cm-data
|
||||||
|
key: cmKey1.txt
|
||||||
|
- name: SECENV
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-data
|
||||||
|
key: password
|
||||||
|
envFrom:
|
||||||
|
- prefix: CM_
|
||||||
|
configMapRef:
|
||||||
|
name: cm-data
|
||||||
|
- prefix: SEC_
|
||||||
|
secretRef:
|
||||||
|
name: secret-data
|
@ -0,0 +1,26 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: env-test
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx
|
||||||
|
name: nginx
|
||||||
|
env:
|
||||||
|
- name: CMENV
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: cm-data
|
||||||
|
key: cmKey1.txt
|
||||||
|
- name: SECENV
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: secret-data
|
||||||
|
key: password
|
||||||
|
envFrom:
|
||||||
|
- prefix: CM_
|
||||||
|
configMapRef:
|
||||||
|
name: cm-data
|
||||||
|
- prefix: SEC_
|
||||||
|
secretRef:
|
||||||
|
name: secret-data
|
@ -0,0 +1,28 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: volume-test
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx
|
||||||
|
name: nginx
|
||||||
|
volumeMounts:
|
||||||
|
- name: cm-volume
|
||||||
|
mountPath: /config
|
||||||
|
- name: sec-volume
|
||||||
|
mountPath: /etc/secrets
|
||||||
|
volumes:
|
||||||
|
- name: cm-volume
|
||||||
|
configMap:
|
||||||
|
name: cm-data
|
||||||
|
defaultMode: 0666
|
||||||
|
items:
|
||||||
|
- key: cmKey1.txt
|
||||||
|
path: cm/config.txt
|
||||||
|
- name: sec-volume
|
||||||
|
secret:
|
||||||
|
secretName: secret-data
|
||||||
|
defaultMode: 0600
|
||||||
|
items:
|
||||||
|
- key: secKey1.txt
|
||||||
|
path: creds/secret.txt
|
@ -0,0 +1,41 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: ReplicaSet
|
||||||
|
metadata:
|
||||||
|
name: vdu2
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: webserver
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: webserver
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
protocol: TCP
|
||||||
|
volumeMounts:
|
||||||
|
- name: cm-volume
|
||||||
|
mountPath: /config
|
||||||
|
- name: sec-volume
|
||||||
|
mountPath: /etc/secrets
|
||||||
|
volumes:
|
||||||
|
- name: cm-volume
|
||||||
|
configMap:
|
||||||
|
name: cm-data
|
||||||
|
defaultMode: 0666
|
||||||
|
items:
|
||||||
|
- key: cmKey1.txt
|
||||||
|
path: cm/config.txt
|
||||||
|
- name: sec-volume
|
||||||
|
secret:
|
||||||
|
secretName: secret-data
|
||||||
|
defaultMode: 0600
|
||||||
|
items:
|
||||||
|
- key: secKey1.txt
|
||||||
|
path: creds/secret.txt
|
@ -0,0 +1,9 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: secret-data
|
||||||
|
stringData:
|
||||||
|
password: 1mbb1G968fb1CUg
|
||||||
|
secKey1.txt: |
|
||||||
|
secret data
|
||||||
|
baz
|
@ -0,0 +1,39 @@
|
|||||||
|
TOSCA-Meta-File-Version: 1.0
|
||||||
|
Created-by: dummy_user
|
||||||
|
CSAR-Version: 1.1
|
||||||
|
Entry-Definitions: Definitions/sample_top.vnfd.yaml
|
||||||
|
|
||||||
|
Name: Files/kubernetes/configmap_1.yaml
|
||||||
|
Content-Type: application/yaml
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: b914022a12c7a0f3a6a7d14013020f1271645ab44b17942a014fdc8ad96f42fe
|
||||||
|
|
||||||
|
Name: Files/kubernetes/deployment.yaml
|
||||||
|
Content-Type: application/yaml
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: 03b61e9646925ddf414489212736b7d799710b26dc70f96641c50e699f0a1d5f
|
||||||
|
|
||||||
|
Name: Files/kubernetes/pod_env.yaml
|
||||||
|
Content-Type: application/yaml
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: 2e69400cf742fd88cb9ba5abd0b83ba9dd12f46f36fa615d5b93e07af47a2b5d
|
||||||
|
|
||||||
|
Name: Files/kubernetes/pod_volume.yaml
|
||||||
|
Content-Type: application/yaml
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: 93bd20b9c2111115131a96769b710e09001248ba9c3d3f33e1d616d0b9546728
|
||||||
|
|
||||||
|
Name: Files/kubernetes/replicaset.yaml
|
||||||
|
Content-Type: application/yaml
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: 5ed8f7acab4ff3f2e32ac00c7d1bb981197877c654bed8e0a6d1b2f603cebf5b
|
||||||
|
|
||||||
|
Name: Files/kubernetes/secret_1.yaml
|
||||||
|
Content-Type: application/yaml
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: 8c170000410e72c1b81784061928cde26d1f9ea027fbece4da34ced08da8c4b4
|
||||||
|
|
||||||
|
Name: Scripts/container_update_mgmt.py
|
||||||
|
Content-Type: text/x-python
|
||||||
|
Algorithm: SHA-256
|
||||||
|
Hash: 9e272402dd0f6f6b39ffc750590eedef028ce22506eb7436a1a1fcfa76c66423
|
@ -88,3 +88,13 @@ class VnflcmMgmtNoop(vnflcm_abstract_driver.VnflcmMgmtAbstractDriver):
|
|||||||
change_ext_conn_request, grant,
|
change_ext_conn_request, grant,
|
||||||
grant_request, **kwargs):
|
grant_request, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def modify_information_start(self, context, vnf_instance,
|
||||||
|
modify_vnf_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def modify_information_end(self, context, vnf_instance,
|
||||||
|
modify_vnf_request, **kwargs):
|
||||||
|
pass
|
||||||
|
@ -9,4 +9,4 @@ Content-type: application/x-iso9066-image
|
|||||||
Name: Scripts/vnflcm_noop.py
|
Name: Scripts/vnflcm_noop.py
|
||||||
Content-Type: text/x-python
|
Content-Type: text/x-python
|
||||||
Algorithm: SHA-256
|
Algorithm: SHA-256
|
||||||
Hash: 950422e356c3eb6a7c92f424d975e2d3f295f480321bd91a97501045eddeab4a
|
Hash: 559837eda52829630f90c803be30c82141aa4c7441736a6827495e7fdb768d0a
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# 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 os
|
import os
|
||||||
|
import shutil
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
@ -56,7 +57,8 @@ class BaseVnfLcmKubernetesTest(base.BaseTackerTest):
|
|||||||
"terminate": timeout,
|
"terminate": timeout,
|
||||||
"heal_sol002": timeout,
|
"heal_sol002": timeout,
|
||||||
"heal_sol003": timeout * 2,
|
"heal_sol003": timeout * 2,
|
||||||
"scale": timeout
|
"scale": timeout,
|
||||||
|
"modify": timeout
|
||||||
}
|
}
|
||||||
cls.vnf_package_ids = []
|
cls.vnf_package_ids = []
|
||||||
|
|
||||||
@ -81,6 +83,63 @@ class BaseVnfLcmKubernetesTest(base.BaseTackerTest):
|
|||||||
self.skipTest(f"Kubernetes VIM '{vim_name}' is missing")
|
self.skipTest(f"Kubernetes VIM '{vim_name}' is missing")
|
||||||
self.vim_id = vim['id']
|
self.vim_id = vim['id']
|
||||||
|
|
||||||
|
def _create_and_upload_vnf_package_add_mgmt(
|
||||||
|
self, tacker_client, csar_package_name,
|
||||||
|
user_defined_data, mgmt_rela_path):
|
||||||
|
# create vnf package
|
||||||
|
body = jsonutils.dumps({"userDefinedData": user_defined_data})
|
||||||
|
_, vnf_package = tacker_client.do_request(
|
||||||
|
self.base_vnf_package_url, "POST", body=body)
|
||||||
|
vnf_pkg_id = vnf_package['id']
|
||||||
|
|
||||||
|
# cp MgmtDriver to package
|
||||||
|
mgmt_abs_path = os.path.abspath(os.path.join(
|
||||||
|
os.path.dirname(__file__), mgmt_rela_path))
|
||||||
|
mgmt_package_abs_path = os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__),
|
||||||
|
f"../../../etc/samples/etsi/nfv/"
|
||||||
|
f"{csar_package_name}/Scripts/"))
|
||||||
|
os.mkdir(mgmt_package_abs_path)
|
||||||
|
shutil.copy(mgmt_abs_path, mgmt_package_abs_path)
|
||||||
|
|
||||||
|
# upload vnf package
|
||||||
|
csar_package_path = ("../../../etc/samples/etsi/nfv/"
|
||||||
|
f"{csar_package_name}")
|
||||||
|
file_path = os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__), csar_package_path))
|
||||||
|
|
||||||
|
# Generating unique vnfd id. This is required when multiple workers
|
||||||
|
# are running concurrently. The call below creates a new temporary
|
||||||
|
# CSAR with unique vnfd id.
|
||||||
|
file_path, _ = utils.create_csar_with_unique_vnfd_id(file_path)
|
||||||
|
|
||||||
|
with open(file_path, 'rb') as file_object:
|
||||||
|
tacker_client.do_request(
|
||||||
|
f"{self.base_vnf_package_url}/{vnf_pkg_id}/package_content",
|
||||||
|
"PUT", body=file_object, content_type='application/zip')
|
||||||
|
|
||||||
|
# wait for onboard
|
||||||
|
start_time = int(time.time())
|
||||||
|
show_url = os.path.join(self.base_vnf_package_url, vnf_pkg_id)
|
||||||
|
vnfd_id = None
|
||||||
|
while True:
|
||||||
|
_, body = tacker_client.do_request(show_url, "GET")
|
||||||
|
if body['onboardingState'] == "ONBOARDED":
|
||||||
|
vnfd_id = body['vnfdId']
|
||||||
|
break
|
||||||
|
|
||||||
|
if (int(time.time()) - start_time) > VNF_PACKAGE_UPLOAD_TIMEOUT:
|
||||||
|
raise Exception(WAIT_TIMEOUT_ERR_MSG %
|
||||||
|
{"action": "onboard vnf package",
|
||||||
|
"timeout": VNF_PACKAGE_UPLOAD_TIMEOUT})
|
||||||
|
|
||||||
|
time.sleep(RETRY_WAIT_TIME)
|
||||||
|
|
||||||
|
# remove temporarily created CSAR file
|
||||||
|
os.remove(file_path)
|
||||||
|
shutil.rmtree(mgmt_package_abs_path)
|
||||||
|
return vnf_package['id'], vnfd_id
|
||||||
|
|
||||||
def _create_and_upload_vnf_package(self, tacker_client, csar_package_name,
|
def _create_and_upload_vnf_package(self, tacker_client, csar_package_name,
|
||||||
user_defined_data):
|
user_defined_data):
|
||||||
# create vnf package
|
# create vnf package
|
||||||
@ -168,6 +227,7 @@ class BaseVnfLcmKubernetesTest(base.BaseTackerTest):
|
|||||||
resp, response_body = self.http_client.do_request(
|
resp, response_body = self.http_client.do_request(
|
||||||
self.base_vnf_instances_url, "POST",
|
self.base_vnf_instances_url, "POST",
|
||||||
body=jsonutils.dumps(request_body))
|
body=jsonutils.dumps(request_body))
|
||||||
|
self.assertEqual(201, resp.status_code)
|
||||||
return resp, response_body
|
return resp, response_body
|
||||||
|
|
||||||
def _delete_wait_vnf_instance(self, id):
|
def _delete_wait_vnf_instance(self, id):
|
||||||
@ -228,6 +288,8 @@ class BaseVnfLcmKubernetesTest(base.BaseTackerTest):
|
|||||||
_, vnf_instance = self._create_vnf_instance(
|
_, vnf_instance = self._create_vnf_instance(
|
||||||
vnfd_id, vnf_instance_name=inst_name,
|
vnfd_id, vnf_instance_name=inst_name,
|
||||||
vnf_instance_description=inst_desc)
|
vnf_instance_description=inst_desc)
|
||||||
|
self.assertEqual(
|
||||||
|
'NOT_INSTANTIATED', vnf_instance['instantiationState'])
|
||||||
|
|
||||||
# instantiate vnf instance
|
# instantiate vnf instance
|
||||||
additional_param = additional_params
|
additional_param = additional_params
|
||||||
@ -237,8 +299,31 @@ class BaseVnfLcmKubernetesTest(base.BaseTackerTest):
|
|||||||
self._instantiate_vnf_instance(vnf_instance['id'], request_body)
|
self._instantiate_vnf_instance(vnf_instance['id'], request_body)
|
||||||
vnf_instance = self._show_vnf_instance(vnf_instance['id'])
|
vnf_instance = self._show_vnf_instance(vnf_instance['id'])
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
'INSTANTIATED', vnf_instance['instantiationState'])
|
||||||
|
vnflcm_op_occ = self._get_vnflcm_op_occs_by_id(
|
||||||
|
self.context, vnf_instance['id'])
|
||||||
|
self.assertEqual('COMPLETED', vnflcm_op_occ.operation_state)
|
||||||
|
self.assertEqual('INSTANTIATE', vnflcm_op_occ.operation)
|
||||||
|
|
||||||
return vnf_instance
|
return vnf_instance
|
||||||
|
|
||||||
|
def _modify_vnf_instance(self, vnf_instance_id, request_body):
|
||||||
|
# modify vnf instance
|
||||||
|
url = os.path.join(self.base_vnf_instances_url, vnf_instance_id)
|
||||||
|
resp, _ = self.http_client.do_request(
|
||||||
|
url, "PATCH", body=jsonutils.dumps(request_body))
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
time.sleep(5)
|
||||||
|
self._wait_vnflcm_op_occs(
|
||||||
|
self.context, vnf_instance_id, self.lcm_timeout['modify'])
|
||||||
|
vnflcm_op_occ = self._get_vnflcm_op_occs_by_id(
|
||||||
|
self.context, vnf_instance_id)
|
||||||
|
self.assertEqual('MODIFY_INFO', vnflcm_op_occ.operation)
|
||||||
|
vnf_instance = self._show_vnf_instance(vnf_instance_id)
|
||||||
|
vnfc_rscs = self._get_vnfc_resource_info(vnf_instance)
|
||||||
|
return vnf_instance, vnfc_rscs
|
||||||
|
|
||||||
def _terminate_vnf_instance(self, id, request_body=None):
|
def _terminate_vnf_instance(self, id, request_body=None):
|
||||||
if request_body is None:
|
if request_body is None:
|
||||||
# Terminate vnf forcefully
|
# Terminate vnf forcefully
|
||||||
|
@ -0,0 +1,102 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from tacker.tests.functional.sol_kubernetes.vnflcm import base as vnflcm_base
|
||||||
|
|
||||||
|
|
||||||
|
class VnfLcmKubernetesContainerUpdate(vnflcm_base.BaseVnfLcmKubernetesTest):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(VnfLcmKubernetesContainerUpdate, cls).setUpClass()
|
||||||
|
mgmt_rela_path = ("../../../../../samples/mgmt_driver/kubernetes/"
|
||||||
|
"container_update/container_update_mgmt.py")
|
||||||
|
vnf_package_id_before, cls.vnfd_id_before = (
|
||||||
|
cls._create_and_upload_vnf_package_add_mgmt(
|
||||||
|
cls, cls.tacker_client, "test_cnf_container_update_before",
|
||||||
|
{"key": "sample_container_update_before_functional"},
|
||||||
|
mgmt_rela_path))
|
||||||
|
cls.vnf_package_ids.append(vnf_package_id_before)
|
||||||
|
|
||||||
|
vnf_package_id_after, cls.vnfd_id_after = (
|
||||||
|
cls._create_and_upload_vnf_package_add_mgmt(
|
||||||
|
cls, cls.tacker_client, "test_cnf_container_update_after",
|
||||||
|
{"key": "sample_container_update_after_functional"},
|
||||||
|
mgmt_rela_path))
|
||||||
|
cls.vnf_package_ids.append(vnf_package_id_after)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
super(VnfLcmKubernetesContainerUpdate, cls).tearDownClass()
|
||||||
|
|
||||||
|
def test_container_update_multi_kinds(self):
|
||||||
|
vnf_instance_name = "container_update_multi_kinds"
|
||||||
|
vnf_instance_description = "container update multi kinds"
|
||||||
|
files = ["Files/kubernetes/configmap_1.yaml",
|
||||||
|
"Files/kubernetes/deployment.yaml",
|
||||||
|
"Files/kubernetes/pod_env.yaml",
|
||||||
|
"Files/kubernetes/pod_volume.yaml",
|
||||||
|
"Files/kubernetes/replicaset.yaml",
|
||||||
|
"Files/kubernetes/secret_1.yaml"]
|
||||||
|
additional_param = {
|
||||||
|
"lcm-kubernetes-def-files": files,
|
||||||
|
"namespace": "default"}
|
||||||
|
|
||||||
|
# instantiate
|
||||||
|
vnf_instance = self._create_and_instantiate_vnf_instance(
|
||||||
|
self.vnfd_id_before, "simple", vnf_instance_name,
|
||||||
|
vnf_instance_description, additional_param)
|
||||||
|
|
||||||
|
before_vnfc_rscs = self._get_vnfc_resource_info(vnf_instance)
|
||||||
|
self.assertEqual(4, len(before_vnfc_rscs))
|
||||||
|
|
||||||
|
# modify
|
||||||
|
vnf_instance_name = "modify_vnf_after"
|
||||||
|
configmap_secret_paths = [
|
||||||
|
"Files/kubernetes/configmap_2.yaml",
|
||||||
|
"Files/kubernetes/secret_2.yaml"]
|
||||||
|
metadata = {"configmap_secret_paths": configmap_secret_paths}
|
||||||
|
modify_request_body = {
|
||||||
|
"vnfdId": self.vnfd_id_after,
|
||||||
|
"vnfInstanceName": vnf_instance_name,
|
||||||
|
"metadata": metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
vnf_instance_after, after_vnfc_rscs = self._modify_vnf_instance(
|
||||||
|
vnf_instance['id'], modify_request_body)
|
||||||
|
|
||||||
|
self.assertEqual(4, len(after_vnfc_rscs))
|
||||||
|
|
||||||
|
for after_vnfc_rsc in after_vnfc_rscs:
|
||||||
|
for before_vnfc_rsc in before_vnfc_rscs:
|
||||||
|
after_resource = after_vnfc_rsc['computeResource']
|
||||||
|
before_resource = before_vnfc_rsc['computeResource']
|
||||||
|
if after_vnfc_rsc['id'] == before_vnfc_rsc['id']:
|
||||||
|
if after_resource['vimLevelResourceType'] == 'Deployment':
|
||||||
|
# check stored pod name is changed (Deployment)
|
||||||
|
self.assertNotEqual(before_resource['resourceId'],
|
||||||
|
after_resource['resourceId'])
|
||||||
|
else:
|
||||||
|
# check stored pod name is not changed (other)
|
||||||
|
self.assertEqual(before_resource['resourceId'],
|
||||||
|
after_resource['resourceId'])
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
self.vnfd_id_after, vnf_instance_after['vnfdId'])
|
||||||
|
self.assertEqual(
|
||||||
|
vnf_instance_name, vnf_instance_after['vnfInstanceName'])
|
||||||
|
|
||||||
|
# terminate
|
||||||
|
self._terminate_vnf_instance(vnf_instance['id'])
|
||||||
|
self._delete_vnf_instance(vnf_instance['id'])
|
@ -69,7 +69,10 @@ CONF = tacker.conf.CONF
|
|||||||
|
|
||||||
|
|
||||||
class FakeVnfLcmDriver(mock.Mock):
|
class FakeVnfLcmDriver(mock.Mock):
|
||||||
pass
|
def modify_vnf(self, context, vnf_lcm_opoccs, body_data,
|
||||||
|
vnfd_pkg_data, vnfd_id):
|
||||||
|
return datetime.datetime(
|
||||||
|
1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
||||||
|
|
||||||
|
|
||||||
class FakeVNFMPlugin(mock.Mock):
|
class FakeVNFMPlugin(mock.Mock):
|
||||||
@ -3235,13 +3238,10 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
|
|||||||
@mock.patch.object(conductor_server, 'revert_update_lcm')
|
@mock.patch.object(conductor_server, 'revert_update_lcm')
|
||||||
@mock.patch.object(t_context.get_admin_context().session, "add")
|
@mock.patch.object(t_context.get_admin_context().session, "add")
|
||||||
@mock.patch.object(objects.vnf_lcm_op_occs.VnfLcmOpOcc, "save")
|
@mock.patch.object(objects.vnf_lcm_op_occs.VnfLcmOpOcc, "save")
|
||||||
@mock.patch.object(objects.VnfInstance, "update")
|
|
||||||
@mock.patch.object(objects.vnf_lcm_op_occs.VnfLcmOpOcc, "create")
|
@mock.patch.object(objects.vnf_lcm_op_occs.VnfLcmOpOcc, "create")
|
||||||
def test_update(self, mock_create, mock_update, mock_save, mock_add,
|
def test_update(self, mock_create, mock_save, mock_add,
|
||||||
mock_revert):
|
mock_revert):
|
||||||
mock_create.return_value = "OK"
|
mock_create.return_value = "OK"
|
||||||
mock_update.return_value = datetime.datetime(
|
|
||||||
1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
|
|
||||||
mock_add.return_value = "OK"
|
mock_add.return_value = "OK"
|
||||||
mock_save.return_value = "OK"
|
mock_save.return_value = "OK"
|
||||||
vnfd_id = "2c69a161-0000-4b0f-bcf8-391f8fc76600"
|
vnfd_id = "2c69a161-0000-4b0f-bcf8-391f8fc76600"
|
||||||
|
@ -192,6 +192,86 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
|||||||
'tenant': uuidsentinel.tenant_id}
|
'tenant': uuidsentinel.tenant_id}
|
||||||
self.vim_client.get_vim.return_value = vim_obj
|
self.vim_client.get_vim.return_value = vim_obj
|
||||||
|
|
||||||
|
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||||
|
return_value={'VNFM': FakeVNFMPlugin()})
|
||||||
|
@mock.patch.object(VnfLcmDriver, '_init_mgmt_driver_hash')
|
||||||
|
@mock.patch.object(objects.VnfInstance, "update")
|
||||||
|
@mock.patch('tacker.vnflcm.utils._get_vnf_package_path')
|
||||||
|
@mock.patch.object(objects.VimConnectionInfo, "obj_from_primitive")
|
||||||
|
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
|
||||||
|
@mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.'
|
||||||
|
'_load_vnf_interface')
|
||||||
|
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
||||||
|
def test_modify_vnf(self, mock_get_by_id, mock_load, mock_vnfd_dict,
|
||||||
|
mock_vim, mock_get_vnf_package_path, mock_update,
|
||||||
|
mock_init_hash, mock_get_service_plugins):
|
||||||
|
vim_connection_info = vim_connection.VimConnectionInfo(
|
||||||
|
vim_type="kubernetes")
|
||||||
|
mock_vim.return_value = vim_connection_info
|
||||||
|
vnf_instance = fakes.return_vnf_instance(
|
||||||
|
fields.VnfInstanceState.INSTANTIATED)
|
||||||
|
mock_get_by_id.return_value = vnf_instance
|
||||||
|
mock_vnfd_dict.return_value = fakes.vnfd_dict_cnf()
|
||||||
|
mock_init_hash.return_value = {
|
||||||
|
"vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859"
|
||||||
|
"b18d663b127100eb72b19eecd7ed51"
|
||||||
|
}
|
||||||
|
vnf_lcm_opoccs = {
|
||||||
|
'vnf_instance_id': 'c5d64d28-c868-4348-8a96-1a6976ce465f',
|
||||||
|
'operationParams': '{"metadata": {"configmap_secret_paths":'
|
||||||
|
'["Files/kubernetes/configmap_2.yaml",'
|
||||||
|
'"Files/kubernetes/secret_2.yaml"]}}'}
|
||||||
|
body_data = {}
|
||||||
|
vnfd_pkg_data = {}
|
||||||
|
vnfd_id = '122cbedf-0b50-4706-aad1-7106d590c0a7'
|
||||||
|
mock_load.return_value = fakes.return_vnf_interfaces()
|
||||||
|
self._mock_vnf_manager()
|
||||||
|
driver = vnflcm_driver.VnfLcmDriver()
|
||||||
|
driver.modify_vnf(self.context, vnf_lcm_opoccs,
|
||||||
|
body_data, vnfd_pkg_data, vnfd_id)
|
||||||
|
|
||||||
|
self.assertEqual(2, self._vnf_manager.invoke.call_count)
|
||||||
|
|
||||||
|
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||||
|
return_value={'VNFM': FakeVNFMPlugin()})
|
||||||
|
@mock.patch.object(VnfLcmDriver, '_init_mgmt_driver_hash')
|
||||||
|
@mock.patch.object(objects.VnfInstance, "update")
|
||||||
|
@mock.patch('tacker.vnflcm.utils._get_vnf_package_path')
|
||||||
|
@mock.patch.object(objects.VimConnectionInfo, "obj_from_primitive")
|
||||||
|
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
|
||||||
|
@mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.'
|
||||||
|
'_load_vnf_interface')
|
||||||
|
@mock.patch.object(objects.VnfInstance, "get_by_id")
|
||||||
|
def test_modify_vnf_params_error(
|
||||||
|
self, mock_get_by_id, mock_load, mock_vnfd_dict, mock_vim,
|
||||||
|
mock_get_vnf_package_path, mock_update, mock_init_hash,
|
||||||
|
mock_get_service_plugins):
|
||||||
|
vim_connection_info = vim_connection.VimConnectionInfo(
|
||||||
|
vim_type="kubernetes")
|
||||||
|
mock_vim.return_value = vim_connection_info
|
||||||
|
vnf_instance = fakes.return_vnf_instance(
|
||||||
|
fields.VnfInstanceState.INSTANTIATED)
|
||||||
|
mock_get_by_id.return_value = vnf_instance
|
||||||
|
mock_vnfd_dict.return_value = fakes.vnfd_dict_cnf()
|
||||||
|
mock_init_hash.return_value = {
|
||||||
|
"vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859"
|
||||||
|
"b18d663b127100eb72b19eecd7ed51"
|
||||||
|
}
|
||||||
|
vnf_lcm_opoccs = {
|
||||||
|
'vnf_instance_id': 'c5d64d28-c868-4348-8a96-1a6976ce465f',
|
||||||
|
'operationParams': 'error_params'
|
||||||
|
}
|
||||||
|
body_data = {}
|
||||||
|
vnfd_pkg_data = {}
|
||||||
|
vnfd_id = '122cbedf-0b50-4706-aad1-7106d590c0a7'
|
||||||
|
mock_load.return_value = fakes.return_vnf_interfaces()
|
||||||
|
self._mock_vnf_manager()
|
||||||
|
driver = vnflcm_driver.VnfLcmDriver()
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.InvalidInput, driver.modify_vnf, self.context,
|
||||||
|
vnf_lcm_opoccs, body_data, vnfd_pkg_data, vnfd_id)
|
||||||
|
self.assertEqual(1, self._vnf_manager.invoke.call_count)
|
||||||
|
|
||||||
@mock.patch('tacker.vnflcm.utils.get_default_scale_status')
|
@mock.patch('tacker.vnflcm.utils.get_default_scale_status')
|
||||||
@mock.patch('tacker.vnflcm.utils._make_final_vnf_dict')
|
@mock.patch('tacker.vnflcm.utils._make_final_vnf_dict')
|
||||||
@mock.patch.object(VnfLcmDriver,
|
@mock.patch.object(VnfLcmDriver,
|
||||||
|
234
tacker/tests/unit/vnfm/mgmt_drivers/fakes.py
Normal file
234
tacker/tests/unit/vnfm/mgmt_drivers/fakes.py
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
# 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 datetime
|
||||||
|
from kubernetes import client
|
||||||
|
import os
|
||||||
|
|
||||||
|
from tacker import objects
|
||||||
|
from tacker.objects import fields
|
||||||
|
from tacker.tests import uuidsentinel
|
||||||
|
|
||||||
|
|
||||||
|
def get_vnf_instance_object(
|
||||||
|
instantiation_state=fields.VnfInstanceState.NOT_INSTANTIATED):
|
||||||
|
|
||||||
|
inst_vnf_info = get_vnf_instantiated_info()
|
||||||
|
|
||||||
|
vnf_instance = objects.VnfInstance(
|
||||||
|
id=uuidsentinel.vnf_instance_id,
|
||||||
|
vnf_instance_name="Test-Vnf-Instance",
|
||||||
|
vnf_instance_description="vnf instance description",
|
||||||
|
instantiation_state=instantiation_state, vnfd_id=uuidsentinel.vnfd_id,
|
||||||
|
vnf_provider="sample provider", vnf_product_name="vnf product name",
|
||||||
|
vnf_software_version='1.0', vnfd_version="2",
|
||||||
|
instantiated_vnf_info=inst_vnf_info,
|
||||||
|
vnf_metadata={"namespace": "default"}
|
||||||
|
)
|
||||||
|
|
||||||
|
return vnf_instance
|
||||||
|
|
||||||
|
|
||||||
|
def get_vnf_instantiated_info(flavour_id='simple',
|
||||||
|
instantiation_level_id=None, vnfc_resource_info=None,
|
||||||
|
virtual_storage_resource_info=None,
|
||||||
|
vnf_virtual_link_resource_info=None,
|
||||||
|
ext_managed_virtual_link_info=None,
|
||||||
|
ext_virtual_link_info=None,
|
||||||
|
ext_cp_info=None):
|
||||||
|
|
||||||
|
vnfc_resource_info = vnfc_resource_info or []
|
||||||
|
vnf_virtual_link_resource_info = vnf_virtual_link_resource_info or []
|
||||||
|
virtual_storage_resource_info = virtual_storage_resource_info or []
|
||||||
|
ext_managed_virtual_link_info = ext_managed_virtual_link_info or []
|
||||||
|
ext_virtual_link_info = ext_virtual_link_info or []
|
||||||
|
ext_cp_info = ext_cp_info or []
|
||||||
|
|
||||||
|
inst_vnf_info = objects.InstantiatedVnfInfo(
|
||||||
|
flavour_id=flavour_id,
|
||||||
|
instantiation_level_id=instantiation_level_id,
|
||||||
|
instance_id='9693ab84-6da8-5926-5e7a-3a1b963156c0',
|
||||||
|
vnfc_resource_info=vnfc_resource_info,
|
||||||
|
vnf_virtual_link_resource_info=vnf_virtual_link_resource_info,
|
||||||
|
virtual_storage_resource_info=virtual_storage_resource_info,
|
||||||
|
ext_managed_virtual_link_info=ext_managed_virtual_link_info,
|
||||||
|
ext_virtual_link_info=ext_virtual_link_info,
|
||||||
|
ext_cp_info=ext_cp_info,
|
||||||
|
additional_params=return_cnf_additional_params())
|
||||||
|
|
||||||
|
return inst_vnf_info
|
||||||
|
|
||||||
|
|
||||||
|
def fake_pod():
|
||||||
|
return client.V1Pod(
|
||||||
|
api_version='v1',
|
||||||
|
kind='Pod',
|
||||||
|
metadata=client.V1ObjectMeta(
|
||||||
|
name='curry-test001',
|
||||||
|
namespace='curryns'
|
||||||
|
),
|
||||||
|
spec=client.V1PodSpec(
|
||||||
|
containers=[
|
||||||
|
client.V1Container(
|
||||||
|
image="curry",
|
||||||
|
name="curry"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
status=client.V1PodStatus(
|
||||||
|
phase='Running',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def fake_list_pod():
|
||||||
|
return client.V1PodList(
|
||||||
|
items=[
|
||||||
|
client.V1Pod(
|
||||||
|
api_version='v1',
|
||||||
|
kind='Pod',
|
||||||
|
metadata=client.V1ObjectMeta(
|
||||||
|
name='curry-test001',
|
||||||
|
namespace='curryns'
|
||||||
|
),
|
||||||
|
spec=client.V1PodSpec(
|
||||||
|
containers=[
|
||||||
|
client.V1Container(
|
||||||
|
image="curry",
|
||||||
|
name="curry"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
status=client.V1PodStatus(
|
||||||
|
phase='Pending',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_fake_pod_info(kind, name='fake_name', pod_status='Running',
|
||||||
|
pod_name=None):
|
||||||
|
if not pod_name:
|
||||||
|
if kind == 'Deployment':
|
||||||
|
pod_name = _('{name}-1234567890-abcde').format(name=name)
|
||||||
|
elif kind in ('ReplicaSet', 'DaemonSet'):
|
||||||
|
pod_name = _('{name}-12345').format(name=name)
|
||||||
|
elif kind == 'StatefulSet':
|
||||||
|
pod_name = _('{name}-1').format(name=name)
|
||||||
|
elif kind == 'Pod':
|
||||||
|
pod_name = name
|
||||||
|
return client.V1Pod(
|
||||||
|
metadata=client.V1ObjectMeta(
|
||||||
|
name=pod_name,
|
||||||
|
creation_timestamp=datetime.datetime.now().isoformat('T')),
|
||||||
|
status=client.V1PodStatus(phase=pod_status))
|
||||||
|
|
||||||
|
|
||||||
|
def vnfd_dict_cnf():
|
||||||
|
tacker_dir = os.getcwd()
|
||||||
|
def_dir = tacker_dir + "/samples/vnf_packages/Definitions/"
|
||||||
|
vnfd_dict = {
|
||||||
|
"tosca_definitions_version": "tosca_simple_yaml_1_2",
|
||||||
|
"description": "Sample VNF flavour for Sample VNF",
|
||||||
|
"imports": [
|
||||||
|
def_dir + "etsi_nfv_sol001_common_types.yaml",
|
||||||
|
def_dir + "etsi_nfv_sol001_vnfd_types.yaml",
|
||||||
|
def_dir + "helloworld3_types.yaml"],
|
||||||
|
"topology_template": {
|
||||||
|
"node_templates": {
|
||||||
|
"VNF": {
|
||||||
|
"type": "company.provider.VNF",
|
||||||
|
"properties": {
|
||||||
|
"flavour_description": "A simple flavour"}},
|
||||||
|
"VDU1": {
|
||||||
|
"type": "tosca.nodes.nfv.Vdu.Compute",
|
||||||
|
"properties": {
|
||||||
|
"name": "vdu1",
|
||||||
|
"description": "vdu1 compute node",
|
||||||
|
"vdu_profile": {
|
||||||
|
"min_number_of_instances": 1,
|
||||||
|
"max_number_of_instances": 3}}}},
|
||||||
|
"policies": [
|
||||||
|
{
|
||||||
|
"scaling_aspects": {
|
||||||
|
"type": "tosca.policies.nfv.ScalingAspects",
|
||||||
|
"properties": {
|
||||||
|
"aspects": {
|
||||||
|
"vdu1_aspect": {
|
||||||
|
"name": "vdu1_aspect",
|
||||||
|
"description": "vdu1 scaling aspect",
|
||||||
|
"max_scale_level": 2,
|
||||||
|
"step_deltas": ["delta_1"]}}}}},
|
||||||
|
{
|
||||||
|
"vdu1_initial_delta": {
|
||||||
|
"type": "tosca.policies.nfv.VduInitialDelta",
|
||||||
|
"properties": {
|
||||||
|
"initial_delta": {
|
||||||
|
"number_of_instances": 0}},
|
||||||
|
"targets": ["VDU1"]}},
|
||||||
|
{
|
||||||
|
"vdu1_scaling_aspect_deltas": {
|
||||||
|
"type": "tosca.policies.nfv.VduScalingAspectDeltas",
|
||||||
|
"properties": {
|
||||||
|
"aspect": "vdu1_aspect",
|
||||||
|
"deltas": {
|
||||||
|
"delta_1": {
|
||||||
|
"number_of_instances": 1}}},
|
||||||
|
"targets": ["VDU1"]}},
|
||||||
|
{
|
||||||
|
"instantiation_levels": {
|
||||||
|
"type": "tosca.policies.nfv.InstantiationLevels",
|
||||||
|
"properties": {
|
||||||
|
"levels": {
|
||||||
|
"instantiation_level_1": {
|
||||||
|
"description": "Smallest size",
|
||||||
|
"scale_info": {
|
||||||
|
"vdu1_aspect": {
|
||||||
|
"scale_level": 0}}},
|
||||||
|
"instantiation_level_2": {
|
||||||
|
"description": "Largest size",
|
||||||
|
"scale_info": {
|
||||||
|
"vdu1_aspect": {
|
||||||
|
"scale_level": 2}}}
|
||||||
|
},
|
||||||
|
"default_level": "instantiation_level_1"}}},
|
||||||
|
{
|
||||||
|
"vdu1_instantiation_levels": {
|
||||||
|
"type": "tosca.policies.nfv.VduInstantiationLevels",
|
||||||
|
"properties": {
|
||||||
|
"levels": {
|
||||||
|
"instantiation_level_1": {
|
||||||
|
"number_of_instances": 0},
|
||||||
|
"instantiation_level_2": {
|
||||||
|
"number_of_instances": 2}}},
|
||||||
|
"targets": ["VDU1"]}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vnfd_dict
|
||||||
|
|
||||||
|
|
||||||
|
def return_cnf_additional_params():
|
||||||
|
additional_params = {
|
||||||
|
"lcm-kubernetes-def-files": [
|
||||||
|
"Files/kubernetes/configmap_1.yaml",
|
||||||
|
"Files/kubernetes/pod_volume.yaml",
|
||||||
|
"Files/kubernetes/replicaset.yaml",
|
||||||
|
"Files/kubernetes/secret_1.yaml"
|
||||||
|
],
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
|
return additional_params
|
@ -0,0 +1,156 @@
|
|||||||
|
# Copyright (c) 2022 FUJITSU
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
|
from kubernetes import client
|
||||||
|
from samples.mgmt_driver.kubernetes.container_update import (
|
||||||
|
container_update_mgmt as mgmt_driver)
|
||||||
|
from tacker.common import exceptions
|
||||||
|
from tacker import context
|
||||||
|
from tacker.tests.unit import base
|
||||||
|
from tacker.tests.unit.vnflcm import fakes
|
||||||
|
from tacker.tests.unit.vnfm.mgmt_drivers import fakes as mgmt_fakes
|
||||||
|
from tacker.vnflcm import utils as vnflcm_utils
|
||||||
|
from tacker.vnfm.infra_drivers.kubernetes.kubernetes_driver import Kubernetes
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
|
||||||
|
class FakeVimClient(mock.Mock):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestContainerUpdate(base.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestContainerUpdate, self).setUp()
|
||||||
|
self.context = context.get_admin_context()
|
||||||
|
self.cntr_update_mgmt = mgmt_driver.ContainerUpdateMgmtDriver()
|
||||||
|
self.vnf_instance = mgmt_fakes.get_vnf_instance_object()
|
||||||
|
self.modify_vnf_request = None
|
||||||
|
self._mock_vim_client()
|
||||||
|
self._stub_get_vim()
|
||||||
|
self._mock_get_vnf_package_path()
|
||||||
|
self.yaml_path_before = os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
|
"../../../etc/samples/etsi/nfv/"
|
||||||
|
"test_cnf_container_update_before/")
|
||||||
|
|
||||||
|
def _mock_vim_client(self):
|
||||||
|
self.vim_client = mock.Mock(wraps=FakeVimClient())
|
||||||
|
fake_vim_client = mock.Mock()
|
||||||
|
fake_vim_client.return_value = self.vim_client
|
||||||
|
self._mock(
|
||||||
|
'tacker.vnfm.vim_client.VimClient', fake_vim_client)
|
||||||
|
|
||||||
|
def _stub_get_vim(self):
|
||||||
|
vim_obj = {'vim_id': '15053249-6979-8ee5-67d2-189fa9379534',
|
||||||
|
'vim_name': 'fake_vim', 'vim_auth':
|
||||||
|
{'auth_url': 'http://localhost/identity', 'password':
|
||||||
|
'test_pw', 'username': 'test_user', 'project_name':
|
||||||
|
'test_project'}, 'vim_type': 'openstack', 'tenant':
|
||||||
|
'15053249-6979-8ee5-67d2-189fa9379534'}
|
||||||
|
self.vim_client.get_vim.return_value = vim_obj
|
||||||
|
|
||||||
|
def _mock_get_vnf_package_path(self):
|
||||||
|
vnflcm_utils.get_vnf_package_path = mock.Mock(
|
||||||
|
return_value=os.path.join(
|
||||||
|
os.path.abspath(os.path.dirname(__file__)),
|
||||||
|
"../../../etc/samples/etsi/nfv/"
|
||||||
|
"test_cnf_container_update_after/")
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_container_update_get_type(self):
|
||||||
|
get_type = self.cntr_update_mgmt.get_type()
|
||||||
|
self.assertEqual('mgmt-container-update', get_type)
|
||||||
|
|
||||||
|
def test_container_update_get_name(self):
|
||||||
|
get_name = self.cntr_update_mgmt.get_name()
|
||||||
|
self.assertEqual('mgmt-container-update', get_name)
|
||||||
|
|
||||||
|
def test_container_update_get_description(self):
|
||||||
|
get_description = self.cntr_update_mgmt.get_description()
|
||||||
|
self.assertEqual(
|
||||||
|
'Tacker Container Update VNF Mgmt Driver', get_description)
|
||||||
|
|
||||||
|
def test_container_update_modify_information_start(self):
|
||||||
|
modify_start = self.cntr_update_mgmt.modify_information_start(
|
||||||
|
self.context, self.vnf_instance, self.modify_vnf_request)
|
||||||
|
self.assertIsNone(modify_start)
|
||||||
|
|
||||||
|
def test_container_update_modify_container_img(self):
|
||||||
|
old_containers = [
|
||||||
|
client.V1Container(image="curry", name="curry")
|
||||||
|
]
|
||||||
|
|
||||||
|
new_containers = [
|
||||||
|
client.V1Container(image="curry1", name="curry")
|
||||||
|
]
|
||||||
|
self.cntr_update_mgmt._modify_container_img(
|
||||||
|
old_containers, new_containers)
|
||||||
|
self.assertEqual('curry1', old_containers[0].image)
|
||||||
|
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'list_namespaced_pod')
|
||||||
|
def test_container_update_replace_wait_k8s_timeout(self, mock_list_pod):
|
||||||
|
k8s_pod_objs = [
|
||||||
|
{'namespace': 'default', 'object': mgmt_fakes.fake_pod()}
|
||||||
|
]
|
||||||
|
kube_driver = Kubernetes()
|
||||||
|
kube_driver.STACK_RETRIES = 1
|
||||||
|
kube_driver.STACK_RETRY_WAIT = 5
|
||||||
|
mock_list_pod.return_value = mgmt_fakes.fake_list_pod()
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.MgmtDriverOtherError,
|
||||||
|
self.cntr_update_mgmt._replace_wait_k8s, kube_driver,
|
||||||
|
k8s_pod_objs, client.CoreV1Api, self.vnf_instance)
|
||||||
|
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'list_namespaced_pod')
|
||||||
|
def test_container_update_replace_wait_k8s_unknown(self, mock_list_pod):
|
||||||
|
k8s_pod_objs = [
|
||||||
|
{'namespace': 'default', 'object': mgmt_fakes.fake_pod()}
|
||||||
|
]
|
||||||
|
kube_driver = Kubernetes()
|
||||||
|
pod_list = mgmt_fakes.fake_list_pod()
|
||||||
|
pod_list.items[0].status.phase = 'Unknown'
|
||||||
|
mock_list_pod.return_value = pod_list
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.MgmtDriverOtherError,
|
||||||
|
self.cntr_update_mgmt._replace_wait_k8s, kube_driver,
|
||||||
|
k8s_pod_objs, client.CoreV1Api, self.vnf_instance)
|
||||||
|
|
||||||
|
@mock.patch('tacker.objects.vnf_instance.VnfInstance.save')
|
||||||
|
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'list_namespaced_pod')
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'replace_namespaced_secret')
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'replace_namespaced_config_map')
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'replace_namespaced_pod')
|
||||||
|
@mock.patch.object(client.CoreV1Api, 'read_namespaced_pod')
|
||||||
|
def test_container_update_modify_information_end(
|
||||||
|
self, mock_read_pod, mock_replace_pod, mock_replace_config_map,
|
||||||
|
mock_replace_secret, mock_list_pod, mock_vnfd_dict, mock_save):
|
||||||
|
mock_read_pod.return_value = mgmt_fakes.fake_pod()
|
||||||
|
mock_list_pod.return_value = client.V1PodList(items=[
|
||||||
|
mgmt_fakes.get_fake_pod_info(kind='Pod', name='vdu1')])
|
||||||
|
mock_vnfd_dict.return_value = fakes.vnfd_dict_cnf()
|
||||||
|
kwargs = {
|
||||||
|
'old_vnf_package_path': self.yaml_path_before,
|
||||||
|
'configmap_secret_paths': [
|
||||||
|
"Files/kubernetes/configmap_2.yaml",
|
||||||
|
"Files/kubernetes/secret_2.yaml"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
self.cntr_update_mgmt.modify_information_end(
|
||||||
|
self.context, self.vnf_instance, self.modify_vnf_request, **kwargs)
|
||||||
|
self.assertEqual(1, mock_save.call_count)
|
@ -35,6 +35,12 @@ LOG = logging.getLogger(__name__)
|
|||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
|
# TODO(fengyi): change _get_vim to a public method
|
||||||
|
# and then delete this method.
|
||||||
|
def get_vim(context, vim_connection_info):
|
||||||
|
return _get_vim(context, vim_connection_info)
|
||||||
|
|
||||||
|
|
||||||
def _get_vim(context, vim_connection_info):
|
def _get_vim(context, vim_connection_info):
|
||||||
vim_client_obj = vim_client.VimClient()
|
vim_client_obj = vim_client.VimClient()
|
||||||
|
|
||||||
@ -1163,6 +1169,12 @@ def _convert_desired_capacity(inst_level_id, vnfd_dict, vdu):
|
|||||||
return desired_capacity
|
return desired_capacity
|
||||||
|
|
||||||
|
|
||||||
|
# TODO(fengyi): change _get_vnf_package_path to a public method
|
||||||
|
# and then delete this method.
|
||||||
|
def get_vnf_package_path(context, vnfd_id):
|
||||||
|
return _get_vnf_package_path(context, vnfd_id)
|
||||||
|
|
||||||
|
|
||||||
def _get_vnf_package_path(context, vnfd_id):
|
def _get_vnf_package_path(context, vnfd_id):
|
||||||
return os.path.join(CONF.vnf_package.vnf_package_csar_path,
|
return os.path.join(CONF.vnf_package.vnf_package_csar_path,
|
||||||
_get_vnf_package_id(context, vnfd_id))
|
_get_vnf_package_id(context, vnfd_id))
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# 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 ast
|
||||||
import copy
|
import copy
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import functools
|
import functools
|
||||||
@ -501,6 +502,67 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
|
|||||||
|
|
||||||
return tacker_mgmt_driver
|
return tacker_mgmt_driver
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def modify_vnf(self, context, vnf_lcm_opoccs,
|
||||||
|
body_data, vnfd_pkg_data, vnfd_id):
|
||||||
|
# Get vnf_instance from DB by vnf_instance_id
|
||||||
|
vnf_instance_id = vnf_lcm_opoccs.get('vnf_instance_id')
|
||||||
|
vnf_instance = objects.VnfInstance.get_by_id(context, vnf_instance_id)
|
||||||
|
vnfd_dict = vnflcm_utils.get_vnfd_dict(
|
||||||
|
context, vnf_instance.vnfd_id,
|
||||||
|
vnf_instance.instantiated_vnf_info.flavour_id)
|
||||||
|
|
||||||
|
# modify_information_start(context, vnf_instance)
|
||||||
|
self._mgmt_manager.invoke(
|
||||||
|
self._load_vnf_interface(
|
||||||
|
context, 'modify_information_start', vnf_instance, vnfd_dict),
|
||||||
|
'modify_information_start', context=context,
|
||||||
|
modify_vnf_request=None, vnf_instance=vnf_instance)
|
||||||
|
|
||||||
|
# Get the old vnf package path according to vnfd_id
|
||||||
|
old_vnf_package_path = vnflcm_utils.get_vnf_package_path(
|
||||||
|
context, vnf_instance.vnfd_id)
|
||||||
|
|
||||||
|
# Update vnf_instance
|
||||||
|
try:
|
||||||
|
_vnf_instance = objects.VnfInstance(context=context)
|
||||||
|
updated_time = _vnf_instance.update(
|
||||||
|
context, vnf_lcm_opoccs, body_data, vnfd_pkg_data, vnfd_id)
|
||||||
|
except Exception as msg:
|
||||||
|
raise Exception(str(msg))
|
||||||
|
vnf_instance = objects.VnfInstance.get_by_id(context, vnf_instance_id)
|
||||||
|
|
||||||
|
vim_info = vnflcm_utils.get_vim(
|
||||||
|
context, vnf_instance.vim_connection_info)
|
||||||
|
|
||||||
|
vim_connection_info = objects.VimConnectionInfo.obj_from_primitive(
|
||||||
|
vim_info, context)
|
||||||
|
|
||||||
|
kwargs = {}
|
||||||
|
if vim_connection_info.vim_type == 'kubernetes':
|
||||||
|
# If the file path of ConfigMap/Secret is changed
|
||||||
|
cm_secret_paths = []
|
||||||
|
# Get the metadata from vnf_lcm_opoccs
|
||||||
|
operation_params = vnf_lcm_opoccs.get('operationParams')
|
||||||
|
if operation_params:
|
||||||
|
try:
|
||||||
|
cm_secret_paths = (ast.literal_eval(operation_params)
|
||||||
|
.get('metadata', {})
|
||||||
|
.get('configmap_secret_paths', []))
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error('Invalid format operationParams')
|
||||||
|
raise exceptions.InvalidInput(str(e))
|
||||||
|
kwargs = {"old_vnf_package_path": old_vnf_package_path,
|
||||||
|
"configmap_secret_paths": cm_secret_paths}
|
||||||
|
|
||||||
|
self._mgmt_manager.invoke(
|
||||||
|
self._load_vnf_interface(
|
||||||
|
context, 'modify_information_end', vnf_instance, vnfd_dict),
|
||||||
|
'modify_information_end', context=context,
|
||||||
|
modify_vnf_request=None,
|
||||||
|
vnf_instance=vnf_instance, **kwargs)
|
||||||
|
return updated_time
|
||||||
|
|
||||||
@log.log
|
@log.log
|
||||||
def instantiate_vnf(self, context, vnf_instance, vnf_dict,
|
def instantiate_vnf(self, context, vnf_instance, vnf_dict,
|
||||||
instantiate_vnf_req):
|
instantiate_vnf_req):
|
||||||
|
@ -124,7 +124,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
"""
|
"""
|
||||||
LOG.debug('vnf %s', vnf)
|
LOG.debug('vnf %s', vnf)
|
||||||
# initialize Kubernetes APIs
|
# initialize Kubernetes APIs
|
||||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||||
try:
|
try:
|
||||||
core_v1_api_client = self.kubernetes.get_core_v1_api_client(
|
core_v1_api_client = self.kubernetes.get_core_v1_api_client(
|
||||||
auth=auth_cred)
|
auth=auth_cred)
|
||||||
@ -153,7 +153,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
"""
|
"""
|
||||||
# initialize Kubernetes APIs
|
# initialize Kubernetes APIs
|
||||||
if '{' not in vnf_id and '}' not in vnf_id:
|
if '{' not in vnf_id and '}' not in vnf_id:
|
||||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||||
try:
|
try:
|
||||||
core_v1_api_client = \
|
core_v1_api_client = \
|
||||||
self.kubernetes.get_core_v1_api_client(auth=auth_cred)
|
self.kubernetes.get_core_v1_api_client(auth=auth_cred)
|
||||||
@ -162,7 +162,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
pods_information = self._get_pods_information(
|
pods_information = self._get_pods_information(
|
||||||
core_v1_api_client=core_v1_api_client,
|
core_v1_api_client=core_v1_api_client,
|
||||||
deployment_info=deployment_info)
|
deployment_info=deployment_info)
|
||||||
status = self._get_pod_status(pods_information)
|
status = self.get_pod_status(pods_information)
|
||||||
stack_retries = self.STACK_RETRIES
|
stack_retries = self.STACK_RETRIES
|
||||||
error_reason = None
|
error_reason = None
|
||||||
while status == 'Pending' and stack_retries > 0:
|
while status == 'Pending' and stack_retries > 0:
|
||||||
@ -171,7 +171,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
self._get_pods_information(
|
self._get_pods_information(
|
||||||
core_v1_api_client=core_v1_api_client,
|
core_v1_api_client=core_v1_api_client,
|
||||||
deployment_info=deployment_info)
|
deployment_info=deployment_info)
|
||||||
status = self._get_pod_status(pods_information)
|
status = self.get_pod_status(pods_information)
|
||||||
LOG.debug('status: %s', status)
|
LOG.debug('status: %s', status)
|
||||||
stack_retries = stack_retries - 1
|
stack_retries = stack_retries - 1
|
||||||
|
|
||||||
@ -553,7 +553,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
pods_information.append(item)
|
pods_information.append(item)
|
||||||
return pods_information
|
return pods_information
|
||||||
|
|
||||||
def _get_pod_status(self, pods_information):
|
def get_pod_status(self, pods_information):
|
||||||
pending_flag = False
|
pending_flag = False
|
||||||
unknown_flag = False
|
unknown_flag = False
|
||||||
for pod_info in pods_information:
|
for pod_info in pods_information:
|
||||||
@ -578,7 +578,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
ConfigMap data
|
ConfigMap data
|
||||||
"""
|
"""
|
||||||
# initialize Kubernetes APIs
|
# initialize Kubernetes APIs
|
||||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||||
try:
|
try:
|
||||||
core_v1_api_client = \
|
core_v1_api_client = \
|
||||||
self.kubernetes.get_core_v1_api_client(auth=auth_cred)
|
self.kubernetes.get_core_v1_api_client(auth=auth_cred)
|
||||||
@ -841,7 +841,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
def delete(self, plugin, context, vnf_id, auth_attr, region_name=None,
|
def delete(self, plugin, context, vnf_id, auth_attr, region_name=None,
|
||||||
vnf_instance=None, terminate_vnf_req=None):
|
vnf_instance=None, terminate_vnf_req=None):
|
||||||
"""Delete function"""
|
"""Delete function"""
|
||||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||||
try:
|
try:
|
||||||
if not vnf_instance:
|
if not vnf_instance:
|
||||||
# execute legacy delete method
|
# execute legacy delete method
|
||||||
@ -977,8 +977,8 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
LOG.error('Deleting wait VNF got an error due to %s', e)
|
LOG.error('Deleting wait VNF got an error due to %s', e)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def _select_k8s_obj_read_api(self, k8s_client_dict, namespace, name,
|
def select_k8s_obj_read_api(self, k8s_client_dict, namespace, name,
|
||||||
kind, api_version):
|
kind, api_version):
|
||||||
"""select kubernetes read api and call"""
|
"""select kubernetes read api and call"""
|
||||||
def convert(name):
|
def convert(name):
|
||||||
name_with_underscores = re.sub(
|
name_with_underscores = re.sub(
|
||||||
@ -1039,7 +1039,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
marked as deleted.
|
marked as deleted.
|
||||||
"""
|
"""
|
||||||
# initialize Kubernetes APIs
|
# initialize Kubernetes APIs
|
||||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not vnf_instance:
|
if not vnf_instance:
|
||||||
@ -1068,7 +1068,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
if not k8s_client_dict.get(api_version):
|
if not k8s_client_dict.get(api_version):
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
self._select_k8s_obj_read_api(
|
self.select_k8s_obj_read_api(
|
||||||
k8s_client_dict=k8s_client_dict,
|
k8s_client_dict=k8s_client_dict,
|
||||||
namespace=namespace,
|
namespace=namespace,
|
||||||
name=name,
|
name=name,
|
||||||
@ -1265,7 +1265,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
of policy scaling when user define VNF descriptor.
|
of policy scaling when user define VNF descriptor.
|
||||||
"""
|
"""
|
||||||
# initialize Kubernetes APIs
|
# initialize Kubernetes APIs
|
||||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||||
try:
|
try:
|
||||||
if not policy.get('vnf_instance_id'):
|
if not policy.get('vnf_instance_id'):
|
||||||
# execute legacy scale method
|
# execute legacy scale method
|
||||||
@ -1358,7 +1358,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
pods_information = self._get_pods_information(
|
pods_information = self._get_pods_information(
|
||||||
core_v1_api_client=core_v1_api_client,
|
core_v1_api_client=core_v1_api_client,
|
||||||
deployment_info=deployment_info)
|
deployment_info=deployment_info)
|
||||||
status = self._get_pod_status(pods_information)
|
status = self.get_pod_status(pods_information)
|
||||||
|
|
||||||
stack_retries = self.STACK_RETRIES
|
stack_retries = self.STACK_RETRIES
|
||||||
error_reason = None
|
error_reason = None
|
||||||
@ -1368,7 +1368,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
pods_information = self._get_pods_information(
|
pods_information = self._get_pods_information(
|
||||||
core_v1_api_client=core_v1_api_client,
|
core_v1_api_client=core_v1_api_client,
|
||||||
deployment_info=deployment_info)
|
deployment_info=deployment_info)
|
||||||
status = self._get_pod_status(pods_information)
|
status = self.get_pod_status(pods_information)
|
||||||
|
|
||||||
# LOG.debug('status: %s', status)
|
# LOG.debug('status: %s', status)
|
||||||
stack_retries = stack_retries - 1
|
stack_retries = stack_retries - 1
|
||||||
@ -1390,7 +1390,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
elif stack_retries != 0 and status != 'Running':
|
elif stack_retries != 0 and status != 'Running':
|
||||||
raise vnfm.VNFCreateWaitFailed(reason=error_reason)
|
raise vnfm.VNFCreateWaitFailed(reason=error_reason)
|
||||||
|
|
||||||
def _is_match_pod_naming_rule(self, rsc_kind, rsc_name, pod_name):
|
def is_match_pod_naming_rule(self, rsc_kind, rsc_name, pod_name):
|
||||||
match_result = None
|
match_result = None
|
||||||
if rsc_kind == 'Pod':
|
if rsc_kind == 'Pod':
|
||||||
# Expected example: name
|
# Expected example: name
|
||||||
@ -1429,7 +1429,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
from Pod objects is RUNNING.
|
from Pod objects is RUNNING.
|
||||||
"""
|
"""
|
||||||
# initialize Kubernetes APIs
|
# initialize Kubernetes APIs
|
||||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||||
try:
|
try:
|
||||||
if not policy.get('vnf_instance_id'):
|
if not policy.get('vnf_instance_id'):
|
||||||
# execute legacy scale_wait method
|
# execute legacy scale_wait method
|
||||||
@ -1481,12 +1481,12 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
respone = core_v1_api_client.list_namespaced_pod(
|
respone = core_v1_api_client.list_namespaced_pod(
|
||||||
namespace=namespace)
|
namespace=namespace)
|
||||||
for pod in respone.items:
|
for pod in respone.items:
|
||||||
match_result = self._is_match_pod_naming_rule(
|
match_result = self.is_match_pod_naming_rule(
|
||||||
kind, name, pod.metadata.name)
|
kind, name, pod.metadata.name)
|
||||||
if match_result:
|
if match_result:
|
||||||
pods_information.append(pod)
|
pods_information.append(pod)
|
||||||
|
|
||||||
status = self._get_pod_status(pods_information)
|
status = self.get_pod_status(pods_information)
|
||||||
if status == 'Running' and \
|
if status == 'Running' and \
|
||||||
scale_info.spec.replicas != len(pods_information):
|
scale_info.spec.replicas != len(pods_information):
|
||||||
status = 'Pending'
|
status = 'Pending'
|
||||||
@ -1523,7 +1523,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
# TODO(phuoc): will update it for other components
|
# TODO(phuoc): will update it for other components
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _get_auth_creds(self, auth_cred):
|
def get_auth_creds(self, auth_cred):
|
||||||
file_descriptor = self._create_ssl_ca_file(auth_cred)
|
file_descriptor = self._create_ssl_ca_file(auth_cred)
|
||||||
if ('username' not in auth_cred) and ('password' not in auth_cred):
|
if ('username' not in auth_cred) and ('password' not in auth_cred):
|
||||||
auth_cred['username'] = 'None'
|
auth_cred['username'] = 'None'
|
||||||
@ -1821,7 +1821,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
None, context, vnf_instance, auth_attr)
|
None, context, vnf_instance, auth_attr)
|
||||||
return instance_id
|
return instance_id
|
||||||
else:
|
else:
|
||||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||||
k8s_client_dict = self.kubernetes.get_k8s_client_dict(auth_cred)
|
k8s_client_dict = self.kubernetes.get_k8s_client_dict(auth_cred)
|
||||||
transformer = translate_outputs.Transformer(
|
transformer = translate_outputs.Transformer(
|
||||||
None, None, None, k8s_client_dict)
|
None, None, None, k8s_client_dict)
|
||||||
@ -1887,7 +1887,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
and metadata, and vdu id.
|
and metadata, and vdu id.
|
||||||
"""
|
"""
|
||||||
auth_attr = vim_connection_info.access_info
|
auth_attr = vim_connection_info.access_info
|
||||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||||
namespace = vnf_instance.vnf_metadata['namespace']
|
namespace = vnf_instance.vnf_metadata['namespace']
|
||||||
try:
|
try:
|
||||||
# get Kubernetes object files
|
# get Kubernetes object files
|
||||||
@ -1943,7 +1943,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
# get initially store VnfcResourceInfo after instantiation
|
# get initially store VnfcResourceInfo after instantiation
|
||||||
for pod in pod_list.items:
|
for pod in pod_list.items:
|
||||||
pod_name = pod.metadata.name
|
pod_name = pod.metadata.name
|
||||||
match_result = self._is_match_pod_naming_rule(
|
match_result = self.is_match_pod_naming_rule(
|
||||||
rsc_kind, rsc_name, pod_name)
|
rsc_kind, rsc_name, pod_name)
|
||||||
if match_result:
|
if match_result:
|
||||||
# get metadata
|
# get metadata
|
||||||
@ -2005,7 +2005,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
# Get the associated pod name that runs with the actual kubernetes
|
# Get the associated pod name that runs with the actual kubernetes
|
||||||
actual_pod_names = list()
|
actual_pod_names = list()
|
||||||
for pod in sorted_pod_list:
|
for pod in sorted_pod_list:
|
||||||
match_result = self._is_match_pod_naming_rule(
|
match_result = self.is_match_pod_naming_rule(
|
||||||
rsc_kind, rsc_name, pod.metadata.name)
|
rsc_kind, rsc_name, pod.metadata.name)
|
||||||
if match_result:
|
if match_result:
|
||||||
actual_pod_names.append(pod.metadata.name)
|
actual_pod_names.append(pod.metadata.name)
|
||||||
@ -2033,7 +2033,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
"""
|
"""
|
||||||
# initialize Kubernetes APIs
|
# initialize Kubernetes APIs
|
||||||
auth_attr = vim_connection_info.access_info
|
auth_attr = vim_connection_info.access_info
|
||||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||||
inst_vnf_info = vnf_instance.instantiated_vnf_info
|
inst_vnf_info = vnf_instance.instantiated_vnf_info
|
||||||
namespace = vnf_instance.vnf_metadata['namespace']
|
namespace = vnf_instance.vnf_metadata['namespace']
|
||||||
try:
|
try:
|
||||||
@ -2189,7 +2189,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
"""
|
"""
|
||||||
# initialize Kubernetes APIs
|
# initialize Kubernetes APIs
|
||||||
auth_attr = vim_connection_info.access_info
|
auth_attr = vim_connection_info.access_info
|
||||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||||
namespace = vnf_instance.vnf_metadata['namespace']
|
namespace = vnf_instance.vnf_metadata['namespace']
|
||||||
try:
|
try:
|
||||||
core_v1_api_client = self.kubernetes.get_core_v1_api_client(
|
core_v1_api_client = self.kubernetes.get_core_v1_api_client(
|
||||||
@ -2251,7 +2251,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
pod_list_dict[namespace] = pod_list
|
pod_list_dict[namespace] = pod_list
|
||||||
tmp_pods_info = list()
|
tmp_pods_info = list()
|
||||||
for pod in pod_list.items:
|
for pod in pod_list.items:
|
||||||
match_result = self._is_match_pod_naming_rule(
|
match_result = self.is_match_pod_naming_rule(
|
||||||
k8s_resource.get('kind'),
|
k8s_resource.get('kind'),
|
||||||
k8s_resource.get('name'),
|
k8s_resource.get('name'),
|
||||||
pod.metadata.name)
|
pod.metadata.name)
|
||||||
@ -2274,7 +2274,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
'actual_pod_num': str(len(tmp_pods_info))})
|
'actual_pod_num': str(len(tmp_pods_info))})
|
||||||
is_unmatch_pods_num = True
|
is_unmatch_pods_num = True
|
||||||
pods_information.extend(tmp_pods_info)
|
pods_information.extend(tmp_pods_info)
|
||||||
status = self._get_pod_status(pods_information)
|
status = self.get_pod_status(pods_information)
|
||||||
|
|
||||||
if status == 'Unknown':
|
if status == 'Unknown':
|
||||||
error_reason = _("Pod status is found Unknown")
|
error_reason = _("Pod status is found Unknown")
|
||||||
@ -2305,7 +2305,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
"""Update VnfcResourceInfo after healing"""
|
"""Update VnfcResourceInfo after healing"""
|
||||||
# initialize Kubernetes APIs
|
# initialize Kubernetes APIs
|
||||||
auth_attr = vim_connection_info.access_info
|
auth_attr = vim_connection_info.access_info
|
||||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||||
inst_vnf_info = vnf_instance.instantiated_vnf_info
|
inst_vnf_info = vnf_instance.instantiated_vnf_info
|
||||||
namespace = vnf_instance.vnf_metadata['namespace']
|
namespace = vnf_instance.vnf_metadata['namespace']
|
||||||
try:
|
try:
|
||||||
@ -2417,7 +2417,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
vim_connection_info):
|
vim_connection_info):
|
||||||
"""Update VnfcResourceInfo after scaling"""
|
"""Update VnfcResourceInfo after scaling"""
|
||||||
auth_attr = vim_connection_info.access_info
|
auth_attr = vim_connection_info.access_info
|
||||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||||
inst_vnf_info = vnf_instance.instantiated_vnf_info
|
inst_vnf_info = vnf_instance.instantiated_vnf_info
|
||||||
try:
|
try:
|
||||||
# initialize Kubernetes APIs
|
# initialize Kubernetes APIs
|
||||||
@ -2471,7 +2471,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
|||||||
namespace=namespace)
|
namespace=namespace)
|
||||||
actual_pod_list = []
|
actual_pod_list = []
|
||||||
for pod in pod_list.items:
|
for pod in pod_list.items:
|
||||||
match_result = self._is_match_pod_naming_rule(
|
match_result = self.is_match_pod_naming_rule(
|
||||||
rsc_kind, rsc_name, pod.metadata.name)
|
rsc_kind, rsc_name, pod.metadata.name)
|
||||||
if match_result:
|
if match_result:
|
||||||
actual_pod_list.append(pod.metadata.name)
|
actual_pod_list.append(pod.metadata.name)
|
||||||
|
@ -97,3 +97,13 @@ class VnflcmMgmtAbstractDriver(metaclass=abc.ABCMeta):
|
|||||||
change_ext_conn_request, grant,
|
change_ext_conn_request, grant,
|
||||||
grant_request, **kwargs):
|
grant_request, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def modify_information_start(self, context, vnf_instance,
|
||||||
|
modify_vnf_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def modify_information_end(self, context, vnf_instance,
|
||||||
|
modify_vnf_request, **kwargs):
|
||||||
|
pass
|
||||||
|
@ -88,3 +88,13 @@ class VnflcmMgmtNoop(vnflcm_abstract_driver.VnflcmMgmtAbstractDriver):
|
|||||||
change_ext_conn_request, grant,
|
change_ext_conn_request, grant,
|
||||||
grant_request, **kwargs):
|
grant_request, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def modify_information_start(self, context, vnf_instance,
|
||||||
|
modify_vnf_request, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def modify_information_end(self, context, vnf_instance,
|
||||||
|
modify_vnf_request, **kwargs):
|
||||||
|
pass
|
||||||
|
@ -24,4 +24,4 @@ openstack vim register \
|
|||||||
--os-user-domain-name Default \
|
--os-user-domain-name Default \
|
||||||
--description "Kubernetes VIM" \
|
--description "Kubernetes VIM" \
|
||||||
--config-file /opt/stack/tacker/tacker/tests/etc/samples/local-k8s-vim.yaml \
|
--config-file /opt/stack/tacker/tacker/tests/etc/samples/local-k8s-vim.yaml \
|
||||||
vim-kubernetes
|
vim-kubernetes
|
29
tools/test-setup-mgmt.sh
Executable file
29
tools/test-setup-mgmt.sh
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash -xe
|
||||||
|
|
||||||
|
# The purpose of this script is to copy the MgmtDriver
|
||||||
|
# of container update in the sample folder to the
|
||||||
|
# tacker code and run it.
|
||||||
|
|
||||||
|
# Copy the MgmtDriver of container_update to the tacker.
|
||||||
|
sudo cp /opt/stack/tacker/samples/mgmt_driver/kubernetes/container_update/\
|
||||||
|
container_update_mgmt.py /opt/stack/tacker/tacker/vnfm/mgmt_drivers/
|
||||||
|
sudo chown stack:stack /opt/stack/tacker/tacker/vnfm/mgmt_drivers/\
|
||||||
|
container_update_mgmt.py
|
||||||
|
|
||||||
|
# In the configuration file of the tacker,
|
||||||
|
# add the MgmtDriver of container_update.
|
||||||
|
sudo sed -i "/VnflcmMgmtNoop/a \ \ \ \ mgmt-container-update = \
|
||||||
|
tacker.vnfm.mgmt_drivers.container_update_mgmt:ContainerUpdateMgmtDriver" \
|
||||||
|
/opt/stack/tacker/setup.cfg
|
||||||
|
sudo sed -i "/vnflcm_mgmt_driver = vnflcm_noop/a vnflcm_mgmt_driver = \
|
||||||
|
vnflcm_noop,mgmt-container-update" /etc/tacker/tacker.conf
|
||||||
|
|
||||||
|
# Reload the tacker configuration file.
|
||||||
|
cd /opt/stack/tacker/
|
||||||
|
sudo python3 setup.py build
|
||||||
|
sudo chown -R stack:stack /opt/stack/tacker/
|
||||||
|
|
||||||
|
# Restart the tacker service for the
|
||||||
|
# configuration file to take effect.
|
||||||
|
sudo systemctl restart devstack@tacker-conductor
|
||||||
|
sleep 10s
|
Loading…
Reference in New Issue
Block a user