# Copyright 2016 AT&T Corp # 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. import random import time import uuid from oslo_log import log as logging from ranger_tempest_plugin.tests.api import base from tempest import config from tempest.lib import exceptions from tempest.lib.common.utils import data_utils CONF = config.CONF LOG = logging.getLogger(__name__) class FmsBaseOrmTest(base.BaseOrmTest): credentials = ['admin', 'primary', 'alt'] @classmethod def setup_clients(cls): super(FmsBaseOrmTest, cls).setup_clients() cls.client = cls.os_primary.fms_client cls.flavors_client = cls.os_admin.flavors_client cls.rms_client = cls.os_primary.rms_client cls.cms_client = cls.os_primary.cms_client @classmethod def resource_setup(cls): super(FmsBaseOrmTest, cls).resource_setup() cls.tenant_id = None cls.alt_tenant_id = None @classmethod def resource_cleanup(cls): super(FmsBaseOrmTest, cls).resource_cleanup() @classmethod def _create_customer(cls): cust = {} cust['description'] = '' cust['enabled'] = True cust['name'] = data_utils.rand_name('ormTempestFms') cust['regions'] = [{'name': CONF.identity.region, 'type': 'single'}] cust['defaultQuotas'] = [] cust['users'] = [] cust['uuid'] = uuid.uuid4().hex _, body = cls.cms_client.create_customer(**cust) customer_id = body["customer"]["id"] _, customer = cls.cms_client.get_customer(customer_id) if customer["name"] != cust["name"]: message = "Customer %s creation FAILED" % cust["name"] exceptions.TempestException(message) customer_status = customer["status"] expected_status = 'Success' start = int(time.time()) while customer_status != expected_status: time.sleep(cls.build_interval) _, customer = cls.cms_client.get_customer(customer_id) customer_status = customer["status"] if customer_status == 'Error': message = "" for region in customer["regions"]: if "error_message" in region: message += "Region %s Error: %s. " % ( region["name"], region["error_message"]) if not message: message = ('Customer %s failed to reach %s status and is' ' in ERROR status' % (customer_id, expected_status)) raise exceptions.ServerFault(message) if int(time.time()) - start >= cls.build_timeout: message = ('Customer %s failed to reach %s' 'status within the required time (%s s) ' 'and is in %s status.' % (customer_id, expected_status, cls.build_timeout, customer_status)) raise exceptions.TimeoutException(message) return customer_id @classmethod def _del_cust_validate_deletion_on_dcp_and_lcp(cls, customer_id): _, customer = cls.cms_client.get_customer(customer_id) regions_on_customer = \ [region['name'] for region in customer["regions"]] for region in regions_on_customer: cls.cms_client.delete_region_from_customer(customer_id, region) time.sleep(cls.build_timeout) _, body = cls.cms_client.get_customer(customer_id) if len(body['regions']) != 0: message = \ 'Failed to delete regions for customer %s' % customer_id raise exceptions.TimeoutException(message) cls.cms_client.delete_customer(customer_id) cls._validate_customer_deletion_on_lcp(customer_id) @classmethod def _validate_customer_deletion_on_lcp(cls, customer_id): body = cls.project_client.list_projects()["projects"] customer_ids = [customer["id"] for customer in body if customer["id"] == customer_id] if customer_ids: message = "customer %s failed to get deleted on lcp" \ % customer_id raise exceptions.TempestException(message) @classmethod def _get_flavor_params(cls, set_region=True, single_tenant=True, public=False): post_body, region = {}, {} region["name"] = CONF.identity.region ram = random.randint(1, 4) * 1024 swap = random.randint(1, 40) * 1024 vcpus = random.randint(2, 36) disk = random.randint(2, 102) post_body['id'] = uuid.uuid4().hex post_body["description"] = \ "orm-plugin-BaseORMTest-flavor" post_body["series"] = random.choice(CONF.ranger.flavor_series) post_body["alias"] = "flavor_alias" post_body["ram"] = str(ram) post_body["vcpus"] = str(vcpus) post_body["disk"] = str(disk) post_body["swap"] = str(swap) post_body["ephemeral"] = "1024" post_body["regions"] = [region] if set_region else [] post_body["visibility"] = "private" if not public else "public" post_body['tag'] = {'a': 'b', 'c': 'd'} if public: post_body["tenants"] = [] else: if single_tenant: post_body["tenants"] = [cls.tenant_id] else: post_body["tenants"] = [cls.tenant_id, cls.alt_tenant_id] return post_body @classmethod def _create_flv_and_validate_creation_on_dcp_and_lcp(cls, **kwargs): """kwargs contain all field data needed in a flavor POST body: - name - description - alias - ram - vcpus - disk - swap - ephemeral - regions - visibility - tenants """ _, body = cls.client.create_flavor(**kwargs) flavor = body["flavor"] flavor_id = flavor["id"] _, body = cls.client.get_flavor(flavor_id) flavor_detail = body["flavor"] if flavor_detail["vcpus"] != kwargs["vcpus"]: message = "Flavor %s not created successfully" % flavor_id raise exceptions.TempestException(message) if flavor_detail["regions"] != []: flavor_id = flavor_detail["id"] cls._wait_for_flavor_status_on_dcp(flavor_id, 'Success') cls._validate_flavor_creation_on_lcp(flavor_id) return flavor @classmethod def _wait_for_flavor_status_on_dcp(cls, flavor_id, status): _, body = cls.client.get_flavor(flavor_id) flavor = body["flavor"] flavor_status = flavor["status"] start = int(time.time()) while flavor_status != status: time.sleep(cls.build_interval) _, body = cls.client.get_flavor(flavor_id) flavor = body["flavor"] flavor_status = flavor["status"] if flavor_status == 'Error': # Some test cases have multiple regions message = "" for region in flavor["regions"]: if "error_message" in region: message += "Region %s Error: %s. " % ( region["name"], region["error_message"]) if not message: message = ('Flavor %s failed to reach %s status' ' and is in ERROR status' % (flavor_id, status)) raise exceptions.ServerFault(message) if int(time.time()) - start >= cls.build_timeout: message = ('Flavor %s failed to reach %s status within' ' the required time (%s s) and is in' ' %s status.') % (flavor_id, status, cls.build_timeout, flavor_status) raise exceptions.TimeoutException(message) @classmethod def _validate_flavor_creation_on_lcp(cls, flavor_id): _, body = cls.client.list_flavors() flavor = [flavor["id"] for flavor in body["flavors"] if flavor["id"] == flavor_id] if not flavor: message = "flavor %s not in nova flavor list" % flavor_id raise exceptions.TempestException(message) @classmethod def _validate_flv_extraspecs_on_dcp_and_lcp(cls, flavor_id, expected_specs): expected_specs_count = len(expected_specs) _, body = cls.client.get_flavor(flavor_id) flavor_orm = body["flavor"] flavor_lcp = cls.flavors_client.show_flavor(flavor_id)["flavor"] def _validate_extra_specs(flv): actual_specs_count = 0 actual_specs = {} for spec in flv["extra-specs"]: actual_specs[spec] = flv["extra-specs"][spec] for spec in expected_specs: if spec in actual_specs: if expected_specs[spec] == actual_specs[spec]: actual_specs_count += 1 return bool(expected_specs_count == actual_specs_count) return bool(_validate_extra_specs(flavor_orm) and _validate_extra_specs(flavor_lcp)) @classmethod def _del_flv_and_validate_deletion_on_dcp_and_lcp(cls, flavor_id): _, body = cls.client.get_flavor(flavor_id) regions_on_flavor = \ [region['name'] for region in body["flavor"]["regions"]] for regs in regions_on_flavor: cls._delete_region_from_flavor_and_validate_deletion( flavor_id, regs) cls.client.delete_flavor(flavor_id) cls._wait_for_flavor_deletion_on_dcp(flavor_id) cls._validate_flavor_deletion_on_lcp(flavor_id) @classmethod def _delete_region_from_flavor_and_validate_deletion( cls, flavor_id, rname): cls.client.delete_region_from_flavor(flavor_id, rname) _, body = cls.client.get_flavor(flavor_id) loopcount = 0 while loopcount < 10: if len(body['flavor']['regions']) == 0: break for regions_on_flavor in body['flavor']['regions']: if regions_on_flavor['name'] == rname: time.sleep(cls.build_interval) _, body = cls.client.get_flavor(flavor_id) break loopcount += 1 for regions_on_flavor in body['flavor']['regions']: if regions_on_flavor['name'] == rname: message = \ 'Region {} failed to get deleted from flavor {}' \ .format(rname, flavor_id) raise exceptions.TimeoutException(message) @classmethod def _wait_for_flavor_deletion_on_dcp(cls, flavor_id): _, body = cls.client.list_flavors() flavor_ids = [flavor["id"] for flavor in body["flavors"] if flavor["id"] == flavor_id] start = int(time.time()) while flavor_ids: time.sleep(cls.build_interval) _, body = cls.client.list_flavors() flavor_ids = [flavor["id"] for flavor in body["flavors"] if flavor["id"] == flavor_id] if flavor_ids: flavor_status = flavor_ids[0]["status"] if flavor_status == 'Error': message = \ ('Flavor %s failed to get deleted' 'and is in error status') % flavor_id raise exceptions.TempestException(message) if int(time.time()) - start >= cls.build_timeout: message = ( 'flavor %s failed to get deleted within ' 'the required time (%s s) and is in %s status.' % (flavor_id, cls.build_timeout, flavor_status)) raise exceptions.TimeoutException(message) @classmethod def _validate_flavor_deletion_on_lcp(cls, flavor_id): body = cls.flavors_client.list_flavors()["flavors"] flavor_ids = [flavor["id"] for flavor in body] if flavor_id in flavor_ids: flavor_status = cls.flavors_client.show_flavor( flavor_id)["flavor"]["status"] message = "flavor %s failed to get deleted and is in %s status" \ % (flavor_id, flavor_status) raise exceptions.TempestException(message) @classmethod def _get_project_id(cls, project_name): body = cls.project_client.list_projects() for project in body["projects"]: if(project["name"] == project_name): return project["id"] message = ('project %s not found on projects list' % project_name) raise exceptions.TempestException(message) @classmethod def _get_expected_flavor_name(cls, post_body): name = post_body["series"] + "." + "c" + \ post_body["vcpus"] + "r" + \ str(int(post_body["ram"]) // 1024) \ + "d" + post_body["disk"] + "s" + \ str(int(post_body["swap"]) // 1024) \ + "e" + str(int(post_body["ephemeral"]) // 1024) return name @classmethod def _validate_flv_geometry_on_lcp(cls, flavor_id, post_body): flv = cls.flavors_client.show_flavor(flavor_id)["flavor"] return bool(flv["vcpus"] == int(post_body["vcpus"]) and flv["ram"] == post_body["ram"] and flv["swap"] == int(post_body["swap"]) and flv["disk"] == int(post_body["disk"]) and flv["ephemeral"] == post_body["ephemeral"])