Blacklick test_add_subport if OVN
[functest.git] / functest / core / singlevm.py
index 7393fdb..5ecd482 100644 (file)
@@ -9,7 +9,7 @@
 
 """Ease deploying a single VM reachable via ssh
 
 
 """Ease deploying a single VM reachable via ssh
 
-It offers a simple way to create all tenant network ressources + a VM for
+It offers a simple way to create all tenant network resources + a VM for
 advanced testcases (e.g. deploying an orchestrator).
 """
 
 advanced testcases (e.g. deploying an orchestrator).
 """
 
@@ -23,6 +23,8 @@ from xtesting.core import testcase
 
 from functest.core import tenantnetwork
 from functest.utils import config
 
 from functest.core import tenantnetwork
 from functest.utils import config
+from functest.utils import env
+from functest.utils import functest_utils
 
 
 class VmReady1(tenantnetwork.TenantNetwork1):
 
 
 class VmReady1(tenantnetwork.TenantNetwork1):
@@ -39,23 +41,25 @@ class VmReady1(tenantnetwork.TenantNetwork1):
     __logger = logging.getLogger(__name__)
     filename = '/home/opnfv/functest/images/cirros-0.4.0-x86_64-disk.img'
     image_format = 'qcow2'
     __logger = logging.getLogger(__name__)
     filename = '/home/opnfv/functest/images/cirros-0.4.0-x86_64-disk.img'
     image_format = 'qcow2'
-    filename_alt = None
+    extra_properties = {}
+    filename_alt = filename
     image_alt_format = image_format
     image_alt_format = image_format
+    extra_alt_properties = extra_properties
     visibility = 'private'
     visibility = 'private'
-    extra_properties = None
     flavor_ram = 512
     flavor_vcpus = 1
     flavor_disk = 1
     flavor_ram = 512
     flavor_vcpus = 1
     flavor_disk = 1
+    flavor_extra_specs = {}
     flavor_alt_ram = 1024
     flavor_alt_vcpus = 1
     flavor_alt_disk = 1
     flavor_alt_ram = 1024
     flavor_alt_vcpus = 1
     flavor_alt_disk = 1
+    flavor_alt_extra_specs = flavor_extra_specs
     create_server_timeout = 180
 
     def __init__(self, **kwargs):
         if "case_name" not in kwargs:
             kwargs["case_name"] = 'vmready1'
         super(VmReady1, self).__init__(**kwargs)
     create_server_timeout = 180
 
     def __init__(self, **kwargs):
         if "case_name" not in kwargs:
             kwargs["case_name"] = 'vmready1'
         super(VmReady1, self).__init__(**kwargs)
-        self.orig_cloud = self.cloud
         self.image = None
         self.flavor = None
 
         self.image = None
         self.flavor = None
 
@@ -70,20 +74,27 @@ class VmReady1(tenantnetwork.TenantNetwork1):
         Raises: expection on error
         """
         assert self.cloud
         Raises: expection on error
         """
         assert self.cloud
+        extra_properties = self.extra_properties.copy()
+        if env.get('IMAGE_PROPERTIES'):
+            extra_properties.update(
+                functest_utils.convert_ini_to_dict(
+                    env.get('IMAGE_PROPERTIES')))
+        extra_properties.update(
+            getattr(config.CONF, '{}_extra_properties'.format(
+                self.case_name), {}))
         image = self.cloud.create_image(
             name if name else '{}-img_{}'.format(self.case_name, self.guid),
             filename=getattr(
                 config.CONF, '{}_image'.format(self.case_name),
                 self.filename),
         image = self.cloud.create_image(
             name if name else '{}-img_{}'.format(self.case_name, self.guid),
             filename=getattr(
                 config.CONF, '{}_image'.format(self.case_name),
                 self.filename),
-            meta=getattr(
-                config.CONF, '{}_extra_properties'.format(self.case_name),
-                self.extra_properties),
+            meta=extra_properties,
             disk_format=getattr(
                 config.CONF, '{}_image_format'.format(self.case_name),
                 self.image_format),
             visibility=getattr(
                 config.CONF, '{}_visibility'.format(self.case_name),
             disk_format=getattr(
                 config.CONF, '{}_image_format'.format(self.case_name),
                 self.image_format),
             visibility=getattr(
                 config.CONF, '{}_visibility'.format(self.case_name),
-                self.visibility))
+                self.visibility),
+            wait=True)
         self.__logger.debug("image: %s", image)
         return image
 
         self.__logger.debug("image: %s", image)
         return image
 
