X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=functest%2Fcore%2Fsinglevm.py;h=5ecd482ac9061a516734839b245fab5620bccd6a;hb=7d2d6d9e9330b5535d52b88a6c864151b512ae5b;hp=2a8ae879aca751a4756e8b65287db25121b6b4b3;hpb=1b75bbbc4e842ca2c8c14957261c88692f9e4828;p=functest.git diff --git a/functest/core/singlevm.py b/functest/core/singlevm.py index 2a8ae879a..5ecd482ac 100644 --- a/functest/core/singlevm.py +++ b/functest/core/singlevm.py @@ -9,7 +9,7 @@ """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). """ @@ -23,6 +23,8 @@ from xtesting.core import testcase 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): @@ -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' - filename_alt = None + extra_properties = {} + filename_alt = filename image_alt_format = image_format + extra_alt_properties = extra_properties visibility = 'private' - extra_properties = None 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_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) - self.orig_cloud = self.cloud self.image = None self.flavor = None @@ -70,20 +74,27 @@ class VmReady1(tenantnetwork.TenantNetwork1): 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), - 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), - self.visibility)) + self.visibility), + wait=True) self.__logger.debug("image: %s", image) return image @@ -98,21 +109,28 @@ class VmReady1(tenantnetwork.TenantNetwork1): 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), - 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), - self.visibility)) + self.visibility), + wait=True) 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) - 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): @@ -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) + 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( - flavor.id, getattr(config.CONF, 'flavor_extra_specs', {})) + flavor.id, flavor_alt_extra_specs) return flavor def boot_vm(self, name=None, **kwargs): @@ -180,8 +213,7 @@ class VmReady1(tenantnetwork.TenantNetwork1): name if name else '{}-vm_{}'.format(self.case_name, self.guid), image=self.image.id, flavor=self.flavor.id, auto_ip=False, network=self.network.id, - timeout=self.create_server_timeout, **kwargs) - vm1 = self.cloud.wait_for_server(vm1, auto_ip=False) + timeout=self.create_server_timeout, wait=True, **kwargs) self.__logger.debug("vm: %s", vm1) return vm1 @@ -195,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): - self.__logger.debug("regex found: ''%s' in console", regex) - return True - else: 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 + 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 @@ -241,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) + if env.get('CLEAN_ORPHAN_SECURITY_GROUPS').lower() == 'true': + self.clean_orphan_security_groups() 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 - 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). @@ -278,7 +348,7 @@ class VmReady2(VmReady1): 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): @@ -294,8 +364,9 @@ class SingleVm1(VmReady1): __logger = logging.getLogger(__name__) username = 'cirros' - ssh_connect_timeout = 60 + ssh_connect_timeout = 1 ssh_connect_loops = 6 + create_floating_ip_timeout = 120 def __init__(self, **kwargs): if "case_name" not in kwargs: @@ -320,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.__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( @@ -344,14 +415,15 @@ class SingleVm1(VmReady1): """ 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) - 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: + 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( @@ -363,11 +435,11 @@ class SingleVm1(VmReady1): '{}_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( - "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) @@ -381,8 +453,9 @@ class SingleVm1(VmReady1): Returns: echo exit codes """ - (_, stdout, _) = self.ssh.exec_command('echo Hello World') + (_, stdout, stderr) = self.ssh.exec_command('echo Hello World') self.__logger.debug("output:\n%s", stdout.read()) + self.__logger.debug("error:\n%s", stderr.read()) return stdout.channel.recv_exit_status() def run(self, **kwargs): @@ -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.__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 - 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). @@ -467,4 +540,4 @@ class SingleVm2(SingleVm1): 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")