Reduce the amount of time of Tacker v2 functional tests

This fix is for improving v2 functional tests by reducing the cost of
tests on zuul.
It implements (1), (2) and (4) as proposed in the following Spec:
* https://review.opendev.org/c/openstack/tacker-specs/+/932974
  * (1)Divide time consuming tests
    * tacker-functional-devstack-multinode-sol-v2-basic
      * tacker-ft-v2-df-userdata-ccvp
      * tacker-ft-v2-st-userdata-basic
      * tacker-ft-v2-st-userdata-ccvp
    * tacker-functional-devstack-multinode-sol-v2-vnflcm
      * tacker-ft-v2-df-userdata-basic-max
      * tacker-ft-v2-df-userdata-basic-min
      * tacker-ft-v2-df-userdata-update
      * tacker-ft-v2-df-userdata-scale
      * tacker-ft-v2-df-userdata-err-handling
    * tacker-functional-devstack-multinode-sol-v2-individual-vnfc-mgmt
      * tacker-ft-v2-st-userdata-basic-max
      * tacker-ft-v2-st-userdata-basic-min
      * tacker-ft-v2-st-userdata-err-handling
      * tacker-ft-v2-st-userdata-various-lcm
  * (2)Move tests less frequently updated to move to non-voting
    * tacker-functional-devstack-multinode-sol-v2-notification
    * tacker-functional-devstack-multinode-sol-v2-prometheus
    * tacker-functional-devstack-multinode-sol-separated-nfvo-v2
    * tacker-functional-devstack-multinode-sol-https-v2
    * tacker-functional-devstack-multinode-sol-encrypt-cred-barbican
    * tacker-functional-devstack-multinode-sol-encrypt-cred-local
    * tacker-functional-devstack-kubernetes-oidc-auth
    * tacker-functional-devstack-multinode-sol-v2-az-retry
    * tacker-functional-devstack-enhanced-policy-sol
    * tacker-functional-devstack-enhanced-policy-sol-kubernetes
    * tacker-functional-devstack-multinode-sol-terraform-v2
  * (4)Rename each test scenarios and revise directory structure

Implements: blueprint reduce-ft-time
Change-Id: I98efef94b779a6a07a8841a85f6b7ba877f50444
Depends-On: I2deb71242f555bbae5ee7c798add18490cb38344
This commit is contained in:
Ai Hamano 2024-11-19 18:50:17 +09:00
parent f41acdc698
commit 5c29fafc5f
36 changed files with 5351 additions and 4424 deletions

View File

@ -1,846 +0,0 @@
- nodeset:
name: openstack-4-nodes-jammy
nodes:
- name: controller
label: ubuntu-jammy
- name: controller-tacker
label: ubuntu-jammy
- name: compute1
label: ubuntu-jammy
- name: compute2
label: ubuntu-jammy
groups:
# Nodes running the compute service
- name: compute
nodes:
- compute1
- compute2
# Nodes that are not the controller
- name: subnode
nodes:
- controller-tacker
- compute1
- compute2
# Switch node for multinode networking setup
- name: switch
nodes:
- controller
# Peer nodes for multinode networking setup
- name: peers
nodes:
- controller-tacker
- compute1
- compute2
- nodeset:
name: openstack-k8s-3-nodes-jammy
nodes:
- name: controller
label: ubuntu-jammy
- name: controller-tacker
label: ubuntu-jammy
- name: controller-k8s
label: ubuntu-jammy
groups:
- name: subnode
nodes:
- controller-tacker
- controller-k8s
- name: switch
nodes:
- controller
- name: peers
nodes:
- controller-tacker
- controller-k8s
- job:
name: tacker-functional-devstack-multinode-sol-parent
parent: devstack
description: |
Abstraction multinodes job for SOL devstack-based functional tests
nodeset: openstack-4-nodes-jammy
pre-run: playbooks/devstack/pre.yaml
run: playbooks/devstack/run.yaml
post-run: playbooks/devstack/post.yaml
roles:
- zuul: openstack-infra/devstack
timeout: 10800
required-projects:
- openstack/cinder
- openstack/glance
- openstack/keystone
- openstack/neutron
- openstack/nova
- openstack/placement
- openstack/aodh
- openstack/horizon
- openstack/barbican
- openstack/heat
- openstack/networking-sfc
- openstack/python-barbicanclient
- openstack/python-tackerclient
- openstack/tacker
- openstack/tacker-horizon
vars:
devstack_localrc:
CELLSV2_SETUP: singleconductor
PHYSICAL_NETWORK: mgmtphysnet0
OVS_BRIDGE_MAPPINGS: public:br-ex,mgmtphysnet0:br-infra
DATABASE_TYPE: mysql
KEYSTONE_SERVICE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
MYSQL_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
GNOCCHI_SERVICE_HOST: "{{ hostvars['controller-tacker']['nodepool']['private_ipv4'] }}"
Q_SERVICE_PLUGIN_CLASSES: ovn-router,networking_sfc.services.flowclassifier.plugin.FlowClassifierPlugin,neutron.services.qos.qos_plugin.QoSPlugin,qos
L2_AGENT_EXTENSIONS: qos
DEVSTACK_PARALLEL: True
OVN_L3_CREATE_PUBLIC_NETWORK: true
OVN_DBS_LOG_LEVEL: dbg
Q_ML2_PLUGIN_MECHANISM_DRIVERS: ovn,logger
test_matrix_configs: [neutron]
zuul_work_dir: src/opendev.org/openstack/tacker
host-vars:
controller:
devstack_local_conf:
post-config:
# NOTE: workaround for Heat timeout due to communication
# problem between nova and neutron.
$NEUTRON_CONF:
DEFAULT:
client_socket_timeout: 60
$NEUTRON_DHCP_CONF:
DEFAULT:
enable_isolated_metadata: True
# NOTE: workaround for Cinder image volume cache problem.
# https://bugs.launchpad.net/cinder/+bug/1953704
$CINDER_CONF:
lvmdriver-1:
image_volume_cache_enabled: False
devstack_plugins:
heat: https://opendev.org/openstack/heat
networking-sfc: https://opendev.org/openstack/networking-sfc
aodh: https://opendev.org/openstack/aodh
barbican: https://opendev.org/openstack/barbican
devstack_services:
# Core services enabled for this branch.
# This list replaces the test-matrix.
# Shared services
mysql: true
rabbit: true
tls-proxy: false
# Keystone services
key: true
# Glance services
g-api: true
# Nova services
n-api: true
n-api-meta: true
n-cond: true
n-cpu: false
n-novnc: true
n-sch: true
placement-api: true
# OVN services
ovn-controller: true
ovn-northd: true
ovs-vswitchd: true
ovsdb-server: true
# Neutron services
q-svc: true
q-qos: true
q-ovn-metadata-agent: true
# Cinder services
c-api: true
c-sch: true
c-vol: true
cinder: true
# Services we don't need.
# This section is not really needed, it's for readability.
horizon: false
tempest: false
swift: false
s-account: false
s-container: false
s-object: false
s-proxy: false
c-bak: false
tox_install_siblings: false
controller-tacker:
devstack_plugins:
tacker: https://opendev.org/openstack/tacker
devstack_services:
# OVN services
ovn-controller: true
ovs-vswitchd: true
ovsdb-server: true
# Neutron services
q-ovn-metadata-agent: true
# Tacker services
tacker: true
tacker-conductor: true
group-vars:
compute:
# Since a VirtualInterfaceCreateException occurs during a test,
# the setting of network-vif-plugged is changed by the reference of
# the following URL.
# https://bugs.launchpad.net/heat/+bug/1694371
devstack_local_conf:
post-config:
$NOVA_CONF:
DEFAULT:
vif_plugging_is_fatal: False
subnode:
devstack_localrc:
CELLSV2_SETUP: singleconductor
PHYSICAL_NETWORK: mgmtphysnet0
OVS_BRIDGE_MAPPINGS: public:br-ex,mgmtphysnet0:br-infra
DATABASE_TYPE: mysql
KEYSTONE_SERVICE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
MYSQL_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
GNOCCHI_SERVICE_HOST: "{{ hostvars['controller-tacker']['nodepool']['private_ipv4'] }}"
TACKER_HOST: "{{ hostvars['controller-tacker']['nodepool']['private_ipv4'] }}"
TACKER_MODE: standalone
IS_ZUUL_FT: True
Q_SERVICE_PLUGIN_CLASSES: "qos,\
networking_sfc.services.flowclassifier.plugin.FlowClassifierPlugin,\
neutron.services.qos.qos_plugin.QoSPlugin,\
ovn-router"
L2_AGENT_EXTENSIONS: qos
ENABLE_CHASSIS_AS_GW: false
Q_ML2_PLUGIN_MECHANISM_DRIVERS: ovn,logger
devstack_services:
# To override the parent job's definitions.
tls-proxy: false
# OVN services
ovn-controller: true
ovs-vswitchd: true
ovsdb-server: true
# Neutron services
q-ovn-metadata-agent: true
- job:
name: tacker-functional-devstack-multinode-sol-legacy-nfvo
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL devstack-based functional tests for legacy_nfvo
host-vars:
controller-tacker:
tox_envlist: dsvm-functional-sol-legacy-nfvo
- job:
name: tacker-functional-devstack-multinode-sol-vnflcm
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL devstack-based functional tests for vnflcm
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
database:
max_pool_size: 0
tox_envlist: dsvm-functional-sol-vnflcm
- job:
name: tacker-functional-devstack-multinode-sol-vnflcm-userdata
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL devstack-based functional tests for vnflcm with userdata
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
database:
max_pool_size: 0
tox_envlist: dsvm-functional-sol-vnflcm-userdata
- job:
name: tacker-functional-devstack-multinode-sol-vnfpkgm
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL devstack-based functional tests for vnfpkgm
host-vars:
controller-tacker:
tox_envlist: dsvm-functional-sol-vnfpkgm
- job:
name: tacker-functional-devstack-multinode-sol-v2-basic
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL V2 devstack-based functional tests
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
server_notification:
server_notification: true
prometheus_plugin:
auto_scaling: true
auto_healing: true
v2_vnfm:
# Notes: The List API will return 'Link' Header in response
# body only if page_size is not 0. This job will check 'Link'
# Header, so these page_size parameters are set to 1.
vnf_instance_page_size: 1
lcm_op_occ_page_size: 1
tox_envlist: dsvm-functional-sol-v2-basic
- job:
name: tacker-functional-devstack-multinode-sol-v2-vnflcm
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL V2 devstack-based functional tests
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
server_notification:
server_notification: true
prometheus_plugin:
auto_scaling: true
auto_healing: true
tox_envlist: dsvm-functional-sol-v2-vnflcm
- job:
name: tacker-functional-devstack-multinode-sol-v2-notification
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL V2 devstack-based functional tests
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
server_notification:
server_notification: true
tox_envlist: dsvm-functional-sol-v2-notification
- job:
name: tacker-functional-devstack-multinode-sol-v2-prometheus
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL V2 devstack-based functional tests
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
prometheus_plugin:
auto_scaling: true
auto_healing: true
tox_envlist: dsvm-functional-sol-v2-prometheus
- job:
name: tacker-functional-devstack-multinode-sol-multi-tenant
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL Multi tenant devstack-based functional tests
host-vars:
controller-tacker:
tox_envlist: dsvm-functional-sol-multi-tenant
vars:
setup_multi_tenant: true
- job:
name: tacker-functional-devstack-multinode-sol-separated-nfvo
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL devstack-based functional tests
with separated NFVO
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
connect_vnf_packages:
base_url: http://127.0.0.1:9990/vnfpkgm/v1/vnf_packages
pipeline: package_content,vnfd,artifacts
connect_grant:
base_url: http://127.0.0.1:9990/grant/v1/grants
openstack_vim:
stack_retries: 120
tox_envlist: dsvm-functional-sol-separated-nfvo
- job:
name: tacker-functional-devstack-multinode-sol-separated-nfvo-v2
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL devstack-based functional tests
with separated V2 NFVO
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
v2_nfvo:
use_external_nfvo: True
endpoint: http://127.0.0.1:9990
token_endpoint: http://127.0.0.1:9990/token
client_id: 229ec984de7547b2b662e968961af5a4
client_password: devstack
use_client_secret_basic: True
tox_envlist: dsvm-functional-sol-separated-nfvo-v2
- job:
name: tacker-functional-devstack-multinode-sol-v2-individual-vnfc-mgmt
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinode job for SOL V2 devstack-based individual vnfc mgmt functional
tests
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
v2_vnfm:
# Notes: The List API will return 'Link' Header in response
# body only if page_size is not 0. This job will check 'Link'
# Header, so these page_size parameters are set to 1.
vnf_instance_page_size: 1
lcm_op_occ_page_size: 1
tox_envlist: dsvm-functional-sol-v2-individual-vnfc-mgmt
- job:
name: tacker-functional-devstack-multinode-sol-kubernetes-parent
parent: devstack
description: |
Abstraction multinodes job for SOL devstack-based kubernetes functional
tests
nodeset: openstack-k8s-3-nodes-jammy
pre-run: playbooks/devstack/pre.yaml
run: playbooks/devstack/run.yaml
post-run: playbooks/devstack/post.yaml
roles:
- zuul: openstack-infra/devstack
timeout: 10800
required-projects:
- openstack/barbican
- openstack/cinder
- openstack/devstack-plugin-container
- openstack/glance
- openstack/heat
- openstack/horizon
- openstack/keystone
- openstack/neutron
- openstack/nova
- openstack/placement
- openstack/python-barbicanclient
- openstack/python-tackerclient
- openstack/tacker
- openstack/tacker-horizon
host-vars:
controller:
devstack_plugins:
barbican: https://opendev.org/openstack/barbican
heat: https://opendev.org/openstack/heat
neutron: https://opendev.org/openstack/neutron
devstack_services:
base: false
c-api: true
c-bak: false
c-sch: true
c-vol: true
cinder: true
coredns: false
etcd3: true
g-api: true
g-reg: true
horizon: false
key: true
mysql: true
n-api-meta: true
n-api: true
n-cond: true
n-cpu: false
n-novnc: true
n-sch: true
neutron: true
placement-api: true
placement-client: true
ovn-controller: true
ovn-northd: true
ovs-vswitchd: true
ovsdb-server: true
q-qos: true
q-svc: true
q-ovn-metadata-agent: true
rabbit: true
s-account: false
s-container: false
s-object: false
s-proxy: false
swift: false
tempest: false
tls-proxy: false
tox_install_siblings: false
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
kubernetes_vim:
stack_retries: 120
devstack_services:
ovn-controller: true
ovn-northd: true
ovs-vswitchd: true
ovsdb-server: true
q-ovn-metadata-agent: true
tacker: true
tacker-conductor: true
devstack_plugins:
tacker: https://opendev.org/openstack/tacker
controller-k8s:
devstack_local_conf: {}
devstack_plugins:
devstack-plugin-container: https://opendev.org/openstack/devstack-plugin-container
devstack_services:
etcd3: false
ovn-controller: true
ovn-northd: true
ovs-vswitchd: true
ovsdb-server: true
q-ovn-metadata-agent: true
container: true
k8s-master: true
tox_install_siblings: false
group-vars:
subnode:
devstack_localrc:
CELLSV2_SETUP: singleconductor
DATABASE_TYPE: mysql
IS_ZUUL_FT: True
K8S_API_SERVER_IP: "{{ hostvars['controller-k8s']['nodepool']['private_ipv4'] }}"
KEYSTONE_SERVICE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
CONTAINER_ENGINE: crio
K8S_VERSION: "1.30.5"
CRIO_VERSION: "1.30.5"
MYSQL_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
OVS_BRIDGE_MAPPINGS: public:br-ex,mgmtphysnet0:br-infra
PHYSICAL_NETWORK: mgmtphysnet0
TACKER_HOST: "{{ hostvars['controller-tacker']['nodepool']['private_ipv4'] }}"
TACKER_MODE: standalone
USE_PYTHON3: true
ENABLE_CHASSIS_AS_GW: false
Q_ML2_PLUGIN_MECHANISM_DRIVERS: ovn,logger
# TODO(ueha): Remove this workarround if the Zuul jobs succeed with GLOBAL_VENV=true
GLOBAL_VENV: false
K8S_TOKEN: "9agf12.zsu5uh2m4pzt3qba"
devstack_services:
dstat: false
horizon: false
n-api-meta: false
n-api: false
n-cauth: false
n-cond: false
n-cpu: true
n-novnc: false
n-obj: false
n-sch: false
ovn-controller: true
ovs-vswitchd: true
ovsdb-server: true
q-ovn-metadata-agent: true
tls-proxy: false
vars:
devstack_localrc:
CELLSV2_SETUP: singleconductor
DATABASE_TYPE: mysql
ETCD_USE_RAMDISK: true
IS_ZUUL_FT: True
KEYSTONE_SERVICE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
L2_AGENT_EXTENSIONS: qos
MYSQL_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
OVS_BRIDGE_MAPPINGS: public:br-ex,mgmtphysnet0:br-infra
PHYSICAL_NETWORK: mgmtphysnet0
Q_SERVICE_PLUGIN_CLASSES: ovn-router,neutron.services.qos.qos_plugin.QoSPlugin,qos
TACKER_HOST: "{{ hostvars['controller-tacker']['nodepool']['private_ipv4'] }}"
TACKER_MODE: standalone
USE_PYTHON3: true
DEVSTACK_PARALLEL: True
OVN_L3_CREATE_PUBLIC_NETWORK: true
OVN_DBS_LOG_LEVEL: dbg
Q_ML2_PLUGIN_MECHANISM_DRIVERS: ovn,logger
# TODO(ueha): Remove this workarround if the Zuul jobs succeed with GLOBAL_VENV=true
GLOBAL_VENV: false
devstack_local_conf:
post-config:
$NEUTRON_DHCP_CONF:
DEFAULT:
enable_isolated_metadata: True
k8s_api_url: "https://{{ hostvars['controller-k8s']['nodepool']['private_ipv4'] }}:6443"
k8s_ssl_verify: true
helm_version: "3.15.4"
test_matrix_configs: [neutron]
zuul_work_dir: src/opendev.org/openstack/tacker
zuul_copy_output:
'{{ devstack_log_dir }}/kubernetes': 'logs'
- job:
name: tacker-functional-devstack-multinode-sol-kubernetes
parent: tacker-functional-devstack-multinode-sol-kubernetes-parent
description: |
Multinodes job for SOL Kubernetes Multi tenant devstack-based functional tests
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
database:
max_pool_size: 0
tox_envlist: dsvm-functional-sol-kubernetes
- job:
name: tacker-functional-devstack-multinode-sol-kubernetes-multi-tenant
parent: tacker-functional-devstack-multinode-sol-kubernetes-parent
description: |
Multinodes job for SOL Kubernetes Multi tenant devstack-based functional tests
host-vars:
controller-tacker:
tox_envlist: dsvm-functional-sol-kubernetes-multi-tenant
vars:
setup_multi_tenant: true
- job:
name: tacker-functional-devstack-multinode-sol-kubernetes-v2
parent: tacker-functional-devstack-multinode-sol-kubernetes-parent
description: |
Multinodes job for SOL Kubernetes V2 devstack-based functional tests
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
v2_vnfm:
kubernetes_vim_rsc_wait_timeout: 800
prometheus_plugin:
fault_management: true
performance_management: true
auto_scaling: true
auto_healing: true
test_rule_with_promtool: true
tox_envlist: dsvm-functional-sol-kubernetes-v2
vars:
prometheus_setup: true
- job:
name: tacker-functional-devstack-multinode-sol-https-v2
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL devstack-based functional tests
with https request
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
v2_vnfm:
notification_verify_cert: true
notification_ca_cert_file: /etc/https_server/ssl/ca.crt
use_oauth2_mtls_for_heat: false
heat_verify_cert: true
heat_ca_cert_file: /etc/https_server/ssl/ca.crt
prometheus_plugin:
fault_management: true
performance_management: true
auto_scaling: true
performance_management_package: tacker.sol_refactored.common.monitoring_plugin_base
performance_management_class: MonitoringPluginStub
v2_nfvo:
use_external_nfvo: true
endpoint: https://localhost:9990
token_endpoint: https://localhost:9990/token
client_id: 229ec984de7547b2b662e968961af5a4
client_password: devstack
nfvo_verify_cert: true
nfvo_ca_cert_file: /etc/https_server/ssl/ca.crt
use_client_secret_basic: true
tox_envlist: dsvm-functional-sol-https-v2
vars:
https_setup: true
- job:
name: tacker-functional-devstack-multinode-sol-encrypt-cred-barbican
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL devstack-based functional tests
with encrypt credentials
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
DEFAULT:
use_credential_encryption: true
keymanager_type: "barbican"
v2_nfvo:
use_external_nfvo: true
endpoint: https://localhost:9990
token_endpoint: https://localhost:9990/token
client_id: 229ec984de7547b2b662e968961af5a4
client_password: devstack
nfvo_ca_cert_file: /etc/https_server/ssl/ca.crt
nfvo_verify_cert: true
use_client_secret_basic: true
tox_envlist: dsvm-functional-sol-encrypt-cred-barbican
vars:
https_setup: true
- job:
name: tacker-functional-devstack-multinode-sol-encrypt-cred-local
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL devstack-based functional tests
with encrypt credentials
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
DEFAULT:
use_credential_encryption: true
keymanager_type: "local"
crypt_key_dir: "/etc/tacker/crypt/fernet_keys"
v2_nfvo:
use_external_nfvo: true
endpoint: https://localhost:9990
token_endpoint: https://localhost:9990/token
client_id: 229ec984de7547b2b662e968961af5a4
client_password: devstack
nfvo_ca_cert_file: /etc/https_server/ssl/ca.crt
nfvo_verify_cert: true
use_client_secret_basic: true
tox_envlist: dsvm-functional-sol-encrypt-cred-local
vars:
https_setup: true
- job:
name: tacker-functional-devstack-kubernetes-oidc-auth
parent: tacker-functional-devstack-multinode-sol-kubernetes-v2
description: |
Multinodes job for Kubernetes OIDC Auth tests
host-vars:
controller-tacker:
tox_envlist: dsvm-functional-sol_kubernetes_oidc_auth
vars:
keycloak_host: "{{ hostvars['controller-k8s']['nodepool']['private_ipv4'] }}"
keycloak_http_port: 8080
keycloak_https_port: 8443
- job:
name: tacker-functional-devstack-multinode-sol-v2-az-retry
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for retry of AZ selection in SOL V2 devstack-based functional tests
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
v2_nfvo:
test_grant_zone_list: az-1
v2_vnfm:
placement_fallback_best_effort: true
enable_rollback_stack: true
devstack_services:
n-cpu: true
placement-client: true
tox_envlist: dsvm-functional-sol-v2-az-retry
vars:
setup_multi_az: true
controller_tacker_hostname: "{{ hostvars['controller-tacker']['ansible_hostname'] }}"
- job:
name: tacker-functional-devstack-enhanced-policy-sol
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Enhanced policy job for SOL devstack-based functional tests
host-vars:
controller-tacker:
tox_envlist: dsvm-functional-enhanced-policy-sol
devstack_local_conf:
post-config:
$TACKER_CONF:
oslo_policy:
enhanced_tacker_policy: True
vars:
config_enhanced_policy: true
- job:
name: tacker-functional-devstack-enhanced-policy-sol-kubernetes
parent: tacker-functional-devstack-multinode-sol-kubernetes-v2
description: |
Enhanced policy job for SOL Kubernetes devstack-based functional tests
host-vars:
controller-tacker:
tox_envlist: dsvm-functional-enhanced-policy-sol-kubernetes
devstack_local_conf:
post-config:
$TACKER_CONF:
oslo_policy:
enhanced_tacker_policy: True
vars:
config_enhanced_policy: true
- job:
name: tacker-functional-devstack-multinode-sol-terraform-v2
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL Terraform devstack-based functional tests
attempts: 1
host-vars:
controller-tacker:
tox_envlist: dsvm-functional-sol-terraform-v2
devstack_local_conf:
post-config:
$TACKER_CONF:
v2_vnfm:
tf_file_dir: /tmp/tacker/terraform
vars:
terraform_setup: true
- job:
name: tacker-compliance-devstack-multinode-sol
parent: tacker-functional-devstack-multinode-sol-parent
description: |
Multinodes job for SOL devstack-based compliance tests
host-vars:
controller-tacker:
tox_envlist: dsvm-compliance-sol-api
voting: false
- project:
templates:
- check-requirements
- openstack-cover-jobs
- openstack-python3-jobs
- publish-openstack-docs-pti
- release-notes-jobs-python3
check:
jobs:
- tacker-functional-devstack-multinode-sol-legacy-nfvo
- tacker-functional-devstack-multinode-sol-vnflcm
- tacker-functional-devstack-multinode-sol-vnflcm-userdata
- tacker-functional-devstack-multinode-sol-vnfpkgm
- tacker-functional-devstack-multinode-sol-separated-nfvo
- tacker-functional-devstack-multinode-sol-kubernetes
- tacker-functional-devstack-multinode-sol-v2-basic
- tacker-functional-devstack-multinode-sol-v2-vnflcm
- tacker-functional-devstack-multinode-sol-v2-notification
- tacker-functional-devstack-multinode-sol-v2-prometheus
- tacker-functional-devstack-multinode-sol-separated-nfvo-v2
- tacker-functional-devstack-multinode-sol-v2-individual-vnfc-mgmt
- tacker-functional-devstack-multinode-sol-kubernetes-v2
- tacker-functional-devstack-multinode-sol-multi-tenant
- tacker-functional-devstack-multinode-sol-https-v2
- tacker-functional-devstack-multinode-sol-encrypt-cred-barbican
- tacker-functional-devstack-multinode-sol-encrypt-cred-local
- tacker-functional-devstack-multinode-sol-kubernetes-multi-tenant
- tacker-functional-devstack-kubernetes-oidc-auth
- tacker-functional-devstack-multinode-sol-v2-az-retry
- tacker-functional-devstack-enhanced-policy-sol
- tacker-functional-devstack-enhanced-policy-sol-kubernetes
- tacker-compliance-devstack-multinode-sol
- tacker-functional-devstack-multinode-sol-terraform-v2

View File

