def run(self): """ The runner for KloudBuster Tests """ vm_creation_concurrency = self.client_cfg.vm_creation_concurrency try: tenant_quota = self.calc_tenant_quota() self.kloud.create_resources(tenant_quota['server']) self.testing_kloud.create_resources(tenant_quota['client']) # Start the runner and ready for the incoming redis messages client_list = self.testing_kloud.get_all_instances() server_list = self.kloud.get_all_instances() # Setting up the KloudBuster Proxy node self.kb_proxy = client_list[-1] client_list.pop() self.kb_proxy.vm_name = 'KB-PROXY' self.kb_proxy.user_data['role'] = 'KB-PROXY' self.kb_proxy.boot_info['flavor_type'] = 'kb.proxy' if \ not self.tenants_list['client'] else self.testing_kloud.flavor_to_use if self.topology: proxy_hyper = self.topology.clients_rack.split()[0] self.kb_proxy.boot_info['avail_zone'] =\ "%s:%s" % (self.testing_kloud.placement_az, proxy_hyper)\ if self.testing_kloud.placement_az else "nova:%s" % (proxy_hyper) self.kb_proxy.boot_info['user_data'] = str(self.kb_proxy.user_data) self.testing_kloud.create_vm(self.kb_proxy) self.kb_runner = KBRunner(client_list, self.client_cfg, kb_vm_agent.get_image_version(), self.single_cloud) self.kb_runner.setup_redis(self.kb_proxy.fip_ip) if self.single_cloud: # Find the shared network if the cloud used to testing is same # Attach the router in tested kloud to the shared network shared_net = self.testing_kloud.get_first_network() self.kloud.attach_to_shared_net(shared_net) # Create VMs in both tested and testing kloud concurrently self.client_vm_create_thread = threading.Thread(target=self.testing_kloud.create_vms, args=[vm_creation_concurrency]) self.server_vm_create_thread = threading.Thread(target=self.kloud.create_vms, args=[vm_creation_concurrency]) self.client_vm_create_thread.daemon = True self.server_vm_create_thread.daemon = True if self.single_cloud: self.gen_user_data("Server") self.server_vm_create_thread.start() self.server_vm_create_thread.join() self.gen_user_data("Client") self.client_vm_create_thread.start() self.client_vm_create_thread.join() else: self.gen_user_data("Server") self.gen_user_data("Client") self.server_vm_create_thread.start() self.client_vm_create_thread.start() self.server_vm_create_thread.join() self.client_vm_create_thread.join() # Function that print all the provisioning info self.print_provision_info() # Run the runner to perform benchmarkings self.kb_runner.run() self.final_result = self.kb_runner.tool_result self.final_result['total_server_vms'] = len(server_list) self.final_result['total_client_vms'] = len(client_list) # self.final_result['host_stats'] = self.kb_runner.host_stats LOG.info(self.final_result) except KeyboardInterrupt: traceback.format_exc() except (ClientException, Exception): traceback.print_exc() # Cleanup: start with tested side first # then testing side last (order is important because of the shared network) if self.server_cfg['cleanup_resources']: try: self.kloud.delete_resources() except Exception: traceback.print_exc() KBResLogger.dump_and_save('svr', self.kloud.res_logger.resource_list) if self.client_cfg['cleanup_resources']: try: self.testing_kloud.delete_resources() except Exception: traceback.print_exc() KBResLogger.dump_and_save('clt', self.testing_kloud.res_logger.resource_list)
class KloudBuster(object): """ Creates resources on the cloud for loading up the cloud 1. Tenants 2. Users per tenant 3. Routers per user 4. Networks per router 5. Instances per network """ def __init__(self, server_cred, client_cred, server_cfg, client_cfg, topology, tenants_list): # List of tenant objects to keep track of all tenants self.server_cred = server_cred self.client_cred = client_cred self.server_cfg = server_cfg self.client_cfg = client_cfg if topology and tenants_list: self.topology = None LOG.warn("REUSING MODE: Topology configs will be ignored.") else: self.topology = topology if tenants_list: 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) if server_cred == client_cred: self.single_cloud = True else: self.single_cloud = False self.kloud = Kloud(server_cfg, server_cred, self.tenants_list['server']) self.testing_kloud = Kloud(client_cfg, client_cred, self.tenants_list['client'], testing_side=True) self.kb_proxy = None self.final_result = None self.server_vm_create_thread = None self.client_vm_create_thread = None self.kb_runner = None self.fp_logfile = None def check_and_upload_images(self): keystone_list = [create_keystone_client(self.server_cred)[0], create_keystone_client(self.client_cred)[0]] keystone_dict = dict(zip(['Server kloud', 'Client kloud'], keystone_list)) img_name_dict = dict(zip(['Server kloud', 'Client kloud'], [self.server_cfg.image_name, self.client_cfg.image_name])) for kloud, keystone in keystone_dict.items(): glance_endpoint = keystone.service_catalog.url_for( service_type='image', endpoint_type='publicURL') glance_client = glanceclient.Client(glance_endpoint, token=keystone.auth_token) try: # Search for the image glance_client.images.list(filters={'name': img_name_dict[kloud]}).next() return True except StopIteration: pass # Trying to upload images kb_image_name = 'dib/' + kb_vm_agent.get_image_name() + '.qcow2' if not os.path.exists(kb_image_name): LOG.error("VM Image not in Glance and could not find " + kb_image_name + " to upload, please refer to dib/README.rst for how to build" " image for KloudBuster.") return False LOG.info("Image is not found in %s, uploading %s..." % (kloud, kb_image_name)) with open(kb_image_name) as fimage: try: image = glance_client.images.create(name=img_name_dict[kloud], disk_format="qcow2", container_format="bare", visibility='public') glance_client.images.upload(image['id'], fimage) except glance_exception.HTTPForbidden: LOG.error("Cannot upload image without admin access. Please make sure the " "image is uploaded and is either public or owned by you.") return False return True def print_provision_info(self): """ Function that iterates and prints all VM info for tested and testing cloud """ table = [["VM Name", "Host", "Internal IP", "Floating IP", "Subnet", "Shared Interface IP"]] client_list = self.kloud.get_all_instances() for instance in client_list: row = [instance.vm_name, instance.host, instance.fixed_ip, instance.fip_ip, instance.subnet_ip, instance.shared_interface_ip] table.append(row) LOG.info('Provision Details (Tested Kloud)\n' + tabulate(table, headers="firstrow", tablefmt="psql")) table = [["VM Name", "Host", "Internal IP", "Floating IP", "Subnet"]] client_list = self.testing_kloud.get_all_instances(include_kb_proxy=True) for instance in client_list: row = [instance.vm_name, instance.host, instance.fixed_ip, instance.fip_ip, instance.subnet_ip] table.append(row) LOG.info('Provision Details (Testing Kloud)\n' + tabulate(table, headers="firstrow", tablefmt="psql")) def gen_user_data(self, role): LOG.info("Preparing metadata for VMs... (%s)" % role) if role == "Server": svr_list = self.kloud.get_all_instances() KBScheduler.setup_vm_placement(role, svr_list, self.topology, self.kloud.placement_az, "Round-robin") for ins in svr_list: ins.user_data['role'] = 'Server' ins.boot_info['flavor_type'] = 'kb.server' if \ not self.tenants_list['server'] else self.kloud.flavor_to_use ins.boot_info['user_data'] = str(ins.user_data) elif role == "Client": client_list = self.testing_kloud.get_all_instances() svr_list = self.kloud.get_all_instances() KBScheduler.setup_vm_mappings(client_list, svr_list, "1:1") KBScheduler.setup_vm_placement(role, client_list, self.topology, self.testing_kloud.placement_az, "Round-robin") for idx, ins in enumerate(client_list): ins.user_data['role'] = 'Client' ins.user_data['vm_name'] = ins.vm_name ins.user_data['redis_server'] = self.kb_proxy.fixed_ip ins.user_data['redis_server_port'] = 6379 ins.user_data['target_subnet_ip'] = svr_list[idx].subnet_ip ins.user_data['target_shared_interface_ip'] = svr_list[idx].shared_interface_ip ins.user_data['http_tool'] = ins.config['http_tool'] ins.user_data['http_tool_configs'] = ins.config['http_tool_configs'] ins.boot_info['flavor_type'] = 'kb.client' if \ not self.tenants_list['client'] else self.testing_kloud.flavor_to_use ins.boot_info['user_data'] = str(ins.user_data) def run(self): """ The runner for KloudBuster Tests """ vm_creation_concurrency = self.client_cfg.vm_creation_concurrency try: tenant_quota = self.calc_tenant_quota() self.kloud.create_resources(tenant_quota['server']) self.testing_kloud.create_resources(tenant_quota['client']) # Start the runner and ready for the incoming redis messages client_list = self.testing_kloud.get_all_instances() server_list = self.kloud.get_all_instances() # Setting up the KloudBuster Proxy node self.kb_proxy = client_list[-1] client_list.pop() self.kb_proxy.vm_name = 'KB-PROXY' self.kb_proxy.user_data['role'] = 'KB-PROXY' self.kb_proxy.boot_info['flavor_type'] = 'kb.proxy' if \ not self.tenants_list['client'] else self.testing_kloud.flavor_to_use if self.topology: proxy_hyper = self.topology.clients_rack.split()[0] self.kb_proxy.boot_info['avail_zone'] =\ "%s:%s" % (self.testing_kloud.placement_az, proxy_hyper)\ if self.testing_kloud.placement_az else "nova:%s" % (proxy_hyper) self.kb_proxy.boot_info['user_data'] = str(self.kb_proxy.user_data) self.testing_kloud.create_vm(self.kb_proxy) self.kb_runner = KBRunner(client_list, self.client_cfg, kb_vm_agent.get_image_version(), self.single_cloud) self.kb_runner.setup_redis(self.kb_proxy.fip_ip) if self.single_cloud: # Find the shared network if the cloud used to testing is same # Attach the router in tested kloud to the shared network shared_net = self.testing_kloud.get_first_network() self.kloud.attach_to_shared_net(shared_net) # Create VMs in both tested and testing kloud concurrently self.client_vm_create_thread = threading.Thread(target=self.testing_kloud.create_vms, args=[vm_creation_concurrency]) self.server_vm_create_thread = threading.Thread(target=self.kloud.create_vms, args=[vm_creation_concurrency]) self.client_vm_create_thread.daemon = True self.server_vm_create_thread.daemon = True if self.single_cloud: self.gen_user_data("Server") self.server_vm_create_thread.start() self.server_vm_create_thread.join() self.gen_user_data("Client") self.client_vm_create_thread.start() self.client_vm_create_thread.join() else: self.gen_user_data("Server") self.gen_user_data("Client") self.server_vm_create_thread.start() self.client_vm_create_thread.start() self.server_vm_create_thread.join() self.client_vm_create_thread.join() # Function that print all the provisioning info self.print_provision_info() # Run the runner to perform benchmarkings self.kb_runner.run() self.final_result = self.kb_runner.tool_result self.final_result['total_server_vms'] = len(server_list) self.final_result['total_client_vms'] = len(client_list) # self.final_result['host_stats'] = self.kb_runner.host_stats LOG.info(self.final_result) except KeyboardInterrupt: traceback.format_exc() except (ClientException, Exception): traceback.print_exc() # Cleanup: start with tested side first # then testing side last (order is important because of the shared network) if self.server_cfg['cleanup_resources']: try: self.kloud.delete_resources() except Exception: traceback.print_exc() KBResLogger.dump_and_save('svr', self.kloud.res_logger.resource_list) if self.client_cfg['cleanup_resources']: try: self.testing_kloud.delete_resources() except Exception: traceback.print_exc() KBResLogger.dump_and_save('clt', self.testing_kloud.res_logger.resource_list) def dump_logs(self, offset=0): if not self.fp_logfile: self.fp_logfile = open(CONF.log_file) self.fp_logfile.seek(offset) return self.fp_logfile.read() def dispose(self): self.fp_logfile.close() logging.delete_logfile('kloudbuster') def get_tenant_vm_count(self, config): return (config['users_per_tenant'] * config['routers_per_user'] * config['networks_per_router'] * config['vms_per_network']) def calc_neutron_quota(self): total_vm = self.get_tenant_vm_count(self.server_cfg) server_quota = {} server_quota['network'] = self.server_cfg['routers_per_user'] *\ self.server_cfg['networks_per_router'] server_quota['subnet'] = server_quota['network'] server_quota['router'] = self.server_cfg['routers_per_user'] if (self.server_cfg['use_floatingip']): # (1) Each VM has one floating IP # (2) Each Router has one external IP server_quota['floatingip'] = total_vm + server_quota['router'] # (1) Each VM Floating IP takes up 1 port, total of $total_vm port(s) # (2) Each VM Fixed IP takes up 1 port, total of $total_vm port(s) # (3) Each Network has one router_interface (gateway), and one DHCP agent, total of # server_quota['network'] * 2 port(s) # (4) Each Router has one external IP, takes up 1 port, total of # server_quota['router'] port(s) server_quota['port'] = 2 * total_vm + 2 * server_quota['network'] +\ server_quota['router'] else: 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'] * 10 client_quota = {} total_vm = total_vm * self.server_cfg['number_tenants'] client_quota['network'] = 1 client_quota['subnet'] = 1 client_quota['router'] = 1 if (self.client_cfg['use_floatingip']): # (1) Each VM has one floating IP # (2) Each Router has one external IP, total of 1 router # (3) KB-Proxy node has one floating IP client_quota['floatingip'] = total_vm + 1 + 1 # (1) Each VM Floating IP takes up 1 port, total of $total_vm port(s) # (2) Each VM Fixed IP takes up 1 port, total of $total_vm port(s) # (3) Each Network has one router_interface (gateway), and one DHCP agent, total of # client_quota['network'] * 2 port(s) # (4) KB-Proxy node takes up 2 ports, one for fixed IP, one for floating IP # (5) Each Router has one external IP, takes up 1 port, total of 1 router/port client_quota['port'] = 2 * total_vm + 2 * client_quota['network'] + 2 + 1 else: client_quota['floatingip'] = 1 + 1 client_quota['port'] = total_vm + 2 * client_quota['network'] + 2 + 1 if self.single_cloud: # Under single-cloud mode, the shared network is attached to every router in server # 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'] * 10 return [server_quota, client_quota] def calc_nova_quota(self): total_vm = self.get_tenant_vm_count(self.server_cfg) server_quota = {} server_quota['instances'] = total_vm server_quota['cores'] = total_vm * self.server_cfg['flavor']['vcpus'] server_quota['ram'] = total_vm * self.server_cfg['flavor']['ram'] client_quota = {} total_vm = total_vm * self.server_cfg['number_tenants'] client_quota['instances'] = total_vm + 1 client_quota['cores'] = total_vm * self.client_cfg['flavor']['vcpus'] + 1 client_quota['ram'] = total_vm * self.client_cfg['flavor']['ram'] + 2048 return [server_quota, client_quota] def calc_cinder_quota(self): total_vm = self.get_tenant_vm_count(self.server_cfg) server_quota = {} server_quota['gigabytes'] = total_vm * self.server_cfg['flavor']['disk'] client_quota = {} total_vm = total_vm * self.server_cfg['number_tenants'] client_quota['gigabytes'] = total_vm * self.client_cfg['flavor']['disk'] + 20 return [server_quota, client_quota] def calc_tenant_quota(self): quota_dict = {'server': {}, 'client': {}} nova_quota = self.calc_nova_quota() neutron_quota = self.calc_neutron_quota() cinder_quota = self.calc_cinder_quota() for idx, val in enumerate(['server', 'client']): quota_dict[val]['nova'] = nova_quota[idx] quota_dict[val]['neutron'] = neutron_quota[idx] quota_dict[val]['cinder'] = cinder_quota[idx] return quota_dict