def create_deployment(username, template, logger): """Deploy a new instance of Deployment :Returns: Dictionary :param username: The name of the user who wants to create a new Deployment :type username: String :param template: The name of template being deployed. :type template: String :param logger: An object for logging messages :type logger: logging.LoggerAdapter """ current_deployment = _check_for_deployment(username) if current_deployment: error = "Multiple deployments per lab not allowed. Current have deployed: {}".format(current_deployment) raise ValueError(error) logger.info("Deploying template: %s", template) try: meta = get_meta(template) except FileNotFoundError: raise ValueError("No deployment template named {} exists.".format(template)) deployments = {} futures = set() with ThreadPoolExecutor(max_workers=const.VLAB_DEPLOY_CONCURRENT_VMS) as executor: for machine_name, details in meta['machines'].items(): # Avoids deploy failure due to the user have a VM by the same name # as a VM in a deployment template. deploy_name = '{}{}'.format(machine_name, VM_NAME_APPEND) future = executor.submit(_create_vm, details['ova_path'], deploy_name, template, username, details['kind'], logger) futures.add(future) for future in as_completed(futures): deployments.update(future.result()) return deployments
def list_images(verbose=False): """Obtain a list of available versions of Deployment that can be created :Returns: List :param verbose: Include details about each deployment template. :type verbose: Boolean """ answer = [] images = [x for x in os.listdir(const.VLAB_DEPLOYMENT_TEMPLATE_DIR) if not x.startswith('.')] if verbose: # This exists so the API can return handy info. The deployments service # is unique, in that the templates are created and managed by users. # The other services (like OneFS, InsightIQ, etc) are all managed by the # sysadmin. So returning a simple list of what's available works for those # services. The default of "False" is so we automatically mimic the behavior # of those services when the RESTful API gets called; just give them a # simple list of what's available. As an API client/consumer, I always get # annoyed when two similar API end points return different data structures... for template in images: meta = get_meta(template) detailed_info = {template: meta} answer.append(detailed_info) else: answer = images return answer
def delete(username, template): """Destroy a deployment template. Raises a ValueError if the user does not own the template. :Returns: None :Raises: ValueError :param username: The name of a user trying to delete a deployment template. :type username: String :param template: The name of the deployment template to destroy. :type template: String """ error = '' for a_template in os.listdir(const.VLAB_DEPLOYMENT_TEMPLATE_DIR): if template == a_template: meta = get_meta(template) if meta['owner'] == username: template_path = os.path.join( const.VLAB_DEPLOYMENT_TEMPLATE_DIR, template) shutil.rmtree(template_path) else: error = 'Unable to delete templates you do not own. {} is owned by {}'.format( template, meta['owner']) if error: raise ValueError(error)
def create_port_maps(username, template, user_token, client_ip, logger): """Add port forwarding rules to the NAT firewall of a user's lab. :Returns: None :Raises: requests.exceptions.RequestException :param username: The name of the vLab user. :type usernamne: String :param user_token: The JWT auth token of the user. :type user_token: String :param client_ip: The IP that issued the request. :type client_ip: String """ url = 'https://{}.{}/api/1/ipam/portmap'.format(username, const.VLAB_FQDN) headers = {'X-Auth': user_token, 'X-Forwarded-For': client_ip} portmaps = get_meta(template)['machines'] for vm_name, info in portmaps.items(): for tcp_port in info['ports']: payload = { 'target_addr': info['ip'], 'target_port': tcp_port, 'target_name': '{}{}'.format(vm_name, VM_NAME_APPEND), 'target_component': template, } resp = requests.post( url, json=payload, headers=headers, verify=False) # user gateways have a self-signed cert resp.raise_for_status()
def show(username, logger): """Lookup templates that a user owns :Returns: Dictionary :param username: The name of the user lookup up templates they own. :type username: String :param logger: An object for logging messages :type logger: logging.LoggerAdapter """ templates = {} for template in os.listdir(const.VLAB_DEPLOYMENT_TEMPLATE_DIR): meta = get_meta(template) if meta['owner'] == username: templates[template] = meta return templates
def test_get_meta(self, fake_read_meta, fake_listdir): """``_get_meta`` Dynamically adds the ova_path property to the meta data returned""" fake_listdir.return_value = ['someVM.ova', 'aFile.json'] fake_read_meta.return_value = { "machines": { "someVM": { "ip": "1.2.3.4", "kind": "foo" } } } meta = template_meta_data.get_meta(template='foo') expected = { 'machines': { 'someVM': { 'ip': '1.2.3.4', 'kind': 'foo', 'ova_path': '/templates/foo/someVM.ova' } } } self.assertEqual(meta, expected)