@@ -98,21 +109,28 @@ class VmReady1(tenantnetwork.TenantNetwork1):
         Raises: expection on error
         """
         assert self.cloud
         Raises: expection on error
         """
         assert self.cloud
+        extra_alt_properties = self.extra_alt_properties.copy()
+        if env.get('IMAGE_PROPERTIES'):
+            extra_alt_properties.update(
+                functest_utils.convert_ini_to_dict(
+                    env.get('IMAGE_PROPERTIES')))
+        extra_alt_properties.update(
+            getattr(config.CONF, '{}_extra_alt_properties'.format(
+                self.case_name), {}))
         image = self.cloud.create_image(
             name if name else '{}-img_alt_{}'.format(
                 self.case_name, self.guid),
             filename=getattr(
                 config.CONF, '{}_image_alt'.format(self.case_name),
                 self.filename_alt),
         image = self.cloud.create_image(
             name if name else '{}-img_alt_{}'.format(
                 self.case_name, self.guid),
             filename=getattr(
                 config.CONF, '{}_image_alt'.format(self.case_name),
                 self.filename_alt),
-            meta=getattr(
-                config.CONF, '{}_extra_properties'.format(self.case_name),
-                self.extra_properties),
+            meta=extra_alt_properties,
             disk_format=getattr(
                 config.CONF, '{}_image_alt_format'.format(self.case_name),
                 self.image_format),
             visibility=getattr(
                 config.CONF, '{}_visibility'.format(self.case_name),
             disk_format=getattr(
                 config.CONF, '{}_image_alt_format'.format(self.case_name),
                 self.image_format),
             visibility=getattr(
                 config.CONF, '{}_visibility'.format(self.case_name),
-                self.visibility))
+                self.visibility),
+            wait=True)
         self.__logger.debug("image: %s", image)
         return image
 
         self.__logger.debug("image: %s", image)
         return image
 
@@ -136,8 +154,15 @@ class VmReady1(tenantnetwork.TenantNetwork1):
             getattr(config.CONF, '{}_flavor_disk'.format(self.case_name),
                     self.flavor_disk))
         self.__logger.debug("flavor: %s", flavor)
             getattr(config.CONF, '{}_flavor_disk'.format(self.case_name),
                     self.flavor_disk))
         self.__logger.debug("flavor: %s", flavor)
-        self.orig_cloud.set_flavor_specs(
-            flavor.id, getattr(config.CONF, 'flavor_extra_specs', {}))
+        flavor_extra_specs = self.flavor_extra_specs.copy()
+        if env.get('FLAVOR_EXTRA_SPECS'):
+            flavor_extra_specs.update(
+                functest_utils.convert_ini_to_dict(
+                    env.get('FLAVOR_EXTRA_SPECS')))
+        flavor_extra_specs.update(
+            getattr(config.CONF,
+                    '{}_flavor_extra_specs'.format(self.case_name), {}))
+        self.orig_cloud.set_flavor_specs(flavor.id, flavor_extra_specs)
         return flavor
 
     def create_flavor_alt(self, name=None):
         return flavor
 
     def create_flavor_alt(self, name=None):
@@ -161,8 +186,16 @@ class VmReady1(tenantnetwork.TenantNetwork1):
             getattr(config.CONF, '{}_flavor_alt_disk'.format(self.case_name),
                     self.flavor_alt_disk))
         self.__logger.debug("flavor: %s", flavor)
             getattr(config.CONF, '{}_flavor_alt_disk'.format(self.case_name),
                     self.flavor_alt_disk))
         self.__logger.debug("flavor: %s", flavor)
+        flavor_alt_extra_specs = self.flavor_alt_extra_specs.copy()
+        if env.get('FLAVOR_EXTRA_SPECS'):
+            flavor_alt_extra_specs.update(
+                functest_utils.convert_ini_to_dict(
+                    env.get('FLAVOR_EXTRA_SPECS')))
+        flavor_alt_extra_specs.update(
+            getattr(config.CONF,
+                    '{}_flavor_alt_extra_specs'.format(self.case_name), {}))
         self.orig_cloud.set_flavor_specs(
         self.orig_cloud.set_flavor_specs(
-            flavor.id, getattr(config.CONF, 'flavor_extra_specs', {}))
+            flavor.id, flavor_alt_extra_specs)
         return flavor
 
     def boot_vm(self, name=None, **kwargs):
         return flavor
 
     def boot_vm(self, name=None, **kwargs):
