Enhance the supports for tenant/user reusing
When running under tenant/user reusing mode: 1. Adding features to automatically pick the flavor; 2. Adding features to check quotas before testing; 3. Merge the server and client tenant into one; Change-Id: Ibdda7624174056d7770de02ed0813cad6db2ac05
This commit is contained in:
parent
edefeac214
commit
65f0580c41
@ -217,6 +217,9 @@ class Flavor(object):
|
||||
def __init__(self, novaclient):
|
||||
self.novaclient = novaclient
|
||||
|
||||
def list(self):
|
||||
return self.novaclient.flavors.list()
|
||||
|
||||
def create_flavor(self, name, ram, vcpus, disk, override=False):
|
||||
# Creating flavors
|
||||
if override:
|
||||
@ -236,6 +239,9 @@ class NovaQuota(object):
|
||||
self.novaclient = novaclient
|
||||
self.tenant_id = tenant_id
|
||||
|
||||
def get(self):
|
||||
return self.novaclient.quotas.get(self.tenant_id).__dict__
|
||||
|
||||
def update_quota(self, **kwargs):
|
||||
self.novaclient.quotas.update(self.tenant_id, **kwargs)
|
||||
|
||||
@ -245,5 +251,8 @@ class CinderQuota(object):
|
||||
self.cinderclient = cinderclient
|
||||
self.tenant_id = tenant_id
|
||||
|
||||
def get(self):
|
||||
return self.cinderclient.quotas.get(self.tenant_id).__dict__
|
||||
|
||||
def update_quota(self, **kwargs):
|
||||
self.cinderclient.quotas.update(self.tenant_id, **kwargs)
|
||||
|
@ -23,12 +23,14 @@ from neutronclient.common.exceptions import NetworkInUseClient
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Global CIDR shared by all objects of this class
|
||||
# Enables each network to get a unique CIDR
|
||||
START_CIDR = "10.0.0.0/16"
|
||||
cidr = START_CIDR
|
||||
|
||||
class KBGetExtNetException(Exception):
|
||||
pass
|
||||
|
||||
def create_floating_ip(neutron_client, ext_net):
|
||||
"""
|
||||
Function that creates a floating ip and returns it
|
||||
@ -63,8 +65,8 @@ def find_external_network(neutron_client):
|
||||
if network['router:external']:
|
||||
return network
|
||||
|
||||
LOG.error("No external network found!!!")
|
||||
return None
|
||||
LOG.error("No external network is found.")
|
||||
raise KBGetExtNetException()
|
||||
|
||||
|
||||
class BaseNetwork(object):
|
||||
@ -375,6 +377,9 @@ class NeutronQuota(object):
|
||||
self.neutronclient = neutronclient
|
||||
self.tenant_id = tenant_id
|
||||
|
||||
def get(self):
|
||||
return self.neutronclient.show_quota(self.tenant_id)['quota']
|
||||
|
||||
def update_quota(self, quotas):
|
||||
body = {
|
||||
'quota': quotas
|
||||
|
@ -7,22 +7,20 @@
|
||||
#
|
||||
# Settings in this file has higher priority than user configs. It determines
|
||||
# the final count of tenants and useres that KloudBuster will use.
|
||||
#
|
||||
# If running under tenant/user reusing mode, KloudBuster will use *only* one
|
||||
# tenant to hold the resources for both server cloud and client cloud.
|
||||
#
|
||||
# NOTE:
|
||||
# (1) For now, we only support one user per tenant;
|
||||
# (2) Under tenant/user resuing mode, all resources will be sitting under
|
||||
# the same tenant, so there will be fixed *ONLY* one user for holding
|
||||
# client side resources;
|
||||
|
||||
|
||||
# Sections for listing the tenant and user lists for server cloud.
|
||||
# Note: For now we only support 1 user per tenant
|
||||
server:
|
||||
- name: ts_1
|
||||
user:
|
||||
- username: tsu_1
|
||||
password: tsu_1
|
||||
- name: ts_2
|
||||
user:
|
||||
- username: tsu_2
|
||||
password: tsu_2
|
||||
|
||||
client:
|
||||
- name: tc_1
|
||||
user:
|
||||
- username: tcu_1
|
||||
password: tcu_1
|
||||
tenant_name: demo_tenant
|
||||
server_user:
|
||||
- username: demo_user_1
|
||||
password: demo_user_1
|
||||
client_user:
|
||||
username: demo_user_2
|
||||
password: demo_user_2
|
||||
|
@ -154,8 +154,8 @@ class KBConfig(object):
|
||||
if CONF.tenants_list:
|
||||
self.tenants_list = configure.Configuration.from_file(CONF.tenants_list).configure()
|
||||
try:
|
||||
self.config_scale['number_tenants'] = len(self.tenants_list['server'])
|
||||
self.config_scale['users_per_tenant'] = len(self.tenants_list['server'][0]['user'])
|
||||
self.config_scale['number_tenants'] = 1
|
||||
self.config_scale['users_per_tenant'] = len(self.tenants_list['server_user'])
|
||||
except Exception as e:
|
||||
LOG.error('Cannot parse the count of tenant/user from the config file.')
|
||||
raise KBConfigParseException(e.message)
|
||||
|
@ -65,8 +65,8 @@ def check_and_upload_images(cred, cred_testing, server_img_name, client_img_name
|
||||
if img['name'] == img_name_dict[kloud]:
|
||||
img_found = True
|
||||
break
|
||||
if img.visibility != 'public' and CONF.tenants_list:
|
||||
LOG.error("Image must be public when running in reusing mode.")
|
||||
if img_found and img.visibility != 'public' and CONF.tenants_list:
|
||||
LOG.error("Image must be public when running in tenant/user reusing mode.")
|
||||
sys.exit(1)
|
||||
|
||||
if not img_found:
|
||||
@ -236,9 +236,13 @@ class KloudBuster(object):
|
||||
else:
|
||||
self.topology = topology
|
||||
if tenants_list:
|
||||
self.tenants_list = tenants_list
|
||||
LOG.warn("REUSING MODE: The quota will not adjust automatically.")
|
||||
LOG.warn("REUSING MODE: The flavor configs will be ignored, and m1.small is used.")
|
||||
self.tenants_list = {}
|
||||
self.tenants_list['server'] =\
|
||||
[{'name': tenants_list['tenant_name'], 'user': tenants_list['server_user']}]
|
||||
self.tenants_list['client'] =\
|
||||
[{'name': tenants_list['tenant_name'], 'user': [tenants_list['client_user']]}]
|
||||
LOG.warn("REUSING MODE: The quotas will not be adjusted automatically.")
|
||||
LOG.warn("REUSING MODE: The flavor configs will be ignored.")
|
||||
else:
|
||||
self.tenants_list = {'server': None, 'client': None}
|
||||
# TODO(check on same auth_url instead)
|
||||
@ -427,7 +431,7 @@ class KloudBuster(object):
|
||||
server_quota['floatingip'] = server_quota['router']
|
||||
server_quota['port'] = total_vm + 2 * server_quota['network'] + server_quota['router']
|
||||
server_quota['security_group'] = server_quota['network'] + 1
|
||||
server_quota['security_group_rule'] = server_quota['security_group'] * 100
|
||||
server_quota['security_group_rule'] = server_quota['security_group'] * 10
|
||||
|
||||
client_quota = {}
|
||||
total_vm = total_vm * self.server_cfg['number_tenants']
|
||||
@ -454,7 +458,7 @@ class KloudBuster(object):
|
||||
# cloud, and each one takes up 1 port on client side.
|
||||
client_quota['port'] = client_quota['port'] + server_quota['router']
|
||||
client_quota['security_group'] = client_quota['network'] + 1
|
||||
client_quota['security_group_rule'] = client_quota['security_group'] * 100
|
||||
client_quota['security_group_rule'] = client_quota['security_group'] * 10
|
||||
|
||||
return [server_quota, client_quota]
|
||||
|
||||
|
@ -34,23 +34,28 @@ class Tenant(object):
|
||||
Stores the shared network in case of testing and
|
||||
tested cloud being on same cloud
|
||||
"""
|
||||
self.tenant_name = tenant_name
|
||||
self.kloud = kloud
|
||||
self.tenant_object = self._get_tenant()
|
||||
self.tenant_id = self.tenant_object.id
|
||||
self.tenant_name = tenant_name
|
||||
if not self.kloud.reusing_tenants:
|
||||
self.tenant_object = self._get_tenant()
|
||||
self.tenant_id = self.tenant_object.id
|
||||
else:
|
||||
LOG.info("Using tenant: " + self.tenant_name)
|
||||
# Only admin can retrive the object via Keystone API
|
||||
self.tenant_object = None
|
||||
try:
|
||||
# Try to see if we have the admin access to retrive the tenant id using
|
||||
# tenant name directly from keystone
|
||||
self.tenant_id = self.kloud.keystone.tenants.find(name=self.tenant_name).id
|
||||
except Exception:
|
||||
self.tenant_id = self.kloud.keystone.tenant_id
|
||||
|
||||
self.tenant_quota = tenant_quota
|
||||
self.reusing_users = reusing_users
|
||||
# Contains a list of user instance objects
|
||||
self.user_list = []
|
||||
|
||||
def _get_tenant(self):
|
||||
if self.kloud.reusing_tenants:
|
||||
LOG.info("Using tenant: " + self.tenant_name)
|
||||
tenant_list = self.kloud.keystone.tenants.list()
|
||||
for tenant in tenant_list:
|
||||
if tenant.name == self.tenant_name:
|
||||
return tenant
|
||||
raise Exception("Tenant not found")
|
||||
|
||||
'''
|
||||
Create or reuse a tenant object of a given name
|
||||
@ -68,15 +73,10 @@ class Tenant(object):
|
||||
if exc.http_status != 409:
|
||||
raise exc
|
||||
LOG.info("Tenant %s already present, reusing it" % self.tenant_name)
|
||||
# It is a hassle to find a tenant by name as the only way seems to retrieve
|
||||
# the list of all tenants which can be very large
|
||||
tenant_list = self.kloud.keystone.tenants.list()
|
||||
for tenant in tenant_list:
|
||||
if tenant.name == self.tenant_name:
|
||||
return tenant
|
||||
return self.kloud.keystone.tenants.find(name=self.tenant_name)
|
||||
|
||||
# Should never come here
|
||||
raise Exception("Tenant not found")
|
||||
raise Exception()
|
||||
|
||||
def create_resources(self):
|
||||
"""
|
||||
@ -87,7 +87,7 @@ class Tenant(object):
|
||||
for user_info in self.reusing_users:
|
||||
user_name = user_info['username']
|
||||
password = user_info['password']
|
||||
user_instance = users.User(user_name, password, self, None)
|
||||
user_instance = users.User(user_name, password, self, '_member_')
|
||||
self.user_list.append(user_instance)
|
||||
else:
|
||||
# Loop over the required number of users and create resources
|
||||
|
@ -22,6 +22,11 @@ from novaclient.client import Client
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
class KBFlavorCheckException(Exception):
|
||||
pass
|
||||
|
||||
class KBQuotaCheckException(Exception):
|
||||
pass
|
||||
|
||||
class User(object):
|
||||
"""
|
||||
@ -45,7 +50,6 @@ class User(object):
|
||||
self.nova_client = None
|
||||
self.neutron_client = None
|
||||
self.cinder_client = None
|
||||
self.user = self._get_user()
|
||||
# Each user is associated to 1 key pair at most
|
||||
self.key_pair = None
|
||||
self.key_name = None
|
||||
@ -56,15 +60,17 @@ class User(object):
|
||||
#
|
||||
# If running on top of existing tenants/users, skip
|
||||
# the step for admin role association.
|
||||
if not self.tenant.kloud.reusing_tenants:
|
||||
current_role = None
|
||||
for role in self.tenant.kloud.keystone.roles.list():
|
||||
if role.name == user_role:
|
||||
current_role = role
|
||||
break
|
||||
if not self.tenant.reusing_users:
|
||||
self.user = self._get_user()
|
||||
current_role = self.tenant.kloud.keystone.roles.find(name=user_role)
|
||||
self.tenant.kloud.keystone.roles.add_user_role(self.user,
|
||||
current_role,
|
||||
tenant.tenant_id)
|
||||
else:
|
||||
# Only admin can retrive the object via Keystone API
|
||||
self.user = None
|
||||
LOG.info("Using user: " + self.user_name)
|
||||
|
||||
|
||||
def _create_user(self):
|
||||
LOG.info("Creating user: " + self.user_name)
|
||||
@ -74,14 +80,6 @@ class User(object):
|
||||
tenant_id=self.tenant.tenant_id)
|
||||
|
||||
def _get_user(self):
|
||||
if self.tenant.reusing_users:
|
||||
LOG.info("Using user: " + self.user_name)
|
||||
users_list = self.tenant.kloud.keystone.users.list()
|
||||
for user in users_list:
|
||||
if user.name == self.user_name:
|
||||
return user
|
||||
raise Exception('Cannot find stale user:' + self.user_name)
|
||||
|
||||
'''
|
||||
Create a new user or reuse if it already exists (on a different tenant)
|
||||
delete the user and create a new one
|
||||
@ -97,19 +95,13 @@ class User(object):
|
||||
# Try to repair keystone by removing that user
|
||||
LOG.warn("User creation failed due to stale user with same name: " +
|
||||
self.user_name)
|
||||
# Again, trying to find a user by name is pretty inefficient as one has to list all
|
||||
# of them
|
||||
users_list = self.tenant.kloud.keystone.users.list()
|
||||
for user in users_list:
|
||||
if user.name == self.user_name:
|
||||
# Found it, time to delete it
|
||||
LOG.info("Deleting stale user with name: " + self.user_name)
|
||||
self.tenant.kloud.keystone.users.delete(user)
|
||||
user = self._create_user()
|
||||
return user
|
||||
user = self.tenant.kloud.keystone.users.find(name=self.user_name)
|
||||
LOG.info("Deleting stale user with name: " + self.user_name)
|
||||
self.tenant.kloud.keystone.users.delete(user)
|
||||
return self._create_user()
|
||||
|
||||
# Not found there is something wrong
|
||||
raise Exception('Cannot find stale user:' + self.user_name)
|
||||
# Should never come here
|
||||
raise Exception()
|
||||
|
||||
def delete_resources(self):
|
||||
LOG.info("Deleting all user resources for user %s" % self.user_name)
|
||||
@ -136,6 +128,47 @@ class User(object):
|
||||
neutron_quota = base_network.NeutronQuota(self.neutron_client, self.tenant.tenant_id)
|
||||
neutron_quota.update_quota(tenant_quota['neutron'])
|
||||
|
||||
def check_resources_quota(self):
|
||||
# Flavor check
|
||||
flavor_manager = base_compute.Flavor(self.nova_client)
|
||||
flavor_to_use = None
|
||||
for flavor in flavor_manager.list():
|
||||
flavor = flavor.__dict__
|
||||
if flavor['vcpus'] < 1 or flavor['ram'] < 1024 or flavor['disk'] < 10:
|
||||
continue
|
||||
flavor_to_use = flavor
|
||||
break
|
||||
if flavor_to_use:
|
||||
LOG.info('Automatically selects flavor %s to instantiate VMs.' %
|
||||
(flavor_to_use['name']))
|
||||
else:
|
||||
LOG.error('Cannot find a flavor which meets the minimum '
|
||||
'requirements to instantiate VMs.')
|
||||
raise KBFlavorCheckException()
|
||||
|
||||
# Nova/Cinder/Neutron quota check
|
||||
tenant_id = self.tenant.tenant_id
|
||||
meet_quota = True
|
||||
for quota_type in ['nova', 'cinder', 'neutron']:
|
||||
if quota_type == 'nova':
|
||||
quota_manager = base_compute.NovaQuota(self.nova_client, tenant_id)
|
||||
elif quota_type == 'cinder':
|
||||
quota_manager = base_compute.CinderQuota(self.cinder_client, tenant_id)
|
||||
else:
|
||||
quota_manager = base_network.NeutronQuota(self.neutron_client, tenant_id)
|
||||
|
||||
meet_quota = True
|
||||
quota = quota_manager.get()
|
||||
for key, value in self.tenant.tenant_quota[quota_type].iteritems():
|
||||
if quota[key] < value:
|
||||
meet_quota = False
|
||||
break
|
||||
|
||||
if not meet_quota:
|
||||
LOG.error('%s quota is too small. Minimum requirement: %s.' %
|
||||
(quota_type, self.tenant.tenant_quota[quota_type]))
|
||||
raise KBQuotaCheckException()
|
||||
|
||||
def create_resources(self):
|
||||
"""
|
||||
Creates all the User elements associated with a User
|
||||
@ -145,7 +178,7 @@ class User(object):
|
||||
# Create a new neutron client for this User with correct credentials
|
||||
creden = {}
|
||||
creden['username'] = self.user_name
|
||||
creden['password'] = self.user_name
|
||||
creden['password'] = self.password
|
||||
creden['auth_url'] = self.tenant.kloud.auth_url
|
||||
creden['tenant_name'] = self.tenant.tenant_name
|
||||
|
||||
@ -155,7 +188,7 @@ class User(object):
|
||||
# Create a new nova and cinder client for this User with correct credentials
|
||||
creden_nova = {}
|
||||
creden_nova['username'] = self.user_name
|
||||
creden_nova['api_key'] = self.user_name
|
||||
creden_nova['api_key'] = self.password
|
||||
creden_nova['auth_url'] = self.tenant.kloud.auth_url
|
||||
creden_nova['project_id'] = self.tenant.tenant_name
|
||||
creden_nova['version'] = 2
|
||||
@ -163,7 +196,9 @@ class User(object):
|
||||
self.nova_client = Client(**creden_nova)
|
||||
self.cinder_client = cinderclient.Client(**creden_nova)
|
||||
|
||||
if not self.tenant.kloud.reusing_tenants:
|
||||
if self.tenant.kloud.reusing_tenants:
|
||||
self.check_resources_quota()
|
||||
else:
|
||||
self.update_tenant_quota(self.tenant.tenant_quota)
|
||||
|
||||
config_scale = self.tenant.kloud.scale_cfg
|
||||
|
Loading…
Reference in New Issue
Block a user