Add cinder_test testcase 57/57257/16
authorDelia Popescu <delia.popescu@enea.com>
Thu, 10 May 2018 09:13:08 +0000 (12:13 +0300)
committerDelia Popescu <delia.popescu@enea.com>
Fri, 25 May 2018 13:50:47 +0000 (16:50 +0300)
Create 2 instances with Floating IPs
Attach data volume to instance 1
Write data on the volume
Detach volume from instance 1
Attach volume to instance 2
Read data from volume

JIRA: FUNCTEST-908

Change-Id: I10efdfb3b7f9d7e6ab5c6a538052437585d52fb0
Signed-off-by: Delia Popescu <delia.popescu@enea.com>
docker/smoke/testcases.yaml
functest/ci/config_functest.yaml
functest/ci/testcases.yaml
functest/opnfv_tests/openstack/cinder/__init__.py [new file with mode: 0644]
functest/opnfv_tests/openstack/cinder/cinder_base.py [new file with mode: 0644]
functest/opnfv_tests/openstack/cinder/cinder_test.py [new file with mode: 0644]
functest/opnfv_tests/openstack/cinder/read_data.sh [new file with mode: 0644]
functest/opnfv_tests/openstack/cinder/write_data.sh [new file with mode: 0644]
tox.ini

index 583cd7b..35695ed 100644 (file)
@@ -39,6 +39,23 @@ tiers:
                         'functest.opnfv_tests.openstack.vping.vping_userdata'
                     class: 'VPingUserdata'
 
+            -
+                case_name: cinder_test
+                project_name: functest
+                criteria: 100
+                blocking: false
+                description: >-
+                    This test case verifies: 1) Attach volume and to 1
+                    instance; 2) Write data on volume 3) Detach volume
+                    from instance 1, attach it on instance 2 3) Read volume
+                    data
+                dependencies:
+                    installer: ''
+                    scenario: '^((?!lxd).)*$'
+                run:
+                    module: 'functest.opnfv_tests.openstack.cinder.cinder_test'
+                    class: 'CinderCheck'
+
             -
                 case_name: tempest_smoke_serial
                 project_name: functest
index 2cdec45..3e49d61 100644 (file)
@@ -80,6 +80,7 @@ snaps:
 #        port: {proxy_port}
 #        ssh_proxy_cmd: {OpenSSH -o ProxyCommand value}
 
+
 vping:
     ping_timeout: 200
     vm_flavor: m1.tiny   # adapt to your environment
@@ -103,6 +104,24 @@ vping:
     vm_ssh_connect_timeout: 60
     cleanup_objects: 'True'
 
+cinder:
+    vm_name_1: opnfv-cinder-1
+    vm_name_2: opnfv-cinder-2
+    image_name: functest-cinder
+    private_net_name: cinder-net
+    private_subnet_name: cinder-subnet
+    private_subnet_cidr: 192.168.130.0/24
+    router_name: cinder-router
+    sg_name: cinder-sg
+    sg_desc: Security group for CinderCheck test case
+    keypair_name: cinder-keypair
+    keypair_priv_file: /tmp/CinderCheck-keypair
+    keypair_pub_file: /tmp/CinderCheck-keypair.pub
+    vm_boot_timeout: 180
+    vm_delete_timeout: 100
+    vm_ssh_connect_timeout: 60
+    cleanup_objects: 'True'
+
 odl_sfc:
     image_base_url: "http://artifacts.opnfv.org/sfc/images"
     image_name: sfc_nsh_danube
index 2b348d5..b3e05f0 100644 (file)
@@ -69,6 +69,7 @@ tiers:
         description: >-
             Set of basic Functional tests to validate the OPNFV scenarios.
         testcases:
+
             -
                 case_name: vping_ssh
                 project_name: functest
@@ -101,6 +102,23 @@ tiers:
                         'functest.opnfv_tests.openstack.vping.vping_userdata'
                     class: 'VPingUserdata'
 
