Publish singlevm scenarios 59/58659/12
authorCédric Ollivier <cedric.ollivier@orange.com>
Mon, 18 Jun 2018 12:18:59 +0000 (14:18 +0200)
committerCédric Ollivier <cedric.ollivier@orange.com>
Thu, 21 Jun 2018 03:40:46 +0000 (05:40 +0200)
They ease creating all ressources to execute command in a new VM
reachable via ssh. This scenarios should be reused by other
more advanced ones (shaker, vping, cinder, etc.).

Change-Id: If23866827c64a7afbdd8ff596dbc33710ee4ee20
Signed-off-by: Cédric Ollivier <cedric.ollivier@orange.com>
docker/healthcheck/testcases.yaml
functest/ci/testcases.yaml
functest/core/singlevm.py [new file with mode: 0644]
functest/core/tenantnetwork.py

index 14d353d..9fb6470 100644 (file)
@@ -28,68 +28,100 @@ tiers:
                     class: 'ConnectionCheck'
 
             -
-                case_name: api_check
+                case_name: tenantnetwork1
                 project_name: functest
                 criteria: 100
                 blocking: true
                 description: >-
-                    This test case verifies the retrieval of OpenStack clients:
-                    Keystone, Glance, Neutron and Nova and may perform some
-                    simple queries. When the config value of
-                    snaps.use_keystone is True, functest must have access to
-                    the cloud's private network.
+                    It creates and configures all tenant network ressources
+                    required by advanced testcases (subnet, network and
+                    router).
                 dependencies:
                     installer: ''
-                    scenario: '^((?!lxd).)*$'
+                    scenario: ''
                 run:
-                    module: 'functest.opnfv_tests.openstack.snaps.api_check'
-                    class: 'ApiCheck'
+                    module: 'functest.core.tenantnetwork'
+                    class: 'TenantNetwork1'
 
             -
-                case_name: snaps_health_check
+                case_name: tenantnetwork2
                 project_name: functest
                 criteria: 100
                 blocking: true
                 description: >-
-                    This test case creates executes the SimpleHealthCheck
-                    Python test class which creates an, image, flavor, network,
-                    and Cirros VM instance and observes the console output to
-                    validate the single port obtains the correct IP address.
+                    It creates new user/project before creating and configuring
+                    all tenant network ressources required by a testcase
+                    (subnet, network and router).
                 dependencies:
                     installer: ''
-                    scenario: '^((?!lxd).)*$'
+                    scenario: ''
                 run:
-                    module: 'functest.opnfv_tests.openstack.snaps.health_check'
-                    class: 'HealthCheck'
+                    module: 'functest.core.tenantnetwork'
+                    class: 'TenantNetwork2'
 
             -
-                case_name: tenantnetwork1
+                case_name: singlevm1
                 project_name: functest
                 criteria: 100
-                blocking: false
+                blocking: true
                 description: >-
-                    It creates and configures all tenant network ressources
-                    required by advanced testcases (subnet, network and
-                    router).
+                    It inherits from TenantNetwork1 which creates all network
+                    resources and completes it by booting a VM attached to that
+                    network.
                 dependencies:
                     installer: ''
                     scenario: ''
                 run:
-                    module: 'functest.core.tenantnetwork'
-                    class: 'TenantNetwork1'
+                    module: 'functest.core.singlevm'
+                    class: 'SingleVm1'
 
             -
-                case_name: tenantnetwork2
+                case_name: singlevm2
                 project_name: functest
                 criteria: 100
-                blocking: false
+                blocking: true
                 description: >-
                     It creates new user/project before creating and configuring
-                    all tenant network ressources required by a testcase
-                    (subnet, network and router).
+                    all tenant network ressources and vms required by advanced
+                    testcases.
                 dependencies:
                     installer: ''
                     scenario: ''
                 run:
