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:
Yi Feng 2022-02-25 19:49:26 +09:00
parent 17b03eb78f
commit d219c49e11
42 changed files with 2198 additions and 50 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ]

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,9 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-data
data:
cmKey1.txt: |
configmap2 data2
foo2
bar2

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,8 @@
apiVersion: v1
kind: Pod
metadata:
name: volume-test
spec:
containers:
- image: cirros
name: nginx

View File

@ -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

View File

@ -0,0 +1,9 @@
apiVersion: v1
kind: Secret
metadata:
name: secret-data
stringData:
password: 1mbb1G968fb1CUg2
secKey1.txt: |
secret2 data2
baz2

View File

@ -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

View File

@ -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 ]

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,9 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-data
data:
cmKey1.txt: |
configmap data
foo
bar

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,9 @@
apiVersion: v1
kind: Secret
metadata:
name: secret-data
stringData:
password: 1mbb1G968fb1CUg
secKey1.txt: |
secret data
baz

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'])

View File

@ -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"

View File

@ -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,

View 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

View File

@ -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)

View File

@ -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))

View File

@ -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):

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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
View 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