+            -
+                case_name: cinder_test
+                project_name: functest
+                criteria: 100
+                blocking: false
+                description: >-
+                    This test case verifies: 1) Attach volume and to 1
+                    instance; 2) Write data on volume 3) Detach volume
+                    from instance 1, attach it on instance 2 3) Read volume
+                    data
+                dependencies:
+                    installer: ''
+                    scenario: '^((?!lxd).)*$'
+                run:
+                    module: 'functest.opnfv_tests.openstack.cinder.cinder_test'
+                    class: 'CinderCheck'
+
             -
                 case_name: tempest_smoke_serial
                 project_name: functest
diff --git a/functest/opnfv_tests/openstack/cinder/__init__.py b/functest/opnfv_tests/openstack/cinder/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/functest/opnfv_tests/openstack/cinder/cinder_base.py b/functest/opnfv_tests/openstack/cinder/cinder_base.py
new file mode 100644 (file)
index 0000000..b5ff36d
--- /dev/null
@@ -0,0 +1,178 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2018 Enea AB and others.
+#
+# This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+
+"""Define the parent class of cinder_test"""
+
+from datetime import datetime
+import logging
+import time
+import uuid
+
+import os_client_config
+from xtesting.core import testcase
+
+from functest.utils import config
+from functest.utils import env
+from functest.utils import functest_utils
+
+
+class CinderBase(testcase.TestCase):
+
+    """
+    Base class for CinderCheck tests that check volume data persistence
+    between two VMs shared internal network.
+    This class is responsible for creating the image, internal network,
+    flavor, volume.
+    """
+    # pylint: disable=too-many-instance-attributes
+
+    def __init__(self, **kwargs):
+        super(CinderBase, self).__init__(**kwargs)
+        self.logger = logging.getLogger(__name__)
+        self.cloud = os_client_config.make_shade()
+        self.ext_net = functest_utils.get_external_network(self.cloud)
+        self.logger.debug("ext_net: %s", self.ext_net)
+        self.guid = '-' + str(uuid.uuid4())
+        self.network = None
+        self.subnet = None
+        self.router = None
+        self.image = None
+        self.flavor = None
+        self.vm1 = None
+        self.volume = None
+
+    def run(self, **kwargs):  # pylint: disable=too-many-locals
+        """
+        Begins the test execution which should originate from the subclass
+        """
+        assert self.cloud
+        assert self.ext_net
+        self.logger.info('Begin virtual environment setup')
+
+        self.start_time = time.time()
+        self.logger.info(
+            "CinderCheck Start Time:'%s'",
+            datetime.fromtimestamp(self.start_time).strftime(
+                '%Y-%m-%d %H:%M:%S'))
+
+        image_base_name = '{}-{}'.format(
+            getattr(config.CONF, 'cinder_image_name'), self.guid)
+        self.logger.info("Creating image with name: '%s'", image_base_name)
+        meta = getattr(config.CONF, 'openstack_extra_properties', None)
+        self.logger.info("Image metadata: %s", meta)
+        self.image = self.cloud.create_image(
+            image_base_name,
+            filename=getattr(config.CONF, 'openstack_image_url'),
+            meta=meta)
+        self.logger.debug("image: %s", self.image)
+
+        private_net_name = getattr(
+            config.CONF, 'cinder_private_net_name') + self.guid
+        private_subnet_name = str(getattr(
+            config.CONF, 'cinder_private_subnet_name') + self.guid)
+        private_subnet_cidr = getattr(
+            config.CONF, 'cinder_private_subnet_cidr')
+
+        provider = {}
+        if hasattr(config.CONF, 'cinder_network_type'):
+            provider["network_type"] = getattr(
+                config.CONF, 'cinder_network_type')
+        if hasattr(config.CONF, 'cinder_physical_network'):
+            provider["physical_network"] = getattr(
+                config.CONF, 'cinder_physical_network')
+        if hasattr(config.CONF, 'cinder_segmentation_id'):
+            provider["segmentation_id"] = getattr(
+                config.CONF, 'cinder_segmentation_id')
+        self.logger.info(
+            "Creating network with name: '%s'", private_net_name)
+        self.network = self.cloud.create_network(
+            private_net_name,
+            provider=provider)
+        self.logger.debug("network: %s", self.network)
+
+        self.subnet = self.cloud.create_subnet(
+            self.network.id,
+            subnet_name=private_subnet_name,
+            cidr=private_subnet_cidr,
+            enable_dhcp=True,
+            dns_nameservers=[env.get('NAMESERVER')])
+        self.logger.debug("subnet: %s", self.subnet)
+
+        router_name = getattr(config.CONF, 'cinder_router_name') + self.guid
+        self.logger.info("Creating router with name: '%s'", router_name)
+        self.router = self.cloud.create_router(
+            name=router_name,
+            ext_gateway_net_id=self.ext_net.id)
+        self.logger.debug("router: %s", self.router)
+        self.cloud.add_router_interface(self.router, subnet_id=self.subnet.id)
+
+        flavor_name = 'cinder-flavor' + self.guid
+        self.logger.info(
+            "Creating flavor with name: '%s'", flavor_name)
+        self.flavor = self.cloud.create_flavor(
+            flavor_name, getattr(config.CONF, 'openstack_flavor_ram'),
+            getattr(config.CONF, 'openstack_flavor_vcpus'),
+            getattr(config.CONF, 'openstack_flavor_disk'))
+        self.logger.debug("flavor: %s", self.flavor)
+        self.cloud.set_flavor_specs(
+            self.flavor.id, getattr(config.CONF, 'flavor_extra_specs', {}))
+        volume_name = 'cinder-volume' + self.guid
+        self.logger.info(
+            "Creating volume with name: %s", volume_name)
+        self.volume = self.cloud.create_volume(
+            name=volume_name, size='2')
+        self.logger.info("volume: %s", self.volume)
+
+    def execute(self):
+        """
+        Method called by subclasses after environment has been setup
+        :return: the exit code
+        """
+        self.logger.info('Begin test execution')
+        self.stop_time = time.time()
+        write_data = self.write_data()
+        if write_data == testcase.TestCase.EX_OK:
+            result = self.read_data()
+        else:
+            result = testcase.TestCase.EX_RUN_ERROR
+        if result != testcase.TestCase.EX_OK:
+            self.result = 0
+            return testcase.TestCase.EX_RUN_ERROR
+        self.result = 100
+        return testcase.TestCase.EX_OK
+
+    def clean(self):
+        """
+        Cleanup all OpenStack objects. Should be called on completion
+        :return:
+        """
+        assert self.cloud
+        self.cloud.delete_image(self.image)
+        self.cloud.remove_router_interface(self.router, self.subnet.id)
+        self.cloud.delete_router(self.router.id)
+        self.cloud.delete_network(self.network.id)
+        self.cloud.delete_flavor(self.flavor.id)
+        self.cloud.delete_volume(self.volume.id)
+
+    def write_data(self):
+        """
+        Method to be implemented by subclasses
+        Begins the real test after the OpenStack environment has been setup
+        :return: T/F
+        """
+        raise NotImplementedError('cinder test execution is not implemented')
+
+    def read_data(self):
+        """
+        Method to be implemented by subclasses
+        Begins the real test after the OpenStack environment has been setup
+        :return: T/F
+        """
+        raise NotImplementedError('cinder test execution is not implemented')
diff --git a/functest/opnfv_tests/openstack/cinder/cinder_test.py b/functest/opnfv_tests/openstack/cinder/cinder_test.py
new file mode 100644 (file)
index 0000000..d4a7cf7
--- /dev/null
@@ -0,0 +1,218 @@
+#!/usr/bin/env python
+
+
+# Copyright (c) 2018 Enea AB and others
+
+# This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+
+"""CinderCheck testcase."""
+
+import logging
+import os
+import tempfile
+import time
+import pkg_resources
+from scp import SCPClient
+
+import paramiko
+from xtesting.core import testcase
+from xtesting.energy import energy
+
+from functest.opnfv_tests.openstack.cinder import cinder_base
+from functest.utils import config
+
+
+class CinderCheck(cinder_base.CinderBase):
+    """
+    CinderCheck testcase implementation.
+
+    Class to execute the CinderCheck test using 2 Floating IPs
+    to connect to the VMs and one data volume
+    """
+    # pylint: disable=too-many-instance-attributes
+
+    def __init__(self, **kwargs):
+        """Initialize testcase."""
+        if "case_name" not in kwargs:
+            kwargs["case_name"] = "cinder_test"
+        super(CinderCheck, self).__init__(**kwargs)
+        self.logger = logging.getLogger(__name__)
+        self.vm1 = None
+        self.vm2 = None
+        self.sec = None
+        self.fip1 = None
+        self.fip2 = None
+        self.keypair1 = None
+        self.keypair2 = None
+        (_, self.key1_filename) = tempfile.mkstemp()
+        self.ssh = paramiko.SSHClient()
+        (_, self.key2_filename) = tempfile.mkstemp()
+
+    @energy.enable_recording
+    def run(self, **kwargs):
+        """
+        Excecute CinderCheck testcase.
+
+        Sets up the OpenStack keypair, router, security group, and VM instance
+        objects then validates the ping.
+        :return: the exit code from the super.execute() method
+        """
+        try:
+            assert self.cloud
+            super(CinderCheck, self).run()
+
+            # Creating key pair 1
+            kp_name1 = getattr(
+                config.CONF, 'cinder_keypair_name') + '_1' + self.guid
+            self.logger.info("Creating keypair with name: '%s'", kp_name1)
+            self.keypair1 = self.cloud.create_keypair(kp_name1)
+            self.logger.debug("keypair: %s", self.keypair1)
+            self.logger.debug("private_key: %s", self.keypair1.private_key)
+            with open(self.key1_filename, 'w') as private_key1_file:
+                private_key1_file.write(self.keypair1.private_key)
+            # Creating key pair 2
+            kp_name2 = getattr(
+                config.CONF, 'cinder_keypair_name') + '_2' + self.guid
+            self.logger.info("Creating keypair with name: '%s'", kp_name2)
+            self.keypair2 = self.cloud.create_keypair(kp_name2)
+            self.logger.debug("keypair: %s", self.keypair2)
+            self.logger.debug("private_key: %s", self.keypair2.private_key)
+            with open(self.key2_filename, 'w') as private_key2_file:
+                private_key2_file.write(self.keypair2.private_key)
+
+            # Creating security group
+            self.sec = self.cloud.create_security_group(
+                getattr(config.CONF, 'cinder_sg_name') + self.guid,
+                getattr(config.CONF, 'cinder_sg_desc'))
+            self.cloud.create_security_group_rule(
+                self.sec.id, port_range_min='22', port_range_max='22',
+                protocol='tcp', direction='ingress')
+            self.cloud.create_security_group_rule(
+                self.sec.id, port_range_min='22', port_range_max='22',
+                protocol='tcp', direction='egress')
+            self.cloud.create_security_group_rule(
+                self.sec.id, protocol='icmp', direction='ingress')
+
+            # Creating VM 1
+            vm1_name = "{}-{}-{}".format(
+                getattr(config.CONF, 'cinder_vm_name_1'), "ssh", self.guid)
+            self.logger.info(
+                "Creating VM 1 instance with name: '%s'", vm1_name)
+            self.vm1 = self.cloud.create_server(
+                vm1_name, image=self.image.id, flavor=self.flavor.id,
+                key_name=self.keypair1.id,
+                auto_ip=False, wait=True,
+                timeout=getattr(config.CONF, 'cinder_vm_boot_timeout'),
+                network=self.network.id,
+                security_groups=[self.sec.id])
+            self.logger.debug("vm1: %s", self.vm2)
+            self.fip1 = self.cloud.create_floating_ip(
+                network=self.ext_net.id, server=self.vm1)
+            self.logger.debug("floating_ip1: %s", self.fip1)
+            self.vm1 = self.cloud.wait_for_server(self.vm1, auto_ip=False)
+            self.cloud.attach_volume(self.vm1, self.volume)
+
+            # Creating VM 2
+            vm2_name = "{}-{}-{}".format(
+                getattr(config.CONF, 'cinder_vm_name_2'), "ssh", self.guid)
+            self.logger.info(
+                "Creating VM 2 instance with name: '%s'", vm2_name)
+            self.vm2 = self.cloud.create_server(
+                vm2_name, image=self.image.id, flavor=self.flavor.id,
+                key_name=self.keypair2.id,
+                auto_ip=False, wait=True,
+                timeout=getattr(config.CONF, 'cinder_vm_boot_timeout'),
+                network=self.network.id,
+                security_groups=[self.sec.id])
+            self.logger.debug("vm2: %s", self.vm2)
+            self.fip2 = self.cloud.create_floating_ip(
+                network=self.ext_net.id, server=self.vm2)
+            self.logger.debug("floating_ip2: %s", self.fip2)
+            self.vm2 = self.cloud.wait_for_server(self.vm2, auto_ip=False)
+
+            return self.execute()
+        except Exception:  # pylint: disable=broad-except
+            self.logger.exception('Unexpected error running cinder_ssh')
+            return testcase.TestCase.EX_RUN_ERROR
+
+    def write_data(self):
+        time.sleep(10)
+        write_data_script = pkg_resources.resource_filename(
+            'functest.opnfv_tests.openstack.cinder', 'write_data.sh')
+        cmd = '. ~/write_data.sh '
+        username = getattr(config.CONF, 'openstack_image_user')
+        destination_path = '/home/' + username
+        self.ssh.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
+        self.ssh.connect(
+            self.vm1.public_v4,
+            username=username,
+            key_filename=self.key1_filename,
+            timeout=getattr(config.CONF, 'cinder_vm_ssh_connect_timeout'))
+        try:
+            scp = SCPClient(self.ssh.get_transport())
+            scp.put(write_data_script, remote_path=destination_path)
+        except Exception:  # pylint: disable=broad-except
+            self.logger.error("File not transfered!")
+            return testcase.TestCase.EX_RUN_ERROR
+
+        self.logger.debug("ssh: %s", self.ssh)
+        self.ssh.exec_command('chmod +x ~/write_data.sh')
+        (_, stdout, _) = self.ssh.exec_command(cmd)
+        self.logger.debug("volume_write output: %s", stdout.read())
+
+        return stdout.channel.recv_exit_status()
+
+    def read_data(self):
+        assert self.cloud
+        # Detach volume from VM 1
+        self.logger.info("Detach volume from VM 1")
+        self.cloud.detach_volume(self.vm1, self.volume)
+        # Attach volume to VM 2
+        self.logger.info("Attach volume to VM 2")
+        self.cloud.attach_volume(self.vm2, self.volume)
+        # Check volume data
+        time.sleep(10)
+        read_data_script = pkg_resources.resource_filename(
+            'functest.opnfv_tests.openstack.cinder', 'read_data.sh')
+        cmd = '. ~/read_data.sh '
+        username = getattr(config.CONF, 'openstack_image_user')
+        destination_path = '/home/' + username
+        self.ssh.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
+        self.ssh.connect(
+            self.vm2.public_v4,
+            username=username,
+            key_filename=self.key2_filename,
+            timeout=getattr(config.CONF, 'cinder_vm_ssh_connect_timeout'))
+        try:
+            scp = SCPClient(self.ssh.get_transport())
+            scp.put(read_data_script, remote_path=destination_path)
+        except Exception:  # pylint: disable=broad-except
+            self.logger.error("File not transfered!")
+            return testcase.TestCase.EX_RUN_ERROR
+        self.logger.debug("ssh: %s", self.ssh)
+        self.ssh.exec_command('chmod +x ~/read_data.sh')
+        (_, stdout, _) = self.ssh.exec_command(cmd)
+        self.logger.debug("read volume output: %s", stdout.read())
+
+        return stdout.channel.recv_exit_status()
+
+    def clean(self):
+        assert self.cloud
+        os.remove(self.key1_filename)
+        os.remove(self.key2_filename)
+        self.cloud.delete_server(
+            self.vm1, wait=True,
+            timeout=getattr(config.CONF, 'cinder_vm_delete_timeout'))
+        self.cloud.delete_server(
+            self.vm2, wait=True,
+            timeout=getattr(config.CONF, 'cinder_vm_delete_timeout'))
+        self.cloud.delete_security_group(self.sec.id)
+        self.cloud.delete_keypair(self.keypair1.id)
+        self.cloud.delete_keypair(self.keypair2.id)
+        self.cloud.delete_floating_ip(self.fip1.id)
+        self.cloud.delete_floating_ip(self.fip2.id)
+        super(CinderCheck, self).clean()
diff --git a/functest/opnfv_tests/openstack/cinder/read_data.sh b/functest/opnfv_tests/openstack/cinder/read_data.sh
new file mode 100644 (file)
index 0000000..9075843
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh -e
+
+# Copyright (c) 2018 Enea AB and others
+
+# This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+
+VOL_DEV_NAME="$(lsblk -l | grep -Po '^[vs]dc\W')"
+
+if [ -n "$VOL_DEV_NAME" ]; then
+    sudo mount /dev/$VOL_DEV_NAME /home/cirros/volume;
+    if [ -f /home/cirros/volume/new_data ]; then
+        echo "Found existing data!";
+    else
+        echo "No data found on the volume!";
+        exit 1
+    fi
+fi
diff --git a/functest/opnfv_tests/openstack/cinder/write_data.sh b/functest/opnfv_tests/openstack/cinder/write_data.sh
new file mode 100644 (file)
index 0000000..eeafbb5
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh -e
+
+# Copyright (c) 2018 Enea AB and others
+
+# This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+
+VOL_DEV_NAME="$(lsblk -l | grep -Po '^[vs]dc\W')"
+
+if [ -n $VOL_DEV_NAME ]; then
+    sudo mkdir -p /home/cirros/volume
+    sudo /usr/sbin/mkfs.ext4 -F /dev/$VOL_DEV_NAME
+    sudo mount /dev/$VOL_DEV_NAME /home/cirros/volume
+    sudo touch /home/cirros/volume/new_data
+    echo "New data added to the volume!"
+else
+    echo "Failed to write data!"
+    exit 1
+fi
diff --git a/tox.ini b/tox.ini
index 3d4c88b..62785a1 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -60,6 +60,7 @@ modules =
   functest.opnfv_tests.openstack.snaps
   functest.opnfv_tests.openstack.tempest
   functest.opnfv_tests.openstack.vping
+  functest.opnfv_tests.openstack.cinder
   functest.opnfv_tests.sdn.odl
   functest.opnfv_tests.vnf.router
   functest.tests.unit.ci
@@ -106,6 +107,8 @@ commands = nosetests {[testenv:py35]dirs}
 [testenv:bashate]
 basepython = python2.7
 files =
+  functest/opnfv_tests/openstack/cinder/write_data.sh
+  functest/opnfv_tests/openstack/cinder/read_data.sh
   functest/opnfv_tests/openstack/rally/scenario/support/instance_dd_test.sh
   functest/ci/download_images.sh
   build.sh