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:
|
||||
- inventory_hostname == 'controller-tacker'
|
||||
- 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):
|
||||
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):
|
||||
ssh_command = 'cat /etc/os-release | grep "PRETTY_NAME=" | ' \
|
||||
'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,
|
||||
grant_request, **kwargs):
|
||||
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,
|
||||
grant_request, **kwargs):
|
||||
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)
|
||||
|
||||
# update vnf_instances
|
||||
try:
|
||||
ins_obj = objects.vnf_instance.VnfInstance(context=context)
|
||||
result = ins_obj.update(
|
||||
context,
|
||||
vnf_lcm_opoccs,
|
||||
body_data,
|
||||
vnfd_pkg_data,
|
||||
vnfd_id)
|
||||
except Exception as msg:
|
||||
raise Exception(str(msg))
|
||||
# update vnf_instance
|
||||
state_entered_time = self.vnflcm_driver.modify_vnf(
|
||||
context, vnf_lcm_opoccs, body_data, vnfd_pkg_data, vnfd_id)
|
||||
|
||||
# update lcm_op_occs
|
||||
if vnfd_pkg_data and len(vnfd_pkg_data) > 0:
|
||||
@ -2340,7 +2332,7 @@ class Conductor(manager.Manager, v2_hook.ConductorV2Hook):
|
||||
now = timeutils.utcnow()
|
||||
lcm_op_obj.id = vnf_lcm_opoccs.get('id')
|
||||
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.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,
|
||||
grant_request, **kwargs):
|
||||
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
|
||||
Content-Type: text/x-python
|
||||
Algorithm: SHA-256
|
||||
Hash: 950422e356c3eb6a7c92f424d975e2d3f295f480321bd91a97501045eddeab4a
|
||||
Hash: 559837eda52829630f90c803be30c82141aa4c7441736a6827495e7fdb768d0a
|
||||
|
@ -12,6 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
@ -56,7 +57,8 @@ class BaseVnfLcmKubernetesTest(base.BaseTackerTest):
|
||||
"terminate": timeout,
|
||||
"heal_sol002": timeout,
|
||||
"heal_sol003": timeout * 2,
|
||||
"scale": timeout
|
||||
"scale": timeout,
|
||||
"modify": timeout
|
||||
}
|
||||
cls.vnf_package_ids = []
|
||||
|
||||
@ -81,6 +83,63 @@ class BaseVnfLcmKubernetesTest(base.BaseTackerTest):
|
||||
self.skipTest(f"Kubernetes VIM '{vim_name}' is missing")
|
||||
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,
|
||||
user_defined_data):
|
||||
# create vnf package
|
||||
@ -168,6 +227,7 @@ class BaseVnfLcmKubernetesTest(base.BaseTackerTest):
|
||||
resp, response_body = self.http_client.do_request(
|
||||
self.base_vnf_instances_url, "POST",
|
||||
body=jsonutils.dumps(request_body))
|
||||
self.assertEqual(201, resp.status_code)
|
||||
return resp, response_body
|
||||
|
||||
def _delete_wait_vnf_instance(self, id):
|
||||
@ -228,6 +288,8 @@ class BaseVnfLcmKubernetesTest(base.BaseTackerTest):
|
||||
_, vnf_instance = self._create_vnf_instance(
|
||||
vnfd_id, vnf_instance_name=inst_name,
|
||||
vnf_instance_description=inst_desc)
|
||||
self.assertEqual(
|
||||
'NOT_INSTANTIATED', vnf_instance['instantiationState'])
|
||||
|
||||
# instantiate vnf instance
|
||||
additional_param = additional_params
|
||||
@ -237,8 +299,31 @@ class BaseVnfLcmKubernetesTest(base.BaseTackerTest):
|
||||
self._instantiate_vnf_instance(vnf_instance['id'], request_body)
|
||||
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
|
||||
|
||||
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):
|
||||
if request_body is None:
|
||||
# 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):
|
||||
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):
|
||||
@ -3235,13 +3238,10 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
|
||||
@mock.patch.object(conductor_server, 'revert_update_lcm')
|
||||
@mock.patch.object(t_context.get_admin_context().session, "add")
|
||||
@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")
|
||||
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_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_save.return_value = "OK"
|
||||
vnfd_id = "2c69a161-0000-4b0f-bcf8-391f8fc76600"
|
||||
|
@ -192,6 +192,86 @@ class TestVnflcmDriver(db_base.SqlTestCase):
|
||||
'tenant': uuidsentinel.tenant_id}
|
||||
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._make_final_vnf_dict')
|
||||
@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
|
||||
|
||||
|
||||
# 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):
|
||||
vim_client_obj = vim_client.VimClient()
|
||||
|
||||
@ -1163,6 +1169,12 @@ def _convert_desired_capacity(inst_level_id, vnfd_dict, vdu):
|
||||
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):
|
||||
return os.path.join(CONF.vnf_package.vnf_package_csar_path,
|
||||
_get_vnf_package_id(context, vnfd_id))
|
||||
|
@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import ast
|
||||
import copy
|
||||
from datetime import datetime
|
||||
import functools
|
||||
@ -501,6 +502,67 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
|
||||
|
||||
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
|
||||
def instantiate_vnf(self, context, vnf_instance, vnf_dict,
|
||||
instantiate_vnf_req):
|
||||
|
@ -124,7 +124,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
"""
|
||||
LOG.debug('vnf %s', vnf)
|
||||
# initialize Kubernetes APIs
|
||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
||||
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||
try:
|
||||
core_v1_api_client = self.kubernetes.get_core_v1_api_client(
|
||||
auth=auth_cred)
|
||||
@ -153,7 +153,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
"""
|
||||
# initialize Kubernetes APIs
|
||||
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:
|
||||
core_v1_api_client = \
|
||||
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(
|
||||
core_v1_api_client=core_v1_api_client,
|
||||
deployment_info=deployment_info)
|
||||
status = self._get_pod_status(pods_information)
|
||||
status = self.get_pod_status(pods_information)
|
||||
stack_retries = self.STACK_RETRIES
|
||||
error_reason = None
|
||||
while status == 'Pending' and stack_retries > 0:
|
||||
@ -171,7 +171,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
self._get_pods_information(
|
||||
core_v1_api_client=core_v1_api_client,
|
||||
deployment_info=deployment_info)
|
||||
status = self._get_pod_status(pods_information)
|
||||
status = self.get_pod_status(pods_information)
|
||||
LOG.debug('status: %s', status)
|
||||
stack_retries = stack_retries - 1
|
||||
|
||||
@ -553,7 +553,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
pods_information.append(item)
|
||||
return pods_information
|
||||
|
||||
def _get_pod_status(self, pods_information):
|
||||
def get_pod_status(self, pods_information):
|
||||
pending_flag = False
|
||||
unknown_flag = False
|
||||
for pod_info in pods_information:
|
||||
@ -578,7 +578,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
ConfigMap data
|
||||
"""
|
||||
# initialize Kubernetes APIs
|
||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
||||
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||
try:
|
||||
core_v1_api_client = \
|
||||
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,
|
||||
vnf_instance=None, terminate_vnf_req=None):
|
||||
"""Delete function"""
|
||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
||||
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||
try:
|
||||
if not vnf_instance:
|
||||
# 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)
|
||||
raise
|
||||
|
||||
def _select_k8s_obj_read_api(self, k8s_client_dict, namespace, name,
|
||||
kind, api_version):
|
||||
def select_k8s_obj_read_api(self, k8s_client_dict, namespace, name,
|
||||
kind, api_version):
|
||||
"""select kubernetes read api and call"""
|
||||
def convert(name):
|
||||
name_with_underscores = re.sub(
|
||||
@ -1039,7 +1039,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
marked as deleted.
|
||||
"""
|
||||
# initialize Kubernetes APIs
|
||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
||||
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||
|
||||
try:
|
||||
if not vnf_instance:
|
||||
@ -1068,7 +1068,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
if not k8s_client_dict.get(api_version):
|
||||
continue
|
||||
try:
|
||||
self._select_k8s_obj_read_api(
|
||||
self.select_k8s_obj_read_api(
|
||||
k8s_client_dict=k8s_client_dict,
|
||||
namespace=namespace,
|
||||
name=name,
|
||||
@ -1265,7 +1265,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
of policy scaling when user define VNF descriptor.
|
||||
"""
|
||||
# initialize Kubernetes APIs
|
||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
||||
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||
try:
|
||||
if not policy.get('vnf_instance_id'):
|
||||
# execute legacy scale method
|
||||
@ -1358,7 +1358,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
pods_information = self._get_pods_information(
|
||||
core_v1_api_client=core_v1_api_client,
|
||||
deployment_info=deployment_info)
|
||||
status = self._get_pod_status(pods_information)
|
||||
status = self.get_pod_status(pods_information)
|
||||
|
||||
stack_retries = self.STACK_RETRIES
|
||||
error_reason = None
|
||||
@ -1368,7 +1368,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
pods_information = self._get_pods_information(
|
||||
core_v1_api_client=core_v1_api_client,
|
||||
deployment_info=deployment_info)
|
||||
status = self._get_pod_status(pods_information)
|
||||
status = self.get_pod_status(pods_information)
|
||||
|
||||
# LOG.debug('status: %s', status)
|
||||
stack_retries = stack_retries - 1
|
||||
@ -1390,7 +1390,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
elif stack_retries != 0 and status != 'Running':
|
||||
raise vnfm.VNFCreateWaitFailed(reason=error_reason)
|
||||
|
||||
def _is_match_pod_naming_rule(self, rsc_kind, rsc_name, pod_name):
|
||||
def is_match_pod_naming_rule(self, rsc_kind, rsc_name, pod_name):
|
||||
match_result = None
|
||||
if rsc_kind == 'Pod':
|
||||
# Expected example: name
|
||||
@ -1429,7 +1429,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
from Pod objects is RUNNING.
|
||||
"""
|
||||
# initialize Kubernetes APIs
|
||||
auth_cred, file_descriptor = self._get_auth_creds(auth_attr)
|
||||
auth_cred, file_descriptor = self.get_auth_creds(auth_attr)
|
||||
try:
|
||||
if not policy.get('vnf_instance_id'):
|
||||
# execute legacy scale_wait method
|
||||
@ -1481,12 +1481,12 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
respone = core_v1_api_client.list_namespaced_pod(
|
||||
namespace=namespace)
|
||||
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)
|
||||
if match_result:
|
||||
pods_information.append(pod)
|
||||
|
||||
status = self._get_pod_status(pods_information)
|
||||
status = self.get_pod_status(pods_information)
|
||||
if status == 'Running' and \
|
||||
scale_info.spec.replicas != len(pods_information):
|
||||
status = 'Pending'
|
||||
@ -1523,7 +1523,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
# TODO(phuoc): will update it for other components
|
||||
pass
|
||||
|
||||
def _get_auth_creds(self, auth_cred):
|
||||
def get_auth_creds(self, auth_cred):
|
||||
file_descriptor = self._create_ssl_ca_file(auth_cred)
|
||||
if ('username' not in auth_cred) and ('password' not in auth_cred):
|
||||
auth_cred['username'] = 'None'
|
||||
@ -1821,7 +1821,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
None, context, vnf_instance, auth_attr)
|
||||
return instance_id
|
||||
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)
|
||||
transformer = translate_outputs.Transformer(
|
||||
None, None, None, k8s_client_dict)
|
||||
@ -1887,7 +1887,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
and metadata, and vdu id.
|
||||
"""
|
||||
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']
|
||||
try:
|
||||
# get Kubernetes object files
|
||||
@ -1943,7 +1943,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
# get initially store VnfcResourceInfo after instantiation
|
||||
for pod in pod_list.items:
|
||||
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)
|
||||
if match_result:
|
||||
# get metadata
|
||||
@ -2005,7 +2005,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
# Get the associated pod name that runs with the actual kubernetes
|
||||
actual_pod_names = 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)
|
||||
if match_result:
|
||||
actual_pod_names.append(pod.metadata.name)
|
||||
@ -2033,7 +2033,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
"""
|
||||
# initialize Kubernetes APIs
|
||||
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
|
||||
namespace = vnf_instance.vnf_metadata['namespace']
|
||||
try:
|
||||
@ -2189,7 +2189,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
"""
|
||||
# initialize Kubernetes APIs
|
||||
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']
|
||||
try:
|
||||
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
|
||||
tmp_pods_info = list()
|
||||
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('name'),
|
||||
pod.metadata.name)
|
||||
@ -2274,7 +2274,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
'actual_pod_num': str(len(tmp_pods_info))})
|
||||
is_unmatch_pods_num = True
|
||||
pods_information.extend(tmp_pods_info)
|
||||
status = self._get_pod_status(pods_information)
|
||||
status = self.get_pod_status(pods_information)
|
||||
|
||||
if status == 'Unknown':
|
||||
error_reason = _("Pod status is found Unknown")
|
||||
@ -2305,7 +2305,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
"""Update VnfcResourceInfo after healing"""
|
||||
# initialize Kubernetes APIs
|
||||
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
|
||||
namespace = vnf_instance.vnf_metadata['namespace']
|
||||
try:
|
||||
@ -2417,7 +2417,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
vim_connection_info):
|
||||
"""Update VnfcResourceInfo after scaling"""
|
||||
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
|
||||
try:
|
||||
# initialize Kubernetes APIs
|
||||
@ -2471,7 +2471,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
|
||||
namespace=namespace)
|
||||
actual_pod_list = []
|
||||
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)
|
||||
if match_result:
|
||||
actual_pod_list.append(pod.metadata.name)
|
||||
|
@ -97,3 +97,13 @@ class VnflcmMgmtAbstractDriver(metaclass=abc.ABCMeta):
|
||||
change_ext_conn_request, grant,
|
||||
grant_request, **kwargs):
|
||||
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,
|
||||
grant_request, **kwargs):
|
||||
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 \
|
||||
--description "Kubernetes VIM" \
|
||||
--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