@ -173,7 +173,7 @@ To change the version execute the below steps:
1. Open ``<tacker_route_directory>/tacker/tox.ini`` file in any editor.
2. Under [testenv:dsvm-compliance-sol-api] section in **commands_pre** while
2. Under [testenv:ft-v1-compliance-sol] section in **commands_pre** while
doing checkout of api-tests mention desired TST's code version.
.. code-block:: console
@ -217,14 +217,14 @@ Execution of testcase
.. code-block:: console
tox -e dsvm-compliance-sol-api
tox -e ft-v1-compliance-sol
* Or from tacker directory, specific testcases can be executed using
the following command:
.. code-block:: console
tox -e dsvm-compliance-sol-api tacker.tests.compliance.xxx.yyy.<testcase>
tox -e ft-v1-compliance-sol tacker.tests.compliance.xxx.yyy.<testcase>
How to proceed when the test fails
@ -237,7 +237,7 @@ How to proceed when the test fails
.. code-block:: console
tacker/.tox/dsvm-compliance-sol-api/log/SOL003/VNFLifecycleManagement-API
tacker/.tox/ft-v1-compliance-sol/log/SOL003/VNFLifecycleManagement-API
/VNFInstances/GET_information_about_multiple_VNF_instances
* Compliance test may also get failed due to code problem at

View File

@ -0,0 +1,53 @@
# Copyright (C) 2021 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from tacker.tests.functional.sol_v2_common import test_vnflcm_basic_common
from tacker.tests import utils
@ddt.ddt
class VnfLcmMaxTest(test_vnflcm_basic_common.CommonVnfLcmTest):
@classmethod
def setUpClass(cls):
super(VnfLcmMaxTest, cls).setUpClass()
image_path = utils.test_etc_sample("etsi/nfv/common/Files/images",
"cirros-0.5.2-x86_64-disk.img")
# for basic lcms tests max pattern
basic_lcms_max_path = utils.test_sample("functional/sol_v2_common",
"basic_lcms_max")
cls.max_pkg, cls.max_vnfd_id = cls.create_vnf_package(
basic_lcms_max_path, image_path=image_path)
# for update vnf test
update_vnf_path = utils.test_sample("functional/sol_v2_common",
"update_vnf")
# no image contained
cls.upd_pkg, cls.upd_vnfd_id = cls.create_vnf_package(update_vnf_path)
@classmethod
def tearDownClass(cls):
super(VnfLcmMaxTest, cls).tearDownClass()
cls.delete_vnf_package(cls.max_pkg)
cls.delete_vnf_package(cls.upd_pkg)
def setUp(self):
super().setUp()
def test_basic_lcms_max(self):
self.basic_lcms_max_common_test()

View File