-                    module: 'functest.core.tenantnetwork'
-                    class: 'TenantNetwork2'
+                    module: 'functest.core.singlevm'
+                    class: 'SingleVm2'
+
+            -
+                case_name: api_check
+                project_name: functest
+                criteria: 100
+                blocking: true
+                description: >-
+                    This test case verifies the retrieval of OpenStack clients:
+                    Keystone, Glance, Neutron and Nova and may perform some
+                    simple queries. When the config value of
+                    snaps.use_keystone is True, functest must have access to
+                    the cloud's private network.
+                dependencies:
+                    installer: ''
+                    scenario: '^((?!lxd).)*$'
+                run:
+                    module: 'functest.opnfv_tests.openstack.snaps.api_check'
+                    class: 'ApiCheck'
+
+            -
+                case_name: snaps_health_check
+                project_name: functest
+                criteria: 100
+                blocking: true
+                description: >-
+                    This test case creates executes the SimpleHealthCheck
+                    Python test class which creates an, image, flavor, network,
+                    and Cirros VM instance and observes the console output to
+                    validate the single port obtains the correct IP address.
+                dependencies:
+                    installer: ''
+                    scenario: '^((?!lxd).)*$'
+                run:
+                    module: 'functest.opnfv_tests.openstack.snaps.health_check'
+                    class: 'HealthCheck'
index 55d9994..f203634 100644 (file)
@@ -28,71 +28,103 @@ tiers:
                     class: 'ConnectionCheck'
 
             -
-                case_name: api_check
+                case_name: tenantnetwork1
                 project_name: functest
                 criteria: 100
                 blocking: true
                 description: >-
-                    This test case verifies the retrieval of OpenStack clients:
-                    Keystone, Glance, Neutron and Nova and may perform some
-                    simple queries. When the config value of
-                    snaps.use_keystone is True, functest must have access to
-                    the cloud's private network.
+                    It creates and configures all tenant network ressources
+                    required by advanced testcases (subnet, network and
+                    router).
                 dependencies:
                     installer: ''
-                    scenario: '^((?!lxd).)*$'
+                    scenario: ''
                 run:
-                    module: 'functest.opnfv_tests.openstack.snaps.api_check'
-                    class: 'ApiCheck'
+                    module: 'functest.core.tenantnetwork'
+                    class: 'TenantNetwork1'
 
             -
-                case_name: snaps_health_check
+                case_name: tenantnetwork2
                 project_name: functest
                 criteria: 100
                 blocking: true
                 description: >-
-                    This test case creates executes the SimpleHealthCheck
-                    Python test class which creates an, image, flavor, network,
-                    and Cirros VM instance and observes the console output to
-                    validate the single port obtains the correct IP address.
+                    It creates new user/project before creating and configuring
+                    all tenant network ressources required by a testcase
+                    (subnet, network and router).
                 dependencies:
                     installer: ''
-                    scenario: '^((?!lxd).)*$'
+                    scenario: ''
                 run:
-                    module: 'functest.opnfv_tests.openstack.snaps.health_check'
-                    class: 'HealthCheck'
+                    module: 'functest.core.tenantnetwork'
+                    class: 'TenantNetwork2'
 
             -
-                case_name: tenantnetwork1
+                case_name: singlevm1
                 project_name: functest
                 criteria: 100
-                blocking: false
+                blocking: true
                 description: >-
-                    It creates and configures all tenant network ressources
-                    required by advanced testcases (subnet, network and
-                    router).
+                    It inherits from TenantNetwork1 which creates all network
+                    resources and completes it by booting a VM attached to that
+                    network.
                 dependencies:
                     installer: ''
                     scenario: ''
                 run:
-                    module: 'functest.core.tenantnetwork'
-                    class: 'TenantNetwork1'
+                    module: 'functest.core.singlevm'
+                    class: 'SingleVm1'
 
             -
-                case_name: tenantnetwork2
+                case_name: singlevm2
                 project_name: functest
                 criteria: 100
-                blocking: false
+                blocking: true
                 description: >-
                     It creates new user/project before creating and configuring
-                    all tenant network ressources required by a testcase
-                    (subnet, network and router).
+                    all tenant network ressources and vms required by advanced
+                    testcases.
                 dependencies:
                     installer: ''
                     scenario: ''
                 run:
-                    module: 'functest.core.tenantnetwork'
-                    class: 'TenantNetwork2'
+                    module: 'functest.core.singlevm'
+                    class: 'SingleVm2'
+
+            -
+                case_name: api_check
+                project_name: functest
+                criteria: 100
+                blocking: true
+                description: >-
+                    This test case verifies the retrieval of OpenStack clients:
+                    Keystone, Glance, Neutron and Nova and may perform some
+                    simple queries. When the config value of
+                    snaps.use_keystone is True, functest must have access to
+                    the cloud's private network.
+                dependencies:
+                    installer: ''
+                    scenario: '^((?!lxd).)*$'
+                run:
+                    module: 'functest.opnfv_tests.openstack.snaps.api_check'
+                    class: 'ApiCheck'
+
+            -
+                case_name: snaps_health_check
+                project_name: functest
+                criteria: 100
+                blocking: true
+                description: >-
+                    This test case creates executes the SimpleHealthCheck
+                    Python test class which creates an, image, flavor, network,
+                    and Cirros VM instance and observes the console output to
+                    validate the single port obtains the correct IP address.
+                dependencies:
+                    installer: ''
+                    scenario: '^((?!lxd).)*$'
+                run:
+                    module: 'functest.opnfv_tests.openstack.snaps.health_check'
+                    class: 'HealthCheck'
 
     -
         name: smoke