@@ -194,16 +227,52 @@ class VmReady1(tenantnetwork.TenantNetwork1):
             console = self.cloud.get_server_console(name)
             self.__logger.debug("console: \n%s", console)
             if re.search(regex, console):
             console = self.cloud.get_server_console(name)
             self.__logger.debug("console: \n%s", console)
             if re.search(regex, console):
-                self.__logger.debug("regex found: ''%s' in console", regex)
-                return True
-            else:
                 self.__logger.debug(
                 self.__logger.debug(
-                    "try %s: cannot find regex '%s' in console",
-                    iloop + 1, regex)
-                time.sleep(10)
+                    "regex found: '%s' in console\n%s", regex, console)
+                return True
+            self.__logger.debug(
+                "try %s: cannot find regex '%s' in console\n%s",
+                iloop + 1, regex, console)
+            time.sleep(10)
         self.__logger.error("cannot find regex '%s' in console", regex)
         return False
 
         self.__logger.error("cannot find regex '%s' in console", regex)
         return False
 
+    def clean_orphan_security_groups(self):
+        """Clean all security groups which are not owned by an existing tenant
+
+        It lists all orphan security groups in use as debug to avoid
+        misunderstanding the testcase results (it could happen if cloud admin
+        removes accounts without cleaning the virtual machines)
+        """
+        sec_groups = self.orig_cloud.list_security_groups()
+        for sec_group in sec_groups:
+            if not sec_group.tenant_id:
+                continue
+            if not self.orig_cloud.get_project(sec_group.tenant_id):
+                self.__logger.debug("Cleaning security group %s", sec_group.id)
+                try:
+                    self.orig_cloud.delete_security_group(sec_group.id)
+                except Exception:  # pylint: disable=broad-except
+                    self.__logger.debug(
+                        "Orphan security group %s in use", sec_group.id)
+
+    def count_hypervisors(self):
+        """Count hypervisors."""
+        if env.get('SKIP_DOWN_HYPERVISORS').lower() == 'false':
+            return len(self.orig_cloud.list_hypervisors())
+        return self.count_active_hypervisors()
+
+    def count_active_hypervisors(self):
+        """Count all hypervisors which are up."""
+        compute_cnt = 0
+        for hypervisor in self.orig_cloud.list_hypervisors():
+            if hypervisor['state'] == 'up':
+                compute_cnt += 1
+            else:
+                self.__logger.warning(
+                    "%s is down", hypervisor['hypervisor_hostname'])
+        return compute_cnt
+
     def run(self, **kwargs):
         """Boot the new VM
 
     def run(self, **kwargs):
         """Boot the new VM
 
@@ -240,15 +309,17 @@ class VmReady1(tenantnetwork.TenantNetwork1):
                 self.cloud.delete_image(self.image.id)
             if self.flavor:
                 self.orig_cloud.delete_flavor(self.flavor.id)
                 self.cloud.delete_image(self.image.id)
             if self.flavor:
                 self.orig_cloud.delete_flavor(self.flavor.id)
+            if env.get('CLEAN_ORPHAN_SECURITY_GROUPS').lower() == 'true':
+                self.clean_orphan_security_groups()
         except Exception:  # pylint: disable=broad-except
         except Exception:  # pylint: disable=broad-except
-            self.__logger.exception("Cannot clean all ressources")
+            self.__logger.exception("Cannot clean all resources")
 
 
 class VmReady2(VmReady1):
     """Deploy a single VM reachable via ssh (scenario2)
 
     It creates new user/project before creating and configuring all tenant
 
 
 class VmReady2(VmReady1):
     """Deploy a single VM reachable via ssh (scenario2)
 
     It creates new user/project before creating and configuring all tenant
-    network ressources, flavors, images, etc. required by advanced testcases.
+    network resources, flavors, images, etc. 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).
 
     It ensures that all testcases inheriting from SingleVm2 could work
     without specific configurations (or at least read the same config data).
@@ -277,7 +348,7 @@ class VmReady2(VmReady1):
             assert self.project
             self.project.clean()
         except Exception:  # pylint: disable=broad-except
             assert self.project
             self.project.clean()
         except Exception:  # pylint: disable=broad-except
-            self.__logger.exception("Cannot clean all ressources")
+            self.__logger.exception("Cannot clean all resources")
 
 
 class SingleVm1(VmReady1):
 
 
 class SingleVm1(VmReady1):