@ -0,0 +1,157 @@
# Copyright (C) 2021 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
import os
from tacker.tests.functional.sol_v2_common import paramgen
from tacker.tests.functional.sol_v2_common import test_vnflcm_basic_common
from tacker.tests import utils
@ddt.ddt
class VnfLcmMinTest(test_vnflcm_basic_common.CommonVnfLcmTest):
@classmethod
def setUpClass(cls):
super(VnfLcmMinTest, cls).setUpClass()
# for basic lcms tests min pattern
basic_lcms_min_path = utils.test_sample("functional/sol_v2_common",
"basic_lcms_min")
# no image contained
cls.min_pkg, cls.min_vnfd_id = cls.create_vnf_package(
basic_lcms_min_path)
# for update vnf test
update_vnf_path = utils.test_sample("functional/sol_v2_common",
"update_vnf")
# no image contained
cls.upd_pkg, cls.upd_vnfd_id = cls.create_vnf_package(update_vnf_path)
@classmethod
def tearDownClass(cls):
super(VnfLcmMinTest, cls).tearDownClass()
cls.delete_vnf_package(cls.min_pkg)
cls.delete_vnf_package(cls.upd_pkg)
def setUp(self):
super().setUp()
def test_api_versions(self):
"""Test version operations
* About version operations:
This test includes the following operations.
- 1. List VNFLCM API versions
- 2. Show VNFLCM API versions
"""
path = "/vnflcm/api_versions"
resp, body = self.tacker_client.do_request(path, "GET")
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
expected_body = {
"uriPrefix": "/vnflcm",
"apiVersions": [
{'version': '1.3.0', 'isDeprecated': False},
{'version': '2.0.0', 'isDeprecated': False}
]
}
self.assertEqual(body, expected_body)
path = "/vnflcm/v2/api_versions"
resp, body = self.tacker_client.do_request(path, "GET")
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
expected_body = {
"uriPrefix": "/vnflcm/v2",
"apiVersions": [
{'version': '2.0.0', 'isDeprecated': False}
]
}
self.assertEqual(body, expected_body)
@ddt.data(True, False)
def test_subscriptions(self, is_all):
"""Test subscription operations
* About attributes:
- is_all=True
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
- is_all=False
Omit except for required attributes.
Only the following cardinality attributes are set.
- 1
- 1..N (1)
* About subscription operations:
This test includes the following operations.
- 0. Pre-setting
- 1. Create a new subscription
- 2. Show subscription
- 3. List subscription with attribute-based filtering
- 4. Delete a subscription
"""
# 0. Pre-setting
callback_url = os.path.join(self.get_notify_callback_url(),
self._testMethodName)
callback_uri = ('http://localhost:'
f'{self.get_server_port()}'
f'{callback_url}')
sub_req = paramgen.sub_create_min(callback_uri)
if is_all:
sub_req = paramgen.sub_create_max(callback_uri)
# 1. Create a new subscription
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# 2. Show subscription
expected_attrs = [
'id', 'callbackUri', 'verbosity', '_links'
]
if is_all:
additional_attrs = ['filter']
expected_attrs.extend(additional_attrs)
resp, body = self.show_subscription(sub_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
# 3. List subscription with attribute-based filtering
filter_expr = {'filter': '(eq,id,%s)' % sub_id}
resp, body = self.list_subscriptions(filter_expr)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
for sbsc in body:
self.check_resp_body(sbsc, expected_attrs)
# 4. Delete a subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
def test_basic_lcms_min(self):
self.basic_lcms_min_common_test()

View File

@ -22,11 +22,11 @@ from tacker.tests import utils
@ddt.ddt
class ChangeVnfPkgVnfLcmTest(test_vnflcm_basic_common.CommonVnfLcmTest):
class VnfLcmCcvpTest(test_vnflcm_basic_common.CommonVnfLcmTest):
@classmethod
def setUpClass(cls):
super(ChangeVnfPkgVnfLcmTest, cls).setUpClass()
super(VnfLcmCcvpTest, cls).setUpClass()
image_path = utils.test_etc_sample("etsi/nfv/common/Files/images",
"cirros-0.5.2-x86_64-disk.img")
@ -57,7 +57,7 @@ class ChangeVnfPkgVnfLcmTest(test_vnflcm_basic_common.CommonVnfLcmTest):
@classmethod
def tearDownClass(cls):
super(ChangeVnfPkgVnfLcmTest, cls).tearDownClass()
super(VnfLcmCcvpTest, cls).tearDownClass()
cls.delete_vnf_package(cls.old_pkg)
cls.delete_vnf_package(cls.new_image_pkg)
@ -65,7 +65,7 @@ class ChangeVnfPkgVnfLcmTest(test_vnflcm_basic_common.CommonVnfLcmTest):
cls.delete_vnf_package(cls.failed_pkg)
def setUp(self):
super(ChangeVnfPkgVnfLcmTest, self).setUp()
super(VnfLcmCcvpTest, self).setUp()
def test_change_vnfpkg_from_image_to_image(self):
self.change_vnfpkg_from_image_to_image_common_test()

View File

@ -23,11 +23,11 @@ from tacker.tests import utils
@ddt.ddt
class VnfLcmTest(test_vnflcm_basic_common.CommonVnfLcmTest):
class VnfLcmScaleTest(test_vnflcm_basic_common.CommonVnfLcmTest):
@classmethod
def setUpClass(cls):
super(VnfLcmTest, cls).setUpClass()
super(VnfLcmScaleTest, cls).setUpClass()
image_path = utils.test_etc_sample("etsi/nfv/common/Files/images",
"cirros-0.5.2-x86_64-disk.img")
@ -37,787 +37,14 @@ class VnfLcmTest(test_vnflcm_basic_common.CommonVnfLcmTest):
cls.max_pkg, cls.max_vnfd_id = cls.create_vnf_package(
basic_lcms_max_path, image_path=image_path)
# for basic lcms tests min pattern
basic_lcms_min_path = utils.test_sample("functional/sol_v2_common",
"basic_lcms_min")
# no image contained
cls.min_pkg, cls.min_vnfd_id = cls.create_vnf_package(
basic_lcms_min_path)
# for update vnf test
update_vnf_path = utils.test_sample("functional/sol_v2_common",
"update_vnf")
# no image contained
cls.upd_pkg, cls.upd_vnfd_id = cls.create_vnf_package(update_vnf_path)
@classmethod
def tearDownClass(cls):
super(VnfLcmTest, cls).tearDownClass()
super(VnfLcmScaleTest, cls).tearDownClass()
cls.delete_vnf_package(cls.max_pkg)
cls.delete_vnf_package(cls.min_pkg)
cls.delete_vnf_package(cls.upd_pkg)
def setUp(self):
super().setUp()
def test_api_versions(self):
"""Test version operations
* About version operations:
This test includes the following operations.
- 1. List VNFLCM API versions
- 2. Show VNFLCM API versions
"""
path = "/vnflcm/api_versions"
resp, body = self.tacker_client.do_request(path, "GET")
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
expected_body = {
"uriPrefix": "/vnflcm",
"apiVersions": [
{'version': '1.3.0', 'isDeprecated': False},
{'version': '2.0.0', 'isDeprecated': False}
]
}
self.assertEqual(body, expected_body)
path = "/vnflcm/v2/api_versions"
resp, body = self.tacker_client.do_request(path, "GET")
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
expected_body = {
"uriPrefix": "/vnflcm/v2",
"apiVersions": [
{'version': '2.0.0', 'isDeprecated': False}
]
}
self.assertEqual(body, expected_body)
@ddt.data(True, False)
def test_subscriptions(self, is_all):
"""Test subscription operations
* About attributes:
- is_all=True
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
- is_all=False
Omit except for required attributes.
Only the following cardinality attributes are set.
- 1
- 1..N (1)
* About subscription operations:
This test includes the following operations.
- 0. Pre-setting
- 1. Create a new subscription
- 2. Show subscription
- 3. List subscription with attribute-based filtering
- 4. Delete a subscription
"""
# 0. Pre-setting
callback_url = os.path.join(self.get_notify_callback_url(),
self._testMethodName)
callback_uri = ('http://localhost:'
f'{self.get_server_port()}'
f'{callback_url}')
sub_req = paramgen.sub_create_min(callback_uri)
if is_all:
sub_req = paramgen.sub_create_max(callback_uri)
# 1. Create a new subscription
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# 2. Show subscription
expected_attrs = [
'id', 'callbackUri', 'verbosity', '_links'
]
if is_all:
additional_attrs = ['filter']
expected_attrs.extend(additional_attrs)
resp, body = self.show_subscription(sub_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
# 3. List subscription with attribute-based filtering
filter_expr = {'filter': '(eq,id,%s)' % sub_id}
resp, body = self.list_subscriptions(filter_expr)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
for sbsc in body:
self.check_resp_body(sbsc, expected_attrs)
# 4. Delete a subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
def test_basic_lcms_max(self):
self.basic_lcms_max_common_test()
def test_basic_lcms_min(self):
self.basic_lcms_min_common_test()
def test_update_scale_lcm(self):
"""Test the sequence of update VNF and scale out
* About attributes:
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
* About LCM operations:
This test includes the following operations.
- 0. Pre-setting
- 1. Create subscription
- 2. Create VNF instance
- 3. Instantiate VNF
- 4. Show VNF instance
- 5. Update VNF
- 6. Show VNF instance
- 7. Scale out operation
- 8. Terminate VNF
- 9. Delete VNF instance
- 10. Delete subscription
"""
# 0. Pre-setting
# Create a new network and subnet to check the IP allocation of
# IPv4 and IPv6
ft_net0_name = 'ft-net0'
ft_net0_subs = {
'ft-ipv4-subnet0': {
'range': '100.100.100.0/24',
'ip_version': 4
},
'ft-ipv6-subnet0': {
'range': '1111:2222:3333::/64',
'ip_version': 6
}
}
ft_net0_id = self.create_network(ft_net0_name)
self.addCleanup(self.delete_network, ft_net0_id)
for sub_name, val in ft_net0_subs.items():
# subnet is automatically deleted with network deletion
self.create_subnet(
ft_net0_id, sub_name, val['range'], val['ip_version'])
net_ids = self.get_network_ids(
['net0', 'net1', 'net_mgmt', 'ft-net0'])
subnet_ids = self.get_subnet_ids(
['subnet0', 'subnet1', 'ft-ipv4-subnet0', 'ft-ipv6-subnet0'])
port_names = ['VDU2_CP1-1', 'VDU2_CP1-2']
port_ids = {}
for port_name in port_names:
port_id = self.create_port(net_ids['net0'], port_name)
port_ids[port_name] = port_id
self.addCleanup(self.delete_port, port_id)
# 1. Create subscription
callback_url = os.path.join(self.get_notify_callback_url(),
self._testMethodName)
callback_uri = ('http://localhost:'
f'{self.get_server_port()}'
f'{callback_url}')
sub_req = paramgen.sub_create_max(callback_uri)
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# 2. Create VNF instance
# ETSI NFV SOL003 v3.3.1 5.5.2.2 VnfInstance
# NOTE: extensions and vnfConfigurableProperties are omitted
# because they are commented out in etsi_nfv_sol001.
expected_inst_attrs = [
'id',
'vnfInstanceName',
'vnfInstanceDescription',
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
# 'vimConnectionInfo', # omitted
'instantiationState',
# 'instantiatedVnfInfo', # omitted
'metadata',
# 'extensions', # omitted
'_links'
]
create_req = paramgen.create_vnf_max(self.max_vnfd_id)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
self.check_resp_body(body, expected_inst_attrs)
inst_id = body['id']
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 3. Instantiate VNF instance
instantiate_req = paramgen.instantiate_vnf_max(
net_ids, subnet_ids, port_ids, self.auth_url)
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 4. Show VNF instance
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo'
]
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.INSTANTIATED,
body['instantiationState'])
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# 5. Update VNF
# check attribute value before update VNF
# check usageState of max pattern VNF Package
self.check_package_usage(self.max_pkg, 'IN_USE')
# check usageState of update VNF Package
self.check_package_usage(self.upd_pkg, 'NOT_IN_USE')
# check vnfd id
self.assertEqual(self.max_vnfd_id, body['vnfdId'])
# check vnfc info
vnfc_info = body['instantiatedVnfInfo']['vnfcInfo']
self.assertGreater(len(vnfc_info), 1)
vnfc_ids = [vnfc['id'] for vnfc in vnfc_info]
for vnfc in vnfc_info:
self.assertIn('id', vnfc)
self.assertIn('vduId', vnfc)
self.assertIsNotNone(vnfc.get('vnfcState'))
self.assertIsNone(vnfc.get('vnfcConfigurableProperties'))
update_req = paramgen.update_vnf_max(self.upd_vnfd_id, vnfc_ids)
resp, body = self.update_vnf_instance(inst_id, update_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 6. Show VNF instance
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo'
]
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# check usageState of max pattern VNF Package
self.check_package_usage(self.max_pkg, 'NOT_IN_USE')
# check usageState of update pattern VNF Package
self.check_package_usage(self.upd_pkg, 'IN_USE')
# check the specified attribute after update VNF
self.assertEqual(self.upd_vnfd_id, body['vnfdId'])
self.assertEqual('new name', body['vnfInstanceName'])
self.assertEqual('new description', body['vnfInstanceDescription'])
dummy_key_value = {'dummy-key': 'dummy-value'}
self.assertEqual(dummy_key_value, body['metadata'])
self.assertEqual(dummy_key_value, body['extensions'])
self.assertEqual(dummy_key_value, body['vnfConfigurableProperties'])
vim_connection_info = {
"vim2": {
"vimId": "ac2d2ece-5e49-4b15-b92d-b681e9c096d8",
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {
"endpoint": "http://127.0.0.1/identity/v3"
},
"accessInfo": {
"username": "dummy_user",
"region": "RegionOne",
"project": "dummy_project",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {
"dummy-key": "dummy-val"
}
}
}
self.assertEqual(vim_connection_info['vim2'],
body['vimConnectionInfo']['vim2'])
# check vnfc info
vnfc_info = body['instantiatedVnfInfo']['vnfcInfo']
self.assertEqual(vnfc_ids[0], vnfc_info[0]['id'])
self.assertEqual(vnfc_ids[1], vnfc_info[1]['id'])
self.assertEqual(dummy_key_value,
vnfc_info[0]['vnfcConfigurableProperties'])
self.assertEqual(dummy_key_value,
vnfc_info[1]['vnfcConfigurableProperties'])
# 7. Scale out operation
scaleout_req = paramgen.scaleout_vnf_max()
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 8. Terminate a VNF instance
terminate_req = paramgen.terminate_vnf_max()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 9. Delete a VNF instance
resp, body = self.exec_lcm_operation(self.delete_vnf_instance, inst_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
# 10. Delete subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
def test_update_heal_lcm(self):
"""Test the sequence of update VNF and heal VNF
* About attributes:
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
* About LCM operations:
This test includes the following operations.
- 0. Pre-setting
- 1. Create subscription
- 2. Create VNF instance
- 3. Instantiate VNF
- 4. Show VNF instance
- 5. Update VNF
- 6. Show VNF instance
- 7. Heal VNF(all with omit all parameter)
- 8. Heal VNF(all with all=False parameter)
- 9. Heal VNF(all with all=True parameter)
- 10. Terminate VNF
- 11. Delete VNF instance
- 12. Delete subscription
"""
# 0. Pre-setting
# Create a new network and subnet to check the IP allocation of
# IPv4 and IPv6
ft_net0_name = 'ft-net0'
ft_net0_subs = {
'ft-ipv4-subnet0': {
'range': '100.100.100.0/24',
'ip_version': 4
},
'ft-ipv6-subnet0': {
'range': '1111:2222:3333::/64',
'ip_version': 6
}
}
ft_net0_id = self.create_network(ft_net0_name)
self.addCleanup(self.delete_network, ft_net0_id)
for sub_name, val in ft_net0_subs.items():
# subnet is automatically deleted with network deletion
self.create_subnet(
ft_net0_id, sub_name, val['range'], val['ip_version'])
net_ids = self.get_network_ids(
['net0', 'net1', 'net_mgmt', 'ft-net0'])
subnet_ids = self.get_subnet_ids(
['subnet0', 'subnet1', 'ft-ipv4-subnet0', 'ft-ipv6-subnet0'])
port_names = ['VDU2_CP1-1', 'VDU2_CP1-2']
port_ids = {}
for port_name in port_names:
port_id = self.create_port(net_ids['net0'], port_name)
port_ids[port_name] = port_id
self.addCleanup(self.delete_port, port_id)
# 1. Create subscription
callback_url = os.path.join(self.get_notify_callback_url(),
self._testMethodName)
callback_uri = ('http://localhost:'
f'{self.get_server_port()}'
f'{callback_url}')
sub_req = paramgen.sub_create_max(callback_uri)
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# 2. Create VNF instance
# ETSI NFV SOL003 v3.3.1 5.5.2.2 VnfInstance
# NOTE: extensions and vnfConfigurableProperties are omitted
# because they are commented out in etsi_nfv_sol001.
expected_inst_attrs = [
'id',
'vnfInstanceName',
'vnfInstanceDescription',
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
# 'vimConnectionInfo', # omitted
'instantiationState',
# 'instantiatedVnfInfo', # omitted
'metadata',
# 'extensions', # omitted
'_links'
]
create_req = paramgen.create_vnf_max(self.max_vnfd_id)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
self.check_resp_body(body, expected_inst_attrs)
inst_id = body['id']
# check usageState of VNF Package
self.check_package_usage(self.max_pkg, 'IN_USE')
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 3. Instantiate VNF instance
instantiate_req = paramgen.instantiate_vnf_max(
net_ids, subnet_ids, port_ids, self.auth_url)
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 4. Show VNF instance
# check creation of Heat-stack
stack_name = f'vnf-{inst_id}'
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("CREATE_COMPLETE", stack_status)
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo'
]
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.INSTANTIATED,
body['instantiationState'])
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# 5. Update VNF
# check attribute value before update VNF
# check usageState of max pattern VNF Package
self.check_package_usage(self.max_pkg, 'IN_USE')
# check usageState of update VNF Package
self.check_package_usage(self.upd_pkg, 'NOT_IN_USE')
# check vnfd id
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(self.max_vnfd_id, body['vnfdId'])
# check vnfc info
vnfc_info = body['instantiatedVnfInfo']['vnfcInfo']
self.assertGreater(len(vnfc_info), 1)
vnfc_ids = [vnfc['id'] for vnfc in vnfc_info]
for vnfc in vnfc_info:
self.assertIn('id', vnfc)
self.assertIn('vduId', vnfc)
self.assertIsNotNone(vnfc.get('vnfcState'))
self.assertIsNone(vnfc.get('vnfcConfigurableProperties'))
update_req = paramgen.update_vnf_max(self.upd_vnfd_id, vnfc_ids)
resp, body = self.update_vnf_instance(inst_id, update_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 6. Show VNF instance
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo'
]
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# check usageState of max pattern VNF Package
self.check_package_usage(self.max_pkg, 'NOT_IN_USE')
# check usageState of update VNF Package
self.check_package_usage(self.upd_pkg, 'IN_USE')
# check the specified attribute after update VNF
self.assertEqual(self.upd_vnfd_id, body['vnfdId'])
self.assertEqual('new name', body['vnfInstanceName'])
self.assertEqual('new description', body['vnfInstanceDescription'])
dummy_key_value = {'dummy-key': 'dummy-value'}
self.assertEqual(dummy_key_value, body['metadata'])
self.assertEqual(dummy_key_value, body['extensions'])
self.assertEqual(dummy_key_value, body['vnfConfigurableProperties'])
vim_connection_info = {
"vim2": {
"vimId": "ac2d2ece-5e49-4b15-b92d-b681e9c096d8",
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {
"endpoint": "http://127.0.0.1/identity/v3"
},
"accessInfo": {
"username": "dummy_user",
"region": "RegionOne",
"project": "dummy_project",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {
"dummy-key": "dummy-val"
}
}
}
self.assertEqual(vim_connection_info['vim2'],
body['vimConnectionInfo']['vim2'])
# check vnfc info
vnfc_info = body['instantiatedVnfInfo']['vnfcInfo']
self.assertEqual(vnfc_ids[0], vnfc_info[0]['id'])
self.assertEqual(vnfc_ids[1], vnfc_info[1]['id'])
self.assertEqual(dummy_key_value,
vnfc_info[0]['vnfcConfigurableProperties'])
self.assertEqual(dummy_key_value,
vnfc_info[1]['vnfcConfigurableProperties'])
# 7. Heal VNF(all with omit all parameter)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2'])]
vdu1_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
heal_req = paramgen.heal_vnf_all_max_with_parameter()
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("UPDATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2'])]
vdu1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
self.assertEqual("CREATE_COMPLETE",
vdu1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu2_stack_after_heal['resource_status'])
self.assertNotEqual(vdu1_stack_before_heal['physical_resource_id'],
vdu1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(vdu2_stack_before_heal['physical_resource_id'],
vdu2_stack_after_heal['physical_resource_id'])
# 8. Heal VNF(all with all=False parameter)
vdu1_stack_before_heal = vdu1_stack_after_heal
vdu2_stack_before_heal = vdu2_stack_after_heal
heal_req = paramgen.heal_vnf_all_max_with_parameter(False)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("UPDATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2'])]
vdu1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
self.assertEqual("CREATE_COMPLETE",
vdu1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu2_stack_after_heal['resource_status'])
self.assertNotEqual(vdu1_stack_before_heal['physical_resource_id'],
vdu1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(vdu2_stack_before_heal['physical_resource_id'],
vdu2_stack_after_heal['physical_resource_id'])
# 9. Heal VNF(all with all=True parameter)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2', 'VDU1-VirtualStorage',
'VDU2-VirtualStorage', 'internalVL3'])]
vdu1_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
storage1_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1-VirtualStorage')][0]
storage2_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2-VirtualStorage')][0]
network_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'internalVL3')][0]
stack_id_before_heal = self.heat_client.get_stack_id(stack_name)
heal_req = paramgen.heal_vnf_all_max_with_parameter(True)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_id_after_heal = self.heat_client.get_stack_id(stack_name)
self.assertNotEqual(stack_id_before_heal, stack_id_after_heal)
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("CREATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2', 'VDU1-VirtualStorage',
'VDU2-VirtualStorage', 'internalVL3'])]
vdu1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
storage1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1-VirtualStorage')][0]
storage2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2-VirtualStorage')][0]
network_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'internalVL3')][0]
self.assertEqual("CREATE_COMPLETE",
vdu1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu2_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
storage1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
storage2_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
network_stack_after_heal['resource_status'])
self.assertNotEqual(vdu1_stack_before_heal['physical_resource_id'],
vdu1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(vdu2_stack_before_heal['physical_resource_id'],
vdu2_stack_after_heal['physical_resource_id'])
self.assertNotEqual(storage1_stack_before_heal['physical_resource_id'],
storage1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(storage2_stack_before_heal['physical_resource_id'],
storage2_stack_after_heal['physical_resource_id'])
self.assertNotEqual(network_stack_before_heal['physical_resource_id'],
network_stack_after_heal['physical_resource_id'])
# 10. Terminate VNF
terminate_req = paramgen.terminate_vnf_max()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 11. Delete VNF instance
resp, body = self.exec_lcm_operation(self.delete_vnf_instance, inst_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
# 12. Delete subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
def test_scale_heal_lcm(self):
"""Test the sequence of scale out/in and heal VNF

View File

@ -0,0 +1,702 @@
# Copyright (C) 2021 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
import os
from tacker.objects import fields
from tacker.tests.functional.sol_v2_common import paramgen
from tacker.tests.functional.sol_v2_common import test_vnflcm_basic_common
from tacker.tests import utils
@ddt.ddt
class VnfLcmUpdateTest(test_vnflcm_basic_common.CommonVnfLcmTest):
@classmethod
def setUpClass(cls):
super(VnfLcmUpdateTest, cls).setUpClass()
image_path = utils.test_etc_sample("etsi/nfv/common/Files/images",
"cirros-0.5.2-x86_64-disk.img")
# for basic lcms tests max pattern
basic_lcms_max_path = utils.test_sample("functional/sol_v2_common",
"basic_lcms_max")
cls.max_pkg, cls.max_vnfd_id = cls.create_vnf_package(
basic_lcms_max_path, image_path=image_path)
# for update vnf test
update_vnf_path = utils.test_sample("functional/sol_v2_common",
"update_vnf")
# no image contained
cls.upd_pkg, cls.upd_vnfd_id = cls.create_vnf_package(update_vnf_path)
@classmethod
def tearDownClass(cls):
super(VnfLcmUpdateTest, cls).tearDownClass()
cls.delete_vnf_package(cls.max_pkg)
cls.delete_vnf_package(cls.upd_pkg)
def setUp(self):
super().setUp()
def test_update_scale_lcm(self):
"""Test the sequence of update VNF and scale out
* About attributes:
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
* About LCM operations:
This test includes the following operations.
- 0. Pre-setting
- 1. Create subscription
- 2. Create VNF instance
- 3. Instantiate VNF
- 4. Show VNF instance
- 5. Update VNF
- 6. Show VNF instance
- 7. Scale out operation
- 8. Terminate VNF
- 9. Delete VNF instance
- 10. Delete subscription
"""
# 0. Pre-setting
# Create a new network and subnet to check the IP allocation of
# IPv4 and IPv6
ft_net0_name = 'ft-net0'
ft_net0_subs = {
'ft-ipv4-subnet0': {
'range': '100.100.100.0/24',
'ip_version': 4
},
'ft-ipv6-subnet0': {
'range': '1111:2222:3333::/64',
'ip_version': 6
}
}
ft_net0_id = self.create_network(ft_net0_name)
self.addCleanup(self.delete_network, ft_net0_id)
for sub_name, val in ft_net0_subs.items():
# subnet is automatically deleted with network deletion
self.create_subnet(
ft_net0_id, sub_name, val['range'], val['ip_version'])
net_ids = self.get_network_ids(
['net0', 'net1', 'net_mgmt', 'ft-net0'])
subnet_ids = self.get_subnet_ids(
['subnet0', 'subnet1', 'ft-ipv4-subnet0', 'ft-ipv6-subnet0'])
port_names = ['VDU2_CP1-1', 'VDU2_CP1-2']
port_ids = {}
for port_name in port_names:
port_id = self.create_port(net_ids['net0'], port_name)
port_ids[port_name] = port_id
self.addCleanup(self.delete_port, port_id)
# 1. Create subscription
callback_url = os.path.join(self.get_notify_callback_url(),
self._testMethodName)
callback_uri = ('http://localhost:'
f'{self.get_server_port()}'
f'{callback_url}')
sub_req = paramgen.sub_create_max(callback_uri)
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# 2. Create VNF instance
# ETSI NFV SOL003 v3.3.1 5.5.2.2 VnfInstance
# NOTE: extensions and vnfConfigurableProperties are omitted
# because they are commented out in etsi_nfv_sol001.
expected_inst_attrs = [
'id',
'vnfInstanceName',
'vnfInstanceDescription',
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
# 'vimConnectionInfo', # omitted
'instantiationState',
# 'instantiatedVnfInfo', # omitted
'metadata',
# 'extensions', # omitted
'_links'
]
create_req = paramgen.create_vnf_max(self.max_vnfd_id)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
self.check_resp_body(body, expected_inst_attrs)
inst_id = body['id']
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 3. Instantiate VNF instance
instantiate_req = paramgen.instantiate_vnf_max(
net_ids, subnet_ids, port_ids, self.auth_url)
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 4. Show VNF instance
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo'
]
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.INSTANTIATED,
body['instantiationState'])
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# 5. Update VNF
# check attribute value before update VNF
# check usageState of max pattern VNF Package
self.check_package_usage(self.max_pkg, 'IN_USE')
# check usageState of update VNF Package
self.check_package_usage(self.upd_pkg, 'NOT_IN_USE')
# check vnfd id
self.assertEqual(self.max_vnfd_id, body['vnfdId'])
# check vnfc info
vnfc_info = body['instantiatedVnfInfo']['vnfcInfo']
self.assertGreater(len(vnfc_info), 1)
vnfc_ids = [vnfc['id'] for vnfc in vnfc_info]
for vnfc in vnfc_info:
self.assertIn('id', vnfc)
self.assertIn('vduId', vnfc)
self.assertIsNotNone(vnfc.get('vnfcState'))
self.assertIsNone(vnfc.get('vnfcConfigurableProperties'))
update_req = paramgen.update_vnf_max(self.upd_vnfd_id, vnfc_ids)
resp, body = self.update_vnf_instance(inst_id, update_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 6. Show VNF instance
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo'
]
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# check usageState of max pattern VNF Package
self.check_package_usage(self.max_pkg, 'NOT_IN_USE')
# check usageState of update pattern VNF Package
self.check_package_usage(self.upd_pkg, 'IN_USE')
# check the specified attribute after update VNF
self.assertEqual(self.upd_vnfd_id, body['vnfdId'])
self.assertEqual('new name', body['vnfInstanceName'])
self.assertEqual('new description', body['vnfInstanceDescription'])
dummy_key_value = {'dummy-key': 'dummy-value'}
self.assertEqual(dummy_key_value, body['metadata'])
self.assertEqual(dummy_key_value, body['extensions'])
self.assertEqual(dummy_key_value, body['vnfConfigurableProperties'])
vim_connection_info = {
"vim2": {
"vimId": "ac2d2ece-5e49-4b15-b92d-b681e9c096d8",
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {
"endpoint": "http://127.0.0.1/identity/v3"
},
"accessInfo": {
"username": "dummy_user",
"region": "RegionOne",
"project": "dummy_project",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {
"dummy-key": "dummy-val"
}
}
}
self.assertEqual(vim_connection_info['vim2'],
body['vimConnectionInfo']['vim2'])
# check vnfc info
vnfc_info = body['instantiatedVnfInfo']['vnfcInfo']
self.assertEqual(vnfc_ids[0], vnfc_info[0]['id'])
self.assertEqual(vnfc_ids[1], vnfc_info[1]['id'])
self.assertEqual(dummy_key_value,
vnfc_info[0]['vnfcConfigurableProperties'])
self.assertEqual(dummy_key_value,
vnfc_info[1]['vnfcConfigurableProperties'])
# 7. Scale out operation
scaleout_req = paramgen.scaleout_vnf_max()
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 8. Terminate a VNF instance
terminate_req = paramgen.terminate_vnf_max()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 9. Delete a VNF instance
resp, body = self.exec_lcm_operation(self.delete_vnf_instance, inst_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
# 10. Delete subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
def test_update_heal_lcm(self):
"""Test the sequence of update VNF and heal VNF
* About attributes:
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
* About LCM operations:
This test includes the following operations.
- 0. Pre-setting
- 1. Create subscription
- 2. Create VNF instance
- 3. Instantiate VNF
- 4. Show VNF instance
- 5. Update VNF
- 6. Show VNF instance
- 7. Heal VNF(all with omit all parameter)
- 8. Heal VNF(all with all=False parameter)
- 9. Heal VNF(all with all=True parameter)
- 10. Terminate VNF
- 11. Delete VNF instance
- 12. Delete subscription
"""
# 0. Pre-setting
# Create a new network and subnet to check the IP allocation of
# IPv4 and IPv6
ft_net0_name = 'ft-net0'
ft_net0_subs = {
'ft-ipv4-subnet0': {
'range': '100.100.100.0/24',
'ip_version': 4
},
'ft-ipv6-subnet0': {
'range': '1111:2222:3333::/64',
'ip_version': 6
}
}
ft_net0_id = self.create_network(ft_net0_name)
self.addCleanup(self.delete_network, ft_net0_id)
for sub_name, val in ft_net0_subs.items():
# subnet is automatically deleted with network deletion
self.create_subnet(
ft_net0_id, sub_name, val['range'], val['ip_version'])
net_ids = self.get_network_ids(
['net0', 'net1', 'net_mgmt', 'ft-net0'])
subnet_ids = self.get_subnet_ids(
['subnet0', 'subnet1', 'ft-ipv4-subnet0', 'ft-ipv6-subnet0'])
port_names = ['VDU2_CP1-1', 'VDU2_CP1-2']
port_ids = {}
for port_name in port_names:
port_id = self.create_port(net_ids['net0'], port_name)
port_ids[port_name] = port_id
self.addCleanup(self.delete_port, port_id)
# 1. Create subscription
callback_url = os.path.join(self.get_notify_callback_url(),
self._testMethodName)
callback_uri = ('http://localhost:'
f'{self.get_server_port()}'
f'{callback_url}')
sub_req = paramgen.sub_create_max(callback_uri)
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# 2. Create VNF instance
# ETSI NFV SOL003 v3.3.1 5.5.2.2 VnfInstance
# NOTE: extensions and vnfConfigurableProperties are omitted
# because they are commented out in etsi_nfv_sol001.
expected_inst_attrs = [
'id',
'vnfInstanceName',
'vnfInstanceDescription',
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
# 'vimConnectionInfo', # omitted
'instantiationState',
# 'instantiatedVnfInfo', # omitted
'metadata',
# 'extensions', # omitted
'_links'
]
create_req = paramgen.create_vnf_max(self.max_vnfd_id)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
self.check_resp_body(body, expected_inst_attrs)
inst_id = body['id']
# check usageState of VNF Package
self.check_package_usage(self.max_pkg, 'IN_USE')
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 3. Instantiate VNF instance
instantiate_req = paramgen.instantiate_vnf_max(
net_ids, subnet_ids, port_ids, self.auth_url)
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 4. Show VNF instance
# check creation of Heat-stack
stack_name = f'vnf-{inst_id}'
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("CREATE_COMPLETE", stack_status)
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo'
]
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.INSTANTIATED,
body['instantiationState'])
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# 5. Update VNF
# check attribute value before update VNF
# check usageState of max pattern VNF Package
self.check_package_usage(self.max_pkg, 'IN_USE')
# check usageState of update VNF Package
self.check_package_usage(self.upd_pkg, 'NOT_IN_USE')
# check vnfd id
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(self.max_vnfd_id, body['vnfdId'])
# check vnfc info
vnfc_info = body['instantiatedVnfInfo']['vnfcInfo']
self.assertGreater(len(vnfc_info), 1)
vnfc_ids = [vnfc['id'] for vnfc in vnfc_info]
for vnfc in vnfc_info:
self.assertIn('id', vnfc)
self.assertIn('vduId', vnfc)
self.assertIsNotNone(vnfc.get('vnfcState'))
self.assertIsNone(vnfc.get('vnfcConfigurableProperties'))
update_req = paramgen.update_vnf_max(self.upd_vnfd_id, vnfc_ids)
resp, body = self.update_vnf_instance(inst_id, update_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 6. Show VNF instance
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo'
]
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# check usageState of max pattern VNF Package
self.check_package_usage(self.max_pkg, 'NOT_IN_USE')
# check usageState of update VNF Package
self.check_package_usage(self.upd_pkg, 'IN_USE')
# check the specified attribute after update VNF
self.assertEqual(self.upd_vnfd_id, body['vnfdId'])
self.assertEqual('new name', body['vnfInstanceName'])
self.assertEqual('new description', body['vnfInstanceDescription'])
dummy_key_value = {'dummy-key': 'dummy-value'}
self.assertEqual(dummy_key_value, body['metadata'])
self.assertEqual(dummy_key_value, body['extensions'])
self.assertEqual(dummy_key_value, body['vnfConfigurableProperties'])
vim_connection_info = {
"vim2": {
"vimId": "ac2d2ece-5e49-4b15-b92d-b681e9c096d8",
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {
"endpoint": "http://127.0.0.1/identity/v3"
},
"accessInfo": {
"username": "dummy_user",
"region": "RegionOne",
"project": "dummy_project",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {
"dummy-key": "dummy-val"
}
}
}
self.assertEqual(vim_connection_info['vim2'],
body['vimConnectionInfo']['vim2'])
# check vnfc info
vnfc_info = body['instantiatedVnfInfo']['vnfcInfo']
self.assertEqual(vnfc_ids[0], vnfc_info[0]['id'])
self.assertEqual(vnfc_ids[1], vnfc_info[1]['id'])
self.assertEqual(dummy_key_value,
vnfc_info[0]['vnfcConfigurableProperties'])
self.assertEqual(dummy_key_value,
vnfc_info[1]['vnfcConfigurableProperties'])
# 7. Heal VNF(all with omit all parameter)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2'])]
vdu1_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
heal_req = paramgen.heal_vnf_all_max_with_parameter()
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("UPDATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2'])]
vdu1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
self.assertEqual("CREATE_COMPLETE",
vdu1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu2_stack_after_heal['resource_status'])
self.assertNotEqual(vdu1_stack_before_heal['physical_resource_id'],
vdu1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(vdu2_stack_before_heal['physical_resource_id'],
vdu2_stack_after_heal['physical_resource_id'])
# 8. Heal VNF(all with all=False parameter)
vdu1_stack_before_heal = vdu1_stack_after_heal
vdu2_stack_before_heal = vdu2_stack_after_heal
heal_req = paramgen.heal_vnf_all_max_with_parameter(False)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("UPDATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2'])]
vdu1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
self.assertEqual("CREATE_COMPLETE",
vdu1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu2_stack_after_heal['resource_status'])
self.assertNotEqual(vdu1_stack_before_heal['physical_resource_id'],
vdu1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(vdu2_stack_before_heal['physical_resource_id'],
vdu2_stack_after_heal['physical_resource_id'])
# 9. Heal VNF(all with all=True parameter)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2', 'VDU1-VirtualStorage',
'VDU2-VirtualStorage', 'internalVL3'])]
vdu1_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
storage1_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1-VirtualStorage')][0]
storage2_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2-VirtualStorage')][0]
network_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'internalVL3')][0]
stack_id_before_heal = self.heat_client.get_stack_id(stack_name)
heal_req = paramgen.heal_vnf_all_max_with_parameter(True)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_id_after_heal = self.heat_client.get_stack_id(stack_name)
self.assertNotEqual(stack_id_before_heal, stack_id_after_heal)
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("CREATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2', 'VDU1-VirtualStorage',
'VDU2-VirtualStorage', 'internalVL3'])]
vdu1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
storage1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1-VirtualStorage')][0]
storage2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2-VirtualStorage')][0]
network_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'internalVL3')][0]
self.assertEqual("CREATE_COMPLETE",
vdu1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu2_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
storage1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
storage2_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
network_stack_after_heal['resource_status'])
self.assertNotEqual(vdu1_stack_before_heal['physical_resource_id'],
vdu1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(vdu2_stack_before_heal['physical_resource_id'],
vdu2_stack_after_heal['physical_resource_id'])
self.assertNotEqual(storage1_stack_before_heal['physical_resource_id'],
storage1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(storage2_stack_before_heal['physical_resource_id'],
storage2_stack_after_heal['physical_resource_id'])
self.assertNotEqual(network_stack_before_heal['physical_resource_id'],
network_stack_after_heal['physical_resource_id'])
# 10. Terminate VNF
terminate_req = paramgen.terminate_vnf_max()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 11. Delete VNF instance
resp, body = self.exec_lcm_operation(self.delete_vnf_instance, inst_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
# 12. Delete subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)

View File

@ -71,26 +71,11 @@ class IndividualVnfcMgmtTest(test_vnflcm_basic_common.CommonVnfLcmTest):
subprocess.run(["sudo", "cp", os.path.join(pkg_path_2, source_path),
os.path.join(csar_dir, dest_path)])
# for change_vnfpkg network/flavor change test
pkg_path_3 = utils.test_sample("functional/sol_v2_common",
"userdata_standard_change_vnfpkg_nw")
cls.new_nw_pkg, cls.new_nw_vnfd_id = cls.create_vnf_package(
pkg_path_3, image_path=image_path, userdata_path=userdata_path)
# for attach non-boot volume to VDU test
pkg_path_4 = utils.test_sample("functional/sol_v2_common",
"userdata_standard_with_non_boot_volume")
cls.non_boot_volume_pkg, cls.non_boot_volume_vnfd_id = (
cls.create_vnf_package(pkg_path_4, image_path=image_path,
userdata_path=userdata_path))
@classmethod
def tearDownClass(cls):
super(IndividualVnfcMgmtTest, cls).tearDownClass()
cls.delete_vnf_package(cls.standard_pkg)
cls.delete_vnf_package(cls.new_pkg)
cls.delete_vnf_package(cls.new_nw_pkg)
cls.delete_vnf_package(cls.non_boot_volume_pkg)
def setUp(self):
super().setUp()
@ -118,10 +103,6 @@ class IndividualVnfcMgmtTest(test_vnflcm_basic_common.CommonVnfLcmTest):
if vnfc_info['vnfcResourceInfoId'] == vnfc['id']:
return vnfc_info['id']
def _get_vnfc_cps(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
return {cp_info['cpdId'] for cp_info in vnfc['vnfcCpInfo']}
def _get_vnfc_cp_net_id(self, inst, vdu, index, cp):
# this is for external CPs
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
@ -136,20 +117,6 @@ class IndividualVnfcMgmtTest(test_vnflcm_basic_common.CommonVnfLcmTest):
# must be found
return ext_vl['resourceHandle']['resourceId']
def _get_vnfc_cp_net_name(self, inst, vdu, index, cp):
# this is for internal CPs
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for cp_info in vnfc['vnfcCpInfo']:
if cp_info['cpdId'] == cp:
# must be found
link_port_id = cp_info['vnfLinkPortId']
break
for vl in inst['instantiatedVnfInfo']['vnfVirtualLinkResourceInfo']:
for port in vl['vnfLinkPorts']:
if port['id'] == link_port_id:
# must be found
return vl['vnfVirtualLinkDescId']
def _get_vnfc_image(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for key, value in vnfc['metadata'].items():
@ -157,16 +124,6 @@ class IndividualVnfcMgmtTest(test_vnflcm_basic_common.CommonVnfLcmTest):
# must be found
return value
def _get_vnfc_flavor(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
# must exist
return vnfc['metadata']['flavor']
def _get_vnfc_storage_ids(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
storage_res_ids = vnfc.get('storageResourceIds', [])
return sorted(storage_res_ids)
def _get_server_name(self, inst, vdu, index):
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
if (vnfc['vduId'] == vdu and
@ -695,328 +652,3 @@ class IndividualVnfcMgmtTest(test_vnflcm_basic_common.CommonVnfLcmTest):
# Delete VNF instance
self.exec_lcm_operation(self.delete_vnf_instance, inst_id)
def test_change_vnfpkg_nw(self):
"""Test change_vnfpkg with additional functions
* Note:
This test focuses change_vnfpkg with the following changes.
- adding external CP
- change internal network
- change flavor
TODO: add anoter patterns (ex. change extMgdVLs)
* About LCM operations:
This test includes the following operations.
- Create VNF instance
- 1. Instantiate VNF instance
- Show VNF instance / check
- 2. Change_vnfpkg operation
- Show VNF instance / check
- Terminate VNF instance
- Delete VNF instance
"""
net_ids = self.get_network_ids(['net0', 'net1', 'net_mgmt'])
subnet_ids = self.get_subnet_ids(['subnet0', 'subnet1'])
# Create VNF instance
create_req = paramgen.sample3_create(self.standard_vnfd_id)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
inst_id = body['id']
# 1. Instantiate VNF instance
instantiate_req = paramgen.sample3_instantiate(
net_ids, subnet_ids, self.auth_url)
instantiate_req['instantiationLevelId'] = "instantiation_level_2"
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Show VNF instance
resp, inst_1 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check number of VDUs and indexes
self.assertEqual({0, 1}, self._get_vdu_indexes(inst_1, 'VDU1'))
self.assertEqual({0}, self._get_vdu_indexes(inst_1, 'VDU2'))
# 2. Change_vnfpkg operation
change_vnfpkg_req = paramgen.sample5_change_vnfpkg(self.new_nw_vnfd_id,
net_ids, subnet_ids)
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Show VNF instance
resp, inst_2 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check vnfdId is changed
self.assertEqual(self.new_nw_vnfd_id, inst_2['vnfdId'])
# check images are changed
self.assertNotEqual(self._get_vnfc_image(inst_1, 'VDU1', 0),
self._get_vnfc_image(inst_2, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_image(inst_1, 'VDU1', 1),
self._get_vnfc_image(inst_2, 'VDU1', 1))
self.assertNotEqual(self._get_vnfc_image(inst_1, 'VDU2', 0),
self._get_vnfc_image(inst_2, 'VDU2', 0))
# check flavor is changed (VDU2 only)
self.assertNotEqual(self._get_vnfc_flavor(inst_1, 'VDU2', 0),
self._get_vnfc_flavor(inst_2, 'VDU2', 0))
# check external CPs; VDU1_CP4 and VDU2_CP4 are added
self.assertFalse('VDU1_CP4' in self._get_vnfc_cps(inst_1, 'VDU1', 0))
self.assertFalse('VDU1_CP4' in self._get_vnfc_cps(inst_1, 'VDU1', 1))
self.assertFalse('VDU2_CP4' in self._get_vnfc_cps(inst_1, 'VDU2', 0))
self.assertTrue('VDU1_CP4' in self._get_vnfc_cps(inst_2, 'VDU1', 0))
self.assertTrue('VDU1_CP4' in self._get_vnfc_cps(inst_2, 'VDU1', 1))
self.assertTrue('VDU2_CP4' in self._get_vnfc_cps(inst_2, 'VDU2', 0))
self.assertEqual(net_ids['net0'],
self._get_vnfc_cp_net_id(inst_2, 'VDU1', 0, 'VDU1_CP4'))
self.assertEqual(net_ids['net0'],
self._get_vnfc_cp_net_id(inst_2, 'VDU1', 1, 'VDU1_CP4'))
self.assertEqual(net_ids['net0'],
self._get_vnfc_cp_net_id(inst_2, 'VDU2', 0, 'VDU2_CP4'))
# check internal CPs; VDU1_CP3 and VDU2_CP3 are changed
self.assertEqual("internalVL2",
self._get_vnfc_cp_net_name(inst_1, 'VDU1', 0, 'VDU1_CP3'))
self.assertEqual("internalVL2",
self._get_vnfc_cp_net_name(inst_1, 'VDU1', 1, 'VDU1_CP3'))
self.assertEqual("internalVL2",
self._get_vnfc_cp_net_name(inst_1, 'VDU2', 0, 'VDU2_CP3'))
self.assertEqual("internalVL3",
self._get_vnfc_cp_net_name(inst_2, 'VDU1', 0, 'VDU1_CP3'))
self.assertEqual("internalVL3",
self._get_vnfc_cp_net_name(inst_2, 'VDU1', 1, 'VDU1_CP3'))
self.assertEqual("internalVL3",
self._get_vnfc_cp_net_name(inst_2, 'VDU2', 0, 'VDU2_CP3'))
# Terminate VNF instance
terminate_req = paramgen.sample5_terminate()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Delete VNF instance
self.exec_lcm_operation(self.delete_vnf_instance, inst_id)
def test_change_vnfpkg_nw_rollback(self):
"""Test rollback of change_vnfpkg with additional functions
* Note:
This test focuses rollback of change_vnfpkg with the following
changes.
- adding external CP
- change internal network
- change flavor
TODO: add anoter patterns (ex. change extMgdVLs)
* About LCM operations:
This test includes the following operations.
- Create VNF instance
- 1. Instantiate VNF instance
- Show VNF instance / check
- 2. Change_vnfpkg operation => FAILED_TEMP
- Rollback
- Show VNF instance / check
- Terminate VNF instance
- Delete VNF instance
"""
net_ids = self.get_network_ids(['net0', 'net1', 'net_mgmt'])
subnet_ids = self.get_subnet_ids(['subnet0', 'subnet1'])
# Create VNF instance
create_req = paramgen.sample3_create(self.standard_vnfd_id)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
inst_id = body['id']
# 1. Instantiate VNF instance
instantiate_req = paramgen.sample3_instantiate(
net_ids, subnet_ids, self.auth_url)
instantiate_req['instantiationLevelId'] = "instantiation_level_2"
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Show VNF instance
resp, inst_1 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check number of VDUs and indexes
self.assertEqual({0, 1}, self._get_vdu_indexes(inst_1, 'VDU1'))
self.assertEqual({0}, self._get_vdu_indexes(inst_1, 'VDU2'))
# 2. Change_vnfpkg operation
self.put_fail_file('change_vnfpkg')
change_vnfpkg_req = paramgen.sample5_change_vnfpkg(self.new_nw_vnfd_id,
net_ids, subnet_ids)
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_failed_temp(lcmocc_id)
self.rm_fail_file('change_vnfpkg')
# Rollback
resp, body = self.rollback_lcmocc(lcmocc_id)
self.assertEqual(202, resp.status_code)
self.wait_lcmocc_rolled_back(lcmocc_id)
# Show VNF instance
resp, inst_2 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check vnfdId is not changed
self.assertEqual(self.standard_vnfd_id, inst_2['vnfdId'])
# check images are not changed
self.assertEqual(self._get_vnfc_image(inst_1, 'VDU1', 0),
self._get_vnfc_image(inst_2, 'VDU1', 0))
self.assertEqual(self._get_vnfc_image(inst_1, 'VDU1', 1),
self._get_vnfc_image(inst_2, 'VDU1', 1))
self.assertEqual(self._get_vnfc_image(inst_1, 'VDU2', 0),
self._get_vnfc_image(inst_2, 'VDU2', 0))
# check flavor is not changed (VDU2 only)
self.assertEqual(self._get_vnfc_flavor(inst_1, 'VDU2', 0),
self._get_vnfc_flavor(inst_2, 'VDU2', 0))
# check external CPs; VDU1_CP4 and VDU2_CP4 are not added
self.assertFalse('VDU1_CP4' in self._get_vnfc_cps(inst_2, 'VDU1', 0))
self.assertFalse('VDU1_CP4' in self._get_vnfc_cps(inst_2, 'VDU1', 1))
self.assertFalse('VDU2_CP4' in self._get_vnfc_cps(inst_2, 'VDU2', 0))
# check internal CPs; VDU1_CP3 and VDU2_CP3 are not changed
self.assertEqual(
self._get_vnfc_cp_net_name(inst_1, 'VDU1', 0, 'VDU1_CP3'),
self._get_vnfc_cp_net_name(inst_2, 'VDU1', 0, 'VDU1_CP3'))
self.assertEqual(
self._get_vnfc_cp_net_name(inst_1, 'VDU1', 1, 'VDU1_CP3'),
self._get_vnfc_cp_net_name(inst_2, 'VDU1', 1, 'VDU1_CP3'))
self.assertEqual(
self._get_vnfc_cp_net_name(inst_1, 'VDU2', 0, 'VDU2_CP3'),
self._get_vnfc_cp_net_name(inst_2, 'VDU2', 0, 'VDU2_CP3'))
# Terminate VNF instance
terminate_req = paramgen.sample3_terminate()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Delete VNF instance
self.exec_lcm_operation(self.delete_vnf_instance, inst_id)
def test_instantiate_attach_non_boot_volume(self):
"""Test Instantiate with non-boot volume attached to VDU
* Note:
This test focuses on the non-boot volume attached by
OS::Cinder::VolumeAttachment in HOT being registered to
storageResourceIds.
* About LCM operations:
This test includes the following operations.
- Create VNF instance
- 1. Instantiate VNF instance
- Show VNF instance / check
- 2. Heal operation("all"=True)
- Show VNF instance / check
- 3. Heal operation("all" is not specified)
- Show VNF instance / check
- Terminate VNF instance
- Delete VNF instance
"""
net_ids = self.get_network_ids(['net0', 'net1', 'net_mgmt'])
subnet_ids = self.get_subnet_ids(['subnet0', 'subnet1'])
# Create VNF instance
create_req = paramgen.sample7_create(self.non_boot_volume_vnfd_id)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
inst_id = body['id']
# 1. Instantiate VNF instance
instantiate_req = paramgen.sample7_instantiate(
net_ids, subnet_ids, self.auth_url)
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Show VNF instance
resp, inst_1 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check number of VDUs and indexes
self.assertEqual({0}, self._get_vdu_indexes(inst_1, 'VDU1'))
# check storageResourceIds of attached non-boot volume
self.assertNotEqual([], self._get_vnfc_storage_ids(inst_1, 'VDU1', 0))
# 2. Heal operation("all"=True)
# VDU1-0 to heal
heal_req = paramgen.sample7_heal('a-001')
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Show VNF instance
resp, inst_2 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check id of VDU1-0 is changed
self.assertNotEqual(self._get_vnfc_id(inst_1, 'VDU1', 0),
self._get_vnfc_id(inst_2, 'VDU1', 0))
# check storageResourceIds of VDU1-0 is changed
self.assertNotEqual([], self._get_vnfc_storage_ids(inst_2, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_storage_ids(inst_1, 'VDU1', 0),
self._get_vnfc_storage_ids(inst_2, 'VDU1', 0))
# 3. Heal operation("all" is not specified)
# VDU1-0 to heal
heal_req['vnfcInstanceId'] = ['a-001']
del heal_req['additionalParams']['all']
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Show VNF instance
resp, inst_3 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check id of VDU1-0 is changed
self.assertNotEqual(self._get_vnfc_id(inst_2, 'VDU1', 0),
self._get_vnfc_id(inst_3, 'VDU1', 0))
# check storageResourceIds of VDU1-0 is not changed
self.assertEqual(self._get_vnfc_storage_ids(inst_2, 'VDU1', 0),
self._get_vnfc_storage_ids(inst_3, 'VDU1', 0))
# Terminate VNF instance
terminate_req = paramgen.sample7_terminate()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Delete VNF instance
self.exec_lcm_operation(self.delete_vnf_instance, inst_id)

View File

@ -0,0 +1,828 @@
# Copyright (C) 2023 Fujitsu
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import string
from tacker.objects import fields
from tacker.tests.functional.sol_v2_common import base_v2
from tacker.tests.functional.sol_v2_common import paramgen
from tacker.tests import utils
SUPPORT_STRING_FOR_VNFD_ID = f"{string.ascii_letters}{string.digits}-._ "
MAX_VNFD_ID = f"{SUPPORT_STRING_FOR_VNFD_ID}max_vnfd_id"
MIN_VNFD_ID = f"{SUPPORT_STRING_FOR_VNFD_ID}min_vnfd_id"
UPD_MAX_VNFD_ID = f"{SUPPORT_STRING_FOR_VNFD_ID}upd_max_vnfd_id"
NEW_MAX_VNFD_ID = f"{SUPPORT_STRING_FOR_VNFD_ID}new_max_vnfd_id"
class IndividualVnfcMgmtBasicMaxTest(base_v2.BaseSolV2Test):
@classmethod
def setUpClass(cls):
super(IndividualVnfcMgmtBasicMaxTest, cls).setUpClass()
image_path = utils.test_etc_sample("etsi/nfv/common/Files/images",
"cirros-0.5.2-x86_64-disk.img")
userdata_path = utils.userdata("userdata_standard.py")
# vnf package for basic lcms tests max pattern
pkg_path_1 = utils.test_sample("functional/sol_v2_common",
"basic_lcms_max_individual_vnfc")
cls.max_pkg, cls.max_vnfd_id = cls.create_vnf_package(
pkg_path_1, image_path=image_path,
userdata_path=userdata_path, vnfd_id=MAX_VNFD_ID)
# vnf package for basic lcms tests min pattern
pkg_path_2 = utils.test_sample("functional/sol_v2_common",
"basic_lcms_min_individual_vnfc")
cls.min_pkg, cls.min_vnfd_id = cls.create_vnf_package(
pkg_path_2, userdata_path=userdata_path,
vnfd_id=MIN_VNFD_ID)
# vnf package for update vnf max pattern
pkg_path_3 = utils.test_sample("functional/sol_v2_common",
"update_vnf_max_individual_vnfc")
cls.upd_max_pkg, cls.upd_max_vnfd_id = cls.create_vnf_package(
pkg_path_3, image_path=image_path,
userdata_path=userdata_path, vnfd_id=UPD_MAX_VNFD_ID)
# vnf package for change vnf package max pattern
pkg_path_4 = utils.test_sample("functional/sol_v2_common",
"change_vnfpkg_max_individual_vnfc")
cls.new_max_pkg, cls.new_max_vnfd_id = cls.create_vnf_package(
pkg_path_4, userdata_path=userdata_path,
vnfd_id=NEW_MAX_VNFD_ID)
cls._pre_setting()
@classmethod
def tearDownClass(cls):
super(IndividualVnfcMgmtBasicMaxTest, cls).tearDownClass()
cls.delete_vnf_package(cls.max_pkg)
cls.delete_vnf_package(cls.min_pkg)
cls.delete_vnf_package(cls.upd_max_pkg)
cls.delete_vnf_package(cls.new_max_pkg)
def setUp(self):
super().setUp()
@classmethod
def _pre_setting(cls):
# Create a new network and subnet to check the IP allocation of
# IPv4 and IPv6
ft_net0_name = 'ft-net0'
ft_net0_subs = {
'ft-ipv4-subnet0': {
'range': '100.100.100.0/24',
'ip_version': 4
},
'ft-ipv6-subnet0': {
'range': '1111:2222:3333::/64',
'ip_version': 6
}
}
ft_net0_id = cls.create_network(cls, ft_net0_name)
cls.addClassCleanup(cls.delete_network, cls, ft_net0_id)
for sub_name, val in ft_net0_subs.items():
# subnet is automatically deleted with network deletion
cls.create_subnet(
cls, ft_net0_id, sub_name, val['range'], val['ip_version'])
# Create a new network for change external connectivity
ft_net1_name = 'ft-net1'
ft_net1_subs = {
'ft-ipv4-subnet1': {
'range': '22.22.22.0/24',
'ip_version': 4
},
'ft-ipv6-subnet1': {
'range': '1111:2222:4444::/64',
'ip_version': 6
}
}
ft_net1_id = cls.create_network(cls, ft_net1_name)
cls.addClassCleanup(cls.delete_network, cls, ft_net1_id)
for sub_name, val in ft_net1_subs.items():
# subnet is automatically deleted with network deletion
cls.create_subnet(
cls, ft_net1_id, sub_name, val['range'], val['ip_version'])
cls.net_ids = cls.get_network_ids(
cls, ['net0', 'net1', 'net_mgmt', 'ft-net0', 'ft-net1'])
cls.subnet_ids = cls.get_subnet_ids(
cls, ['subnet0', 'subnet1', 'ft-ipv4-subnet0', 'ft-ipv6-subnet0',
'ft-ipv4-subnet1', 'ft-ipv6-subnet1'])
def _get_vdu_indexes(self, inst, vdu):
return {
vnfc['metadata'].get('vdu_idx')
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']
if vnfc['vduId'] == vdu
}
def _get_vnfc_metadata_keys(self, inst, vdu):
vnfc_metadata_keys = set()
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
if vnfc['vduId'] == vdu:
vnfc_metadata_keys.update(set(vnfc['metadata'].keys()))
return vnfc_metadata_keys
def _add_additional_params(self, req):
if not req.get('additionalParams'):
req['additionalParams'] = {}
req['additionalParams']['lcm-operation-user-data'] = (
'./UserData/userdata_standard.py')
req['additionalParams']['lcm-operation-user-data-class'] = (
'StandardUserData')
def _get_vnfc_by_vdu_index(self, inst, vdu, index):
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
if (vnfc['vduId'] == vdu and
vnfc['metadata'].get('vdu_idx') == index):
return vnfc
def _get_vnfc_id(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
return vnfc['id']
def _get_vnfc_image(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for key, value in vnfc['metadata'].items():
if key.startswith('image-'):
# must be found
return value
def _get_vnfc_storage_ids(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
return vnfc['storageResourceIds']
def _get_vnf_ext_cp_id(self, inst, vdu, index, cp):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for cp_info in vnfc['vnfcCpInfo']:
if cp_info['cpdId'] == cp:
# must be found
ext_cp_id = cp_info['vnfExtCpId']
break
return ext_cp_id
def _get_vnf_link_port_id(self, inst, vdu, index, cp):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for cp_info in vnfc['vnfcCpInfo']:
if cp_info['cpdId'] == cp:
# must be found
link_port_id = cp_info['vnfLinkPortId']
break
return link_port_id
def _get_vnfc_flavor(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
# must exist
return vnfc['metadata']['flavor']
def _get_vnfc_cps(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
return {cp_info['cpdId'] for cp_info in vnfc['vnfcCpInfo']}
def _get_vnfc_cp_net_name(self, inst, vdu, index, cp):
# this is for internal CPs
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for cp_info in vnfc['vnfcCpInfo']:
if cp_info['cpdId'] == cp:
# must be found
link_port_id = cp_info['vnfLinkPortId']
break
for vl in inst['instantiatedVnfInfo']['vnfVirtualLinkResourceInfo']:
for port in vl['vnfLinkPorts']:
if port['id'] == link_port_id:
# must be found
return vl['vnfVirtualLinkDescId']
def _check_for_show_operation(
self, operation, expected_inst_attrs, inst_id,
vdu_result=None, image_result=None):
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
if operation == 'INSTANTIATE':
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.INSTANTIATED,
body['instantiationState'])
if operation == 'SCALE':
# check scaleStatus
scale_status = body['instantiatedVnfInfo']['scaleStatus']
self.assertGreater(len(scale_status), 0)
for status in scale_status:
self.assertIn('aspectId', status)
self.assertIn('scaleLevel', status)
# check instantiatedVnfInfo's information
# check number of VDU, and index
if vdu_result:
self.assertEqual(vdu_result['VDU1'],
self._get_vdu_indexes(body, 'VDU1'))
self.assertEqual(vdu_result['VDU2'],
self._get_vdu_indexes(body, 'VDU2'))
# check exist of VDU-image
if image_result:
for result_1 in image_result['VDU1']:
self.assertIn(
result_1,
self._get_vnfc_metadata_keys(body, 'VDU1'))
for result_2 in image_result['VDU2']:
self.assertIn(
result_2,
self._get_vnfc_metadata_keys(body, 'VDU2'))
return body
def test_basic_lcms_max(self):
"""Test LCM operations for individual vnfc mgmt with all attributes set
* About attributes:
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
* About LCM operations:
This test includes the following operations.
- 0. Create VNF
- 1. Instantiate VNF
- 2. Show VNF instance(check for instantiate)
- 3. List VNF instance with attribute-based filtering
- 4. Show VNF LCM operation occurrence
- 5. Heal VNF(all with omit all parameter)
- 6. Show VNF instance(check for heal)
- 7. List VNF LCM operation occurrence with attribute-based
filtering
- 8. Heal VNF(all with all=True parameter)
- 9. Show VNF instance(check for heal)
- 10. Scale out operation
- 11. Show VNF instance(check for scale)
- 12. Scale in operation
- 13. Show VNF instance(check for scale)
- 14. Heal VNF(vnfc)
- 15. Show VNF instance(check for heal)
- 16. Change external connectivity
- 17. Show VNF instance(check for change-ext-conn)
- 18. Heal VNF(vnfc with omit all parameter)
- 19. Show VNF instance(check for heal)
- 20. Heal VNF(vnfc with all=False parameter)
- 21. Show VNF instance(check for heal)
- 22. Heal VNF(vnfc with all=True parameter)
- 23. Show VNF instance(check for heal)
- 24. Update VNF
- 25. Show VNF instance(check for update)
- 26. Update VNF(again)
- 27. Change current VNF Package
- 28. Show VNF instance(check for change-vnfpkg)
- 29. Terminate VNF
- 30. Delete VNF
"""
# 0. Create VNF
create_req = paramgen.create_vnf_max(
self.max_vnfd_id,
description="test for basic_lcms_max_individual_vnfc")
_, body = self.create_vnf_instance(create_req)
inst_id = body['id']
# 1. Instantiate VNF
instantiate_req = paramgen.instantiate_vnf_max(
self.net_ids, self.subnet_ids, None, self.auth_url,
user_data=True)
resp, body = self.instantiate_vnf_instance(
inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check that the servers set in "zone:Affinity" are
# deployed on 'nova' AZ.
# NOTE: local_nfvo returns this AZ
vdu1_details = self.get_server_details('VDU1')
vdu2_details = self.get_server_details('VDU2')
vdu1_az = vdu1_details.get('OS-EXT-AZ:availability_zone')
vdu2_az = vdu2_details.get('OS-EXT-AZ:availability_zone')
self.assertEqual('nova', vdu1_az)
self.assertEqual('nova', vdu2_az)
# 2. Show VNF instance(check for instantiate)
# NOTE: extensions and vnfConfigurableProperties are omitted
# because they are commented out in etsi_nfv_sol001.
expected_inst_attrs = [
'id',
'vnfInstanceName',
'vnfInstanceDescription',
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
'vimConnectionInfo',
'instantiationState',
'instantiatedVnfInfo',
'metadata',
# 'extensions', # omitted
'_links'
]
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
image_result = {'VDU1': {'image-VDU1-VirtualStorage-0'},
'VDU2': {'image-VDU2-VirtualStorage-0'}}
inst_2 = self._check_for_show_operation(
'INSTANTIATE', expected_inst_attrs, inst_id,
vdu_result, image_result)
# 3. List VNF instance with attribute-based filtering
# check Link in 'list_vnf_instance'
# The Response Header will contain a 'Link' Header only when there
# are at least two vnf instance data in the database, so it needs to
# create an unused data.
create_req = paramgen.create_vnf_min(self.min_vnfd_id)
_, tmp_body = self.create_vnf_instance(create_req)
resp, body = self.list_vnf_instance()
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_index(resp)
# * all_fields
# -> check the attribute omitted in "exclude_default" is set.
filter_expr = {'filter': f'(eq,id,{inst_id})', 'all_fields': ''}
resp, body = self.list_vnf_instance(filter_expr)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
for inst in body:
self.assertIn('vimConnectionInfo', inst)
self.assertIn('instantiatedVnfInfo', inst)
self.assertIn('metadata', inst)
# * fields=<list>
# -> check the attribute specified in "fields" is set
filter_expr = {'filter': f'(eq,id,{inst_id})',
'fields': 'metadata'}
resp, body = self.list_vnf_instance(filter_expr)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
for inst in body:
self.assertNotIn('vimConnectionInfo', inst)
self.assertNotIn('instantiatedVnfInfo', inst)
self.assertIn('metadata', inst)
# * exclude_fields=<list>
# -> check the attribute specified in "exclude_fields" is not set
filter_expr = {'filter': f'(eq,id,{inst_id})',
'exclude_fields': 'metadata'}
resp, body = self.list_vnf_instance(filter_expr)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
for inst in body:
self.assertIn('vimConnectionInfo', inst)
self.assertIn('instantiatedVnfInfo', inst)
self.assertNotIn('metadata', inst)
# * exclude_default
# -> check the attribute omitted in "exclude_default" is not set.
filter_expr = {'filter': f'(eq,id,{inst_id})',
'exclude_default': ''}
resp, body = self.list_vnf_instance(filter_expr)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
for inst in body:
self.assertNotIn('vimConnectionInfo', inst)
self.assertNotIn('instantiatedVnfInfo', inst)
self.assertNotIn('metadata', inst)
self.delete_vnf_instance(tmp_body['id'])
# 4. Show VNF LCM operation occurrence
expected_attrs = [
'id',
'operationState',
'stateEnteredTime',
'startTime',
'vnfInstanceId',
'grantId',
'operation',
'isAutomaticInvocation',
'operationParams',
'isCancelPending',
# 'cancelMode', # omitted
# 'error', # omitted
'resourceChanges',
# 'changedInfo', # omitted
# 'changedExtConnectivity', # omitted
# 'modificationsTriggeredByVnfPkgChange', # omitted
# 'vnfSnapshotInfoId', # omitted
'_links'
]
resp, body = self.show_lcmocc(lcmocc_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
# 5. Heal VNF(all with omit all parameter)
heal_req = paramgen.heal_vnf_all_max_with_parameter()
self._add_additional_params(heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 6. Show VNF instance(check for heal)
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
inst_6 = self._check_for_show_operation(
'HEAL', expected_inst_attrs, inst_id, vdu_result)
# check ids of VDU are changed
self.assertNotEqual(self._get_vnfc_id(inst_2, 'VDU1', 0),
self._get_vnfc_id(inst_6, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_id(inst_2, 'VDU2', 0),
self._get_vnfc_id(inst_6, 'VDU2', 0))
# check images are not changed
self.assertEqual(self._get_vnfc_image(inst_2, 'VDU1', 0),
self._get_vnfc_image(inst_6, 'VDU1', 0))
self.assertEqual(self._get_vnfc_image(inst_2, 'VDU2', 0),
self._get_vnfc_image(inst_6, 'VDU2', 0))
# 7. List VNF LCM operation occurrence with attribute-based filtering
# * all_fields
# -> check the attribute omitted in "exclude_default" is set.
resp, body = self.list_lcmocc()
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_index(resp)
filter_expr = {'filter': f'(eq,id,{lcmocc_id})', 'all_fields': ''}
resp, body = self.list_lcmocc(filter_expr)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
for lcmocc in body:
self.assertIn('operationParams', lcmocc)
self.assertIn('resourceChanges', lcmocc)
# * fields=<list>
# -> check the attribute specified in "fields" is set
filter_expr = {'filter': f'(eq,id,{lcmocc_id})',
'fields': 'operationParams'}
resp, body = self.list_lcmocc(filter_expr)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
for lcmocc in body:
self.assertIn('operationParams', lcmocc)
self.assertNotIn('resourceChanges', lcmocc)
# * exclude_fields=<list>
# -> check the attribute specified in "exclude_fields" is not set
filter_expr = {'filter': f'(eq,id,{lcmocc_id})',
'exclude_fields': 'operationParams'}
resp, body = self.list_lcmocc(filter_expr)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
for lcmocc in body:
self.assertNotIn('operationParams', lcmocc)
self.assertIn('resourceChanges', lcmocc)
# * exclude_default
# -> check the attribute omitted in "exclude_default" is not set.
filter_expr = {'filter': f'(eq,id,{lcmocc_id})',
'exclude_default': ''}
resp, body = self.list_lcmocc(filter_expr)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
for lcmocc in body:
self.assertNotIn('operationParams', lcmocc)
self.assertNotIn('resourceChanges', lcmocc)
# 8. Heal VNF(all with all=True parameter)
heal_req = paramgen.heal_vnf_all_max_with_parameter(True)
self._add_additional_params(heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 9. Show VNF instance(check for heal)
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
inst_9 = self._check_for_show_operation(
'HEAL', expected_inst_attrs, inst_id, vdu_result)
# check all ids of VDU are changed
self.assertNotEqual(self._get_vnfc_id(inst_6, 'VDU1', 0),
self._get_vnfc_id(inst_9, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_id(inst_6, 'VDU2', 0),
self._get_vnfc_id(inst_9, 'VDU2', 0))
# check storage ids are changed
self.assertNotEqual(self._get_vnfc_storage_ids(inst_6, 'VDU1', 0),
self._get_vnfc_storage_ids(inst_9, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_storage_ids(inst_6, 'VDU2', 0),
self._get_vnfc_storage_ids(inst_9, 'VDU2', 0))
# check cps are changed
for cp_1 in ['VDU1_CP1', 'VDU1_CP2', 'VDU2_CP2']:
self.assertNotEqual(
self._get_vnf_ext_cp_id(inst_6, cp_1.split('_')[0], 0, cp_1),
self._get_vnf_ext_cp_id(inst_9, cp_1.split('_')[0], 0, cp_1))
for cp_2 in ['VDU1_CP3', 'VDU1_CP4', 'VDU1_CP5',
'VDU2_CP3', 'VDU2_CP4', 'VDU2_CP5']:
self.assertNotEqual(
self._get_vnf_link_port_id(inst_6,
cp_2.split('_')[0], 0, cp_2),
self._get_vnf_link_port_id(inst_9,
cp_2.split('_')[0], 0, cp_2))
# 10. Scale out operation
scaleout_req = paramgen.scaleout_vnf_max()
self._add_additional_params(scaleout_req)
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 11. Show VNF instance(check for scale)
vdu_result = {'VDU1': {0, 1}, 'VDU2': {0}}
image_result = {'VDU1': {'image-VDU1-VirtualStorage-0',
'image-VDU1-VirtualStorage-1'},
'VDU2': {'image-VDU2-VirtualStorage-0'}}
_ = self._check_for_show_operation(
'SCALE', expected_inst_attrs, inst_id,
vdu_result, image_result)
# 12. Scale in operation
scalein_req = paramgen.scalein_vnf_max()
self._add_additional_params(scalein_req)
resp, body = self.scale_vnf_instance(inst_id, scalein_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 13. Show VNF instance(check for scale)
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
image_result = {'VDU1': {'image-VDU1-VirtualStorage-0'},
'VDU2': {'image-VDU2-VirtualStorage-0'}}
inst_13 = self._check_for_show_operation(
'SCALE', expected_inst_attrs, inst_id,
vdu_result, image_result)
# 14. Heal VNF(vnfc)
vnfc_info = inst_13['instantiatedVnfInfo']['vnfcInfo']
vnfc_id = [vnfc['id'] for vnfc in vnfc_info
if ("VDU2" == vnfc['vduId'])][0]
heal_req = paramgen.heal_vnf_vnfc_max(vnfc_id)
self._add_additional_params(heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 15. Show VNF instance(check for heal)
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
inst_15 = self._check_for_show_operation(
'HEAL', expected_inst_attrs, inst_id, vdu_result)
# check id of VDU2 is changed
self.assertNotEqual(self._get_vnfc_id(inst_13, 'VDU2', 0),
self._get_vnfc_id(inst_15, 'VDU2', 0))
# check image of VDU2 is not changed
self.assertEqual(self._get_vnfc_image(inst_13, 'VDU2', 0),
self._get_vnfc_image(inst_15, 'VDU2', 0))
# 16. Change external connectivity
change_ext_conn_req = paramgen.change_ext_conn_max(
self.net_ids, self.subnet_ids, self.auth_url)
self._add_additional_params(change_ext_conn_req)
resp, body = self.change_ext_conn(inst_id, change_ext_conn_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 17. Show VNF instance(check for change-ext-conn)
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
inst_17 = self._check_for_show_operation(
'CHANGE_EXT_CONN', expected_inst_attrs, inst_id, vdu_result)
# check vnfExtCPIds of VDU are changed
for ext_cp in ['VDU1_CP1', 'VDU2_CP2']:
self.assertNotEqual(
self._get_vnf_ext_cp_id(
inst_15, ext_cp.split('_')[0], 0, ext_cp),
self._get_vnf_ext_cp_id(
inst_17, ext_cp.split('_')[0], 0, ext_cp))
# 18. Heal VNF(vnfc with omit all parameter)
vnfc_info = inst_17['instantiatedVnfInfo']['vnfcInfo']
vnfc_ids = [vnfc['id'] for vnfc in vnfc_info]
heal_req = paramgen.heal_vnf_vnfc_max_with_parameter(vnfc_ids)
self._add_additional_params(heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 19. Show VNF instance(check for heal)
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
inst_19 = self._check_for_show_operation(
'HEAL', expected_inst_attrs, inst_id, vdu_result)
# check all ids of VDU are changed
self.assertNotEqual(self._get_vnfc_id(inst_17, 'VDU1', 0),
self._get_vnfc_id(inst_19, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_id(inst_17, 'VDU2', 0),
self._get_vnfc_id(inst_19, 'VDU2', 0))
# check images are not changed
self.assertEqual(self._get_vnfc_image(inst_17, 'VDU1', 0),
self._get_vnfc_image(inst_19, 'VDU1', 0))
self.assertEqual(self._get_vnfc_image(inst_17, 'VDU2', 0),
self._get_vnfc_image(inst_19, 'VDU2', 0))
# 20. Heal VNF(vnfc with all=False parameter)
vnfc_info = inst_19['instantiatedVnfInfo']['vnfcInfo']
vnfc_ids = [vnfc['id'] for vnfc in vnfc_info]
heal_req = paramgen.heal_vnf_vnfc_max_with_parameter(vnfc_ids, False)
self._add_additional_params(heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 21. Show VNF instance(check for heal)
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
inst_21 = self._check_for_show_operation(
'HEAL', expected_inst_attrs, inst_id, vdu_result)
# check all ids of VDU are changed
self.assertNotEqual(self._get_vnfc_id(inst_19, 'VDU1', 0),
self._get_vnfc_id(inst_21, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_id(inst_19, 'VDU2', 0),
self._get_vnfc_id(inst_21, 'VDU2', 0))
# check images are not changed
self.assertEqual(self._get_vnfc_image(inst_19, 'VDU1', 0),
self._get_vnfc_image(inst_21, 'VDU1', 0))
self.assertEqual(self._get_vnfc_image(inst_19, 'VDU2', 0),
self._get_vnfc_image(inst_21, 'VDU2', 0))
# 22. Heal VNF(vnfc with all=True parameter)
vnfc_info = inst_21['instantiatedVnfInfo']['vnfcInfo']
vnfc_ids = [vnfc['id'] for vnfc in vnfc_info]
heal_req = paramgen.heal_vnf_vnfc_max_with_parameter(vnfc_ids, True)
self._add_additional_params(heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 23. Show VNF instance(check for heal)
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
inst_23 = self._check_for_show_operation(
'HEAL', expected_inst_attrs, inst_id, vdu_result)
# check all ids of VDU are changed
self.assertNotEqual(self._get_vnfc_id(inst_21, 'VDU1', 0),
self._get_vnfc_id(inst_23, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_id(inst_21, 'VDU2', 0),
self._get_vnfc_id(inst_23, 'VDU2', 0))
# check storage ids are changed
self.assertNotEqual(self._get_vnfc_storage_ids(inst_21, 'VDU1', 0),
self._get_vnfc_storage_ids(inst_23, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_storage_ids(inst_21, 'VDU2', 0),
self._get_vnfc_storage_ids(inst_23, 'VDU2', 0))
# 24. Update VNF
# check attribute value before update VNF
# check usageState of max pattern VNF Package
self.check_package_usage(self.max_pkg, 'IN_USE')
# check usageState of update VNF Package
self.check_package_usage(self.upd_max_pkg)
# check vnfd id
self.assertEqual(self.max_vnfd_id, inst_23['vnfdId'])
vnfc_info = inst_23['instantiatedVnfInfo']['vnfcInfo']
vnfc_ids = [vnfc['id'] for vnfc in vnfc_info]
update_req = paramgen.update_vnf_max(self.upd_max_vnfd_id, vnfc_ids)
resp, body = self.update_vnf_instance(inst_id, update_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 25. Show VNF instance(check for update)
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
inst_25 = self._check_for_show_operation(
'MODIFY_INFO', expected_inst_attrs, inst_id, vdu_result)
# check usageState of max pattern VNF Package
self.check_package_usage(self.max_pkg)
# check usageState of update max pattern VNF Package
self.check_package_usage(self.upd_max_pkg, 'IN_USE')
self.assertEqual(self.upd_max_vnfd_id, inst_25['vnfdId'])
self.assertEqual('new name', inst_25['vnfInstanceName'])
self.assertEqual('new description', inst_25['vnfInstanceDescription'])
dummy_key_value = {'dummy-key': 'dummy-value'}
self.assertEqual(dummy_key_value, inst_25['metadata'])
self.assertEqual(dummy_key_value, inst_25['extensions'])
self.assertEqual(dummy_key_value, inst_25['vnfConfigurableProperties'])
# 26. Update VNF(again)
# check usageState of max pattern VNF Package
self.check_package_usage(self.max_pkg)
# check usageState of update max pattern VNF Package
self.check_package_usage(self.upd_max_pkg, 'IN_USE')
# check vnfd id
self.assertEqual(self.upd_max_vnfd_id, inst_25['vnfdId'])
update_req = paramgen.update_vnf_min_with_parameter(self.max_vnfd_id)
resp, body = self.update_vnf_instance(inst_id, update_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 27. Change current VNF Package
# check usageState of max pattern VNF Package
self.check_package_usage(self.max_pkg, 'IN_USE')
# check usageState of update max pattern VNF Package
self.check_package_usage(self.upd_max_pkg)
# check usageState of new max pattern VNF Package
self.check_package_usage(self.new_max_pkg)
change_vnf_pkg_req = paramgen.change_vnf_pkg_individual_vnfc_max(
self.new_max_vnfd_id, self.net_ids, self.subnet_ids)
resp, body = self.change_vnfpkg(inst_id, change_vnf_pkg_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 28. Show VNF instance(check for change-vnfpkg)
# check usageState of max pattern VNF Package
self.check_package_usage(self.max_pkg)
# check usageState of new max pattern VNF Package
self.check_package_usage(self.new_max_pkg, 'IN_USE')
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
inst_28 = self._check_for_show_operation(
'CHANGE_VNFPKG', expected_inst_attrs, inst_id, vdu_result)
# check vnfdId
self.assertEqual(self.new_max_vnfd_id, inst_28['vnfdId'])
# check all ids of VDU are changed
self.assertNotEqual(self._get_vnfc_id(inst_25, 'VDU1', 0),
self._get_vnfc_id(inst_28, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_id(inst_25, 'VDU2', 0),
self._get_vnfc_id(inst_28, 'VDU2', 0))
# check images of VDU are changed
self.assertNotEqual(self._get_vnfc_image(inst_25, 'VDU1', 0),
self._get_vnfc_image(inst_28, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_image(inst_25, 'VDU2', 0),
self._get_vnfc_image(inst_28, 'VDU2', 0))
self.assertEqual('cirros-0.5.2-x86_64-disk',
self._get_vnfc_image(inst_28, 'VDU1', 0))
self.assertEqual('cirros-0.5.2-x86_64-disk',
self._get_vnfc_image(inst_28, 'VDU2', 0))
# check flavor is changed (VDU2 only)
self.assertNotEqual(self._get_vnfc_flavor(inst_25, 'VDU2', 0),
self._get_vnfc_flavor(inst_28, 'VDU2', 0))
# check vnfExtCPId of VDU1_CP1 is changed
self.assertNotEqual(
self._get_vnf_ext_cp_id(inst_25, 'VDU1', 0, 'VDU1_CP1'),
self._get_vnf_ext_cp_id(inst_28, 'VDU1', 0, 'VDU1_CP1'))
# check external CPs, VDU1_CP6 and VDU2_CP6 are added
self.assertFalse('VDU1_CP6' in self._get_vnfc_cps(inst_25, 'VDU1', 0))
self.assertFalse('VDU2_CP6' in self._get_vnfc_cps(inst_25, 'VDU2', 0))
self.assertTrue('VDU1_CP6' in self._get_vnfc_cps(inst_28, 'VDU1', 0))
self.assertTrue('VDU2_CP6' in self._get_vnfc_cps(inst_28, 'VDU2', 0))
# check internal CPs, VDU1_CP5 and VDU2_CP5 are changed
self.assertEqual("internalVL3", self._get_vnfc_cp_net_name(
inst_25, 'VDU1', 0, 'VDU1_CP5'))
self.assertEqual("internalVL3", self._get_vnfc_cp_net_name(
inst_25, 'VDU2', 0, 'VDU2_CP5'))
self.assertEqual("internalVL4", self._get_vnfc_cp_net_name(
inst_28, 'VDU1', 0, 'VDU1_CP5'))
self.assertEqual("internalVL4", self._get_vnfc_cp_net_name(
inst_28, 'VDU2', 0, 'VDU2_CP5'))
# 29. Terminate VNF
terminate_req = paramgen.terminate_vnf_max()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 30. Delete VNF
self.exec_lcm_operation(self.delete_vnf_instance, inst_id)

View File

@ -0,0 +1,425 @@
# Copyright (C) 2023 Fujitsu
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import string
from tacker.objects import fields
from tacker.tests.functional.sol_v2_common import base_v2
from tacker.tests.functional.sol_v2_common import paramgen
from tacker.tests import utils
SUPPORT_STRING_FOR_VNFD_ID = f"{string.ascii_letters}{string.digits}-._ "
MIN_VNFD_ID = f"{SUPPORT_STRING_FOR_VNFD_ID}min_vnfd_id"
UPD_NEW_MIN_VNFD_ID = f"{SUPPORT_STRING_FOR_VNFD_ID}upd_new_min_vnfd_id"
class IndividualVnfcMgmtBasicMinTest(base_v2.BaseSolV2Test):
@classmethod
def setUpClass(cls):
super(IndividualVnfcMgmtBasicMinTest, cls).setUpClass()
image_path = utils.test_etc_sample("etsi/nfv/common/Files/images",
"cirros-0.5.2-x86_64-disk.img")
userdata_path = utils.userdata("userdata_standard.py")
# vnf package for basic lcms tests min pattern
pkg_path_1 = utils.test_sample("functional/sol_v2_common",
"basic_lcms_min_individual_vnfc")
cls.min_pkg, cls.min_vnfd_id = cls.create_vnf_package(
pkg_path_1, userdata_path=userdata_path,
vnfd_id=MIN_VNFD_ID)
# vnf package for change vnf package or update min pattern
pkg_path_2 = utils.test_sample("functional/sol_v2_common",
"change_vnfpkg_or_update_min_individual_vnfc")
cls.upd_new_min_pkg, cls.upd_new_min_vnfd_id = cls.create_vnf_package(
pkg_path_2, image_path=image_path, userdata_path=userdata_path,
vnfd_id=UPD_NEW_MIN_VNFD_ID)
@classmethod
def tearDownClass(cls):
super(IndividualVnfcMgmtBasicMinTest, cls).tearDownClass()
cls.delete_vnf_package(cls.min_pkg)
cls.delete_vnf_package(cls.upd_new_min_pkg)
def setUp(self):
super().setUp()
def _get_vdu_indexes(self, inst, vdu):
return {
vnfc['metadata'].get('vdu_idx')
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']
if vnfc['vduId'] == vdu
}
def _get_vnfc_metadata_keys(self, inst, vdu):
vnfc_metadata_keys = set()
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
if vnfc['vduId'] == vdu:
vnfc_metadata_keys.update(set(vnfc['metadata'].keys()))
return vnfc_metadata_keys
def _add_additional_params(self, req):
if not req.get('additionalParams'):
req['additionalParams'] = {}
req['additionalParams']['lcm-operation-user-data'] = (
'./UserData/userdata_standard.py')
req['additionalParams']['lcm-operation-user-data-class'] = (
'StandardUserData')
def _get_vnfc_by_vdu_index(self, inst, vdu, index):
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
if (vnfc['vduId'] == vdu and
vnfc['metadata'].get('vdu_idx') == index):
return vnfc
def _get_vnfc_id(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
return vnfc['id']
def _get_vnfc_image(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for key, value in vnfc['metadata'].items():
if key.startswith('image-'):
# must be found
return value
def _get_vnfc_flavor(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
# must exist
return vnfc['metadata']['flavor']
def _check_for_show_operation(
self, operation, expected_inst_attrs, inst_id,
vdu_result=None, image_result=None):
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
if operation == 'INSTANTIATE':
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.INSTANTIATED,
body['instantiationState'])
if operation == 'SCALE':
# check scaleStatus
scale_status = body['instantiatedVnfInfo']['scaleStatus']
self.assertGreater(len(scale_status), 0)
for status in scale_status:
self.assertIn('aspectId', status)
self.assertIn('scaleLevel', status)
# check instantiatedVnfInfo's information
# check number of VDU, and index
if vdu_result:
self.assertEqual(vdu_result['VDU1'],
self._get_vdu_indexes(body, 'VDU1'))
self.assertEqual(vdu_result['VDU2'],
self._get_vdu_indexes(body, 'VDU2'))
# check exist of VDU-image
if image_result:
for result_1 in image_result['VDU1']:
self.assertIn(
result_1,
self._get_vnfc_metadata_keys(body, 'VDU1'))
for result_2 in image_result['VDU2']:
self.assertIn(
result_2,
self._get_vnfc_metadata_keys(body, 'VDU2'))
return body
def test_basic_lcms_min(self):
"""Test LCM operations with omitting except for required attributes
The change_ext_conn can't be tested here because min pattern VNF
package 2 don't have external connectivity. So moved it to the
test_various_lcm_operations_before_and_after().
* About attributes:
Omit except for required attributes.
Only the following cardinality attributes are set.
- 1
- 1..N (1)
* About LCM operations:
This test includes the following operations.
- 0. Create VNF
- 1. Instantiate VNF
- 2. Show VNF instance(check for instantiate)
- 3. Heal VNF(all with omit all parameter)
- 4. Show VNF instance(check for heal)
- 5. Scale out operation
- 6. Show VNF instance(check for scale)
- 7. Update VNF
- 8. Show VNF instance(check for update)
- 9. Heal VNF(vnfc)
- 10. Show VNF instance(check for heal)
- 11. Scale in operation
- 12. Show VNF instance(check for scale)
- 13. Terminate VNF
- 14. Update VNF again
- 15. Instantiate VNF again
- 16. Change current VNF Package
- 17. Show VNF instance(check for change-vnfpkg)
- 18. Terminate VNF again
- 19. Delete VNF
"""
# 0. Create VNF
create_req = paramgen.create_vnf_min(self.min_vnfd_id)
_, body = self.create_vnf_instance(create_req)
inst_id = body['id']
# 1. Instantiate VNF
instantiate_req = paramgen.instantiate_vnf_min()
self._add_additional_params(instantiate_req)
resp, body = self.instantiate_vnf_instance(
inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check that the servers set in "nfvi_node:Affinity" are
# deployed on the same host.
# NOTE: it's up to heat to decide which host to deploy to
vdu1_details = self.get_server_details('VDU1')
vdu2_details = self.get_server_details('VDU2')
vdu1_host = vdu1_details['hostId']
vdu2_host = vdu2_details['hostId']
self.assertEqual(vdu1_host, vdu2_host)
# 2. Show VNF instance(check for instantiate)
expected_inst_attrs = [
'id',
# 'vnfInstanceName', # omitted
# 'vnfInstanceDescription', # omitted
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
'vimConnectionInfo',
'instantiationState',
'instantiatedVnfInfo',
# 'metadata', # omitted
# 'extensions', # omitted
'_links'
]
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
image_result = {'VDU1': {'image-VDU1-0'},
'VDU2': {'image-VDU2-0'}}
inst_2 = self._check_for_show_operation(
'INSTANTIATE', expected_inst_attrs, inst_id,
vdu_result, image_result)
# 3. Heal VNF(all with omit all parameter)
heal_req = paramgen.heal_vnf_all_min()
self._add_additional_params(heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 4. Show VNF instance(check for heal)
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
inst_4 = self._check_for_show_operation(
'HEAL', expected_inst_attrs, inst_id, vdu_result)
# check all ids of VDU are changed
self.assertNotEqual(self._get_vnfc_id(inst_2, 'VDU1', 0),
self._get_vnfc_id(inst_4, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_id(inst_2, 'VDU2', 0),
self._get_vnfc_id(inst_4, 'VDU2', 0))
# 5. Scale out operation
scaleout_req = paramgen.scaleout_vnf_min()
self._add_additional_params(scaleout_req)
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 6. Show VNF instance(check for scale)
vdu_result = {'VDU1': {0, 1}, 'VDU2': {0}}
image_result = {'VDU1': {'image-VDU1-0', 'image-VDU1-1'},
'VDU2': {'image-VDU2-0'}}
_ = self._check_for_show_operation(
'SCALE', expected_inst_attrs, inst_id,
vdu_result, image_result)
# 7. Update VNF
update_req = paramgen.update_vnf_min_with_parameter(
self.upd_new_min_vnfd_id)
resp, body = self.update_vnf_instance(inst_id, update_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check usageState of min pattern VNF Package
self.check_package_usage(self.min_pkg)
# check usageState of update or new min pattern VNF Package
self.check_package_usage(self.upd_new_min_pkg, 'IN_USE')
# 8. Show VNF instance(check for update)
inst_8 = self._check_for_show_operation(
'MODIFY_INFO', expected_inst_attrs, inst_id)
self.assertEqual(self.upd_new_min_vnfd_id, inst_8['vnfdId'])
# 9. Heal VNF(vnfc)
vnfc_info = inst_8['instantiatedVnfInfo']['vnfcInfo']
vnfc_id = [vnfc['id'] for vnfc in vnfc_info
if (vnfc['vnfcResourceInfoId'] ==
self._get_vnfc_id(inst_8, 'VDU1', 1))][0]
heal_req = paramgen.heal_vnf_vnfc_min(vnfc_id)
self._add_additional_params(heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 10. Show VNF instance(check for heal)
vdu_result = {'VDU1': {0, 1}, 'VDU2': {0}}
inst_10 = self._check_for_show_operation(
'HEAL', expected_inst_attrs, inst_id, vdu_result)
# check id of VDU1 with index 1 is changed, with index 0 is not changed
self.assertNotEqual(self._get_vnfc_id(inst_8, 'VDU1', 1),
self._get_vnfc_id(inst_10, 'VDU1', 1))
self.assertEqual(self._get_vnfc_id(inst_8, 'VDU1', 0),
self._get_vnfc_id(inst_10, 'VDU1', 0))
# check image value of image-VDU1-1 is changed, others are not changed
self.assertNotEqual(self._get_vnfc_image(inst_8, 'VDU1', 1),
self._get_vnfc_image(inst_10, 'VDU1', 1))
self.assertEqual(self._get_vnfc_image(inst_8, 'VDU1', 0),
self._get_vnfc_image(inst_10, 'VDU1', 0))
# 11. Scale in operation
scalein_req = paramgen.scalein_vnf_min()
self._add_additional_params(scalein_req)
resp, body = self.scale_vnf_instance(inst_id, scalein_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 12. Show VNF instance(check for scale)
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
image_result = {'VDU1': {'image-VDU1-0'},
'VDU2': {'image-VDU2-0'}}
_ = self._check_for_show_operation(
'SCALE', expected_inst_attrs, inst_id,
vdu_result, image_result)
# 13. Terminate VNF
terminate_req = paramgen.terminate_vnf_min()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 14. Update VNF again
update_req = paramgen.update_vnf_min_with_parameter(self.min_vnfd_id)
resp, body = self.exec_lcm_operation(self.update_vnf_instance,
inst_id, update_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check usageState of min pattern VNF Package
self.check_package_usage(self.min_pkg, 'IN_USE')
# check usageState of update or new min pattern VNF Package
self.check_package_usage(self.upd_new_min_pkg)
# 15. Instantiate VNF again
resp, body = self.instantiate_vnf_instance(
inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
inst_15 = self._check_for_show_operation(
'INSTANTIATE', expected_inst_attrs, inst_id)
# 16. Change current VNF Package
change_vnf_pkg_req = paramgen.change_vnf_pkg_individual_vnfc_min(
self.upd_new_min_vnfd_id)
resp, body = self.change_vnfpkg(inst_id, change_vnf_pkg_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 17. Show VNF instance(check for change-vnfpkg)
# check usageState of min pattern VNF Package
self.check_package_usage(self.min_pkg)
# check usageState of update or new min pattern VNF Package
self.check_package_usage(self.upd_new_min_pkg, 'IN_USE')
inst_17 = self._check_for_show_operation(
'CHANGE_VNFPKG', expected_inst_attrs, inst_id)
# check vnfdId
self.assertEqual(self.upd_new_min_vnfd_id, inst_17['vnfdId'])
# check ids of VDU are not changed
self.assertEqual(self._get_vnfc_id(inst_15, 'VDU1', 0),
self._get_vnfc_id(inst_17, 'VDU1', 0))
self.assertEqual(self._get_vnfc_id(inst_15, 'VDU2', 0),
self._get_vnfc_id(inst_17, 'VDU2', 0))
# check image of VDU1 is changed
self.assertNotEqual(self._get_vnfc_image(inst_15, 'VDU1', 0),
self._get_vnfc_image(inst_17, 'VDU1', 0))
# check flavors of VDU are not changed
self.assertEqual(self._get_vnfc_flavor(inst_15, 'VDU1', 0),
self._get_vnfc_flavor(inst_17, 'VDU1', 0))
self.assertEqual(self._get_vnfc_flavor(inst_15, 'VDU2', 0),
self._get_vnfc_flavor(inst_17, 'VDU2', 0))
# 18. Terminate VNF again
terminate_req = paramgen.terminate_vnf_min()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 19. Delete VNF
self.exec_lcm_operation(self.delete_vnf_instance, inst_id)

View File

@ -0,0 +1,454 @@
# Copyright (C) 2022 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import tacker.conf
from tacker.tests.functional.sol_v2_common import paramgen
from tacker.tests.functional.sol_v2_common import test_vnflcm_basic_common
from tacker.tests.functional.sol_v2_common import zipgen
from tacker.tests import utils
CONF = tacker.conf.CONF
class IndividualVnfcMgmtCcvpTest(test_vnflcm_basic_common.CommonVnfLcmTest):
@classmethod
def setUpClass(cls):
super(IndividualVnfcMgmtCcvpTest, cls).setUpClass()
image_path = utils.test_etc_sample("etsi/nfv/common/Files/images",
"cirros-0.5.2-x86_64-disk.img")
userdata_path = utils.userdata("userdata_standard.py")
# main vnf package for StandardUserData test
cls.standard_vnfd_id, zip_path = zipgen.userdata_standard()
cls.standard_pkg = cls.upload_vnf_package(zip_path)
# for change_vnfpkg network/flavor change test
pkg_path_2 = utils.test_sample("functional/sol_v2_common",
"userdata_standard_change_vnfpkg_nw")
cls.new_nw_pkg, cls.new_nw_vnfd_id = cls.create_vnf_package(
pkg_path_2, image_path=image_path, userdata_path=userdata_path)
# for attach non-boot volume to VDU test
pkg_path_3 = utils.test_sample("functional/sol_v2_common",
"userdata_standard_with_non_boot_volume")
cls.non_boot_volume_pkg, cls.non_boot_volume_vnfd_id = (
cls.create_vnf_package(pkg_path_3, image_path=image_path,
userdata_path=userdata_path))
@classmethod
def tearDownClass(cls):
super(IndividualVnfcMgmtCcvpTest, cls).tearDownClass()
cls.delete_vnf_package(cls.standard_pkg)
cls.delete_vnf_package(cls.new_nw_pkg)
cls.delete_vnf_package(cls.non_boot_volume_pkg)
def setUp(self):
super().setUp()
def _get_vdu_indexes(self, inst, vdu):
return {
vnfc['metadata'].get('vdu_idx')
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']
if vnfc['vduId'] == vdu
}
def _get_vnfc_by_vdu_index(self, inst, vdu, index):
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
if (vnfc['vduId'] == vdu and
vnfc['metadata'].get('vdu_idx') == index):
return vnfc
def _get_vnfc_id(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
return vnfc['id']
def _get_vnfc_cps(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
return {cp_info['cpdId'] for cp_info in vnfc['vnfcCpInfo']}
def _get_vnfc_cp_net_id(self, inst, vdu, index, cp):
# this is for external CPs
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for cp_info in vnfc['vnfcCpInfo']:
if cp_info['cpdId'] == cp:
# must be found
ext_cp_id = cp_info['vnfExtCpId']
break
for ext_vl in inst['instantiatedVnfInfo']['extVirtualLinkInfo']:
for port in ext_vl['extLinkPorts']:
if port['cpInstanceId'] == ext_cp_id:
# must be found
return ext_vl['resourceHandle']['resourceId']
def _get_vnfc_cp_net_name(self, inst, vdu, index, cp):
# this is for internal CPs
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for cp_info in vnfc['vnfcCpInfo']:
if cp_info['cpdId'] == cp:
# must be found
link_port_id = cp_info['vnfLinkPortId']
break
for vl in inst['instantiatedVnfInfo']['vnfVirtualLinkResourceInfo']:
for port in vl['vnfLinkPorts']:
if port['id'] == link_port_id:
# must be found
return vl['vnfVirtualLinkDescId']
def _get_vnfc_image(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for key, value in vnfc['metadata'].items():
if key.startswith('image-'):
# must be found
return value
def _get_vnfc_flavor(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
# must exist
return vnfc['metadata']['flavor']
def _get_vnfc_storage_ids(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
storage_res_ids = vnfc.get('storageResourceIds', [])
return sorted(storage_res_ids)
def test_change_vnfpkg_nw(self):
"""Test change_vnfpkg with additional functions
* Note:
This test focuses change_vnfpkg with the following changes.
- adding external CP
- change internal network
- change flavor
TODO: add another patterns (ex. change extMgdVLs)
* About LCM operations:
This test includes the following operations.
- Create VNF instance
- 1. Instantiate VNF instance
- Show VNF instance / check
- 2. Change_vnfpkg operation
- Show VNF instance / check
- Terminate VNF instance
- Delete VNF instance
"""
net_ids = self.get_network_ids(['net0', 'net1', 'net_mgmt'])
subnet_ids = self.get_subnet_ids(['subnet0', 'subnet1'])
# Create VNF instance
create_req = paramgen.sample3_create(self.standard_vnfd_id)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
inst_id = body['id']
# 1. Instantiate VNF instance
instantiate_req = paramgen.sample3_instantiate(
net_ids, subnet_ids, self.auth_url)
instantiate_req['instantiationLevelId'] = "instantiation_level_2"
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Show VNF instance
resp, inst_1 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check number of VDUs and indexes
self.assertEqual({0, 1}, self._get_vdu_indexes(inst_1, 'VDU1'))
self.assertEqual({0}, self._get_vdu_indexes(inst_1, 'VDU2'))
# 2. Change_vnfpkg operation
change_vnfpkg_req = paramgen.sample5_change_vnfpkg(self.new_nw_vnfd_id,
net_ids, subnet_ids)
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Show VNF instance
resp, inst_2 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check vnfdId is changed
self.assertEqual(self.new_nw_vnfd_id, inst_2['vnfdId'])
# check images are changed
self.assertNotEqual(self._get_vnfc_image(inst_1, 'VDU1', 0),
self._get_vnfc_image(inst_2, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_image(inst_1, 'VDU1', 1),
self._get_vnfc_image(inst_2, 'VDU1', 1))
self.assertNotEqual(self._get_vnfc_image(inst_1, 'VDU2', 0),
self._get_vnfc_image(inst_2, 'VDU2', 0))
# check flavor is changed (VDU2 only)
self.assertNotEqual(self._get_vnfc_flavor(inst_1, 'VDU2', 0),
self._get_vnfc_flavor(inst_2, 'VDU2', 0))
# check external CPs; VDU1_CP4 and VDU2_CP4 are added
self.assertFalse('VDU1_CP4' in self._get_vnfc_cps(inst_1, 'VDU1', 0))
self.assertFalse('VDU1_CP4' in self._get_vnfc_cps(inst_1, 'VDU1', 1))
self.assertFalse('VDU2_CP4' in self._get_vnfc_cps(inst_1, 'VDU2', 0))
self.assertTrue('VDU1_CP4' in self._get_vnfc_cps(inst_2, 'VDU1', 0))
self.assertTrue('VDU1_CP4' in self._get_vnfc_cps(inst_2, 'VDU1', 1))
self.assertTrue('VDU2_CP4' in self._get_vnfc_cps(inst_2, 'VDU2', 0))
self.assertEqual(net_ids['net0'],
self._get_vnfc_cp_net_id(inst_2, 'VDU1', 0, 'VDU1_CP4'))
self.assertEqual(net_ids['net0'],
self._get_vnfc_cp_net_id(inst_2, 'VDU1', 1, 'VDU1_CP4'))
self.assertEqual(net_ids['net0'],
self._get_vnfc_cp_net_id(inst_2, 'VDU2', 0, 'VDU2_CP4'))
# check internal CPs; VDU1_CP3 and VDU2_CP3 are changed
self.assertEqual("internalVL2",
self._get_vnfc_cp_net_name(inst_1, 'VDU1', 0, 'VDU1_CP3'))
self.assertEqual("internalVL2",
self._get_vnfc_cp_net_name(inst_1, 'VDU1', 1, 'VDU1_CP3'))
self.assertEqual("internalVL2",
self._get_vnfc_cp_net_name(inst_1, 'VDU2', 0, 'VDU2_CP3'))
self.assertEqual("internalVL3",
self._get_vnfc_cp_net_name(inst_2, 'VDU1', 0, 'VDU1_CP3'))
self.assertEqual("internalVL3",
self._get_vnfc_cp_net_name(inst_2, 'VDU1', 1, 'VDU1_CP3'))
self.assertEqual("internalVL3",
self._get_vnfc_cp_net_name(inst_2, 'VDU2', 0, 'VDU2_CP3'))
# Terminate VNF instance
terminate_req = paramgen.sample5_terminate()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Delete VNF instance
self.exec_lcm_operation(self.delete_vnf_instance, inst_id)
def test_change_vnfpkg_nw_rollback(self):
"""Test rollback of change_vnfpkg with additional functions
* Note:
This test focuses rollback of change_vnfpkg with the following
changes.
- adding external CP
- change internal network
- change flavor
TODO: add another patterns (ex. change extMgdVLs)
* About LCM operations:
This test includes the following operations.
- Create VNF instance
- 1. Instantiate VNF instance
- Show VNF instance / check
- 2. Change_vnfpkg operation => FAILED_TEMP
- Rollback
- Show VNF instance / check
- Terminate VNF instance
- Delete VNF instance
"""
net_ids = self.get_network_ids(['net0', 'net1', 'net_mgmt'])
subnet_ids = self.get_subnet_ids(['subnet0', 'subnet1'])
# Create VNF instance
create_req = paramgen.sample3_create(self.standard_vnfd_id)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
inst_id = body['id']
# 1. Instantiate VNF instance
instantiate_req = paramgen.sample3_instantiate(
net_ids, subnet_ids, self.auth_url)
instantiate_req['instantiationLevelId'] = "instantiation_level_2"
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Show VNF instance
resp, inst_1 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check number of VDUs and indexes
self.assertEqual({0, 1}, self._get_vdu_indexes(inst_1, 'VDU1'))
self.assertEqual({0}, self._get_vdu_indexes(inst_1, 'VDU2'))
# 2. Change_vnfpkg operation
self.put_fail_file('change_vnfpkg')
change_vnfpkg_req = paramgen.sample5_change_vnfpkg(self.new_nw_vnfd_id,
net_ids, subnet_ids)
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_failed_temp(lcmocc_id)
self.rm_fail_file('change_vnfpkg')
# Rollback
resp, body = self.rollback_lcmocc(lcmocc_id)
self.assertEqual(202, resp.status_code)
self.wait_lcmocc_rolled_back(lcmocc_id)
# Show VNF instance
resp, inst_2 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check vnfdId is not changed
self.assertEqual(self.standard_vnfd_id, inst_2['vnfdId'])
# check images are not changed
self.assertEqual(self._get_vnfc_image(inst_1, 'VDU1', 0),
self._get_vnfc_image(inst_2, 'VDU1', 0))
self.assertEqual(self._get_vnfc_image(inst_1, 'VDU1', 1),
self._get_vnfc_image(inst_2, 'VDU1', 1))
self.assertEqual(self._get_vnfc_image(inst_1, 'VDU2', 0),
self._get_vnfc_image(inst_2, 'VDU2', 0))
# check flavor is not changed (VDU2 only)
self.assertEqual(self._get_vnfc_flavor(inst_1, 'VDU2', 0),
self._get_vnfc_flavor(inst_2, 'VDU2', 0))
# check external CPs; VDU1_CP4 and VDU2_CP4 are not added
self.assertFalse('VDU1_CP4' in self._get_vnfc_cps(inst_2, 'VDU1', 0))
self.assertFalse('VDU1_CP4' in self._get_vnfc_cps(inst_2, 'VDU1', 1))
self.assertFalse('VDU2_CP4' in self._get_vnfc_cps(inst_2, 'VDU2', 0))
# check internal CPs; VDU1_CP3 and VDU2_CP3 are not changed
self.assertEqual(
self._get_vnfc_cp_net_name(inst_1, 'VDU1', 0, 'VDU1_CP3'),
self._get_vnfc_cp_net_name(inst_2, 'VDU1', 0, 'VDU1_CP3'))
self.assertEqual(
self._get_vnfc_cp_net_name(inst_1, 'VDU1', 1, 'VDU1_CP3'),
self._get_vnfc_cp_net_name(inst_2, 'VDU1', 1, 'VDU1_CP3'))
self.assertEqual(
self._get_vnfc_cp_net_name(inst_1, 'VDU2', 0, 'VDU2_CP3'),
self._get_vnfc_cp_net_name(inst_2, 'VDU2', 0, 'VDU2_CP3'))
# Terminate VNF instance
terminate_req = paramgen.sample3_terminate()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Delete VNF instance
self.exec_lcm_operation(self.delete_vnf_instance, inst_id)
def test_instantiate_attach_non_boot_volume(self):
"""Test Instantiate with non-boot volume attached to VDU
* Note:
This test focuses on the non-boot volume attached by
OS::Cinder::VolumeAttachment in HOT being registered to
storageResourceIds.
* About LCM operations:
This test includes the following operations.
- Create VNF instance
- 1. Instantiate VNF instance
- Show VNF instance / check
- 2. Heal operation("all"=True)
- Show VNF instance / check
- 3. Heal operation("all" is not specified)
- Show VNF instance / check
- Terminate VNF instance
- Delete VNF instance
"""
net_ids = self.get_network_ids(['net0', 'net1', 'net_mgmt'])
subnet_ids = self.get_subnet_ids(['subnet0', 'subnet1'])
# Create VNF instance
create_req = paramgen.sample7_create(self.non_boot_volume_vnfd_id)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
inst_id = body['id']
# 1. Instantiate VNF instance
instantiate_req = paramgen.sample7_instantiate(
net_ids, subnet_ids, self.auth_url)
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Show VNF instance
resp, inst_1 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check number of VDUs and indexes
self.assertEqual({0}, self._get_vdu_indexes(inst_1, 'VDU1'))
# check storageResourceIds of attached non-boot volume
self.assertNotEqual([], self._get_vnfc_storage_ids(inst_1, 'VDU1', 0))
# 2. Heal operation("all"=True)
# VDU1-0 to heal
heal_req = paramgen.sample7_heal('a-001')
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Show VNF instance
resp, inst_2 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check id of VDU1-0 is changed
self.assertNotEqual(self._get_vnfc_id(inst_1, 'VDU1', 0),
self._get_vnfc_id(inst_2, 'VDU1', 0))
# check storageResourceIds of VDU1-0 is changed
self.assertNotEqual([], self._get_vnfc_storage_ids(inst_2, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_storage_ids(inst_1, 'VDU1', 0),
self._get_vnfc_storage_ids(inst_2, 'VDU1', 0))
# 3. Heal operation("all" is not specified)
# VDU1-0 to heal
heal_req['vnfcInstanceId'] = ['a-001']
del heal_req['additionalParams']['all']
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Show VNF instance
resp, inst_3 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check id of VDU1-0 is changed
self.assertNotEqual(self._get_vnfc_id(inst_2, 'VDU1', 0),
self._get_vnfc_id(inst_3, 'VDU1', 0))
# check storageResourceIds of VDU1-0 is not changed
self.assertEqual(self._get_vnfc_storage_ids(inst_2, 'VDU1', 0),
self._get_vnfc_storage_ids(inst_3, 'VDU1', 0))
# Terminate VNF instance
terminate_req = paramgen.sample7_terminate()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# Delete VNF instance
self.exec_lcm_operation(self.delete_vnf_instance, inst_id)

View File

@ -0,0 +1,917 @@
# Copyright (C) 2023 Fujitsu
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import string
from tacker.objects import fields
from tacker.tests.functional.sol_v2_common import base_v2
from tacker.tests.functional.sol_v2_common import paramgen
from tacker.tests import utils
SUPPORT_STRING_FOR_VNFD_ID = f"{string.ascii_letters}{string.digits}-._ "
MAX_VNFD_ID = f"{SUPPORT_STRING_FOR_VNFD_ID}max_vnfd_id"
MIN_VNFD_ID = f"{SUPPORT_STRING_FOR_VNFD_ID}min_vnfd_id"
NEW_MAX_VNFD_ID = f"{SUPPORT_STRING_FOR_VNFD_ID}new_max_vnfd_id"
UPD_NEW_MIN_VNFD_ID = f"{SUPPORT_STRING_FOR_VNFD_ID}upd_new_min_vnfd_id"
class IndividualVnfcMgmtErrorHandlingTest(base_v2.BaseSolV2Test):
@classmethod
def setUpClass(cls):
super(IndividualVnfcMgmtErrorHandlingTest, cls).setUpClass()
image_path = utils.test_etc_sample("etsi/nfv/common/Files/images",
"cirros-0.5.2-x86_64-disk.img")
userdata_path = utils.userdata("userdata_standard.py")
# vnf package for basic lcms tests max pattern
pkg_path_1 = utils.test_sample("functional/sol_v2_common",
"basic_lcms_max_individual_vnfc")
cls.max_pkg, cls.max_vnfd_id = cls.create_vnf_package(
pkg_path_1, image_path=image_path,
userdata_path=userdata_path, vnfd_id=MAX_VNFD_ID)
# vnf package for basic lcms tests min pattern
pkg_path_2 = utils.test_sample("functional/sol_v2_common",
"basic_lcms_min_individual_vnfc")
cls.min_pkg, cls.min_vnfd_id = cls.create_vnf_package(
pkg_path_2, userdata_path=userdata_path,
vnfd_id=MIN_VNFD_ID)
# vnf package for change vnf package max pattern
pkg_path_3 = utils.test_sample("functional/sol_v2_common",
"change_vnfpkg_max_individual_vnfc")
cls.new_max_pkg, cls.new_max_vnfd_id = cls.create_vnf_package(
pkg_path_3, userdata_path=userdata_path,
vnfd_id=NEW_MAX_VNFD_ID)
# vnf package for change vnf package or update min pattern
pkg_path_4 = utils.test_sample("functional/sol_v2_common",
"change_vnfpkg_or_update_min_individual_vnfc")
cls.upd_new_min_pkg, cls.upd_new_min_vnfd_id = cls.create_vnf_package(
pkg_path_4, image_path=image_path, userdata_path=userdata_path,
vnfd_id=UPD_NEW_MIN_VNFD_ID)
cls.expected_list_attrs = [
'id',
'operationState',
'stateEnteredTime',
'startTime',
'vnfInstanceId',
# 'grantId', # omitted
'operation',
'isAutomaticInvocation',
# 'operationParams', # omitted
'isCancelPending',
# 'cancelMode', # omitted
# 'error', # omitted
# 'resourceChanges', # omitted
# 'changedInfo', # omitted
# 'changedExtConnectivity', # omitted
# 'modificationsTriggeredByVnfPkgChange', # omitted
# 'vnfSnapshotInfoId', # omitted
'_links'
]
cls._pre_setting()
@classmethod
def tearDownClass(cls):
super(IndividualVnfcMgmtErrorHandlingTest, cls).tearDownClass()
cls.delete_vnf_package(cls.max_pkg)
cls.delete_vnf_package(cls.min_pkg)
cls.delete_vnf_package(cls.new_max_pkg)
cls.delete_vnf_package(cls.upd_new_min_pkg)
def setUp(self):
super().setUp()
@classmethod
def _pre_setting(cls):
# Create a new network and subnet to check the IP allocation of
# IPv4 and IPv6
ft_net0_name = 'ft-net0'
ft_net0_subs = {
'ft-ipv4-subnet0': {
'range': '100.100.100.0/24',
'ip_version': 4
},
'ft-ipv6-subnet0': {
'range': '1111:2222:3333::/64',
'ip_version': 6
}
}
ft_net0_id = cls.create_network(cls, ft_net0_name)
cls.addClassCleanup(cls.delete_network, cls, ft_net0_id)
for sub_name, val in ft_net0_subs.items():
# subnet is automatically deleted with network deletion
cls.create_subnet(
cls, ft_net0_id, sub_name, val['range'], val['ip_version'])
# Create a new network for change external connectivity
ft_net1_name = 'ft-net1'
ft_net1_subs = {
'ft-ipv4-subnet1': {
'range': '22.22.22.0/24',
'ip_version': 4
},
'ft-ipv6-subnet1': {
'range': '1111:2222:4444::/64',
'ip_version': 6
}
}
ft_net1_id = cls.create_network(cls, ft_net1_name)
cls.addClassCleanup(cls.delete_network, cls, ft_net1_id)
for sub_name, val in ft_net1_subs.items():
# subnet is automatically deleted with network deletion
cls.create_subnet(
cls, ft_net1_id, sub_name, val['range'], val['ip_version'])
cls.net_ids = cls.get_network_ids(
cls, ['net0', 'net1', 'net_mgmt', 'ft-net0', 'ft-net1'])
cls.subnet_ids = cls.get_subnet_ids(
cls, ['subnet0', 'subnet1', 'ft-ipv4-subnet0', 'ft-ipv6-subnet0',
'ft-ipv4-subnet1', 'ft-ipv6-subnet1'])
def _get_vdu_indexes(self, inst, vdu):
return {
vnfc['metadata'].get('vdu_idx')
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']
if vnfc['vduId'] == vdu
}
def _get_vnfc_metadata_keys(self, inst, vdu):
vnfc_metadata_keys = set()
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
if vnfc['vduId'] == vdu:
vnfc_metadata_keys.update(set(vnfc['metadata'].keys()))
return vnfc_metadata_keys
def _add_additional_params(self, req):
if not req.get('additionalParams'):
req['additionalParams'] = {}
req['additionalParams']['lcm-operation-user-data'] = (
'./UserData/userdata_standard.py')
req['additionalParams']['lcm-operation-user-data-class'] = (
'StandardUserData')
def _get_vnfc_by_vdu_index(self, inst, vdu, index):
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
if (vnfc['vduId'] == vdu and
vnfc['metadata'].get('vdu_idx') == index):
return vnfc
def _get_vnfc_image(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for key, value in vnfc['metadata'].items():
if key.startswith('image-'):
# must be found
return value
def _get_vnfc_flavor(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
# must exist
return vnfc['metadata']['flavor']
def _get_vnfc_cps(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
return {cp_info['cpdId'] for cp_info in vnfc['vnfcCpInfo']}
def _get_vnfc_cp_net_name(self, inst, vdu, index, cp):
# this is for internal CPs
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for cp_info in vnfc['vnfcCpInfo']:
if cp_info['cpdId'] == cp:
# must be found
link_port_id = cp_info['vnfLinkPortId']
break
for vl in inst['instantiatedVnfInfo']['vnfVirtualLinkResourceInfo']:
for port in vl['vnfLinkPorts']:
if port['id'] == link_port_id:
# must be found
return vl['vnfVirtualLinkDescId']
def _check_for_show_operation(
self, operation, expected_inst_attrs, inst_id,
vdu_result=None, image_result=None):
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
if operation == 'INSTANTIATE':
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.INSTANTIATED,
body['instantiationState'])
if operation == 'SCALE':
# check scaleStatus
scale_status = body['instantiatedVnfInfo']['scaleStatus']
self.assertGreater(len(scale_status), 0)
for status in scale_status:
self.assertIn('aspectId', status)
self.assertIn('scaleLevel', status)
# check instantiatedVnfInfo's information
# check number of VDU, and index
if vdu_result:
self.assertEqual(vdu_result['VDU1'],
self._get_vdu_indexes(body, 'VDU1'))
self.assertEqual(vdu_result['VDU2'],
self._get_vdu_indexes(body, 'VDU2'))
# check exist of VDU-image
if image_result:
for result_1 in image_result['VDU1']:
self.assertIn(
result_1,
self._get_vnfc_metadata_keys(body, 'VDU1'))
for result_2 in image_result['VDU2']:
self.assertIn(
result_2,
self._get_vnfc_metadata_keys(body, 'VDU2'))
return body
def test_retry_rollback_scale_out(self):
"""Test retry and rollback scale out operations
* About attributes:
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
* About LCM operations:
This test includes the following operations.
- 0. Create VNF
- 1. Instantiate VNF
- 2. Show VNF instance
- 3. Scale out operation(will fail)
- 4. Show VNF instance
- 5. Retry operation
- 6. Rollback scale out operation
- 7. Show VNF LCM operation occurrence
- 8. List VNF LCM operation occurrence
- 9. Terminate VNF
- 10. Delete VNF
"""
# 0. Create VNF
create_req = paramgen.create_vnf_max(
self.max_vnfd_id,
description="test for retry and rollback scale out")
_, body = self.create_vnf_instance(create_req)
inst_id = body['id']
# 1. Instantiate VNF instance
instantiate_req = paramgen.instantiate_vnf_max(
self.net_ids, self.subnet_ids, None, self.auth_url,
user_data=True)
resp, body = self.instantiate_vnf_instance(
inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 2. Show VNF instance(check for instantiate)
expected_inst_attrs = [
'id',
'vnfInstanceName',
'vnfInstanceDescription',
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
'vimConnectionInfo',
'instantiationState',
'instantiatedVnfInfo',
'metadata',
# 'extensions', # omitted
'_links'
]
_ = self._check_for_show_operation(
'INSTANTIATE', expected_inst_attrs, inst_id)
# 3. Scale out operation(will fail)
self.put_fail_file('scale_end')
scaleout_req = paramgen.scaleout_vnf_max()
self._add_additional_params(scaleout_req)
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_failed_temp(lcmocc_id)
# 4. Show VNF instance
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# 5. Retry scale out operation
resp, body = self.retry_lcmocc(lcmocc_id)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_delete(resp)
self.wait_lcmocc_failed_temp(lcmocc_id)
self.rm_fail_file('scale_end')
# 6. Rollback scale out operation
resp, body = self.rollback_lcmocc(lcmocc_id)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_delete(resp)
self.wait_lcmocc_rolled_back(lcmocc_id)
# 7. Show VNF LCM operation occurrence
expected_attrs = [
'id',
'operationState',
'stateEnteredTime',
'startTime',
'vnfInstanceId',
'grantId',
'operation',
'isAutomaticInvocation',
'operationParams',
'isCancelPending',
# 'cancelMode', # omitted
'error',
# 'resourceChanges', # omitted
# 'changedInfo', # omitted
# 'changedExtConnectivity', # omitted
# 'modificationsTriggeredByVnfPkgChange', # omitted
# 'vnfSnapshotInfoId', # omitted
'_links'
]
resp, body = self.show_lcmocc(lcmocc_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
# 8. List VNF LCM operation occurrence
resp, body = self.list_lcmocc()
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_index(resp)
for lcmocc in body:
self.check_resp_body(lcmocc, self.expected_list_attrs)
# 9. Terminate VNF instance
terminate_req = paramgen.terminate_vnf_max()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body.get('instantiationState'))
# 10. Delete VNF
self.exec_lcm_operation(self.delete_vnf_instance, inst_id)
def test_rollback_instantiate(self):
"""Test rollback instantiate operation
* About attributes:
Omit except for required attributes.
Only the following cardinality attributes are set.
- 1
- 1..N (1)
* About LCM operations:
This test includes the following operations.
- 0. Create VNF
- 1. Instantiate VNF(will fail)
- 2. Show VNF instance
- 3. Rollback instantiation operation
- 4. Show VNF LCM operation occurrence
- 5. List VNF LCM operation occurrence
- 6. Delete VNF
"""
# 0. Create VNF
create_req = paramgen.create_vnf_min(self.min_vnfd_id)
_, body = self.create_vnf_instance(create_req)
inst_id = body['id']
# 1. Instantiate VNF(will fail)
self.put_fail_file('instantiate_end')
instantiate_req = paramgen.instantiate_vnf_min()
self._add_additional_params(instantiate_req)
resp, body = self.instantiate_vnf_instance(
inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_failed_temp(lcmocc_id)
self.rm_fail_file('instantiate_end')
# 2. Show VNF instance
expected_inst_attrs = [
'id',
# 'vnfInstanceName', # omitted
# 'vnfInstanceDescription', # omitted
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
# 'vimConnectionInfo', # omitted
'instantiationState',
# 'instantiatedVnfInfo', # omitted
# 'metadata', # omitted
# 'extensions', # omitted
'_links'
]
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body.get('instantiationState'))
# 3. Rollback instantiation operation
resp, body = self.rollback_lcmocc(lcmocc_id)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_delete(resp)
self.wait_lcmocc_rolled_back(lcmocc_id)
# 4. Show VNF LCM operation occurrence
expected_attrs = [
'id',
'operationState',
'stateEnteredTime',
'startTime',
'vnfInstanceId',
'grantId',
'operation',
'isAutomaticInvocation',
'operationParams',
'isCancelPending',
# 'cancelMode', # omitted
'error',
# 'resourceChanges', # omitted
# 'changedInfo', # omitted
# 'changedExtConnectivity', # omitted
# 'modificationsTriggeredByVnfPkgChange', # omitted
# 'vnfSnapshotInfoId', # omitted
'_links'
]
resp, body = self.show_lcmocc(lcmocc_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
# 5. List VNF LCM operation occurrence
resp, body = self.list_lcmocc()
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_index(resp)
for lcmocc in body:
self.check_resp_body(lcmocc, self.expected_list_attrs)
# 6. Delete VNF
self.exec_lcm_operation(self.delete_vnf_instance, inst_id)
def test_rollback_chgextconn_and_update(self):
"""Test rollback change_ext_conn and update operation
* About attributes:
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
* About LCM operations:
This test includes the following operations.
- 0. Create VNF
- 1. Instantiate VNF
- 2. Show VNF instance
- 3. Change external connectivity(will fail)
- 4. Rollback change_ext_conn operation
- 5. Show VNF LCM operation occurrence
- 6. List VNF LCM operation occurrence
- 7. Update VNF(will fail)
- 8. Rollback update operation
- 9. Show VNF LCM operation occurrence
- 10. List VNF LCM operation occurrence
- 11. Terminate VNF
- 12. Delete VNF
"""
# 0. Create VNF
create_req = paramgen.create_vnf_max(self.max_vnfd_id)
_, body = self.create_vnf_instance(create_req)
inst_id = body['id']
# 1. Instantiate VNF
instantiate_req = paramgen.instantiate_vnf_max(
self.net_ids, self.subnet_ids, None, self.auth_url,
user_data=True)
self._add_additional_params(instantiate_req)
resp, body = self.instantiate_vnf_instance(
inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 2. Show VNF instance
expected_inst_attrs = [
'id',
# 'vnfInstanceName', # omitted
# 'vnfInstanceDescription', # omitted
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
'vimConnectionInfo',
'instantiationState',
'instantiatedVnfInfo',
# 'metadata', # omitted
# 'extensions', # omitted
'_links'
]
_ = self._check_for_show_operation(
'INSTANTIATE', expected_inst_attrs, inst_id)
# 3. Change external connectivity(will fail)
self.put_fail_file('change_external_connectivity_end')
change_ext_conn_req = paramgen.change_ext_conn_min(
self.net_ids, self.subnet_ids)
self._add_additional_params(change_ext_conn_req)
resp, body = self.change_ext_conn(inst_id, change_ext_conn_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_failed_temp(lcmocc_id)
self.rm_fail_file('change_external_connectivity_end')
# 4. Rollback change_ext_conn operation
resp, body = self.rollback_lcmocc(lcmocc_id)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_delete(resp)
self.wait_lcmocc_rolled_back(lcmocc_id)
# 5. Show VNF LCM operation occurrence
expected_attrs = [
'id',
'operationState',
'stateEnteredTime',
'startTime',
'vnfInstanceId',
'grantId',
'operation',
'isAutomaticInvocation',
'operationParams',
'isCancelPending',
# 'cancelMode', # omitted
'error',
# 'resourceChanges', # omitted
# 'changedInfo', # omitted
# 'changedExtConnectivity', # omitted
# 'modificationsTriggeredByVnfPkgChange', # omitted
# 'vnfSnapshotInfoId', # omitted
'_links'
]
resp, body = self.show_lcmocc(lcmocc_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
# Confirm that the operation failed in change_ext_conn_end after
# change_ext_conn.
self.assertIn(
'change_external_connectivity_end failed', body['error']['detail'])
# 6. List VNF LCM operation occurrence
resp, body = self.list_lcmocc()
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_index(resp)
for lcmocc in body:
self.check_resp_body(lcmocc, self.expected_list_attrs)
# 7. Update VNF(will fail)
self.put_fail_file('modify_information_start')
update_req = paramgen.update_vnf_min()
resp, body = self.update_vnf_instance(inst_id, update_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_failed_temp(lcmocc_id)
self.rm_fail_file('modify_information_start')
# 8. Rollback update operation
resp, body = self.rollback_lcmocc(lcmocc_id)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_delete(resp)
self.wait_lcmocc_rolled_back(lcmocc_id)
# 9. Show VNF LCM operation occurrence
expected_attrs = [
'id',
'operationState',
'stateEnteredTime',
'startTime',
'vnfInstanceId',
# 'grantId', # omitted
'operation',
'isAutomaticInvocation',
'operationParams',
'isCancelPending',
# 'cancelMode', # omitted
'error',
# 'resourceChanges', # omitted
# 'changedInfo', # omitted
# 'changedExtConnectivity', # omitted
# 'modificationsTriggeredByVnfPkgChange', # omitted
# 'vnfSnapshotInfoId', # omitted
'_links'
]
resp, body = self.show_lcmocc(lcmocc_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
# 10. List VNF LCM operation occurrence
resp, body = self.list_lcmocc()
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_index(resp)
for lcmocc in body:
self.check_resp_body(lcmocc, self.expected_list_attrs)
# 11. Terminate VNF
terminate_req = paramgen.terminate_vnf_min()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 12. Delete VNF
self.exec_lcm_operation(self.delete_vnf_instance, inst_id)
def test_rollback_change_vnfpkg(self):
"""Test rollback change_vnfpkg operation
* About attributes:
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
* About LCM operations:
This test includes the following operations.
- 0. Create VNF
- 1. Instantiate VNF
- 2. Show VNF instance
- 3. Change Current VNF Package(will fail)
- 4. Rollback change_vnfpkg operation
- 5. Show VNF LCM operation occurrence
- 6. Show VNF instance
- 7. Change Current VNF Package(will fail)
- 8. Rollback change_vnfpkg operation
- 9. Show VNF LCM operation occurrence
- 10. List VNF LCM operation occurrence
- 11. Show VNF instance
- 12. Terminate VNF
- 13. Delete VNF
"""
# 0. Create VNF
create_req = paramgen.create_vnf_max(
self.max_vnfd_id,
description="test for rollback change vnf package")
_, body = self.create_vnf_instance(create_req)
inst_id = body['id']
# 1. Instantiate VNF
instantiate_req = paramgen.instantiate_vnf_max(
self.net_ids, self.subnet_ids, None, self.auth_url,
user_data=True)
resp, body = self.instantiate_vnf_instance(
inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 2. Show VNF instance(check for instantiate)
expected_inst_attrs = [
'id',
'vnfInstanceName',
'vnfInstanceDescription',
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
'vimConnectionInfo',
'instantiationState',
'instantiatedVnfInfo',
'metadata',
# 'extensions', # omitted
'_links'
]
inst_2 = self._check_for_show_operation(
'INSTANTIATE', expected_inst_attrs, inst_id)
# 3. Change Current VNF Package(will fail)
self.put_fail_file('change_vnfpkg')
change_vnf_pkg_req = paramgen.change_vnf_pkg_individual_vnfc_max(
self.new_max_vnfd_id, self.net_ids, self.subnet_ids)
resp, body = self.change_vnfpkg(inst_id, change_vnf_pkg_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_failed_temp(lcmocc_id)
self.rm_fail_file('change_vnfpkg')
# 4. Rollback change_vnfpkg operation
resp, body = self.rollback_lcmocc(lcmocc_id)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_delete(resp)
self.wait_lcmocc_rolled_back(lcmocc_id)
# 5. Show VNF LCM operation occurrence
expected_attrs = [
'id',
'operationState',
'stateEnteredTime',
'startTime',
'vnfInstanceId',
'grantId',
'operation',
'isAutomaticInvocation',
'operationParams',
'isCancelPending',
# 'cancelMode', # omitted
'error',
# 'resourceChanges', # omitted
# 'changedInfo', # omitted
# 'changedExtConnectivity', # omitted
# 'modificationsTriggeredByVnfPkgChange', # omitted
# 'vnfSnapshotInfoId', # omitted
'_links'
]
resp, body = self.show_lcmocc(lcmocc_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
# 6. Show VNF instance
_, inst_6 = self.show_vnf_instance(inst_id)
# check vnfdId
self.assertEqual(self.max_vnfd_id, inst_6['vnfdId'])
# check images of VDU are not changed
self.assertEqual(self._get_vnfc_image(inst_2, 'VDU1', 0),
self._get_vnfc_image(inst_6, 'VDU1', 0))
self.assertEqual(self._get_vnfc_image(inst_2, 'VDU2', 0),
self._get_vnfc_image(inst_6, 'VDU2', 0))
# check flavor of VDU2 is not changed
self.assertEqual(self._get_vnfc_flavor(inst_2, 'VDU2', 0),
self._get_vnfc_flavor(inst_6, 'VDU2', 0))
# check external CPs, VDU1_CP6 and VDU2_CP6 are not added
self.assertFalse('VDU1_CP6' in self._get_vnfc_cps(inst_2, 'VDU1', 0))
self.assertFalse('VDU2_CP6' in self._get_vnfc_cps(inst_2, 'VDU2', 0))
self.assertFalse('VDU1_CP6' in self._get_vnfc_cps(inst_6, 'VDU1', 0))
self.assertFalse('VDU2_CP6' in self._get_vnfc_cps(inst_6, 'VDU2', 0))
# check internal CPs, VDU1_CP5 and VDU2_CP5 are not changed
self.assertEqual("internalVL3", self._get_vnfc_cp_net_name(
inst_2, 'VDU1', 0, 'VDU1_CP5'))
self.assertEqual("internalVL3", self._get_vnfc_cp_net_name(
inst_2, 'VDU2', 0, 'VDU2_CP5'))
self.assertEqual("internalVL3", self._get_vnfc_cp_net_name(
inst_6, 'VDU1', 0, 'VDU1_CP5'))
self.assertEqual("internalVL3", self._get_vnfc_cp_net_name(
inst_6, 'VDU2', 0, 'VDU2_CP5'))
# 7. Change Current VNF Package(will fail)
# This operation intentionally performs an operation outside the
# supported range, making it in the FAILED_TEMP state to test the
# rollback operation in this case.
# supported cases:
# 1. change VM created by image to VM created by new image
# 2. change VM created by volume to VM created by new volume
change_vnf_pkg_req = paramgen.change_vnf_pkg_individual_vnfc_min(
self.upd_new_min_vnfd_id, vdu2_old_vnfc='VDU2_CP2')
resp, body = self.change_vnfpkg(inst_id, change_vnf_pkg_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_failed_temp(lcmocc_id)
# 8. Rollback change_vnfpkg operation
resp, body = self.rollback_lcmocc(lcmocc_id)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_delete(resp)
self.wait_lcmocc_rolled_back(lcmocc_id)
# 9. Show VNF LCM operation occurrence
expected_attrs = [
'id',
'operationState',
'stateEnteredTime',
'startTime',
'vnfInstanceId',
'grantId',
'operation',
'isAutomaticInvocation',
'operationParams',
'isCancelPending',
# 'cancelMode', # omitted
'error',
# 'resourceChanges', # omitted
# 'changedInfo', # omitted
# 'changedExtConnectivity', # omitted
# 'modificationsTriggeredByVnfPkgChange', # omitted
# 'vnfSnapshotInfoId', # omitted
'_links'
]
resp, body = self.show_lcmocc(lcmocc_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
# Confirm that the operation failed in change vnf package progress
# while executing update_stack operation
self.assertIn('StackValidationFailed', body['error']['detail'])
# 10. List VNF LCM operation occurrence
resp, body = self.list_lcmocc()
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_index(resp)
for lcmocc in body:
self.check_resp_body(lcmocc, self.expected_list_attrs)
# 11. Show VNF instance
_, inst_11 = self.show_vnf_instance(inst_id)
# check vnfdId
self.assertEqual(self.max_vnfd_id, inst_11['vnfdId'])
# check images of VDU are not changed
self.assertEqual(self._get_vnfc_image(inst_2, 'VDU1', 0),
self._get_vnfc_image(inst_11, 'VDU1', 0))
self.assertEqual(self._get_vnfc_image(inst_2, 'VDU2', 0),
self._get_vnfc_image(inst_11, 'VDU2', 0))
# 12. Terminate VNF
terminate_req = paramgen.terminate_vnf_min()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 13. Delete VNF
self.exec_lcm_operation(self.delete_vnf_instance, inst_id)

View File

@ -0,0 +1,801 @@
# Copyright (C) 2023 Fujitsu
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import string
from tacker.objects import fields
from tacker.tests.functional.sol_v2_common import base_v2
from tacker.tests.functional.sol_v2_common import paramgen
from tacker.tests import utils
SUPPORT_STRING_FOR_VNFD_ID = f"{string.ascii_letters}{string.digits}-._ "
MAX_VNFD_ID = f"{SUPPORT_STRING_FOR_VNFD_ID}max_vnfd_id"
UPD_MAX_VNFD_ID = f"{SUPPORT_STRING_FOR_VNFD_ID}upd_max_vnfd_id"
class IndividualVnfcMgmtVariousLcmTest(base_v2.BaseSolV2Test):
@classmethod
def setUpClass(cls):
super(IndividualVnfcMgmtVariousLcmTest, cls).setUpClass()
image_path = utils.test_etc_sample("etsi/nfv/common/Files/images",
"cirros-0.5.2-x86_64-disk.img")
userdata_path = utils.userdata("userdata_standard.py")
# vnf package for basic lcms tests max pattern
pkg_path_1 = utils.test_sample("functional/sol_v2_common",
"basic_lcms_max_individual_vnfc")
cls.max_pkg, cls.max_vnfd_id = cls.create_vnf_package(
pkg_path_1, image_path=image_path,
userdata_path=userdata_path, vnfd_id=MAX_VNFD_ID)
# vnf package for update vnf max pattern
pkg_path_2 = utils.test_sample("functional/sol_v2_common",
"update_vnf_max_individual_vnfc")
cls.upd_max_pkg, cls.upd_max_vnfd_id = cls.create_vnf_package(
pkg_path_2, image_path=image_path,
userdata_path=userdata_path, vnfd_id=UPD_MAX_VNFD_ID)
cls._pre_setting()
@classmethod
def tearDownClass(cls):
super(IndividualVnfcMgmtVariousLcmTest, cls).tearDownClass()
cls.delete_vnf_package(cls.max_pkg)
cls.delete_vnf_package(cls.upd_max_pkg)
def setUp(self):
super().setUp()
@classmethod
def _pre_setting(cls):
# Create a new network and subnet to check the IP allocation of
# IPv4 and IPv6
ft_net0_name = 'ft-net0'
ft_net0_subs = {
'ft-ipv4-subnet0': {
'range': '100.100.100.0/24',
'ip_version': 4
},
'ft-ipv6-subnet0': {
'range': '1111:2222:3333::/64',
'ip_version': 6
}
}
ft_net0_id = cls.create_network(cls, ft_net0_name)
cls.addClassCleanup(cls.delete_network, cls, ft_net0_id)
for sub_name, val in ft_net0_subs.items():
# subnet is automatically deleted with network deletion
cls.create_subnet(
cls, ft_net0_id, sub_name, val['range'], val['ip_version'])
# Create a new network for change external connectivity
ft_net1_name = 'ft-net1'
ft_net1_subs = {
'ft-ipv4-subnet1': {
'range': '22.22.22.0/24',
'ip_version': 4
},
'ft-ipv6-subnet1': {
'range': '1111:2222:4444::/64',
'ip_version': 6
}
}
ft_net1_id = cls.create_network(cls, ft_net1_name)
cls.addClassCleanup(cls.delete_network, cls, ft_net1_id)
for sub_name, val in ft_net1_subs.items():
# subnet is automatically deleted with network deletion
cls.create_subnet(
cls, ft_net1_id, sub_name, val['range'], val['ip_version'])
cls.net_ids = cls.get_network_ids(
cls, ['net0', 'net1', 'net_mgmt', 'ft-net0', 'ft-net1'])
cls.subnet_ids = cls.get_subnet_ids(
cls, ['subnet0', 'subnet1', 'ft-ipv4-subnet0', 'ft-ipv6-subnet0',
'ft-ipv4-subnet1', 'ft-ipv6-subnet1'])
def _get_vdu_indexes(self, inst, vdu):
return {
vnfc['metadata'].get('vdu_idx')
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']
if vnfc['vduId'] == vdu
}
def _get_vnfc_metadata_keys(self, inst, vdu):
vnfc_metadata_keys = set()
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
if vnfc['vduId'] == vdu:
vnfc_metadata_keys.update(set(vnfc['metadata'].keys()))
return vnfc_metadata_keys
def _add_additional_params(self, req):
if not req.get('additionalParams'):
req['additionalParams'] = {}
req['additionalParams']['lcm-operation-user-data'] = (
'./UserData/userdata_standard.py')
req['additionalParams']['lcm-operation-user-data-class'] = (
'StandardUserData')
def _get_vnfc_by_vdu_index(self, inst, vdu, index):
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
if (vnfc['vduId'] == vdu and
vnfc['metadata'].get('vdu_idx') == index):
return vnfc
def _get_vnfc_id(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
return vnfc['id']
def _get_vnfc_image(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for key, value in vnfc['metadata'].items():
if key.startswith('image-'):
# must be found
return value
def _get_vnfc_storage_ids(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
return vnfc['storageResourceIds']
def _get_vnf_ext_cp_id(self, inst, vdu, index, cp):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for cp_info in vnfc['vnfcCpInfo']:
if cp_info['cpdId'] == cp:
# must be found
ext_cp_id = cp_info['vnfExtCpId']
break
return ext_cp_id
def _get_vnf_link_port_id(self, inst, vdu, index, cp):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for cp_info in vnfc['vnfcCpInfo']:
if cp_info['cpdId'] == cp:
# must be found
link_port_id = cp_info['vnfLinkPortId']
break
return link_port_id
def _check_for_show_operation(
self, operation, expected_inst_attrs, inst_id,
vdu_result=None, image_result=None):
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
if operation == 'INSTANTIATE':
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.INSTANTIATED,
body['instantiationState'])
if operation == 'SCALE':
# check scaleStatus
scale_status = body['instantiatedVnfInfo']['scaleStatus']
self.assertGreater(len(scale_status), 0)
for status in scale_status:
self.assertIn('aspectId', status)
self.assertIn('scaleLevel', status)
# check instantiatedVnfInfo's information
# check number of VDU, and index
if vdu_result:
self.assertEqual(vdu_result['VDU1'],
self._get_vdu_indexes(body, 'VDU1'))
self.assertEqual(vdu_result['VDU2'],
self._get_vdu_indexes(body, 'VDU2'))
# check exist of VDU-image
if image_result:
for result_1 in image_result['VDU1']:
self.assertIn(
result_1,
self._get_vnfc_metadata_keys(body, 'VDU1'))
for result_2 in image_result['VDU2']:
self.assertIn(
result_2,
self._get_vnfc_metadata_keys(body, 'VDU2'))
return body
def test_various_lcm_operations_before_and_after(self):
"""Test various vnflcm operations before and after
* About attributes:
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
* About LCM operations:
This test includes the following operations.
- 0. Create VNF
- 1. Instantiate VNF
- 2. Show VNF instance
- 3. Scale out operation
- 4. Show VNF instance(check for scale)
- 5. Heal VNF(vnfc)
- 6. Show VNF instance(check for heal)
- 7. Scale out operation
- 8. Show VNF instance(check for scale)
- 9. Scale in operation
- 10. Show VNF instance(check for scale)
- 11. Heal VNF(all with all=True parameter)
- 12. Show VNF instance(check for heal)
- 13. Scale in operation
- 14. Show VNF instance(check for scale)
- 15. Heal VNF(vnfc)
- 16. Show VNF instance(check for heal)
- 17. Scale out operation
- 18. Show VNF instance(check for scale)
- 19. Heal VNF(all with all=True parameter)
- 20. Show VNF instance(check for heal)
- 21. Change external connectivity
- 22. Show VNF instance(check for change-ext-conn)
- 23. Scale in operation
- 24. Show VNF instance(check for scale)
- 25. Update VNF
- 26. Show VNF instance(check for update)
- 27. Scale out operation
- 28. Heal VNF(all with omit all parameter)
- 29. Heal VNF(all with all=False parameter)
- 30. Heal VNF(all with all=True parameter)
- 31. Terminate VNF
- 32. Delete VNF
"""
# 0. Create VNF
create_req = paramgen.create_vnf_max(
self.max_vnfd_id, description="test for various lcm operations")
_, body = self.create_vnf_instance(create_req)
inst_id = body['id']
# 1. Instantiate VNF
instantiate_req = paramgen.instantiate_vnf_max(
self.net_ids, self.subnet_ids, None, self.auth_url,
user_data=True)
resp, body = self.instantiate_vnf_instance(
inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 2. Show VNF instance
expected_inst_attrs = [
'id',
'vnfInstanceName',
'vnfInstanceDescription',
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
'vimConnectionInfo',
'instantiationState',
'instantiatedVnfInfo',
'metadata',
# 'extensions', # omitted
'_links'
]
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
image_result = {'VDU1': {'image-VDU1-VirtualStorage-0'},
'VDU2': {'image-VDU2-VirtualStorage-0'}}
_ = self._check_for_show_operation(
'INSTANTIATE', expected_inst_attrs, inst_id,
vdu_result, image_result)
# 3. Scale out operation
scaleout_req = paramgen.scaleout_vnf_max()
self._add_additional_params(scaleout_req)
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 4. Show VNF instance(check for scale)
vdu_result = {'VDU1': {0, 1}, 'VDU2': {0}}
image_result = {'VDU1': {'image-VDU1-VirtualStorage-0',
'image-VDU1-VirtualStorage-1'},
'VDU2': {'image-VDU2-VirtualStorage-0'}}
inst_4 = self._check_for_show_operation(
'SCALE', expected_inst_attrs, inst_id,
vdu_result, image_result)
# 5. Heal VNF(vnfc)
vnfc_info = inst_4['instantiatedVnfInfo']['vnfcInfo']
vnfc_id = [vnfc['id'] for vnfc in vnfc_info
if (vnfc['vnfcResourceInfoId'] ==
self._get_vnfc_id(inst_4, 'VDU1', 1))][0]
heal_req = paramgen.heal_vnf_vnfc_max(vnfc_id)
self._add_additional_params(heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 6. Show VNF instance(check for heal)
inst_6 = self._check_for_show_operation(
'HEAL', expected_inst_attrs, inst_id)
# check id of VDU1 with index 1 is changed
self.assertNotEqual(self._get_vnfc_id(inst_4, 'VDU1', 1),
self._get_vnfc_id(inst_6, 'VDU1', 1))
# check image of VDU1 with index 1 is not changed
self.assertEqual(self._get_vnfc_image(inst_4, 'VDU1', 1),
self._get_vnfc_image(inst_6, 'VDU1', 1))
# 7. Scale out operation
scaleout_req = paramgen.scaleout_vnf_max()
self._add_additional_params(scaleout_req)
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 8. Show VNF instance(check for scale)
vdu_result = {'VDU1': {0, 1, 2}, 'VDU2': {0}}
image_result = {'VDU1': {'image-VDU1-VirtualStorage-0',
'image-VDU1-VirtualStorage-1',
'image-VDU1-VirtualStorage-2'},
'VDU2': {'image-VDU2-VirtualStorage-0'}}
_ = self._check_for_show_operation(
'SCALE', expected_inst_attrs, inst_id,
vdu_result, image_result)
# 9. Scale in operation
scalein_req = paramgen.scalein_vnf_max()
self._add_additional_params(scalein_req)
resp, body = self.scale_vnf_instance(inst_id, scalein_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 10. Show VNF instance(check for scale)
vdu_result = {'VDU1': {0, 1}, 'VDU2': {0}}
image_result = {'VDU1': {'image-VDU1-VirtualStorage-0',
'image-VDU1-VirtualStorage-1'},
'VDU2': {'image-VDU2-VirtualStorage-0'}}
inst_10 = self._check_for_show_operation(
'SCALE', expected_inst_attrs, inst_id,
vdu_result, image_result)
# 11. Heal VNF(all with all=True parameter)
heal_req = paramgen.heal_vnf_all_max_with_parameter(True)
self._add_additional_params(heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 12. Show VNF instance(check for heal)
vdu_result = {'VDU1': {0, 1}, 'VDU2': {0}}
inst_12 = self._check_for_show_operation(
'HEAL', expected_inst_attrs, inst_id, vdu_result)
# check all ids of VDU are changed
self.assertNotEqual(self._get_vnfc_id(inst_10, 'VDU1', 0),
self._get_vnfc_id(inst_12, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_id(inst_10, 'VDU1', 1),
self._get_vnfc_id(inst_12, 'VDU1', 1))
self.assertNotEqual(self._get_vnfc_id(inst_10, 'VDU2', 0),
self._get_vnfc_id(inst_12, 'VDU2', 0))
# check storage ids are changed
self.assertNotEqual(self._get_vnfc_storage_ids(inst_10, 'VDU1', 0),
self._get_vnfc_storage_ids(inst_12, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_storage_ids(inst_10, 'VDU1', 1),
self._get_vnfc_storage_ids(inst_12, 'VDU1', 1))
self.assertNotEqual(self._get_vnfc_storage_ids(inst_10, 'VDU2', 0),
self._get_vnfc_storage_ids(inst_12, 'VDU2', 0))
# check cps are changed
for cp_1 in ['VDU1_CP1', 'VDU1_CP2', 'VDU2_CP2']:
self.assertNotEqual(
self._get_vnf_ext_cp_id(inst_10, cp_1.split('_')[0], 0, cp_1),
self._get_vnf_ext_cp_id(inst_12, cp_1.split('_')[0], 0, cp_1))
for cp_2 in ['VDU1_CP3', 'VDU1_CP4', 'VDU1_CP5',
'VDU2_CP3', 'VDU2_CP4', 'VDU2_CP5']:
self.assertNotEqual(
self._get_vnf_link_port_id(inst_10,
cp_2.split('_')[0], 0, cp_2),
self._get_vnf_link_port_id(inst_12,
cp_2.split('_')[0], 0, cp_2))
for ext_cp in ['VDU1_CP1', 'VDU1_CP2']:
self.assertNotEqual(
self._get_vnf_ext_cp_id(
inst_10, ext_cp.split('_')[0], 1, ext_cp),
self._get_vnf_ext_cp_id(
inst_12, ext_cp.split('_')[0], 1, ext_cp))
for link_port_cp in ['VDU1_CP3', 'VDU1_CP4', 'VDU1_CP5']:
self.assertNotEqual(
self._get_vnf_link_port_id(
inst_10, link_port_cp.split('_')[0], 1, link_port_cp),
self._get_vnf_link_port_id(
inst_12, link_port_cp.split('_')[0], 1, link_port_cp))
# 13. Scale in operation
scalein_req = paramgen.scalein_vnf_max()
self._add_additional_params(scalein_req)
resp, body = self.scale_vnf_instance(inst_id, scalein_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 14. Show VNF instance(check for scale)
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
image_result = {'VDU1': {'image-VDU1-VirtualStorage-0'},
'VDU2': {'image-VDU2-VirtualStorage-0'}}
inst_14 = self._check_for_show_operation(
'SCALE', expected_inst_attrs, inst_id,
vdu_result, image_result)
# 15. Heal VNF(vnfc)
vnfc_info = inst_14['instantiatedVnfInfo']['vnfcInfo']
vnfc_id = [vnfc['id'] for vnfc in vnfc_info
if (vnfc['vnfcResourceInfoId'] ==
self._get_vnfc_id(inst_14, 'VDU1', 0))][0]
heal_req = paramgen.heal_vnf_vnfc_max(vnfc_id)
self._add_additional_params(heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 16. Show VNF instance(check for heal)
inst_16 = self._check_for_show_operation(
'HEAL', expected_inst_attrs, inst_id)
# check id of VDU1 with index 0 is changed
self.assertNotEqual(self._get_vnfc_id(inst_14, 'VDU1', 0),
self._get_vnfc_id(inst_16, 'VDU1', 0))
# check image of VDU1 with index 0 is not changed
self.assertEqual(self._get_vnfc_image(inst_14, 'VDU1', 0),
self._get_vnfc_image(inst_16, 'VDU1', 0))
# 17. Scale out operation
scaleout_req = paramgen.scaleout_vnf_max()
self._add_additional_params(scaleout_req)
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 18. Show VNF instance(check for scale)
vdu_result = {'VDU1': {0, 1}, 'VDU2': {0}}
image_result = {'VDU1': {'image-VDU1-VirtualStorage-0',
'image-VDU1-VirtualStorage-1'},
'VDU2': {'image-VDU2-VirtualStorage-0'}}
inst_18 = self._check_for_show_operation(
'SCALE', expected_inst_attrs, inst_id,
vdu_result, image_result)
# 19. Heal VNF(all with all=True parameter)
heal_req = paramgen.heal_vnf_all_max_with_parameter(True)
self._add_additional_params(heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 20. Show VNF instance(check for heal)
vdu_result = {'VDU1': {0, 1}, 'VDU2': {0}}
inst_20 = self._check_for_show_operation(
'HEAL', expected_inst_attrs, inst_id, vdu_result)
# check all ids of VDU are changed
self.assertNotEqual(self._get_vnfc_id(inst_18, 'VDU1', 0),
self._get_vnfc_id(inst_20, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_id(inst_18, 'VDU1', 1),
self._get_vnfc_id(inst_20, 'VDU1', 1))
self.assertNotEqual(self._get_vnfc_id(inst_18, 'VDU2', 0),
self._get_vnfc_id(inst_20, 'VDU2', 0))
# check storage ids are changed
self.assertNotEqual(self._get_vnfc_storage_ids(inst_18, 'VDU1', 0),
self._get_vnfc_storage_ids(inst_20, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_storage_ids(inst_18, 'VDU1', 1),
self._get_vnfc_storage_ids(inst_20, 'VDU1', 1))
self.assertNotEqual(self._get_vnfc_storage_ids(inst_18, 'VDU2', 0),
self._get_vnfc_storage_ids(inst_20, 'VDU2', 0))
# check cps are changed
for cp_1 in ['VDU1_CP1', 'VDU1_CP2', 'VDU2_CP2']:
self.assertNotEqual(
self._get_vnf_ext_cp_id(inst_18, cp_1.split('_')[0], 0, cp_1),
self._get_vnf_ext_cp_id(inst_20, cp_1.split('_')[0], 0, cp_1))
for cp_2 in ['VDU1_CP3', 'VDU1_CP4', 'VDU1_CP5',
'VDU2_CP3', 'VDU2_CP4', 'VDU2_CP5']:
self.assertNotEqual(
self._get_vnf_link_port_id(inst_18,
cp_2.split('_')[0], 0, cp_2),
self._get_vnf_link_port_id(inst_20,
cp_2.split('_')[0], 0, cp_2))
for ext_cp in ['VDU1_CP1', 'VDU1_CP2']:
self.assertNotEqual(
self._get_vnf_ext_cp_id(
inst_10, ext_cp.split('_')[0], 1, ext_cp),
self._get_vnf_ext_cp_id(
inst_12, ext_cp.split('_')[0], 1, ext_cp))
for link_port_cp in ['VDU1_CP3', 'VDU1_CP4', 'VDU1_CP5']:
self.assertNotEqual(
self._get_vnf_link_port_id(
inst_10, link_port_cp.split('_')[0], 1, link_port_cp),
self._get_vnf_link_port_id(
inst_12, link_port_cp.split('_')[0], 1, link_port_cp))
# 21. Change external connectivity
change_ext_conn_req = paramgen.change_ext_conn_max(
self.net_ids, self.subnet_ids, self.auth_url)
self._add_additional_params(change_ext_conn_req)
resp, body = self.change_ext_conn(inst_id, change_ext_conn_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 22. Show VNF instance(check for change-ext-conn)
inst_22 = self._check_for_show_operation(
'CHANGE_EXT_CONN', expected_inst_attrs, inst_id)
# check vnfExtCPIds of VDU are changed
for ext_cp in ['VDU1_CP1', 'VDU2_CP2']:
self.assertNotEqual(
self._get_vnf_ext_cp_id(
inst_20, ext_cp.split('_')[0], 0, ext_cp),
self._get_vnf_ext_cp_id(
inst_22, ext_cp.split('_')[0], 0, ext_cp))
if ext_cp.split('_')[0] == 'VDU1':
self.assertNotEqual(
self._get_vnf_ext_cp_id(
inst_20, ext_cp.split('_')[0], 1, ext_cp),
self._get_vnf_ext_cp_id(
inst_22, ext_cp.split('_')[0], 1, ext_cp))
# 23. Scale in operation
scalein_req = paramgen.scalein_vnf_max()
self._add_additional_params(scalein_req)
resp, body = self.scale_vnf_instance(inst_id, scalein_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 24. Show VNF instance(check for scale)
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
image_result = {'VDU1': {'image-VDU1-VirtualStorage-0'},
'VDU2': {'image-VDU2-VirtualStorage-0'}}
inst_24 = self._check_for_show_operation(
'SCALE', expected_inst_attrs, inst_id,
vdu_result, image_result)
# 25. Update VNF
# check attribute value before update VNF
# check usageState of max pattern VNF Package
self.check_package_usage(self.max_pkg, 'IN_USE')
# check usageState of update max pattern VNF Package
self.check_package_usage(self.upd_max_pkg)
# check vnfd id
self.assertEqual(self.max_vnfd_id, inst_24['vnfdId'])
# check vnfc info
vnfc_info = inst_24['instantiatedVnfInfo']['vnfcInfo']
self.assertGreater(len(vnfc_info), 1)
vnfc_ids = [vnfc['id'] for vnfc in vnfc_info]
for vnfc in vnfc_info:
self.assertIn('id', vnfc)
self.assertIn('vduId', vnfc)
self.assertIsNotNone(vnfc.get('vnfcState'))
self.assertIsNone(vnfc.get('vnfcConfigurableProperties'))
update_req = paramgen.update_vnf_max(self.upd_max_vnfd_id, vnfc_ids)
resp, body = self.update_vnf_instance(inst_id, update_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 26. Show VNF instance(check for update)
vdu_result = {'VDU1': {0}, 'VDU2': {0}}
inst_26 = self._check_for_show_operation(
'MODIFY_INFO', expected_inst_attrs, inst_id, vdu_result)
# check usageState of max pattern VNF Package
self.check_package_usage(self.max_pkg)
# check usageState of update max pattern VNF Package
self.check_package_usage(self.upd_max_pkg, 'IN_USE')
self.assertEqual(self.upd_max_vnfd_id, inst_26['vnfdId'])
self.assertEqual('new name', inst_26['vnfInstanceName'])
self.assertEqual('new description', inst_26['vnfInstanceDescription'])
dummy_key_value = {'dummy-key': 'dummy-value'}
self.assertEqual(dummy_key_value, inst_26['metadata'])
self.assertEqual(dummy_key_value, inst_26['extensions'])
self.assertEqual(dummy_key_value, inst_26['vnfConfigurableProperties'])
vim_connection_info = {
"vim2": {
"vimId": "ac2d2ece-5e49-4b15-b92d-b681e9c096d8",
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {
"endpoint": "http://127.0.0.1/identity/v3"
},
"accessInfo": {
"username": "dummy_user",
"region": "RegionOne",
"project": "dummy_project",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {
"dummy-key": "dummy-val"
}
}
}
self.assertEqual(vim_connection_info['vim2'],
inst_26['vimConnectionInfo']['vim2'])
# check vnfc info
vnfc_info = inst_26['instantiatedVnfInfo']['vnfcInfo']
self.assertEqual(vnfc_ids[0], vnfc_info[0]['id'])
self.assertEqual(vnfc_ids[1], vnfc_info[1]['id'])
self.assertEqual(dummy_key_value,
vnfc_info[0]['vnfcConfigurableProperties'])
self.assertEqual(dummy_key_value,
vnfc_info[1]['vnfcConfigurableProperties'])
# 27. Scale out operation
scaleout_req = paramgen.scaleout_vnf_max()
self._add_additional_params(scaleout_req)
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 28. Heal VNF(all with omit all parameter)
heal_req = paramgen.heal_vnf_all_max_with_parameter()
self._add_additional_params(heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_name = f'vnf-{inst_id}'
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("UPDATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2'])]
vdu1_0_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu1_1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][1]
vdu2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
self.assertEqual("CREATE_COMPLETE",
vdu1_0_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu1_1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu2_stack_after_heal['resource_status'])
# 29. Heal VNF(all with all=False parameter)
heal_req = paramgen.heal_vnf_all_max_with_parameter(False)
self._add_additional_params(heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("UPDATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2'])]
vdu1_0_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu1_1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][1]
vdu2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
self.assertEqual("CREATE_COMPLETE",
vdu1_0_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu1_1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu2_stack_after_heal['resource_status'])
# 30. Heal VNF(all with all=True parameter)
heal_req = paramgen.heal_vnf_all_max_with_parameter(True)
self._add_additional_params(heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("CREATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [
stack for stack in nested_stacks
if (stack['resource_name'] in
['VDU1', 'VDU2', 'VDU1-VirtualStorage',
'VDU2-VirtualStorage', 'internalVL3'])]
vdu1_0_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu1_1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][1]
vdu2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
storage1_0_stack_after_heal = [
stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1-VirtualStorage')][0]
storage1_1_stack_after_heal = [
stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1-VirtualStorage')][1]
storage2_stack_after_heal = [
stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2-VirtualStorage')][0]
network_stack_after_heal = [
stack for stack in temp_stacks if
(stack['resource_name'] == 'internalVL3')][0]
self.assertEqual("CREATE_COMPLETE",
vdu1_0_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu1_1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu2_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
storage1_0_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
storage1_1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
storage2_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
network_stack_after_heal['resource_status'])
# 31. Terminate VNF
terminate_req = paramgen.terminate_vnf_max()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 32. Delete VNF
self.exec_lcm_operation(self.delete_vnf_instance, inst_id)

118
tox.ini
View File

@ -22,149 +22,187 @@ deps =
commands =
stestr run --slowest --concurrency 1 {posargs}
[testenv:functional]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional run --slowest {posargs}
[testenv:dsvm-functional]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional run --slowest --concurrency 2 {posargs}
[testenv:dsvm-functional-sol-legacy-nfvo]
[testenv:ft-legacy-vim]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol/legacy_nfvo run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-vnflcm]
[testenv:ft-v1-tosca-vnflcm]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol/vnflcm run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-vnflcm-userdata]
[testenv:ft-v1-userdata-vnflcm]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol/vnflcm_userdata run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-vnfpkgm]
[testenv:ft-v1-vnfpkgm]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol/vnfpkgm run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-separated-nfvo]
[testenv:ft-v1-userdata-separated-nfvo]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_separated_nfvo run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-separated-nfvo-v2]
[testenv:ft-v2-df-userdata-separated-nfvo]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_separated_nfvo_v2 run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-kubernetes]
[testenv:ft-v1-k8s]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_kubernetes run --slowest --concurrency 2 {posargs}
[testenv:dsvm-functional-sol-v2-basic]
[testenv:ft-v2-df-userdata-ccvp]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2/basic run --slowest --concurrency 1 {posargs}
stestr --test-path=./tacker/tests/functional/sol_v2/vnflcm/df_userdata/change_vnfpkg run --slowest {posargs}
[testenv:dsvm-functional-sol-v2-vnflcm]
[testenv:ft-v2-st-userdata-basic]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2/vnflcm run --slowest --concurrency 1 {posargs}
stestr --test-path=./tacker/tests/functional/sol_v2/vnflcm/st_userdata/basic run --slowest {posargs}
[testenv:dsvm-functional-sol-v2-notification]
[testenv:ft-v2-st-userdata-ccvp]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2/vnflcm/st_userdata/change_vnfpkg run --slowest {posargs}
[testenv:ft-v2-df-userdata-basic-max]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2/vnflcm/df_userdata/basic_max run --slowest {posargs}
[testenv:ft-v2-df-userdata-basic-min]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2/vnflcm/df_userdata/basic_min run --slowest {posargs}
[testenv:ft-v2-df-userdata-update]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2/vnflcm/df_userdata/update run --slowest {posargs}
[testenv:ft-v2-df-userdata-scale]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2/vnflcm/df_userdata/scale run --slowest {posargs}
[testenv:ft-v2-df-userdata-err-handling]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2/vnflcm/df_userdata/err_handling run --slowest {posargs}
[testenv:ft-v2-df-userdata-notification]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2/notification run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-v2-prometheus]
[testenv:ft-v2-prometheus]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2/prometheus run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-v2-ubuntu-focal]
[testenv:ft-v2-st-userdata-basic-max]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2 run --slowest --concurrency 1 {posargs}
stestr --test-path=./tacker/tests/functional/sol_v2/vnflcm/st_userdata/basic_max run --slowest {posargs}
[testenv:dsvm-functional-sol-v2-individual-vnfc-mgmt]
[testenv:ft-v2-st-userdata-basic-min]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2_individual_vnfc_mgmt run --slowest --concurrency 1 {posargs}
stestr --test-path=./tacker/tests/functional/sol_v2/vnflcm/st_userdata/basic_min run --slowest {posargs}
[testenv:dsvm-functional-sol-kubernetes-v2]
[testenv:ft-v2-st-userdata-various-lcm]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2/vnflcm/st_userdata/various_lcm run --slowest {posargs}
[testenv:ft-v2-st-userdata-err-handling]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2/vnflcm/st_userdata/err_handling run --slowest {posargs}
[testenv:ft-v2-k8s]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_kubernetes_v2 run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol_kubernetes_oidc_auth]
[testenv:ft-k8s-oidc-auth]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_kubernetes_oidc_auth run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-multi-tenant]
[testenv:ft-v1-userdata-multi-tenant]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_multi_tenant run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-kubernetes-multi-tenant]
[testenv:ft-v1-k8s-multi-tenant]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_kubernetes_multi_tenant run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-v2-az-retry]
[testenv:ft-v2-st-userdata-az-retry]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_v2_az_retry run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-enhanced-policy-sol]
[testenv:ft-enhanced-policy-sol]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_enhanced_policy/sol run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-enhanced-policy-sol-kubernetes]
[testenv:ft-k8s-enhanced-policy-sol]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_enhanced_policy/sol_kubernetes run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-https-v2]
[testenv:ft-v2-df-userdata-https]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_https_v2 run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-encrypt-cred-barbican]
[testenv:ft-v2-df-userdata-encrypt-cred-barbican]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_encrypt_cred_v2 run --slowest --concurrency 1 {posargs}
[testenv:dsvm-functional-sol-encrypt-cred-local]
[testenv:ft-v2-df-userdata-encrypt-cred-local]
setenv = {[testenv]setenv}
# Temporarily change directory permissions to read the contents of the fernet
@ -178,13 +216,13 @@ commands_post =
allowlist_externals =
sudo
[testenv:dsvm-functional-sol-terraform-v2]
[testenv:ft-v2-terraform]
setenv = {[testenv]setenv}
commands =
stestr --test-path=./tacker/tests/functional/sol_terraform_v2 run --slowest --concurrency 1 {posargs}
[testenv:dsvm-compliance-sol-api]
[testenv:ft-v1-compliance-sol]
passenv =
{[testenv]passenv}
*_PROXY

338
zuul.d/jobs/base.yaml Normal file
View File

@ -0,0 +1,338 @@
- job:
name: tacker-ft-openstack-base
parent: devstack
description: |
Abstraction multinodes job for SOL devstack-based functional tests
nodeset: openstack-4-nodes-jammy
pre-run: playbooks/devstack/pre.yaml
run: playbooks/devstack/run.yaml
post-run: playbooks/devstack/post.yaml
roles:
- zuul: openstack-infra/devstack
timeout: 10800
required-projects:
- openstack/cinder
- openstack/glance
- openstack/keystone
- openstack/neutron
- openstack/nova
- openstack/placement
- openstack/aodh
- openstack/horizon
- openstack/barbican
- openstack/heat
- openstack/networking-sfc
- openstack/python-barbicanclient
- openstack/python-tackerclient
- openstack/tacker
- openstack/tacker-horizon
vars:
devstack_localrc:
CELLSV2_SETUP: singleconductor
PHYSICAL_NETWORK: mgmtphysnet0
OVS_BRIDGE_MAPPINGS: public:br-ex,mgmtphysnet0:br-infra
DATABASE_TYPE: mysql
KEYSTONE_SERVICE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
MYSQL_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
GNOCCHI_SERVICE_HOST: "{{ hostvars['controller-tacker']['nodepool']['private_ipv4'] }}"
Q_SERVICE_PLUGIN_CLASSES: ovn-router,networking_sfc.services.flowclassifier.plugin.FlowClassifierPlugin,neutron.services.qos.qos_plugin.QoSPlugin,qos
L2_AGENT_EXTENSIONS: qos
DEVSTACK_PARALLEL: True
OVN_L3_CREATE_PUBLIC_NETWORK: true
OVN_DBS_LOG_LEVEL: dbg
Q_ML2_PLUGIN_MECHANISM_DRIVERS: ovn,logger
test_matrix_configs: [neutron]
zuul_work_dir: src/opendev.org/openstack/tacker
host-vars:
controller:
devstack_local_conf:
post-config:
# NOTE: workaround for Heat timeout due to communication
# problem between nova and neutron.
$NEUTRON_CONF:
DEFAULT:
client_socket_timeout: 60
$NEUTRON_DHCP_CONF:
DEFAULT:
enable_isolated_metadata: True
# NOTE: workaround for Cinder image volume cache problem.
# https://bugs.launchpad.net/cinder/+bug/1953704
$CINDER_CONF:
lvmdriver-1:
image_volume_cache_enabled: False
devstack_plugins:
heat: https://opendev.org/openstack/heat
networking-sfc: https://opendev.org/openstack/networking-sfc
aodh: https://opendev.org/openstack/aodh
barbican: https://opendev.org/openstack/barbican
devstack_services:
# Core services enabled for this branch.
# This list replaces the test-matrix.
# Shared services
mysql: true
rabbit: true
tls-proxy: false
# Keystone services
key: true
# Glance services
g-api: true
# Nova services
n-api: true
n-api-meta: true
n-cond: true
n-cpu: false
n-novnc: true
n-sch: true
placement-api: true
# OVN services
ovn-controller: true
ovn-northd: true
ovs-vswitchd: true
ovsdb-server: true
# Neutron services
q-svc: true
q-qos: true
q-ovn-metadata-agent: true
# Cinder services
c-api: true
c-sch: true
c-vol: true
cinder: true
# Services we don't need.
# This section is not really needed, it's for readability.
horizon: false
tempest: false
swift: false
s-account: false
s-container: false
s-object: false
s-proxy: false
c-bak: false
tox_install_siblings: false
controller-tacker:
devstack_plugins:
tacker: https://opendev.org/openstack/tacker
devstack_services:
# OVN services
ovn-controller: true
ovs-vswitchd: true
ovsdb-server: true
# Neutron services
q-ovn-metadata-agent: true
# Tacker services
tacker: true
tacker-conductor: true
group-vars:
compute:
# Since a VirtualInterfaceCreateException occurs during a test,
# the setting of network-vif-plugged is changed by the reference of
# the following URL.
# https://bugs.launchpad.net/heat/+bug/1694371
devstack_local_conf:
post-config:
$NOVA_CONF:
DEFAULT:
vif_plugging_is_fatal: False
subnode:
devstack_localrc:
CELLSV2_SETUP: singleconductor
PHYSICAL_NETWORK: mgmtphysnet0
OVS_BRIDGE_MAPPINGS: public:br-ex,mgmtphysnet0:br-infra
DATABASE_TYPE: mysql
KEYSTONE_SERVICE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
MYSQL_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
GNOCCHI_SERVICE_HOST: "{{ hostvars['controller-tacker']['nodepool']['private_ipv4'] }}"
TACKER_HOST: "{{ hostvars['controller-tacker']['nodepool']['private_ipv4'] }}"
TACKER_MODE: standalone
IS_ZUUL_FT: True
Q_SERVICE_PLUGIN_CLASSES: "qos,\
networking_sfc.services.flowclassifier.plugin.FlowClassifierPlugin,\
neutron.services.qos.qos_plugin.QoSPlugin,\
ovn-router"
L2_AGENT_EXTENSIONS: qos
ENABLE_CHASSIS_AS_GW: false
Q_ML2_PLUGIN_MECHANISM_DRIVERS: ovn,logger
devstack_services:
# To override the parent job's definitions.
tls-proxy: false
# OVN services
ovn-controller: true
ovs-vswitchd: true
ovsdb-server: true
# Neutron services
q-ovn-metadata-agent: true
- job:
name: tacker-ft-k8s-base
parent: devstack
description: |
Abstraction multinodes job for SOL devstack-based kubernetes functional
tests
nodeset: openstack-k8s-3-nodes-jammy
pre-run: playbooks/devstack/pre.yaml
run: playbooks/devstack/run.yaml
post-run: playbooks/devstack/post.yaml
roles:
- zuul: openstack-infra/devstack
timeout: 10800
required-projects:
- openstack/barbican
- openstack/cinder
- openstack/devstack-plugin-container
- openstack/glance
- openstack/heat
- openstack/horizon
- openstack/keystone
- openstack/neutron
- openstack/nova
- openstack/placement
- openstack/python-barbicanclient
- openstack/python-tackerclient
- openstack/tacker
- openstack/tacker-horizon
host-vars:
controller:
devstack_plugins:
barbican: https://opendev.org/openstack/barbican
heat: https://opendev.org/openstack/heat
neutron: https://opendev.org/openstack/neutron
devstack_services:
base: false
c-api: true
c-bak: false
c-sch: true
c-vol: true
cinder: true
coredns: false
etcd3: true
g-api: true
g-reg: true
horizon: false
key: true
mysql: true
n-api-meta: true
n-api: true
n-cond: true
n-cpu: false
n-novnc: true
n-sch: true
neutron: true
placement-api: true
placement-client: true
ovn-controller: true
ovn-northd: true
ovs-vswitchd: true
ovsdb-server: true
q-qos: true
q-svc: true
q-ovn-metadata-agent: true
rabbit: true
s-account: false
s-container: false
s-object: false
s-proxy: false
swift: false
tempest: false
tls-proxy: false
tox_install_siblings: false
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
kubernetes_vim:
stack_retries: 120
devstack_services:
ovn-controller: true
ovn-northd: true
ovs-vswitchd: true
ovsdb-server: true
q-ovn-metadata-agent: true
tacker: true
tacker-conductor: true
devstack_plugins:
tacker: https://opendev.org/openstack/tacker
controller-k8s:
devstack_local_conf: {}
devstack_plugins:
devstack-plugin-container: https://opendev.org/openstack/devstack-plugin-container
devstack_services:
etcd3: false
ovn-controller: true
ovn-northd: true
ovs-vswitchd: true
ovsdb-server: true
q-ovn-metadata-agent: true
container: true
k8s-master: true
tox_install_siblings: false
group-vars:
subnode:
devstack_localrc:
CELLSV2_SETUP: singleconductor
DATABASE_TYPE: mysql
IS_ZUUL_FT: True
K8S_API_SERVER_IP: "{{ hostvars['controller-k8s']['nodepool']['private_ipv4'] }}"
KEYSTONE_SERVICE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
CONTAINER_ENGINE: crio
K8S_VERSION: "1.30.5"
CRIO_VERSION: "1.30.5"
MYSQL_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
OVS_BRIDGE_MAPPINGS: public:br-ex,mgmtphysnet0:br-infra
PHYSICAL_NETWORK: mgmtphysnet0
TACKER_HOST: "{{ hostvars['controller-tacker']['nodepool']['private_ipv4'] }}"
TACKER_MODE: standalone
USE_PYTHON3: true
ENABLE_CHASSIS_AS_GW: false
Q_ML2_PLUGIN_MECHANISM_DRIVERS: ovn,logger
# TODO(ueha): Remove this workarround if the Zuul jobs succeed with GLOBAL_VENV=true
GLOBAL_VENV: false
K8S_TOKEN: "9agf12.zsu5uh2m4pzt3qba"
devstack_services:
dstat: false
horizon: false
n-api-meta: false
n-api: false
n-cauth: false
n-cond: false
n-cpu: true
n-novnc: false
n-obj: false
n-sch: false
ovn-controller: true
ovs-vswitchd: true
ovsdb-server: true
q-ovn-metadata-agent: true
tls-proxy: false
vars:
devstack_localrc:
CELLSV2_SETUP: singleconductor
DATABASE_TYPE: mysql
ETCD_USE_RAMDISK: true
IS_ZUUL_FT: True
KEYSTONE_SERVICE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
L2_AGENT_EXTENSIONS: qos
MYSQL_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
OVS_BRIDGE_MAPPINGS: public:br-ex,mgmtphysnet0:br-infra
PHYSICAL_NETWORK: mgmtphysnet0
Q_SERVICE_PLUGIN_CLASSES: ovn-router,neutron.services.qos.qos_plugin.QoSPlugin,qos
TACKER_HOST: "{{ hostvars['controller-tacker']['nodepool']['private_ipv4'] }}"
TACKER_MODE: standalone
USE_PYTHON3: true
DEVSTACK_PARALLEL: True
OVN_L3_CREATE_PUBLIC_NETWORK: true
OVN_DBS_LOG_LEVEL: dbg
Q_ML2_PLUGIN_MECHANISM_DRIVERS: ovn,logger
# TODO(ueha): Remove this workarround if the Zuul jobs succeed with GLOBAL_VENV=true
GLOBAL_VENV: false
devstack_local_conf:
post-config:
$NEUTRON_DHCP_CONF:
DEFAULT:
enable_isolated_metadata: True
k8s_api_url: "https://{{ hostvars['controller-k8s']['nodepool']['private_ipv4'] }}:6443"
k8s_ssl_verify: true
helm_version: "3.15.4"
test_matrix_configs: [neutron]
zuul_work_dir: src/opendev.org/openstack/tacker
zuul_copy_output:
'{{ devstack_log_dir }}/kubernetes': 'logs'

View File

@ -0,0 +1,77 @@
- job:
name: tacker-ft-v2-k8s
parent: tacker-ft-k8s-base
description: |
V2 Kubernetes functional tests
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
v2_vnfm:
kubernetes_vim_rsc_wait_timeout: 800
prometheus_plugin:
fault_management: true
performance_management: true
auto_scaling: true
auto_healing: true
test_rule_with_promtool: true
tox_envlist: ft-v2-k8s
vars:
prometheus_setup: true
- job:
name: tacker-ft-k8s-oidc-auth
parent: tacker-ft-v2-k8s
description: |
V1 Kubernetes functional tests for OIDC Auth
host-vars:
controller-tacker:
tox_envlist: ft-k8s-oidc-auth
vars:
keycloak_host: "{{ hostvars['controller-k8s']['nodepool']['private_ipv4'] }}"
keycloak_http_port: 8080
keycloak_https_port: 8443
voting: false
- job:
name: tacker-ft-k8s-enhanced-policy-sol
parent: tacker-ft-v2-k8s
description: |
Enhanced policy Kubernetes functional tests
host-vars:
controller-tacker:
tox_envlist: ft-k8s-enhanced-policy-sol
devstack_local_conf:
post-config:
$TACKER_CONF:
oslo_policy:
enhanced_tacker_policy: True
vars:
config_enhanced_policy: true
voting: false
- job:
name: tacker-ft-v1-k8s
parent: tacker-ft-k8s-base
description: |
V1 Kubernetes functional tests
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
database:
max_pool_size: 0
tox_envlist: ft-v1-k8s
- job:
name: tacker-ft-v1-k8s-multi-tenant
parent: tacker-ft-k8s-base
description: |
V1 Kubernetes functional tests for multi tenant
host-vars:
controller-tacker:
tox_envlist: ft-v1-k8s-multi-tenant
vars:
setup_multi_tenant: true

396
zuul.d/jobs/openstack.yaml Normal file
View File

@ -0,0 +1,396 @@
- job:
name: tacker-ft-v2-df-userdata-basic-max
parent: tacker-ft-openstack-base
description: |
V2 DefaultUserData functional tests for max pattern
host-vars:
controller-tacker:
tox_envlist: ft-v2-df-userdata-basic-max
- job:
name: tacker-ft-v2-df-userdata-basic-min
parent: tacker-ft-openstack-base
description: |
V2 DefaultUserData functional tests for min pattern
host-vars:
controller-tacker:
tox_envlist: ft-v2-df-userdata-basic-min
- job:
name: tacker-ft-v2-df-userdata-update
parent: tacker-ft-openstack-base
description: |
V2 DefaultUserData functional tests for Modify
host-vars:
controller-tacker:
tox_envlist: ft-v2-df-userdata-update
- job:
name: tacker-ft-v2-df-userdata-scale
parent: tacker-ft-openstack-base
description: |
V2 DefaultUserData functional tests for Scale
host-vars:
controller-tacker:
tox_envlist: ft-v2-df-userdata-scale
- job:
name: tacker-ft-v2-df-userdata-ccvp
parent: tacker-ft-openstack-base
description: |
V2 DefaultUserData functional tests for Change Current VNF Package
host-vars:
controller-tacker:
tox_envlist: ft-v2-df-userdata-ccvp
- job:
name: tacker-ft-v2-df-userdata-err-handling
parent: tacker-ft-openstack-base
description: |
V2 DefaultUserData functional tests for Error Handling
host-vars:
controller-tacker:
tox_envlist: ft-v2-df-userdata-err-handling
- job:
name: tacker-ft-v2-st-userdata-basic
parent: tacker-ft-openstack-base
description: |
V2 StandardUserData functional tests
host-vars:
controller-tacker:
tox_envlist: ft-v2-st-userdata-basic
- job:
name: tacker-ft-v2-st-userdata-basic-max
parent: tacker-ft-openstack-base
description: |
V2 StandardUserData functional tests for max pattern
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
$TACKER_CONF:
v2_vnfm:
# Notes: The List API will return 'Link' Header in response
# body only if page_size is not 0. This job will check 'Link'
# Header, so these page_size parameters are set to 1.
vnf_instance_page_size: 1
lcm_op_occ_page_size: 1
tox_envlist: ft-v2-st-userdata-basic-max
- job:
name: tacker-ft-v2-st-userdata-basic-min
parent: tacker-ft-openstack-base
description: |
V2 StandardUserData functional tests for min pattern
host-vars:
controller-tacker:
tox_envlist: ft-v2-st-userdata-basic-min
- job:
name: tacker-ft-v2-st-userdata-ccvp
parent: tacker-ft-openstack-base
description: |
V2 StandardUserData functional tests for Change Current VNF Package