From: Pablo Montes Moreno Date: Mon, 27 Feb 2017 11:33:10 +0000 (+0100) Subject: Implemented basic test for RO. It runs basic RO functionality tests as well as scenar... X-Git-Tag: v2.0.0~42 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=refs%2Fchanges%2F92%2F1192%2F2;p=osm%2FRO.git Implemented basic test for RO. It runs basic RO functionality tests as well as scenario based tests. Tested with different vendors OpenStack installations Change-Id: I3217da96e1ae7e42942b6f5a4e127fab7ed25635 Signed-off-by: Pablo Montes Moreno --- diff --git a/openmanoclient.py b/openmanoclient.py index 0d9aa4ae..c11f747e 100644 --- a/openmanoclient.py +++ b/openmanoclient.py @@ -74,7 +74,7 @@ class openmanoclient(): self.datacenter_id = kwargs.get("datacenter_id") self.datacenter_name = kwargs.get("datacenter_name") self.datacenter = None - self.logger = logging.getLogger('manoclient') + self.logger = logging.getLogger(kwargs.get('logger','manoclient')) if kwargs.get("debug"): self.logger.setLevel(logging.DEBUG) @@ -424,7 +424,10 @@ class openmanoclient(): Return: Raises an exception on error, not found, found several, not free Obtain a dictionary with format {'result': text indicating deleted} ''' - return self._del_item("datacenters", uuid, name, all_tenants=True) + if not uuid: + # check that exist + uuid = self._get_item_uuid("datacenters", uuid, name, all_tenants=True) + return self._del_item("datacenters", uuid, name, all_tenants=None) def create_datacenter(self, descriptor=None, descriptor_format=None, name=None, vim_url=None, **kwargs): #, type="openvim", public=False, description=None): @@ -832,9 +835,14 @@ class openmanoclient(): Return: Raises an exception on error Obtain a dictionary with format {'tenant':{new_tenant_info}} ''' - if item not in ("tenants", "networks"): - raise OpenmanoBadParamsException("Unknown value for item '{}', must be 'tenants' or 'nets'".format(str(item))) - + if item not in ("tenants", "networks", "images"): + raise OpenmanoBadParamsException("Unknown value for item '{}', must be 'tenants', 'nets' or " + "images".format(str(item))) + + image_actions = ['list','get','show','delete'] + if item == "images" and action not in image_actions: + raise OpenmanoBadParamsException("Only available actions for item '{}' are {}\n" + "Requested action was '{}'".format(item, ', '.join(image_actions), action)) if all_tenants: tenant_text = "/any" else: diff --git a/test/RO_tests/empy_volume/scenario_additional_disk_empty_volume.yaml b/test/RO_tests/empy_volume/scenario_additional_disk_empty_volume.yaml new file mode 100644 index 00000000..61f5f38a --- /dev/null +++ b/test/RO_tests/empy_volume/scenario_additional_disk_empty_volume.yaml @@ -0,0 +1,40 @@ +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## +--- +schema_version: 2 +scenario: + name: vnf_additional_disk_empty_volume + description: Just deploy vnf_2_disks + public: false # if available for other tenants + vnfs: + vnf_2_disks: # vnf name in the scenario + #identify an already openmano uploaded VNF either by vnf_id (uuid, prefered) or vnf_name + #vnf_id: 0c0dcc20-c5d5-11e6-a9fb-fa163e2ae06e #prefered id method + vnf_name: vnf_additional_disk_empty_volume #can fail if several vnfs matches this name + #graph: {"y":399,"x":332,"ifaces":{"left":[["xe0","d"],["xe1","d"]],"bottom":[["eth0","v"],["eth1","m"]]}} + networks: + mgmt: + # Connections based on external networks (datacenter nets) must include the external network in the list of nodes + type: bridge + external: true #this will be connected outside + interfaces: + - vnf_2_disks: mgmt0 + diff --git a/test/RO_tests/empy_volume/vnfd_additional_disk_empty_volume.yaml b/test/RO_tests/empy_volume/vnfd_additional_disk_empty_volume.yaml new file mode 100644 index 00000000..0e0f3eb3 --- /dev/null +++ b/test/RO_tests/empy_volume/vnfd_additional_disk_empty_volume.yaml @@ -0,0 +1,63 @@ +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## +--- +vnf: + name: vnf_additional_disk_empty_volume + description: VNF with additional volume based on image + # class: parent # Optional. Used to organize VNFs + external-connections: + - name: mgmt0 + type: mgmt # "mgmt" (autoconnect to management net), "bridge", "data" + VNFC: TEMPLATE-VM # Virtual Machine this interface belongs to + local_iface_name: mgmt0 # interface name inside this Virtual Machine (must be defined in the VNFC section) + description: Management interface + VNFC: # Virtual machine array + - name: TEMPLATE-VM # name of Virtual Machine + description: TEMPLATE description + image name: ubuntu16.04 + # image metadata: {"bus":"ide", "os_type":"windows", "use_incremental": "no" } #Optional + # processor: #Optional + # model: Intel(R) Xeon(R) CPU E5-4620 0 @ 2.20GHz + # features: ["64b", "iommu", "lps", "tlbps", "hwsv", "dioc", "ht"] + # hypervisor: #Optional + # type: QEMU-kvm + # version: "10002|12001|2.6.32-358.el6.x86_64" + vcpus: 1 # Only for traditional cloud VMs. Number of virtual CPUs (oversubscription is allowed). + ram: 1000 # Only for traditional cloud VMs. Memory in MBytes (not from hugepages, oversubscription is allowed) + disk: 5 # disk size in GiB, by default 1 + #numas: + #- paired-threads: 5 # "cores", "paired-threads", "threads" + # paired-threads-id: [ [0,1], [2,3], [4,5], [6,7], [8,9] ] # By default follows incremental order + # memory: 14 # GBytes + # interfaces: [] + bridge-ifaces: + - name: mgmt0 + vpci: "0000:00:0a.0" # Optional. Virtual PCI address + bandwidth: 1 Mbps # Optional. Informative only + # mac_address: '20:33:45:56:77:46' #avoid this option if possible + # model: 'virtio' # ("virtio","e1000","ne2k_pci","pcnet","rtl8139") By default, it is automatically filled by libvirt + devices: # Optional, order determines device letter asignation (hda, hdb, ...) + - type: disk # "disk","cdrom","xml" + size: 1 + # image metadata: {"bus":"ide", "os_type":"windows", "use_incremental": "no" } + # vpci: "0000:00:03.0" # Optional, not for disk or cdrom + # Additional Virtual Machines would be included here + diff --git a/test/RO_tests/floating_ip/scenario_floating_ip.yaml b/test/RO_tests/floating_ip/scenario_floating_ip.yaml new file mode 100644 index 00000000..48470508 --- /dev/null +++ b/test/RO_tests/floating_ip/scenario_floating_ip.yaml @@ -0,0 +1,40 @@ +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## +--- +schema_version: 2 +scenario: + name: vnf_floating_ip + description: vnf_floating_ip + public: false # if available for other tenants + vnfs: + vnf_floating_ip: # vnf name in the scenario + #identify an already openmano uploaded VNF either by vnf_id (uuid, prefered) or vnf_name + #vnf_id: 0c0dcc20-c5d5-11e6-a9fb-fa163e2ae06e #prefered id method + vnf_name: vnf_floating_ip #can fail if several vnfs matches this name + #graph: {"y":399,"x":332,"ifaces":{"left":[["xe0","d"],["xe1","d"]],"bottom":[["eth0","v"],["eth1","m"]]}} + networks: + mgmt: + # Connections based on external networks (datacenter nets) must include the external network in the list of nodes + type: bridge + external: true #this will be connected outside + interfaces: + - vnf_floating_ip: mgmt0 + diff --git a/test/RO_tests/floating_ip/vnfd_floating_ip.yaml b/test/RO_tests/floating_ip/vnfd_floating_ip.yaml new file mode 100644 index 00000000..47c73ba1 --- /dev/null +++ b/test/RO_tests/floating_ip/vnfd_floating_ip.yaml @@ -0,0 +1,59 @@ +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## +--- +vnf: + name: vnf_floating_ip + description: VNF disabling port_security option in mgmt interface + # class: parent # Optional. Used to organize VNFs + external-connections: + - name: mgmt0 + type: mgmt # "mgmt" (autoconnect to management net), "bridge", "data" + VNFC: vnf_floating_ip # Virtual Machine this interface belongs to + local_iface_name: mgmt0 # interface name inside this Virtual Machine (must be defined in the VNFC section) + description: Management interface + VNFC: # Virtual machine array + - name: vnf_floating_ip # name of Virtual Machine + description: vnf_floating_ip + image name: ubuntu16.04 + # image metadata: {"bus":"ide", "os_type":"windows", "use_incremental": "no" } #Optional + # processor: #Optional + # model: Intel(R) Xeon(R) CPU E5-4620 0 @ 2.20GHz + # features: ["64b", "iommu", "lps", "tlbps", "hwsv", "dioc", "ht"] + # hypervisor: #Optional + # type: QEMU-kvm + # version: "10002|12001|2.6.32-358.el6.x86_64" + vcpus: 1 # Only for traditional cloud VMs. Number of virtual CPUs (oversubscription is allowed). + ram: 1000 # Only for traditional cloud VMs. Memory in MBytes (not from hugepages, oversubscription is allowed) + disk: 5 # disk size in GiB, by default 1 + #numas: + #- paired-threads: 5 # "cores", "paired-threads", "threads" + # paired-threads-id: [ [0,1], [2,3], [4,5], [6,7], [8,9] ] # By default follows incremental order + # memory: 14 # GBytes + # interfaces: [] + bridge-ifaces: + - name: mgmt0 + vpci: "0000:00:0a.0" # Optional. Virtual PCI address + bandwidth: 1 Mbps # Optional. Informative only + floating-ip: True + # mac_address: '20:33:45:56:77:46' #avoid this option if possible + # model: 'virtio' # ("virtio","e1000","ne2k_pci","pcnet","rtl8139") By default, it is automatically filled by libvirt + # Additional Virtual Machines would be included here + diff --git a/test/RO_tests/image_based_volume/scenario_additional_disk_based_image.yaml b/test/RO_tests/image_based_volume/scenario_additional_disk_based_image.yaml new file mode 100644 index 00000000..20f28cb4 --- /dev/null +++ b/test/RO_tests/image_based_volume/scenario_additional_disk_based_image.yaml @@ -0,0 +1,40 @@ +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## +--- +schema_version: 2 +scenario: + name: vnf_additional_disk_based_image + description: Just deploy vnf_2_disks + public: false # if available for other tenants + vnfs: + vnf_2_disks: # vnf name in the scenario + #identify an already openmano uploaded VNF either by vnf_id (uuid, prefered) or vnf_name + #vnf_id: 0c0dcc20-c5d5-11e6-a9fb-fa163e2ae06e #prefered id method + vnf_name: vnf_additional_disk_based_image #can fail if several vnfs matches this name + #graph: {"y":399,"x":332,"ifaces":{"left":[["xe0","d"],["xe1","d"]],"bottom":[["eth0","v"],["eth1","m"]]}} + networks: + mgmt: + # Connections based on external networks (datacenter nets) must include the external network in the list of nodes + type: bridge + external: true #this will be connected outside + interfaces: + - vnf_2_disks: mgmt0 + diff --git a/test/RO_tests/image_based_volume/vnfd_additional_disk_based_image.yaml b/test/RO_tests/image_based_volume/vnfd_additional_disk_based_image.yaml new file mode 100644 index 00000000..57898318 --- /dev/null +++ b/test/RO_tests/image_based_volume/vnfd_additional_disk_based_image.yaml @@ -0,0 +1,64 @@ +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## +--- +vnf: + name: vnf_additional_disk_based_image + description: VNF with additional volume based on image + # class: parent # Optional. Used to organize VNFs + external-connections: + - name: mgmt0 + type: mgmt # "mgmt" (autoconnect to management net), "bridge", "data" + VNFC: TEMPLATE-VM # Virtual Machine this interface belongs to + local_iface_name: mgmt0 # interface name inside this Virtual Machine (must be defined in the VNFC section) + description: Management interface + VNFC: # Virtual machine array + - name: TEMPLATE-VM # name of Virtual Machine + description: TEMPLATE description + image name: image_name.qcow2 + # image metadata: {"bus":"ide", "os_type":"windows", "use_incremental": "no" } #Optional + # processor: #Optional + # model: Intel(R) Xeon(R) CPU E5-4620 0 @ 2.20GHz + # features: ["64b", "iommu", "lps", "tlbps", "hwsv", "dioc", "ht"] + # hypervisor: #Optional + # type: QEMU-kvm + # version: "10002|12001|2.6.32-358.el6.x86_64" + vcpus: 1 # Only for traditional cloud VMs. Number of virtual CPUs (oversubscription is allowed). + ram: 1000 # Only for traditional cloud VMs. Memory in MBytes (not from hugepages, oversubscription is allowed) + disk: 5 # disk size in GiB, by default 1 + #numas: + #- paired-threads: 5 # "cores", "paired-threads", "threads" + # paired-threads-id: [ [0,1], [2,3], [4,5], [6,7], [8,9] ] # By default follows incremental order + # memory: 14 # GBytes + # interfaces: [] + bridge-ifaces: + - name: mgmt0 + vpci: "0000:00:0a.0" # Optional. Virtual PCI address + bandwidth: 1 Mbps # Optional. Informative only + # mac_address: '20:33:45:56:77:46' #avoid this option if possible + # model: 'virtio' # ("virtio","e1000","ne2k_pci","pcnet","rtl8139") By default, it is automatically filled by libvirt + devices: # Optional, order determines device letter asignation (hda, hdb, ...) + - type: disk # "disk","cdrom","xml" + image name: image_name.qcow2 + size: 2 + # image metadata: {"bus":"ide", "os_type":"windows", "use_incremental": "no" } + # vpci: "0000:00:03.0" # Optional, not for disk or cdrom + # Additional Virtual Machines would be included here + diff --git a/test/RO_tests/no_port_security/scenario_vnf_no_port_security.yaml b/test/RO_tests/no_port_security/scenario_vnf_no_port_security.yaml new file mode 100644 index 00000000..07c85abb --- /dev/null +++ b/test/RO_tests/no_port_security/scenario_vnf_no_port_security.yaml @@ -0,0 +1,40 @@ +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## +--- +schema_version: 2 +scenario: + name: vnf_no_port_security + description: vnf_no_port_security + public: false # if available for other tenants + vnfs: + vnf_no_port_security: # vnf name in the scenario + #identify an already openmano uploaded VNF either by vnf_id (uuid, prefered) or vnf_name + #vnf_id: 0c0dcc20-c5d5-11e6-a9fb-fa163e2ae06e #prefered id method + vnf_name: vnf_no_port_security #can fail if several vnfs matches this name + #graph: {"y":399,"x":332,"ifaces":{"left":[["xe0","d"],["xe1","d"]],"bottom":[["eth0","v"],["eth1","m"]]}} + networks: + mgmt: + # Connections based on external networks (datacenter nets) must include the external network in the list of nodes + type: bridge + external: true #this will be connected outside + interfaces: + - vnf_no_port_security: mgmt0 + diff --git a/test/RO_tests/no_port_security/vnfd_no_port_security.yaml b/test/RO_tests/no_port_security/vnfd_no_port_security.yaml new file mode 100644 index 00000000..4815298a --- /dev/null +++ b/test/RO_tests/no_port_security/vnfd_no_port_security.yaml @@ -0,0 +1,59 @@ +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## +--- +vnf: + name: vnf_no_port_security + description: VNF disabling port_security option in mgmt interface + # class: parent # Optional. Used to organize VNFs + external-connections: + - name: mgmt0 + type: mgmt # "mgmt" (autoconnect to management net), "bridge", "data" + VNFC: vnf_no_port_security # Virtual Machine this interface belongs to + local_iface_name: mgmt0 # interface name inside this Virtual Machine (must be defined in the VNFC section) + description: Management interface + VNFC: # Virtual machine array + - name: vnf_no_port_security # name of Virtual Machine + description: vnf_no_port_security + image name: ubuntu16.04 + # image metadata: {"bus":"ide", "os_type":"windows", "use_incremental": "no" } #Optional + # processor: #Optional + # model: Intel(R) Xeon(R) CPU E5-4620 0 @ 2.20GHz + # features: ["64b", "iommu", "lps", "tlbps", "hwsv", "dioc", "ht"] + # hypervisor: #Optional + # type: QEMU-kvm + # version: "10002|12001|2.6.32-358.el6.x86_64" + vcpus: 1 # Only for traditional cloud VMs. Number of virtual CPUs (oversubscription is allowed). + ram: 1000 # Only for traditional cloud VMs. Memory in MBytes (not from hugepages, oversubscription is allowed) + disk: 5 # disk size in GiB, by default 1 + #numas: + #- paired-threads: 5 # "cores", "paired-threads", "threads" + # paired-threads-id: [ [0,1], [2,3], [4,5], [6,7], [8,9] ] # By default follows incremental order + # memory: 14 # GBytes + # interfaces: [] + bridge-ifaces: + - name: mgmt0 + vpci: "0000:00:0a.0" # Optional. Virtual PCI address + bandwidth: 1 Mbps # Optional. Informative only + port-security: False + # mac_address: '20:33:45:56:77:46' #avoid this option if possible + # model: 'virtio' # ("virtio","e1000","ne2k_pci","pcnet","rtl8139") By default, it is automatically filled by libvirt + # Additional Virtual Machines would be included here + diff --git a/test/RO_tests/simple_cloud_init/scenario_simple-cloud-init.yaml b/test/RO_tests/simple_cloud_init/scenario_simple-cloud-init.yaml new file mode 100644 index 00000000..18ed3e9b --- /dev/null +++ b/test/RO_tests/simple_cloud_init/scenario_simple-cloud-init.yaml @@ -0,0 +1,34 @@ +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## +--- +schema_version: 2 +scenario: + name: simple-cloud-init + description: Simple network scenario consisting of a single VNF connected to an external network + vnfs: + linux1: # vnf/net name in the scenario + vnf_name: linux-cloud-init # VNF name as introduced in OPENMANO DB + networks: + mgmt: # provide a name for this net or connection + external: true + interfaces: + - linux1: eth0 # Node and its interface + diff --git a/test/RO_tests/simple_cloud_init/vnfd_linux-cloud-init.yaml b/test/RO_tests/simple_cloud_init/vnfd_linux-cloud-init.yaml new file mode 100644 index 00000000..92e14fb2 --- /dev/null +++ b/test/RO_tests/simple_cloud_init/vnfd_linux-cloud-init.yaml @@ -0,0 +1,67 @@ +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## + +--- +schema_version: "0.2" +vnf: + name: linux-cloud-init + description: Single-VM VNF with a traditional cloud VM based on generic Linux OS + external-connections: + - name: eth0 + type: mgmt + description: General purpose interface + VNFC: linux-VM + local_iface_name: eth0 + VNFC: + - name: linux-VM + description: Generic Linux Virtual Machine + #Copy the image to a compute path and edit this path + image name: ubuntu16.04 + vcpus: 1 # Only for traditional cloud VMs. Number of virtual CPUs (oversubscription is allowed). + ram: 2048 # Only for traditional cloud VMs. Memory in MBytes (not from hugepages, oversubscription is allowed) + disk: 20 + bridge-ifaces: + - name: eth0 + vpci: "0000:00:11.0" + numas: [] + boot-data: + key-pairs: + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCy2w9GHMKKNkpCmrDK2ovc3XBYDETuLWwaW24S+feHhLBQiZlzh3gSQoINlA+2ycM9zYbxl4BGzEzpTVyCQFZv5PidG4m6ox7LR+KYkDcITMyjsVuQJKDvt6oZvRt6KbChcCi0n2JJD/oUiJbBFagDBlRslbaFI2mmqmhLlJ5TLDtmYxzBLpjuX4m4tv+pdmQVfg7DYHsoy0hllhjtcDlt1nn05WgWYRTu7mfQTWfVTavu+OjIX3e0WN6NW7yIBWZcE/Q9lC0II3W7PZDE3QaT55se4SPIO2JTdqsx6XGbekdG1n6adlduOI27sOU5m4doiyJ8554yVbuDB/z5lRBD alfonso.tiernosepulveda@telefonica.com + users: + - name: atierno + key-pairs: + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCy2w9GHMKKNkpCmrDK2ovc3XBYDETuLWwaW24S+feHhLBQiZlzh3gSQoINlA+2ycM9zYbxl4BGzEzpTVyCQFZv5PidG4m6ox7LR+KYkDcITMyjsVuQJKDvt6oZvRt6KbChcCi0n2JJD/oUiJbBFagDBlRslbaFI2mmqmhLlJ5TLDtmYxzBLpjuX4m4tv+pdmQVfg7DYHsoy0hllhjtcDlt1nn05WgWYRTu7mfQTWfVTavu+OjIX3e0WN6NW7yIBWZcE/Q9lC0II3W7PZDE3QaT55se4SPIO2JTdqsx6XGbekdG1n6adlduOI27sOU5m4doiyJ8554yVbuDB/z5lRBD alfonso.tiernosepulveda@telefonica.com + boot-data-drive: true + config-files: + - content: | + auto enp0s3 + iface enp0s3 inet dhcp + dest: /etc/network/interfaces.d/enp0s3.cfg + permissions: '0644' + owner: root:root + - content: | + #! /bin/bash + ls -al >> /var/log/osm.log + dest: /etc/rc.local + permissions: '0755' + - content: "file content" + dest: /etc/test_delete + diff --git a/test/RO_tests/simple_linux/scenario_simple_linux.yaml b/test/RO_tests/simple_linux/scenario_simple_linux.yaml new file mode 100644 index 00000000..a7c2087b --- /dev/null +++ b/test/RO_tests/simple_linux/scenario_simple_linux.yaml @@ -0,0 +1,34 @@ +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## +--- +schema_version: 2 +scenario: + name: simple + description: Simple network scenario consisting of a single VNF connected to an external network + vnfs: + linux1: # vnf/net name in the scenario + vnf_name: linux # VNF name as introduced in OPENMANO DB + networks: + mgmt: # provide a name for this net or connection + external: true + interfaces: + - linux1: eth0 # Node and its interface + diff --git a/test/RO_tests/simple_linux/vnfd_linux.yaml b/test/RO_tests/simple_linux/vnfd_linux.yaml new file mode 100644 index 00000000..47c84984 --- /dev/null +++ b/test/RO_tests/simple_linux/vnfd_linux.yaml @@ -0,0 +1,42 @@ +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## +--- +vnf: + name: linux + description: Single-VM VNF with a traditional cloud VM based on generic Linux OS + external-connections: + - name: eth0 + type: bridge + VNFC: linux-VM + local_iface_name: eth0 + description: General purpose interface + VNFC: + - name: linux-VM + description: Generic Linux Virtual Machine + #Copy the image to a compute path and edit this path + image name: image_name.qcow2 + vcpus: 1 # Only for traditional cloud VMs. Number of virtual CPUs (oversubscription is allowed). + ram: 1024 # Only for traditional cloud VMs. Memory in MBytes (not from hugepages, oversubscription is allowed) + disk: 10 + bridge-ifaces: + - name: eth0 + vpci: "0000:00:11.0" + numas: [] diff --git a/test/RO_tests/simple_multi_vnfc/scenario_multi_vnfc.yaml b/test/RO_tests/simple_multi_vnfc/scenario_multi_vnfc.yaml new file mode 100644 index 00000000..8de79937 --- /dev/null +++ b/test/RO_tests/simple_multi_vnfc/scenario_multi_vnfc.yaml @@ -0,0 +1,34 @@ +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## +--- +schema_version: 2 +scenario: + name: simple_multi_vnfc + description: Simple network scenario consisting of a multi VNFC VNF connected to an external network + vnfs: + linux1: # vnf/net name in the scenario + vnf_name: linux_2VMs_v02 # VNF name as introduced in OPENMANO DB + networks: + mgmt: # provide a name for this net or connection + external: true + interfaces: + - linux1: control0 # Node and its interface + diff --git a/test/RO_tests/simple_multi_vnfc/vnfd_linux_2VMs_v02.yaml b/test/RO_tests/simple_multi_vnfc/vnfd_linux_2VMs_v02.yaml new file mode 100644 index 00000000..3a09672e --- /dev/null +++ b/test/RO_tests/simple_multi_vnfc/vnfd_linux_2VMs_v02.yaml @@ -0,0 +1,104 @@ +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## +--- +schema_version: "0.2" +vnf: + name: linux_2VMs_v02 + description: "Example of a linux VNF consisting of two VMs with one internal network" + # class: parent # Optional. Used to organize VNFs + internal-connections: + - name: internalnet + description: internalnet + type: e-lan + implementation: overlay + ip-profile: + ip-version: IPv4 + subnet-address: 192.168.1.0/24 + gateway-address: 192.168.1.1 + dns-address: 8.8.8.8 + dhcp: + enabled: true + start-address: 192.168.1.100 + count: 100 + elements: + - VNFC: linux_2VMs-VM1 + local_iface_name: xe0 + ip_address: 192.168.1.2 + - VNFC: linux_2VMs-VM2 + local_iface_name: xe0 + ip_address: 192.168.1.3 + external-connections: + - name: control0 + type: mgmt + VNFC: linux_2VMs-VM1 + local_iface_name: eth0 + description: control interface VM1 + - name: control1 + type: mgmt + VNFC: linux_2VMs-VM2 + local_iface_name: eth0 + description: control interface VM2 + - name: in + type: bridge + VNFC: linux_2VMs-VM1 + local_iface_name: xe1 + description: data interface input + - name: out + type: bridge + VNFC: linux_2VMs-VM2 + local_iface_name: xe1 + description: data interface output + VNFC: + - name: linux_2VMs-VM1 + description: "Linux VM1 with 4 CPUs, 2 GB RAM and 3 bridge interfaces" + #Copy the image to a compute path and edit this path + image name: TestVM + disk: 10 + vcpus: 4 + ram: 2048 + bridge-ifaces: + - name: eth0 + vpci: "0000:00:09.0" + bandwidth: 1 Mbps # Optional, informative only + - name: xe0 + vpci: "0000:00:11.0" + bandwidth: 1 Mbps + - name: xe1 + vpci: "0000:00:12.0" + bandwidth: 1 Mbps + - name: linux_2VMs-VM2 + description: "Linux VM2 with 2 CPUs, 2 GB RAM and 3 bridge interfaces" + #Copy the image to a compute path and edit this path + image name: TestVM + disk: 10 + vcpus: 2 + ram: 2048 + bridge-ifaces: + - name: eth0 + vpci: "0000:00:09.0" + bandwidth: 1 Mbps # Optional, informative only + - name: xe0 + vpci: "0000:00:11.0" + bandwidth: 1 Mbps + - name: xe1 + vpci: "0000:00:12.0" + bandwidth: 1 Mbps + diff --git a/test/test_RO.py b/test/test_RO.py new file mode 100755 index 00000000..d6667556 --- /dev/null +++ b/test/test_RO.py @@ -0,0 +1,623 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: nfvlabs@tid.es +## + +''' +Module for testing openmano functionality. It uses openmanoclient.py for invoking openmano +''' +__author__="Pablo Montes" +__date__ ="$16-Feb-2017 17:08:16$" +__version__="0.0.1" +version_date="Feb 2017" + +import logging +import imp +import os +from optparse import OptionParser +import unittest +import string +import inspect +import random +import traceback +import glob +import yaml +import sys +import time + +global test_number +global test_directory +global scenario_test_folder +global test_image_name +global management_network + +''' +IMPORTANT NOTE +All unittest classes for code based tests must have prefix 'test_' in order to be taken into account for tests +''' +class test_tenant_operations(unittest.TestCase): + test_index = 1 + tenant_name = None + test_text = None + + @classmethod + def setUpClass(cls): + logger.info("{}. {}".format(test_number, cls.__name__)) + + @classmethod + def tearDownClass(cls): + globals().__setitem__('test_number', globals().__getitem__('test_number') + 1) + + def tearDown(self): + exec_info = sys.exc_info() + if exec_info == (None, None, None): + logger.info(self.__class__.test_text+" -> TEST OK") + else: + logger.warning(self.__class__.test_text+" -> TEST NOK") + error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2]) + msg = "" + for line in error_trace: + msg = msg + line + logger.critical("{}".format(msg)) + + def test_000_create_RO_tenant(self): + self.__class__.tenant_name = _get_random_string(20) + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + tenant = client.create_tenant(name=self.__class__.tenant_name, description=self.__class__.tenant_name) + logger.debug("{}".format(tenant)) + self.assertEqual(tenant.get('tenant', {}).get('name', ''), self.__class__.tenant_name) + + def test_010_list_RO_tenant(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + tenant = client.get_tenant(name=self.__class__.tenant_name) + logger.debug("{}".format(tenant)) + self.assertEqual(tenant.get('tenant', {}).get('name', ''), self.__class__.tenant_name) + + def test_020_delete_RO_tenant(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + tenant = client.delete_tenant(name=self.__class__.tenant_name) + logger.debug("{}".format(tenant)) + assert('deleted' in tenant.get('result',"")) + +class test_datacenter_operations(unittest.TestCase): + test_index = 1 + datacenter_name = None + test_text = None + + @classmethod + def setUpClass(cls): + logger.info("{}. {}".format(test_number, cls.__name__)) + + @classmethod + def tearDownClass(cls): + globals().__setitem__('test_number', globals().__getitem__('test_number') + 1) + + def tearDown(self): + exec_info = sys.exc_info() + if exec_info == (None, None, None): + logger.info(self.__class__.test_text+" -> TEST OK") + else: + logger.warning(self.__class__.test_text+" -> TEST NOK") + error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2]) + msg = "" + for line in error_trace: + msg = msg + line + logger.critical("{}".format(msg)) + + def test_000_create_datacenter(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.datacenter_name = _get_random_string(20) + self.__class__.test_index += 1 + self.datacenter = client.create_datacenter(name=self.__class__.datacenter_name, vim_url="http://fakeurl/fake") + logger.debug("{}".format(self.datacenter)) + self.assertEqual (self.datacenter.get('datacenter', {}).get('name',''), self.__class__.datacenter_name) + + def test_010_list_datacenter(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + + self.__class__.test_index += 1 + self.datacenter = client.get_datacenter(all_tenants=True, name=self.__class__.datacenter_name) + logger.debug("{}".format(self.datacenter)) + self.assertEqual (self.datacenter.get('datacenter', {}).get('name', ''), self.__class__.datacenter_name) + + def test_020_attach_datacenter(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + + self.__class__.test_index += 1 + self.datacenter = client.attach_datacenter(name=self.__class__.datacenter_name, vim_tenant_name='fake') + logger.debug("{}".format(self.datacenter)) + assert ('vim_tenants' in self.datacenter.get('datacenter', {})) + + def test_030_list_attached_datacenter(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + + self.__class__.test_index += 1 + self.datacenter = client.get_datacenter(all_tenants=False, name=self.__class__.datacenter_name) + logger.debug("{}".format(self.datacenter)) + self.assertEqual (self.datacenter.get('datacenter', {}).get('name', ''), self.__class__.datacenter_name) + + def test_040_detach_datacenter(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + + self.__class__.test_index += 1 + self.datacenter = client.detach_datacenter(name=self.__class__.datacenter_name) + logger.debug("{}".format(self.datacenter)) + assert ('detached' in self.datacenter.get('result', "")) + + def test_050_delete_datacenter(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + + self.__class__.test_index += 1 + self.datacenter = client.delete_datacenter(name=self.__class__.datacenter_name) + logger.debug("{}".format(self.datacenter)) + assert('deleted' in self.datacenter.get('result',"")) + +class test_VIM_network_operations(unittest.TestCase): + test_index = 1 + vim_network_name = None + test_text = None + vim_network_uuid = None + + @classmethod + def setUpClass(cls): + logger.info("{}. {}".format(test_number, cls.__name__)) + + @classmethod + def tearDownClass(cls): + globals().__setitem__('test_number', globals().__getitem__('test_number') + 1) + + def tearDown(self): + exec_info = sys.exc_info() + if exec_info == (None, None, None): + logger.info(self.__class__.test_text + " -> TEST OK") + else: + logger.warning(self.__class__.test_text + " -> TEST NOK") + error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2]) + msg = "" + for line in error_trace: + msg = msg + line + logger.critical("{}".format(msg)) + + def test_000_create_VIM_network(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.vim_network_name = _get_random_string(20) + self.__class__.test_index += 1 + network = client.vim_action("create", "networks", name=self.__class__.vim_network_name) + logger.debug("{}".format(network)) + self.__class__.vim_network_uuid = network["network"]["id"] + self.assertEqual(network.get('network', {}).get('name', ''), self.__class__.vim_network_name) + + def test_010_list_VIM_networks(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + networks = client.vim_action("list", "networks") + logger.debug("{}".format(networks)) + + def test_020_get_VIM_network_by_uuid(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + + self.__class__.test_index += 1 + network = client.vim_action("show", "networks", uuid=self.__class__.vim_network_uuid) + logger.debug("{}".format(network)) + self.assertEqual(network.get('network', {}).get('name', ''), self.__class__.vim_network_name) + + def test_030_delete_VIM_network_by_uuid(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + + self.__class__.test_index += 1 + network = client.vim_action("delete", "networks", uuid=self.__class__.vim_network_uuid) + logger.debug("{}".format(network)) + assert ('deleted' in network.get('result', "")) + +class test_VIM_image_operations(unittest.TestCase): + test_index = 1 + test_text = None + + @classmethod + def setUpClass(cls): + logger.info("{}. {}".format(test_number, cls.__name__)) + + @classmethod + def tearDownClass(cls): + globals().__setitem__('test_number', globals().__getitem__('test_number') + 1) + + def tearDown(self): + exec_info = sys.exc_info() + if exec_info == (None, None, None): + logger.info(self.__class__.test_text + " -> TEST OK") + else: + logger.warning(self.__class__.test_text + " -> TEST NOK") + error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2]) + msg = "" + for line in error_trace: + msg = msg + line + logger.critical("{}".format(msg)) + + def test_000_list_VIM_images(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + images = client.vim_action("list", "images") + logger.debug("{}".format(images)) + +''' +The following is a non critical test that will fail most of the times. +In case of OpenStack datacenter these tests will only success if RO has access to the admin endpoint +This test will only be executed in case it is specifically requested by the user +''' +class test_VIM_tenant_operations(unittest.TestCase): + test_index = 1 + vim_tenant_name = None + test_text = None + vim_tenant_uuid = None + + @classmethod + def setUpClass(cls): + logger.info("{}. {}".format(test_number, cls.__name__)) + logger.warning("In case of OpenStack datacenter these tests will only success " + "if RO has access to the admin endpoint") + + @classmethod + def tearDownClass(cls): + globals().__setitem__('test_number', globals().__getitem__('test_number') + 1) + + def tearDown(self): + exec_info = sys.exc_info() + if exec_info == (None, None, None): + logger.info(self.__class__.test_text + " -> TEST OK") + else: + logger.warning(self.__class__.test_text + " -> TEST NOK") + error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2]) + msg = "" + for line in error_trace: + msg = msg + line + logger.critical("{}".format(msg)) + + def test_000_create_VIM_tenant(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.vim_tenant_name = _get_random_string(20) + self.__class__.test_index += 1 + tenant = client.vim_action("create", "tenants", name=self.__class__.vim_tenant_name) + logger.debug("{}".format(tenant)) + self.__class__.vim_tenant_uuid = tenant["tenant"]["id"] + self.assertEqual(tenant.get('tenant', {}).get('name', ''), self.__class__.vim_tenant_name) + + def test_010_list_VIM_tenants(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + self.__class__.test_index += 1 + tenants = client.vim_action("list", "tenants") + logger.debug("{}".format(tenants)) + + def test_020_get_VIM_tenant_by_uuid(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + + self.__class__.test_index += 1 + tenant = client.vim_action("show", "tenants", uuid=self.__class__.vim_tenant_uuid) + logger.debug("{}".format(tenant)) + self.assertEqual(tenant.get('tenant', {}).get('name', ''), self.__class__.vim_tenant_name) + + def test_030_delete_VIM_tenant_by_uuid(self): + self.__class__.test_text = "{}.{}. TEST {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name) + + self.__class__.test_index += 1 + tenant = client.vim_action("delete", "tenants", uuid=self.__class__.vim_tenant_uuid) + logger.debug("{}".format(tenant)) + assert ('deleted' in tenant.get('result', "")) + +''' +IMPORTANT NOTE +The following unittest class does not have the 'test_' on purpose. This test is the one used for the +scenario based tests. +''' +class descriptor_based_scenario_test(unittest.TestCase): + test_index = 0 + test_text = None + scenario_test_path = None + scenario_uuid = None + instance_scenario_uuid = None + to_delete_list = [] + + @classmethod + def setUpClass(cls): + cls.test_index = 1 + cls.to_delete_list = [] + cls.scenario_test_path = test_directory + '/' + scenario_test_folder + logger.info("{}. {} {}".format(test_number, cls.__name__, scenario_test_folder)) + + @classmethod + def tearDownClass(cls): + globals().__setitem__('test_number', globals().__getitem__('test_number') + 1) + + def tearDown(self): + exec_info = sys.exc_info() + if exec_info == (None, None, None): + logger.info(self.__class__.test_text + " -> TEST OK") + else: + logger.warning(self.__class__.test_text + " -> TEST NOK") + error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2]) + msg = "" + for line in error_trace: + msg = msg + line + logger.critical("{}".format(msg)) + + + def test_000_load_scenario(self): + self.__class__.test_text = "{}.{}. TEST {} {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name, + scenario_test_folder) + self.__class__.test_index += 1 + vnfd_files = glob.glob(self.__class__.scenario_test_path+'/vnfd_*.yaml') + scenario_file = glob.glob(self.__class__.scenario_test_path + '/scenario_*.yaml') + if len(vnfd_files) == 0 or len(scenario_file) > 1: + raise Exception('Test '+scenario_test_folder+' not valid. It must contain an scenario file and at least one' + ' vnfd file') + + #load all vnfd + for vnfd in vnfd_files: + with open(vnfd, 'r') as stream: + vnf_descriptor = yaml.load(stream) + + vnfc_list = vnf_descriptor['vnf']['VNFC'] + for vnfc in vnfc_list: + vnfc['image name'] = test_image_name + devices = vnfc.get('devices',[]) + for device in devices: + if device['type'] == 'disk' and 'image name' in device: + device['image name'] = test_image_name + + logger.debug("VNF descriptor: {}".format(vnf_descriptor)) + vnf = client.create_vnf(descriptor=vnf_descriptor) + logger.debug(vnf) + self.__class__.to_delete_list.insert(0, {"item": "vnf", "function": client.delete_vnf, + "params": {"uuid": vnf['vnf']['uuid']}}) + + #load the scenario definition + with open(scenario_file[0], 'r') as stream: + scenario_descriptor = yaml.load(stream) + networks = scenario_descriptor['scenario']['networks'] + networks[management_network] = networks.pop('mgmt') + logger.debug("Scenario descriptor: {}".format(scenario_descriptor)) + scenario = client.create_scenario(descriptor=scenario_descriptor) + logger.debug(scenario) + self.__class__.to_delete_list.insert(0,{"item": "scenario", "function": client.delete_scenario, + "params":{"uuid": scenario['scenario']['uuid']} }) + self.__class__.scenario_uuid = scenario['scenario']['uuid'] + + def test_010_instantiate_scenario(self): + self.__class__.test_text = "{}.{}. TEST {} {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name, + scenario_test_folder) + self.__class__.test_index += 1 + + instance = client.create_instance(scenario_id=self.__class__.scenario_uuid, name=self.__class__.test_text) + logger.debug(instance) + self.__class__.to_delete_list.insert(0, {"item": "instance", "function": client.delete_instance, + "params": {"uuid": instance['uuid']}}) + + def test_020_clean_deployment(self): + self.__class__.test_text = "{}.{}. TEST {} {}".format(test_number, self.__class__.test_index, + inspect.currentframe().f_code.co_name, + scenario_test_folder) + self.__class__.test_index += 1 + #At the moment if you delete an scenario right after creating it, in openstack datacenters + #sometimes scenario ports get orphaned. This sleep is just a dirty workaround + time.sleep(5) + for item in self.__class__.to_delete_list: + response = item["function"](**item["params"]) + logger.debug(response) + +def _get_random_string(maxLength): + '''generates a string with random characters string.letters and string.digits + with a random length up to maxLength characters. If maxLength is <15 it will be changed automatically to 15 + ''' + prefix = 'testing_' + min_string = 15 + minLength = min_string - len(prefix) + if maxLength < min_string: maxLength = min_string + maxLength -= len(prefix) + length = random.randint(minLength,maxLength) + return 'testing_'+"".join([random.choice(string.letters+string.digits) for i in xrange(length)]) + +if __name__=="__main__": + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + import openmanoclient + + parser = OptionParser() + + #Optional arguments + parser.add_option("-v",'--version', help='Show current version', dest='version', action="store_true", default=False) + parser.add_option('--debug', help='Set logs to debug level', dest='debug', action="store_true", default=False) + parser.add_option('--failed', help='Set logs to show only failed tests. --debug disables this option', + dest='failed', action="store_true", default=False) + parser.add_option('-u', '--url', dest='endpoint_url', help='Set the openmano server url. By default ' + 'http://localhost:9090/openmano', + default='http://localhost:9090/openmano') + default_logger_file = os.path.dirname(__file__)+'/'+os.path.splitext(os.path.basename(__file__))[0]+'.log' + parser.add_option('--logger_file', dest='logger_file', help='Set the logger file. By default '+default_logger_file, + default=default_logger_file) + parser.add_option('--list-tests', help='List all available tests', dest='list-tests', action="store_true", + default=False) + parser.add_option('--test', '--tests', help='Specify the tests to run', dest='tests', default=None) + + #Mandatory arguments + parser.add_option("-t", '--tenant', dest='tenant_name', help='MANDATORY. Set the tenant name to test') + parser.add_option('-d', '--datacenter', dest='datacenter_name', help='MANDATORY, Set the datacenter name to test') + parser.add_option("-i", '--image-name', dest='image-name', help='MANDATORY. Image name of an Ubuntu 16.04 image ' + 'that will be used for testing available in the ' + 'datacenter.') + parser.add_option("-n", '--mgmt-net-name', dest='mgmt-net', help='MANDATORY. Set the tenant name to test') + + (options, args) = parser.parse_args() + + # default logger level is INFO. Options --debug and --failed override this, being --debug prioritary + logger_level = 'INFO' + if options.__dict__['debug']: + logger_level = 'DEBUG' + elif options.__dict__['failed']: + logger_level = 'WARNING' + logger_name = os.path.basename(__file__) + logger = logging.getLogger(logger_name) + logger.setLevel(logger_level) + + # Configure a logging handler to store in a logging file + fileHandler = logging.FileHandler(options.__dict__['logger_file']) + formatter_fileHandler = logging.Formatter('%(asctime)s %(name)s %(levelname)s: %(message)s') + fileHandler.setFormatter(formatter_fileHandler) + logger.addHandler(fileHandler) + + # Configure a handler to print to stdout + consoleHandler = logging.StreamHandler(sys.stdout) + formatter_consoleHandler = logging.Formatter('%(message)s') + consoleHandler.setFormatter(formatter_consoleHandler) + logger.addHandler(consoleHandler) + + logger.debug('Program started with the following arguments: ' + str(options.__dict__)) + + #If version is required print it and exit + if options.__dict__['version']: + logger.info("{}".format((sys.argv[0], __version__+" version", version_date))) + logger.info ("(c) Copyright Telefonica") + sys.exit(0) + + test_directory = os.path.dirname(__file__) + "/RO_tests" + test_directory_content = os.listdir(test_directory) + clsmembers = inspect.getmembers(sys.modules[__name__], inspect.isclass) + + # If only want to obtain a tests list print it and exit + if options.__dict__['list-tests']: + tests_names = [] + for cls in clsmembers: + if cls[0].startswith('test_'): + tests_names.append(cls[0]) + + msg = "The code based tests are:\n\t" + ', '.join(sorted(tests_names))+'\n'+\ + "The descriptor based tests are:\n\t"+ ', '.join(sorted(test_directory_content))+'\n'+\ + "NOTE: The test test_VIM_tenant_operations will fail in case the used datacenter is type OpenStack " \ + "unless RO has access to the admin endpoint. Therefore this test is excluded by default" + + logger.info(msg) + sys.exit(0) + + #Make sure required arguments are present + required = "tenant_name datacenter_name image-name mgmt-net".split() + error = False + for r in required: + if options.__dict__[r] is None: + print "ERROR: parameter "+r+" is required" + error = True + if error: + parser.print_help() + sys.exit(1) + + # set test image name and management network + test_image_name = options.__dict__['image-name'] + management_network = options.__dict__['mgmt-net'] + + #Create the list of tests to be run + descriptor_based_tests = [] + code_based_tests = [] + if options.__dict__['tests'] != None: + tests = sorted(options.__dict__['tests'].split(',')) + for test in tests: + matches_code_based_tests = [item for item in clsmembers if item[0] == test] + if test in test_directory_content: + descriptor_based_tests.append(test) + elif len(matches_code_based_tests) > 0: + code_based_tests.append(matches_code_based_tests[0][1]) + else: + logger.critical("Test {} is not among the possible ones".format(test)) + sys.exit(1) + else: + #include all tests + descriptor_based_tests = test_directory_content + for cls in clsmembers: + #We exclude 'test_VIM_tenant_operations' unless it is specifically requested by the user + if cls[0].startswith('test_') and cls[0] != 'test_VIM_tenant_operations': + code_based_tests.append(cls[1]) + + logger.debug("descriptor_based_tests to be executed: {}".format(descriptor_based_tests)) + logger.debug("code_based_tests to be executed: {}".format(code_based_tests)) + + # import openmanoclient from relative path + client = openmanoclient.openmanoclient( + endpoint_url=options.__dict__['endpoint_url'], + tenant_name=options.__dict__['tenant_name'], + datacenter_name = options.__dict__['datacenter_name'], + debug = options.__dict__['debug'], logger = logger_name) + + # TextTestRunner stream is set to /dev/null in order to avoid the method to directly print the result of tests. + # This is handled in the tests using logging. + stream = open('/dev/null', 'w') + test_number=1 + executed = 0 + failed = 0 + + #Run code based tests + basic_tests_suite = unittest.TestSuite() + for test in code_based_tests: + basic_tests_suite.addTest(unittest.makeSuite(test)) + result = unittest.TextTestRunner(stream=stream).run(basic_tests_suite) + executed += result.testsRun + failed += len(result.failures) + len(result.errors) + if len(result.failures) > 0: + logger.debug("failures : {}".format(result.failures)) + if len(result.errors) > 0: + logger.debug("errors : {}".format(result.errors)) + + # Additionally to the previous tests, scenario based tests will be executed. + # This scenario based tests are defined as directories inside the directory defined in 'test_directory' + for test in descriptor_based_tests: + scenario_test_folder = test + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(descriptor_based_scenario_test)) + result = unittest.TextTestRunner(stream=stream).run(test_suite) + executed += result.testsRun + failed += len(result.failures) + len(result.errors) + if len(result.failures) > 0: + logger.debug("failures : {}".format(result.failures)) + if len(result.errors) > 0: + logger.debug("errors : {}".format(result.errors)) + + #Log summary + logger.warning("Total number of tests: {}; Total number of failures/errors: {}".format(executed, failed)) + + sys.exit(0) \ No newline at end of file