@@ -293,8 +364,9 @@ class SingleVm1(VmReady1):
 
     __logger = logging.getLogger(__name__)
     username = 'cirros'
 
     __logger = logging.getLogger(__name__)
     username = 'cirros'
-    ssh_connect_timeout = 60
+    ssh_connect_timeout = 1
     ssh_connect_loops = 6
     ssh_connect_loops = 6
+    create_floating_ip_timeout = 120
 
     def __init__(self, **kwargs):
         if "case_name" not in kwargs:
 
     def __init__(self, **kwargs):
         if "case_name" not in kwargs:
@@ -319,7 +391,7 @@ class SingleVm1(VmReady1):
         self.keypair = self.cloud.create_keypair(
             '{}-kp_{}'.format(self.case_name, self.guid))
         self.__logger.debug("keypair: %s", self.keypair)
         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)
+        self.__logger.debug("private_key:\n%s", self.keypair.private_key)
         with open(self.key_filename, 'w') as private_key_file:
             private_key_file.write(self.keypair.private_key)
         self.sec = self.cloud.create_security_group(
         with open(self.key_filename, 'w') as private_key_file:
             private_key_file.write(self.keypair.private_key)
         self.sec = self.cloud.create_security_group(
@@ -343,14 +415,15 @@ class SingleVm1(VmReady1):
         """
         assert vm1
         fip = self.cloud.create_floating_ip(
         """
         assert vm1
         fip = self.cloud.create_floating_ip(
-            network=self.ext_net.id, server=vm1)
+            network=self.ext_net.id, server=vm1, wait=True,
+            timeout=self.create_floating_ip_timeout)
         self.__logger.debug("floating_ip: %s", fip)
         self.__logger.debug("floating_ip: %s", fip)
-        p_console = self.cloud.get_server_console(vm1)
-        self.__logger.debug("vm console: \n%s", p_console)
         ssh = paramiko.SSHClient()
         ssh.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
         for loop in range(self.ssh_connect_loops):
             try:
         ssh = paramiko.SSHClient()
         ssh.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
         for loop in range(self.ssh_connect_loops):
             try:
+                p_console = self.cloud.get_server_console(vm1)
+                self.__logger.debug("vm console: \n%s", p_console)
                 ssh.connect(
                     fip.floating_ip_address,
                     username=getattr(
                 ssh.connect(
                     fip.floating_ip_address,
                     username=getattr(
@@ -362,11 +435,11 @@ class SingleVm1(VmReady1):
                         '{}_vm_ssh_connect_timeout'.format(self.case_name),
                         self.ssh_connect_timeout))
                 break
                         '{}_vm_ssh_connect_timeout'.format(self.case_name),
                         self.ssh_connect_timeout))
                 break
-            except Exception:  # pylint: disable=broad-except
+            except Exception as exc:  # pylint: disable=broad-except
                 self.__logger.debug(
                 self.__logger.debug(
-                    "try %s: cannot connect to %s", loop + 1,
-                    fip.floating_ip_address)
-                time.sleep(10)
+                    "try %s: cannot connect to %s: %s", loop + 1,
+                    fip.floating_ip_address, exc)
+                time.sleep(9)
         else:
             self.__logger.error(
                 "cannot connect to %s", fip.floating_ip_address)
         else:
             self.__logger.error(
                 "cannot connect to %s", fip.floating_ip_address)
@@ -431,14 +504,14 @@ class SingleVm1(VmReady1):
                 self.cloud.delete_keypair(self.keypair.name)
             super(SingleVm1, self).clean()
         except Exception:  # pylint: disable=broad-except
                 self.cloud.delete_keypair(self.keypair.name)
             super(SingleVm1, self).clean()
         except Exception:  # pylint: disable=broad-except
-            self.__logger.exception("Cannot clean all ressources")
+            self.__logger.exception("Cannot clean all resources")
 
 
 class SingleVm2(SingleVm1):
     """Deploy a single VM reachable via ssh (scenario2)
 
     It creates new user/project before creating and configuring all tenant
 
 
 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.
+    network resources 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).
 
     It ensures that all testcases inheriting from SingleVm2 could work
     without specific configurations (or at least read the same config data).
@@ -467,4 +540,4 @@ class SingleVm2(SingleVm1):
             assert self.project
             self.project.clean()
         except Exception:  # pylint: disable=broad-except
             assert self.project
             self.project.clean()
         except Exception:  # pylint: disable=broad-except
-            self.__logger.exception("Cannot clean all ressources")
+            self.__logger.exception("Cannot clean all resources")