diff --git a/functest/core/singlevm.py b/functest/core/singlevm.py
new file mode 100644 (file)
index 0000000..c6f4493
--- /dev/null
@@ -0,0 +1,283 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2018 Orange and others.
+#
+# All rights reserved. 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
+
+"""Ease deploying a single VM reachable via ssh
+
+It offers a simple way to create all tenant network ressources + a VM for
+advanced testcases (e.g. deploying an orchestrator).
+"""
+
+import logging
+import os
+import tempfile
+import time
+import uuid
+
+import os_client_config
+import paramiko
+import shade
+from xtesting.core import testcase
+
+from functest.core import tenantnetwork
+from functest.utils import config
+
+
+class SingleVm1(tenantnetwork.TenantNetwork1):
+    """Deploy a single VM reachable via ssh (scenario1)
+
+    It inherits from TenantNetwork1 which creates all network resources and
+    completes it by booting a VM attached to that network.
+
+    It ensures that all testcases inheriting from SingleVm1 could work
+    without specific configurations (or at least read the same config data).
+    """
+    # pylint: disable=too-many-instance-attributes
+
+    __logger = logging.getLogger(__name__)
+    filename = '/home/opnfv/functest/images/cirros-0.4.0-x86_64-disk.img'
+    flavor_ram = 1024
+    flavor_vcpus = 1
+    flavor_disk = 1
+    username = 'cirros'
+    ssh_connect_timeout = 60
+
+    def __init__(self, **kwargs):
+        if "case_name" not in kwargs:
+            kwargs["case_name"] = 'singlevm1'
+        super(SingleVm1, self).__init__(**kwargs)
+        self.orig_cloud = self.cloud
+        self.image = None
+        self.sshvm = None
+        self.flavor = None
+        self.sec = None
+        self.fip = None
+        self.keypair = None
+        self.ssh = paramiko.SSHClient()
+        (_, self.key_filename) = tempfile.mkstemp()
+
+    def _publish_image(self):
+        assert self.cloud
+        meta = getattr(
+            config.CONF, '{}_extra_properties'.format(self.case_name), None)
+        self.image = self.cloud.create_image(
+            '{}-img_{}'.format(self.case_name, self.guid),
+            filename=getattr(
+                config.CONF, '{}_image'.format(self.case_name),
+                self.filename),
+            meta=meta)
+        self.__logger.debug("image: %s", self.image)
+
+    def create_sg_rules(self):
+        """Create the security group
+
+        It can be overriden to set other rules according to the services
+        running in the VM
+
+        Raises: Exception on error
+        """
+        assert self.cloud
+        self.sec = self.cloud.create_security_group(
+            '{}-sg_{}'.format(self.case_name, self.guid),
+            'created by OPNFV Functest ({})'.format(self.case_name))
+        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, protocol='icmp', direction='ingress')
+
+    def _boot_vm(self):
+        assert self.orig_cloud
+        assert self.cloud
+        self.flavor = self.orig_cloud.create_flavor(
+            '{}-flavor_{}'.format(self.case_name, self.guid),
+            getattr(config.CONF, '{}_flavor_ram'.format(self.case_name),
+                    self.flavor_ram),
+            getattr(config.CONF, '{}_flavor_vcpus'.format(self.case_name),
+                    self.flavor_vcpus),
+            getattr(config.CONF, '{}_flavor_disk'.format(self.case_name),
+                    self.flavor_disk))
+        self.__logger.debug("flavor: %s", self.flavor)
+        self.cloud.set_flavor_specs(
+            self.flavor.id, getattr(config.CONF, 'flavor_extra_specs', {}))
+
+        self.keypair = self.cloud.create_keypair(
+            '{}-kp_{}'.format(self.case_name, self.guid))
+        self.__logger.debug("keypair: %s", self.keypair)
+        self.__logger.debug("private_key: %s", self.keypair.private_key)
+        with open(self.key_filename, 'w') as private_key_file:
+            private_key_file.write(self.keypair.private_key)
+
+        self.sshvm = self.cloud.create_server(
+            '{}-vm_{}'.format(self.case_name, self.guid),
+            image=self.image.id, flavor=self.flavor.id,
+            key_name=self.keypair.id,
+            auto_ip=False, wait=True,
+            network=self.network.id,
+            security_groups=[self.sec.id])
+        self.__logger.debug("vm: %s", self.sshvm)
+        self.fip = self.cloud.create_floating_ip(
+            network=self.ext_net.id, server=self.sshvm)
+        self.__logger.debug("floating_ip: %s", self.fip)
+        self.sshvm = self.cloud.wait_for_server(self.sshvm, auto_ip=False)
+
+    def _connect(self):
+        assert self.cloud
+        p_console = self.cloud.get_server_console(self.sshvm.id)
+        self.__logger.debug("vm console: \n%s", p_console)
+        self.ssh.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
+        for loop in range(6):
+            try:
+                self.ssh.connect(
+                    self.sshvm.public_v4,
+                    username=getattr(
+                        config.CONF,
+                        '{}_image_user'.format(self.case_name), self.username),
+                    key_filename=self.key_filename,
+                    timeout=getattr(
+                        config.CONF,
+                        '{}_vm_ssh_connect_timeout'.format(self.case_name),
+                        self.ssh_connect_timeout))
+                break
+            except Exception:  # pylint: disable=broad-except
+                self.__logger.info(
+                    "try %s: cannot connect to %s", loop + 1,
+                    self.sshvm.public_v4)
+                time.sleep(10)
+        else:
+            self.__logger.error("cannot connect to %s", self.sshvm.public_v4)
+            return False
+        return True
+
+    def execute(self):
+        """Say hello world via ssh
+
+        It can be overriden to execute any command.
+
+        Returns: echo exit codes
+        """
+        (_, stdout, _) = self.ssh.exec_command('echo Hello World')
+        self.__logger.info("output:\n%s", stdout.read())
+        return stdout.channel.recv_exit_status()
+
+    def run(self, **kwargs):
+        """Boot the new VM
+
+        Here are the main actions:
+        - publish the image
+        - add a new ssh key
+        - boot the VM
+        - create the security group
+        - execute the right command over ssh
+
+        Returns:
+        - TestCase.EX_OK
+        - TestCase.EX_RUN_ERROR on error
+        """
+        status = testcase.TestCase.EX_RUN_ERROR
+        try:
+            assert self.cloud
+            super(SingleVm1, self).run(**kwargs)
+            self.result = 0
+            self._publish_image()
+            self.create_sg_rules()
+            self._boot_vm()
+            assert self._connect()
+            if not self.execute():
+                self.result = 100
+                status = testcase.TestCase.EX_OK
+        except Exception:  # pylint: disable=broad-except
+            self.__logger.exception('Cannot run %s', self.case_name)
+        finally:
+            self.stop_time = time.time()
+        return status
+
+    def clean(self):
+        try:
+            assert self.orig_cloud
+            assert self.cloud
+            self.cloud.delete_server(self.sshvm, wait=True)
+            self.cloud.delete_security_group(self.sec.id)
+            self.cloud.delete_image(self.image)
+            self.orig_cloud.delete_flavor(self.flavor.id)
+            self.cloud.delete_keypair(self.keypair.id)
+            self.cloud.delete_floating_ip(self.fip.id)
+            self.cloud.delete_image(self.image)
+        except Exception:  # pylint: disable=broad-except
+            pass
+
+
+class SingleVm2(SingleVm1):
+    """Deploy a single VM reachable via ssh (scenario2)
+
+    It creates new user/project before creating and configuring all tenant
+    network ressources and vms required by advanced testcases.
+
+    It ensures that all testcases inheriting from SingleVm2 could work
+    without specific configurations (or at least read the same config data).
+    """
+
+    __logger = logging.getLogger(__name__)
+
+    def __init__(self, **kwargs):
+        if "case_name" not in kwargs:
+            kwargs["case_name"] = 'singlevm2'
+        super(SingleVm2, self).__init__(**kwargs)
+        try:
+            assert self.cloud
+            self.domain = self.cloud.get_domain(
+                name_or_id=self.cloud.auth.get(
+                    "project_domain_name", "Default"))
+        except Exception:  # pylint: disable=broad-except
+            self.domain = None
+            self.__logger.exception("Cannot connect to Cloud")
+        self.project = None
+        self.user = None
+        self.orig_cloud = None
+        self.password = str(uuid.uuid4())
+
+    def run(self, **kwargs):
+        assert self.cloud
+        assert self.domain
+        try:
+            self.project = self.cloud.create_project(
+                name='{}-project_{}'.format(self.case_name, self.guid),
+                description="Created by OPNFV Functest: {}".format(
+                    self.case_name),
+                domain_id=self.domain.id)
+            self.__logger.debug("project: %s", self.project)
+            self.user = self.cloud.create_user(
+                name='{}-user_{}'.format(self.case_name, self.guid),
+                password=self.password,
+                default_project=self.project.id,
+                domain_id=self.domain.id)
+            self.__logger.debug("user: %s", self.user)
+            self.orig_cloud = self.cloud
+            os.environ["OS_USERNAME"] = self.user.name
+            os.environ["OS_PROJECT_NAME"] = self.user.default_project_id
+            cloud_config = os_client_config.get_config()
+            self.cloud = shade.OpenStackCloud(cloud_config=cloud_config)
+            os.environ["OS_USERNAME"] = self.orig_cloud.auth["username"]
+            os.environ["OS_PROJECT_NAME"] = self.orig_cloud.auth[
+                "project_name"]
+        except Exception:  # pylint: disable=broad-except
+            self.__logger.exception("Cannot create user or project")
+            return testcase.TestCase.EX_RUN_ERROR
+        return super(SingleVm2, self).run(**kwargs)
+
+    def clean(self):
+        try:
+            assert self.cloud
+            assert self.orig_cloud
+            super(SingleVm2, self).clean()
+            assert self.user.id
+            assert self.project.id
+            self.orig_cloud.delete_user(self.user.id)
+            self.orig_cloud.delete_project(self.project.id)
+        except Exception:  # pylint: disable=broad-except
+            self.__logger.exception("cannot clean all ressources")
index e43971f..4a740d6 100644 (file)
@@ -188,6 +188,7 @@ class TenantNetwork2(TenantNetwork1):
     def clean(self):
         try:
             assert self.cloud
+            assert self.orig_cloud
             super(TenantNetwork2, self).clean()
             assert self.user.id
             assert self.project.id