def cse_restore_session(ctx, vdc_required=False) -> None: """Restores the session with vcd client with right server api version. Replace the vcd client in ctx.obj with new client created with server api version. Also saves the server api version in profiles. :param <click.core.Context> ctx: click context :param bool vdc_required: is vdc required or not :return: """ # Always override the vcd_client by new client with CSE server api version. if type(ctx.obj) is not dict or not ctx.obj.get('client'): profiles = Profiles.load() token = profiles.get('token') if token is None or len(token) == 0: raise Exception('Can\'t restore session, please login again.') if not profiles.get('verify'): if not profiles.get('disable_warnings'): click.secho( 'InsecureRequestWarning: ' 'Unverified HTTPS request is being made. ' 'Adding certificate verification is strongly ' 'advised.', fg='yellow', err=True) requests.packages.urllib3.disable_warnings() client = Client( profiles.get('host'), api_version=profiles.get('api_version'), verify_ssl_certs=profiles.get('verify'), log_file='vcd.log', log_requests=profiles.get('log_request'), log_headers=profiles.get('log_header'), log_bodies=profiles.get('log_body')) client.rehydrate_from_token( profiles.get('token'), profiles.get('is_jwt_token')) ctx.obj = {} ctx.obj['client'] = client _override_client(ctx) if vdc_required: if not ctx.obj['profiles'].get('vdc_in_use') or \ not ctx.obj['profiles'].get('vdc_href'): raise Exception('select a virtual datacenter')
def _override_client(ctx) -> None: """Replace the vcd client in ctx.obj with new one. New vcd client takes the CSE server_api_version as api_version param. Save profile also with 'cse_server_api_version' for subsequent commands. :param <click.core.Context> ctx: click context """ profiles = Profiles.load() # if the key CSE_SERVER_RUNNING is not present in the profiles.yaml, # we make an assumption that CSE server is running is_cse_server_running = profiles.get(CSE_SERVER_RUNNING, default=True) cse_server_api_version = profiles.get(CSE_SERVER_API_VERSION) if not is_cse_server_running: restrict_cli_to_tkg_operations() ctx.obj['profiles'] = profiles return # Get server_api_version; save it in profiles if doesn't exist if not cse_server_api_version: try: system = syst.System(ctx.obj['client']) sys_info = system.get_info() cse_server_api_version = sys_info.get(CSE_SERVER_API_VERSION) profiles.set(CSE_SERVER_API_VERSION, cse_server_api_version) profiles.set(CSE_SERVER_RUNNING, True) profiles.save() except CseResponseError: # If request to CSE server times out profiles.set(CSE_SERVER_RUNNING, False) # restrict CLI for only TKG operations restrict_cli_to_tkg_operations() ctx.obj['profiles'] = profiles profiles.save() return client = Client(profiles.get('host'), api_version=cse_server_api_version, verify_ssl_certs=profiles.get('verify'), log_file='vcd.log', log_requests=profiles.get('log_request'), log_headers=profiles.get('log_header'), log_bodies=profiles.get('log_body')) client.rehydrate_from_token(profiles.get('token'), profiles.get('is_jwt_token')) # noqa: E501 ctx.obj['client'] = client ctx.obj['profiles'] = profiles
def cse_restore_session(ctx) -> None: """Restores the session with vcd client with right server api version. Replace the vcd client in ctx.obj with new client created with server api version. Also saves the server api version in profiles. :param <click.core.Context> ctx: click context :return: """ # Always override the vcd_client by new client with CSE server api version. if type(ctx.obj) is not dict or not ctx.obj.get('client'): CLIENT_LOGGER.debug('Restoring client from profile.') profiles = Profiles.load() token = profiles.get('token') if token is None or len(token) == 0: msg = "Can't restore session, please login again." CLIENT_LOGGER.debug(f"Missing Token : {msg}") raise Exception(msg) if not profiles.get('verify'): if not profiles.get('disable_warnings'): click.secho( 'InsecureRequestWarning: ' 'Unverified HTTPS request is being made. ' 'Adding certificate verification is strongly ' 'advised.', fg='yellow', err=True) requests.packages.urllib3.disable_warnings() client = Client(profiles.get('host'), api_version=profiles.get('api_version'), verify_ssl_certs=profiles.get('verify'), log_file='vcd.log', log_requests=profiles.get('log_request'), log_headers=profiles.get('log_header'), log_bodies=profiles.get('log_body')) client.rehydrate_from_token(profiles.get('token'), profiles.get('is_jwt_token')) ctx.obj = {'client': client} else: CLIENT_LOGGER.debug('Reusing client from context.') _override_client(ctx)
def vcd_login_with_token(host): logging.basicConfig(level=logging.DEBUG) logging.info("INIT vcd_login_with_token") profiles = Profiles.load() token = profiles.get('token') client = Client(host, api_version="27.0", verify_ssl_certs=False, log_file='vcd.log', log_requests=True, log_headers=True, log_bodies=True) try: client.rehydrate_from_token(token) x = client._session.headers['x-vcloud-authorization'] logging.info(" ===== X VCloud ========\n \n" + x + "\n \n") except Exception as e: print('error occured', e)
def vcd_login_with_token(host): logging.basicConfig(level=logging.DEBUG) logging.info("INIT vcd_login_with_token") profiles = Profiles.load() token = profiles.get('token') client = Client( host, api_version="27.0", verify_ssl_certs=False, log_file='vcd.log', log_requests=True, log_headers=True, log_bodies=True) try: client.rehydrate_from_token(token) x = client._session.headers['x-vcloud-authorization'] logging.info(" ===== X VCloud ========\n \n" + x + "\n \n") except Exception as e: print('error occured', e)
def login(session_id=None): log_requests = True if config['log'] == True else False log_headers = True if config['log'] == True else False log_bodies = True if config['log'] == True else False client = Client(config['host'], api_version=config['api_version'], verify_ssl_certs=config['verify_ssl_certs'], log_file=config['log_file'], log_requests=log_requests, log_headers=log_headers, log_bodies=log_bodies) if not config['verify_ssl_certs']: requests.packages.urllib3.disable_warnings() try: if session_id is not None: client.rehydrate_from_token(session_id) else: client.set_credentials( BasicLoginCredentials(config['username'], config['org'], config['password'])) logged_in_org = client.get_org() org = Org(client, resource=logged_in_org) org_url = "https://%s/cloud/org/%s" % (config['host'], logged_in_org) the_vdc = org.get_vdc(config['vdc']) # Set contextual data: ctx.client = client ctx.vdc = VDC(client, href=the_vdc.get('href')) ctx.token = client._session.headers['x-vcloud-authorization'] except Exception as e: if config['debug'] == True: print("Exception: {}\n".format(e)) return False return True
def restore_session(ctx, vdc_required=False): if type(ctx.obj) is dict and 'client' in ctx.obj and ctx.obj['client']: return profiles = Profiles.load() token = profiles.get('token') if token is None or len(token) == 0: raise Exception('Can\'t restore session, please login again.') if not profiles.get('verify'): if profiles.get('disable_warnings'): pass else: click.secho( 'InsecureRequestWarning: ' 'Unverified HTTPS request is being made. ' 'Adding certificate verification is strongly ' 'advised.', fg='yellow', err=True) requests.packages.urllib3.disable_warnings() client = Client( profiles.get('host'), api_version=profiles.get('api_version'), verify_ssl_certs=profiles.get('verify'), log_file='vcd.log', log_requests=profiles.get('log_request'), log_headers=profiles.get('log_header'), log_bodies=profiles.get('log_body')) client.rehydrate_from_token( profiles.get('token'), profiles.get('is_jwt_token')) ctx.obj = {} ctx.obj['client'] = client ctx.obj['profiles'] = profiles if vdc_required: if not ctx.obj['profiles'].get('vdc_in_use') or \ not ctx.obj['profiles'].get('vdc_href'): raise Exception('select a virtual datacenter')
def connect_vcd_user_via_token(tenant_auth_token): server_config = get_server_runtime_config() vcd_uri = server_config['vcd']['host'] version = server_config['vcd']['api_version'] verify_ssl_certs = server_config['vcd']['verify'] client_tenant = Client(uri=vcd_uri, api_version=version, verify_ssl_certs=verify_ssl_certs, log_file=SERVER_DEBUG_WIRELOG_FILEPATH, log_requests=True, log_headers=True, log_bodies=True) session = client_tenant.rehydrate_from_token(tenant_auth_token) return (client_tenant, session)
def connect_tenant(self, headers): token = headers.get('x-vcloud-authorization') accept_header = headers.get('Accept') version = accept_header.split('version=')[1] client_tenant = Client(uri=self.config['vcd']['host'], api_version=version, verify_ssl_certs=self.config['vcd']['verify'], log_headers=True, log_bodies=True) session = client_tenant.rehydrate_from_token(token) return ( client_tenant, session, )
def vcd_login(context, lc): logging.info("__INIT__vcd_login [%s]", lc) login_path = "" client = Client( lc.ip, api_version="27.0", verify_ssl_certs=not lc.allow_insecure_flag, log_file='vcd.log', log_requests=True, log_headers=True, log_bodies=True) try: if lc.use_vcd_cli_profile: login_path = "rehydrate_from_token" profiles = Profiles.load() token = profiles.get('token') client.rehydrate_from_token(token) else: login_path = "set_credentials" client.set_credentials( BasicLoginCredentials(lc.username, lc.org, lc.password)) vref = VCDClientRef() vref.set_ref(client) logging.debug('LOGIN VIA :[{0}] ARG :[{1}]'.format( login_path, str(lc))) return client except Exception as e: traceback.print_exc() error_message = 'ERROR IN LOGIN .. Exception [{0}] LOGIN VIA :[{1}] ARG :[{2}]'.format( str(e), login_path, str(lc)) error_message = error_message.replace('\n', ' ') context.set_code(grpc.StatusCode.INVALID_ARGUMENT) context.set_details(error_message) raise
def vcd_login(context, lc): logging.info("__INIT__vcd_login [%s]", lc) login_path = "" client = Client(lc.ip, api_version="27.0", verify_ssl_certs=not lc.allow_insecure_flag, log_file='vcd.log', log_requests=True, log_headers=True, log_bodies=True) try: if lc.use_vcd_cli_profile: login_path = "rehydrate_from_token" profiles = Profiles.load() token = profiles.get('token') client.rehydrate_from_token(token) else: login_path = "set_credentials" client.set_credentials( BasicLoginCredentials(lc.username, lc.org, lc.password)) vref = VCDClientRef() vref.set_ref(client) logging.debug('LOGIN VIA :[{0}] ARG :[{1}]'.format( login_path, str(lc))) return client except Exception as e: traceback.print_exc() error_message = 'ERROR IN LOGIN .. Exception [{0}] LOGIN VIA :[{1}] ARG :[{2}]'.format( str(e), login_path, str(lc)) error_message = error_message.replace('\n', ' ') context.set_code(grpc.StatusCode.INVALID_ARGUMENT) context.set_details(error_message) raise
def connect_vcd_user_via_token(tenant_auth_token): server_config = get_server_runtime_config() vcd_uri = server_config['vcd']['host'] version = server_config['vcd']['api_version'] verify_ssl_certs = server_config['vcd']['verify'] log_filename = None log_wire = str_to_bool(server_config['service'].get('log_wire')) if log_wire: log_filename = SERVER_DEBUG_WIRELOG_FILEPATH client_tenant = Client(uri=vcd_uri, api_version=version, verify_ssl_certs=verify_ssl_certs, log_file=log_filename, log_requests=log_wire, log_headers=log_wire, log_bodies=log_wire) session = client_tenant.rehydrate_from_token(tenant_auth_token) return (client_tenant, session)
def login_from_token(token): """Return a client session to use with the user's token. Args: token (string): Auth token provided by user. Returns: pyvcloud.vcd.client.Client: Session to use with the user's token. """ client = Client(conf('global.vcloud.hostname'), api_version=conf('global.vcloud.api_version'), verify_ssl_certs=conf('global.vcloud.ssl_verify', True), log_file=conf("global.pyvcloud.log_file"), log_requests=conf("global.pyvcloud.log_requests"), log_headers=conf("global.pyvcloud.log_headers"), log_bodies=conf("global.pyvcloud.log_bodies")) session = client.rehydrate_from_token(token) return client, session
def connect_vcd_user_via_token(vcd_uri, headers, verify_ssl_certs=True): if not verify_ssl_certs: LOGGER.warning("InsecureRequestWarning: Unverified HTTPS request is " "being made. Adding certificate verification is " "strongly advised.") requests.packages.urllib3.disable_warnings() token = headers.get('x-vcloud-authorization') accept_header = headers.get('Accept') version = accept_header.split('version=')[1] client_tenant = Client(uri=vcd_uri, api_version=version, verify_ssl_certs=verify_ssl_certs, log_file=SERVER_DEBUG_WIRELOG_FILEPATH, log_requests=True, log_headers=True, log_bodies=True) session = client_tenant.rehydrate_from_token(token) return ( client_tenant, session, )
def login(ctx, user, host, password, api_version, org, verify_ssl_certs, disable_warnings, vdc, session_id, use_browser_session): """Login to vCloud Director \b Login to a vCloud Director service. \b Examples vcd login mysp.com org1 usr1 Login to host 'mysp.com'. \b vcd login test.mysp.com org1 usr1 -i -w Login to a host with self-signed SSL certificate. \b vcd login mysp.com org1 usr1 --use-browser-session Login using active session from browser. \b vcd login session list chrome List active session ids from browser. \b vcd login mysp.com org1 usr1 \\ --session-id ee968665bf3412d581bbc6192508eec4 Login using active session id. \b Environment Variables VCD_PASSWORD If this environment variable is set, the command will use its value as the password to login and will not ask for one. The --password option has precedence over the environment variable. """ if not verify_ssl_certs: if disable_warnings: pass else: click.secho( 'InsecureRequestWarning: ' 'Unverified HTTPS request is being made. ' 'Adding certificate verification is strongly ' 'advised.', fg='yellow', err=True) requests.packages.urllib3.disable_warnings() if host == 'session' and org == 'list': sessions = [] if user == 'chrome': cookies = browsercookie.chrome() for c in cookies: if c.name == 'vcloud_session_id': sessions.append({'host': c.domain, 'session_id': c.value}) stdout(sessions, ctx) return client = Client( host, api_version=api_version, verify_ssl_certs=verify_ssl_certs, log_file='vcd.log', log_requests=True, log_headers=True, log_bodies=True) try: if session_id is not None or use_browser_session: if use_browser_session: browser_session_id = None cookies = browsercookie.chrome() for c in cookies: if c.name == 'vcloud_session_id' and \ c.domain == host: browser_session_id = c.value break if browser_session_id is None: raise Exception('Session not found in browser.') session_id = browser_session_id client.rehydrate_from_token(session_id) else: if password is None: password = click.prompt('Password', hide_input=True, type=str) client.set_credentials(BasicLoginCredentials(user, org, password)) negotiated_api_version = client.get_api_version() profiles = Profiles.load() logged_in_org = client.get_org() org_href = logged_in_org.get('href') vdc_href = '' in_use_vdc = '' links = [] if float(negotiated_api_version) < float(ApiVersion.VERSION_33.value): links = get_links(logged_in_org, media_type=EntityType.VDC.value) else: if logged_in_org.get('name') != 'System': links = client.get_resource_link_from_query_object( logged_in_org, media_type=EntityType.RECORDS.value, type='vdc') if vdc is None: if len(links) > 0: in_use_vdc = links[0].name vdc_href = links[0].href else: for v in links: if vdc == v.name: in_use_vdc = v.name vdc_href = v.href break if len(in_use_vdc) == 0: raise Exception('VDC not found') token = client.get_access_token() is_jwt_token = True if not token: token = client.get_xvcloud_authorization_token() is_jwt_token = False profiles.update( host, org, user, token, negotiated_api_version, verify_ssl_certs, disable_warnings, vdc=in_use_vdc, org_href=org_href, vdc_href=vdc_href, log_request=True, log_header=True, log_body=True, vapp='', vapp_href='', is_jwt_token=is_jwt_token) alt_text = f"{user} logged in, org: '{org}', vdc: '{in_use_vdc}'" d = { 'user': user, 'org': org, 'vdc': in_use_vdc, 'logged_in': True } stdout(d, ctx, alt_text) except Exception as e: try: profiles = Profiles.load() profiles.set('token', '') except Exception: pass stderr(e, ctx)
class DefaultBroker(threading.Thread): def __init__(self, config): threading.Thread.__init__(self) self.config = config self.host = config['vcd']['host'] self.username = config['vcd']['username'] self.password = config['vcd']['password'] self.version = config['vcd']['api_version'] self.verify = config['vcd']['verify'] self.log = config['vcd']['log'] def _connect_sysadmin(self): if not self.verify: LOGGER.warning('InsecureRequestWarning: ' 'Unverified HTTPS request is being made. ' 'Adding certificate verification is strongly ' 'advised.') requests.packages.urllib3.disable_warnings() self.client_sysadmin = Client(uri=self.host, api_version=self.version, verify_ssl_certs=self.verify, log_headers=True, log_bodies=True) self.client_sysadmin.set_credentials( BasicLoginCredentials(self.username, 'System', self.password)) def _connect_tenant(self, headers): token = headers.get('x-vcloud-authorization') accept_header = headers.get('Accept') version = accept_header.split('version=')[1] self.client_tenant = Client(uri=self.host, api_version=version, verify_ssl_certs=self.verify, log_headers=True, log_bodies=True) session = self.client_tenant.rehydrate_from_token(token) return { 'user_name': session.get('user'), 'user_id': session.get('userId'), 'org_name': session.get('org'), 'org_href': self.client_tenant._get_wk_endpoint( _WellKnownEndpoint.LOGGED_IN_ORG) } def _to_message(self, e): if hasattr(e, 'message'): return {'message': e.message} else: return {'message': str(e)} def update_task(self, status, message=None, error_message=None): if not hasattr(self, 'task'): self.task = Task(self.client_sysadmin) if message is None: message = OP_MESSAGE[self.op] if hasattr(self, 'task_resource'): task_href = self.task_resource.get('href') else: task_href = None self.task_resource = self.task.update( status.value, 'vcloud.cse', message, self.op, '', None, 'urn:cse:cluster:%s' % self.cluster_id, self.cluster_name, 'application/vcloud.cse.cluster+xml', self.tenant_info['user_id'], self.tenant_info['user_name'], org_href=self.tenant_info['org_href'], task_href=task_href, error_message=error_message) def is_valid_name(self, name): """Validate that the cluster name against the pattern.""" if len(name) > MAX_HOST_NAME_LENGTH: return False if name[-1] == '.': name = name[:-1] allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE) return all(allowed.match(x) for x in name.split(".")) def get_template(self, name=None): if name is None: if 'template' in self.body and self.body['template'] is not None: name = self.body['template'] else: name = self.config['broker']['default_template'] for template in self.config['broker']['templates']: if template['name'] == name: return template raise Exception('Template %s not found' % name) def run(self): LOGGER.debug('thread started op=%s' % self.op) if self.op == OP_CREATE_CLUSTER: self.create_cluster_thread() elif self.op == OP_DELETE_CLUSTER: self.delete_cluster_thread() elif self.op == OP_CREATE_NODES: self.create_nodes_thread() elif self.op == OP_DELETE_NODES: self.delete_nodes_thread() def list_clusters(self, headers, body): result = {} try: result['body'] = [] result['status_code'] = OK self._connect_tenant(headers) clusters = load_from_metadata(self.client_tenant) result['body'] = clusters except Exception: LOGGER.error(traceback.format_exc()) result['body'] = [] result['status_code'] = INTERNAL_SERVER_ERROR result['message'] = traceback.format_exc() return result def get_cluster_info(self, name, headers, body): result = {} try: result['body'] = [] result['status_code'] = OK self._connect_tenant(headers) clusters = load_from_metadata(self.client_tenant, name=name) if len(clusters) == 0: raise Exception('Cluster \'%s\' not found.' % name) vapp = VApp(self.client_tenant, href=clusters[0]['vapp_href']) vms = vapp.get_all_vms() for vm in vms: node_info = { 'name': vm.get('name'), 'numberOfCpus': '', 'memoryMB': '', 'status': VCLOUD_STATUS_MAP.get(int(vm.get('status'))), 'ipAddress': '' } if hasattr(vm, 'VmSpecSection'): node_info['numberOfCpus'] = vm.VmSpecSection.NumCpus.text node_info[ 'memoryMB'] = \ vm.VmSpecSection.MemoryResourceMb.Configured.text try: node_info['ipAddress'] = vapp.get_primary_ip( vm.get('name')) except Exception: LOGGER.debug('cannot get ip address for node %s' % vm.get('name')) if vm.get('name').startswith(TYPE_MASTER): node_info['node_type'] = 'master' clusters[0].get('master_nodes').append(node_info) elif vm.get('name').startswith(TYPE_NODE): node_info['node_type'] = 'node' clusters[0].get('nodes').append(node_info) result['body'] = clusters[0] except Exception as e: LOGGER.error(traceback.format_exc()) result['body'] = [] result['status_code'] = INTERNAL_SERVER_ERROR result['message'] = str(e) return result def create_cluster(self, headers, body): result = {} result['body'] = {} cluster_name = body['name'] vdc_name = body['vdc'] node_count = body['node_count'] LOGGER.debug('about to create cluster %s on %s with %s nodes, sp=%s', cluster_name, vdc_name, node_count, body['storage_profile']) result['body'] = { 'message': 'can\'t create cluster \'%s\'' % cluster_name } result['status_code'] = INTERNAL_SERVER_ERROR try: if not self.is_valid_name(cluster_name): raise Exception('Invalid cluster name') self.tenant_info = self._connect_tenant(headers) self.headers = headers self.body = body self.cluster_name = cluster_name self.cluster_id = str(uuid.uuid4()) self.op = OP_CREATE_CLUSTER self._connect_sysadmin() self.update_task(TaskStatus.RUNNING, message='Creating cluster %s(%s)' % (cluster_name, self.cluster_id)) self.daemon = True self.start() response_body = {} response_body['name'] = self.cluster_name response_body['cluster_id'] = self.cluster_id response_body['task_href'] = self.task_resource.get('href') result['body'] = response_body result['status_code'] = ACCEPTED except Exception as e: result['body'] = self._to_message(e) LOGGER.error(traceback.format_exc()) return result def create_cluster_thread(self): network_name = self.body['network'] try: clusters = load_from_metadata(self.client_tenant, name=self.cluster_name) if len(clusters) != 0: raise Exception('Cluster already exists.') org_resource = self.client_tenant.get_org() org = Org(self.client_tenant, resource=org_resource) vdc_resource = org.get_vdc(self.body['vdc']) vdc = VDC(self.client_tenant, resource=vdc_resource) template = self.get_template() self.update_task(TaskStatus.RUNNING, message='Creating cluster vApp %s(%s)' % (self.cluster_name, self.cluster_id)) vapp_resource = vdc.create_vapp(self.cluster_name, description='cluster %s' % self.cluster_name, network=network_name, fence_mode='bridged') self.client_tenant.get_task_monitor().wait_for_status( vapp_resource.Tasks.Task[0]) tags = {} tags['cse.cluster.id'] = self.cluster_id tags['cse.version'] = pkg_resources.require( 'container-service-extension')[0].version tags['cse.template'] = template['name'] vapp = VApp(self.client_tenant, href=vapp_resource.get('href')) for k, v in tags.items(): task = vapp.set_metadata('GENERAL', 'READWRITE', k, v) self.client_tenant.get_task_monitor().wait_for_status(task) self.update_task(TaskStatus.RUNNING, message='Creating master node for %s(%s)' % (self.cluster_name, self.cluster_id)) vapp.reload() add_nodes(1, template, TYPE_MASTER, self.config, self.client_tenant, org, vdc, vapp, self.body) self.update_task(TaskStatus.RUNNING, message='Initializing cluster %s(%s)' % (self.cluster_name, self.cluster_id)) vapp.reload() init_cluster(self.config, vapp, template) master_ip = get_master_ip(self.config, vapp, template) task = vapp.set_metadata('GENERAL', 'READWRITE', 'cse.master.ip', master_ip) self.client_tenant.get_task_monitor().wait_for_status(task) if self.body['node_count'] > 0: self.update_task(TaskStatus.RUNNING, message='Creating %s node(s) for %s(%s)' % (self.body['node_count'], self.cluster_name, self.cluster_id)) add_nodes(self.body['node_count'], template, TYPE_NODE, self.config, self.client_tenant, org, vdc, vapp, self.body) self.update_task(TaskStatus.RUNNING, message='Adding %s node(s) to %s(%s)' % (self.body['node_count'], self.cluster_name, self.cluster_id)) vapp.reload() join_cluster(self.config, vapp, template) self.update_task(TaskStatus.SUCCESS, message='Created cluster %s(%s)' % (self.cluster_name, self.cluster_id)) except Exception as e: LOGGER.error(traceback.format_exc()) self.update_task(TaskStatus.ERROR, error_message=str(e)) def delete_cluster(self, headers, body): result = {} result['body'] = {} LOGGER.debug('about to delete cluster with name: %s' % body['name']) result['status_code'] = INTERNAL_SERVER_ERROR try: self.cluster_name = body['name'] self.tenant_info = self._connect_tenant(headers) self.headers = headers self.body = body self.op = OP_DELETE_CLUSTER self._connect_sysadmin() clusters = load_from_metadata(self.client_tenant, name=self.cluster_name) if len(clusters) != 1: raise Exception('Cluster %s not found.' % self.cluster_name) self.cluster = clusters[0] self.cluster_id = self.cluster['cluster_id'] self.update_task(TaskStatus.RUNNING, message='Deleting cluster %s(%s)' % (self.cluster_name, self.cluster_id)) self.daemon = True self.start() response_body = {} response_body['cluster_name'] = self.cluster_name response_body['task_href'] = self.task_resource.get('href') result['body'] = response_body result['status_code'] = ACCEPTED except Exception as e: result['body'] = self._to_message(e) LOGGER.error(traceback.format_exc()) return result def delete_cluster_thread(self): LOGGER.debug('about to delete cluster with name: %s', self.cluster_name) try: vdc = VDC(self.client_tenant, href=self.cluster['vdc_href']) task = vdc.delete_vapp(self.cluster['name'], force=True) self.client_tenant.get_task_monitor().wait_for_status(task) self.update_task(TaskStatus.SUCCESS, message='Deleted cluster %s(%s)' % (self.cluster_name, self.cluster_id)) except Exception as e: LOGGER.error(traceback.format_exc()) self.update_task(TaskStatus.ERROR, error_message=str(e)) def get_cluster_config(self, cluster_name, headers): result = {} try: self._connect_tenant(headers) clusters = load_from_metadata(self.client_tenant, name=cluster_name) if len(clusters) != 1: raise Exception('Cluster \'%s\' not found' % cluster_name) vapp = VApp(self.client_tenant, href=clusters[0]['vapp_href']) template = self.get_template(name=clusters[0]['template']) result['body'] = get_cluster_config(self.config, vapp, template['admin_password']) result['status_code'] = OK except Exception as e: result['body'] = self._to_message(e) result['status_code'] = INTERNAL_SERVER_ERROR return result def create_nodes(self, headers, body): result = {'body': {}} self.cluster_name = body['name'] LOGGER.debug('about to add %s nodes to cluster %s on VDC %s, sp=%s', body['node_count'], self.cluster_name, body['vdc'], body['storage_profile']) result['status_code'] = INTERNAL_SERVER_ERROR try: if body['node_count'] < 1: raise Exception('Invalid node count: %s.' % body['node_count']) self.tenant_info = self._connect_tenant(headers) clusters = load_from_metadata(self.client_tenant, name=self.cluster_name) if len(clusters) != 1: raise Exception('Cluster \'%s\' not found.' % self.cluster_name) self.cluster = clusters[0] self.headers = headers self.body = body self.op = OP_CREATE_NODES self._connect_sysadmin() self.cluster_id = self.cluster['cluster_id'] self.update_task( TaskStatus.RUNNING, message='Adding %s node(s) to cluster %s(%s)' % (body['node_count'], self.cluster_name, self.cluster_id)) self.daemon = True self.start() response_body = {} response_body['cluster_name'] = self.cluster_name response_body['task_href'] = self.task_resource.get('href') result['body'] = response_body result['status_code'] = ACCEPTED except Exception as e: result['body'] = self._to_message(e) LOGGER.error(traceback.format_exc()) return result def create_nodes_thread(self): LOGGER.debug('about to add nodes to cluster with name: %s', self.cluster_name) try: org_resource = self.client_tenant.get_org() org = Org(self.client_tenant, resource=org_resource) vdc = VDC(self.client_tenant, href=self.cluster['vdc_href']) vapp = VApp(self.client_tenant, href=self.cluster['vapp_href']) template = self.get_template() self.update_task( TaskStatus.RUNNING, message='Creating %s node(s) for %s(%s)' % (self.body['node_count'], self.cluster_name, self.cluster_id)) new_nodes = add_nodes(self.body['node_count'], template, TYPE_NODE, self.config, self.client_tenant, org, vdc, vapp, self.body) self.update_task( TaskStatus.RUNNING, message='Adding %s node(s) to %s(%s)' % (self.body['node_count'], self.cluster_name, self.cluster_id)) target_nodes = [] for spec in new_nodes['specs']: target_nodes.append(spec['target_vm_name']) vapp.reload() join_cluster(self.config, vapp, template, target_nodes) self.update_task( TaskStatus.SUCCESS, message='Added %s node(s) to cluster %s(%s)' % (self.body['node_count'], self.cluster_name, self.cluster_id)) except Exception as e: LOGGER.error(traceback.format_exc()) self.update_task(TaskStatus.ERROR, error_message=str(e)) def delete_nodes(self, headers, body): result = {'body': {}} self.cluster_name = body['name'] LOGGER.debug('about to delete nodes from cluster with name: %s' % body['name']) result['status_code'] = INTERNAL_SERVER_ERROR try: if len(body['nodes']) < 1: raise Exception('Invalid list of nodes: %s.' % body['nodes']) for node in body['nodes']: if node.startswith(TYPE_MASTER): raise Exception('Can\'t delete a master node: \'%s\'.' % node) self.tenant_info = self._connect_tenant(headers) clusters = load_from_metadata(self.client_tenant, name=self.cluster_name) if len(clusters) != 1: raise Exception('Cluster \'%s\' not found.' % self.cluster_name) self.cluster = clusters[0] self.headers = headers self.body = body self.op = OP_DELETE_NODES self._connect_sysadmin() self.cluster_id = self.cluster['cluster_id'] self.update_task( TaskStatus.RUNNING, message='Deleting %s node(s) from cluster %s(%s)' % (len(body['nodes']), self.cluster_name, self.cluster_id)) self.daemon = True self.start() response_body = {} response_body['cluster_name'] = self.cluster_name response_body['task_href'] = self.task_resource.get('href') result['body'] = response_body result['status_code'] = ACCEPTED except Exception as e: result['body'] = self._to_message(e) LOGGER.error(traceback.format_exc()) return result def delete_nodes_thread(self): LOGGER.debug('about to delete nodes from cluster with name: %s', self.cluster_name) try: vapp = VApp(self.client_tenant, href=self.cluster['vapp_href']) template = self.get_template() self.update_task( TaskStatus.RUNNING, message='Deleting %s node(s) from %s(%s)' % (len(self.body['nodes']), self.cluster_name, self.cluster_id)) delete_nodes_from_cluster(self.config, vapp, template, self.body['nodes'], self.body['force']) self.update_task( TaskStatus.RUNNING, message='Undeploying %s node(s) for %s(%s)' % (len(self.body['nodes']), self.cluster_name, self.cluster_id)) for vm_name in self.body['nodes']: vm = VM(self.client_tenant, resource=vapp.get_vm(vm_name)) try: task = vm.undeploy() self.client_tenant.get_task_monitor().wait_for_status(task) except Exception as e: LOGGER.warning('couldn\'t undeploy VM %s' % vm_name) self.update_task( TaskStatus.RUNNING, message='Deleting %s VM(s) for %s(%s)' % (len(self.body['nodes']), self.cluster_name, self.cluster_id)) task = vapp.delete_vms(self.body['nodes']) self.client_tenant.get_task_monitor().wait_for_status(task) self.update_task( TaskStatus.SUCCESS, message='Deleted %s node(s) to cluster %s(%s)' % (len(self.body['nodes']), self.cluster_name, self.cluster_id)) except Exception as e: LOGGER.error(traceback.format_exc()) self.update_task(TaskStatus.ERROR, error_message=str(e))
def login(ctx, user, host, password, api_version, org, verify_ssl_certs, disable_warnings, vdc, session_id, use_browser_session): """Login to vCloud Director \b Login to a vCloud Director service. \b Examples vcd login mysp.com org1 usr1 Login to host 'mysp.com'. \b vcd login test.mysp.com org1 usr1 -i -w Login to a host with self-signed SSL certificate. \b vcd login mysp.com org1 usr1 --use-browser-session Login using active session from browser. \b vcd login session list chrome List active session ids from browser. \b vcd login mysp.com org1 usr1 \\ --session-id ee968665bf3412d581bbc6192508eec4 Login using active session id. \b Environment Variables VCD_PASSWORD If this environment variable is set, the command will use its value as the password to login and will not ask for one. The --password option has precedence over the environment variable. """ if not verify_ssl_certs: if disable_warnings: pass else: click.secho('InsecureRequestWarning: ' 'Unverified HTTPS request is being made. ' 'Adding certificate verification is strongly ' 'advised.', fg='yellow', err=True) requests.packages.urllib3.disable_warnings() if host == 'session' and org == 'list': sessions = [] if user == 'chrome': cookies = browsercookie.chrome() for c in cookies: if c.name == 'vcloud_session_id': sessions.append({'host': c.domain, 'session_id': c.value}) stdout(sessions, ctx) return client = Client(host, api_version=api_version, verify_ssl_certs=verify_ssl_certs, log_file='vcd.log', log_requests=True, log_headers=True, log_bodies=True ) try: if api_version is None: api_version = client.set_highest_supported_version() if session_id is not None or use_browser_session: if use_browser_session: browser_session_id = None cookies = browsercookie.chrome() for c in cookies: if c.name == 'vcloud_session_id' and \ c.domain == host: browser_session_id = c.value break if browser_session_id is None: raise Exception('Session not found in browser.') session_id = browser_session_id client.rehydrate_from_token(session_id) else: if password is None: password = click.prompt('Password', hide_input=True, type=str) client.set_credentials(BasicLoginCredentials(user, org, password)) wkep = {} for endpoint in _WellKnownEndpoint: if endpoint in client._session_endpoints: wkep[endpoint.name] = client._session_endpoints[endpoint] profiles = Profiles.load() logged_in_org = client.get_org() org_href = logged_in_org.get('href') vdc_href = '' in_use_vdc = '' if vdc is None: for v in get_links(logged_in_org, media_type=EntityType.VDC.value): in_use_vdc = v.name vdc_href = v.href break else: for v in get_links(logged_in_org, media_type=EntityType.VDC.value): if vdc == v.name: in_use_vdc = v.name vdc_href = v.href break if len(in_use_vdc) == 0: raise Exception('VDC not found') profiles.update(host, org, user, client._session.headers['x-vcloud-authorization'], api_version, wkep, verify_ssl_certs, disable_warnings, vdc=in_use_vdc, org_href=org_href, vdc_href=vdc_href, log_request=profiles.get('log_request', default=False), log_header=profiles.get('log_header', default=False), log_body=profiles.get('log_body', default=False), vapp='', vapp_href='') alt_text = '%s logged in, org: \'%s\', vdc: \'%s\'' % \ (user, org, in_use_vdc) stdout({'user': user, 'org': org, 'vdc': in_use_vdc, 'logged_in': True}, ctx, alt_text) except Exception as e: try: profiles = Profiles.load() profiles.set('token', '') except Exception: pass stderr(e, ctx)
def login(ctx, user, host, password, api_version, org, verify_ssl_certs, disable_warnings, vdc, session_id, use_browser_session): """Login to vCloud Director \b Login to a vCloud Director service. \b Examples vcd login mysp.com org1 usr1 Login to host 'mysp.com'. \b vcd login test.mysp.com org1 usr1 -i -w Login to a host with self-signed SSL certificate. \b vcd login mysp.com org1 usr1 --use-browser-session Login using active session from browser. \b vcd login session list chrome List active session ids from browser. \b vcd login mysp.com org1 usr1 \\ --session-id ee968665bf3412d581bbc6192508eec4 Login using active session id. \b Environment Variables VCD_PASSWORD If this environment variable is set, the command will use its value as the password to login and will not ask for one. The --password option has precedence over the environment variable. """ if not verify_ssl_certs: if disable_warnings: pass else: click.secho( 'InsecureRequestWarning: ' 'Unverified HTTPS request is being made. ' 'Adding certificate verification is strongly ' 'advised.', fg='yellow', err=True) requests.packages.urllib3.disable_warnings() if host == 'session' and org == 'list': sessions = [] if user == 'chrome': cookies = browsercookie.chrome() for c in cookies: if c.name == 'vcloud_session_id': sessions.append({'host': c.domain, 'session_id': c.value}) stdout(sessions, ctx) return client = Client( host, api_version=api_version, verify_ssl_certs=verify_ssl_certs, log_file='vcd.log', log_requests=True, log_headers=True, log_bodies=True) try: if api_version is None: api_version = client.set_highest_supported_version() if session_id is not None or use_browser_session: if use_browser_session: browser_session_id = None cookies = browsercookie.chrome() for c in cookies: if c.name == 'vcloud_session_id' and \ c.domain == host: browser_session_id = c.value break if browser_session_id is None: raise Exception('Session not found in browser.') session_id = browser_session_id client.rehydrate_from_token(session_id) else: if password is None: password = click.prompt('Password', hide_input=True, type=str) client.set_credentials(BasicLoginCredentials(user, org, password)) wkep = {} for endpoint in _WellKnownEndpoint: if endpoint in client._session_endpoints: wkep[endpoint.name] = client._session_endpoints[endpoint] profiles = Profiles.load() logged_in_org = client.get_org() org_href = logged_in_org.get('href') vdc_href = '' in_use_vdc = '' if vdc is None: for v in get_links(logged_in_org, media_type=EntityType.VDC.value): in_use_vdc = v.name vdc_href = v.href break else: for v in get_links(logged_in_org, media_type=EntityType.VDC.value): if vdc == v.name: in_use_vdc = v.name vdc_href = v.href break if len(in_use_vdc) == 0: raise Exception('VDC not found') profiles.update( host, org, user, client._session.headers['x-vcloud-authorization'], api_version, wkep, verify_ssl_certs, disable_warnings, vdc=in_use_vdc, org_href=org_href, vdc_href=vdc_href, log_request=True, log_header=True, log_body=True, vapp='', vapp_href='') alt_text = '%s logged in, org: \'%s\', vdc: \'%s\'' % \ (user, org, in_use_vdc) stdout({ 'user': user, 'org': org, 'vdc': in_use_vdc, 'logged_in': True }, ctx, alt_text) except Exception as e: try: profiles = Profiles.load() profiles.set('token', '') except Exception: pass stderr(e, ctx)
def _override_client(ctx) -> None: """Replace the vcd client in ctx.obj with new one. New vcd client takes the CSE server_api_version as api_version param. Save profile also with 'cse_server_api_version' for subsequent commands. :param <click.core.Context> ctx: click context """ profiles = Profiles.load() # if the key CSE_SERVER_RUNNING is not present in the profiles.yaml, # we make an assumption that CSE server is running is_cse_server_running = profiles.get(CSE_SERVER_RUNNING, default=True) cse_server_api_version = profiles.get(CSE_SERVER_API_VERSION) if not is_cse_server_running: CLIENT_LOGGER.debug( "CSE server not running as per profile, restricting CLI to only TKG operations." ) # noqa: E501 restrict_cli_to_tkg_s_operations() ctx.obj['profiles'] = profiles return # Get server_api_version; save it in profiles if doesn't exist if not cse_server_api_version: try: system = syst.System(ctx.obj['client']) sys_info = system.get_info() is_pre_cse_3_1_server = False if CSE_SERVER_LEGACY_MODE not in sys_info or \ CSE_SERVER_SUPPORTED_API_VERSIONS not in sys_info: is_pre_cse_3_1_server = True if not is_pre_cse_3_1_server: is_cse_server_running_in_legacy_mode = \ sys_info.get(CSE_SERVER_LEGACY_MODE) cse_server_supported_api_versions = \ set(sys_info.get(CSE_SERVER_SUPPORTED_API_VERSIONS)) cse_client_supported_api_versions = \ set(shared_constants.SUPPORTED_VCD_API_VERSIONS) common_supported_api_versions = \ list(cse_server_supported_api_versions.intersection( cse_client_supported_api_versions)) # ToDo: Instead of float use proper version comparison if is_cse_server_running_in_legacy_mode: common_supported_api_versions = \ [float(x) for x in common_supported_api_versions if float(x) < 35.0] else: common_supported_api_versions = \ [float(x) for x in common_supported_api_versions if float(x) >= 35.0] cse_server_api_version = \ str(max(common_supported_api_versions)) CLIENT_LOGGER.debug( f"Server api versions : {cse_server_supported_api_versions}, " # noqa: E501 f"Client api versions : {cse_client_supported_api_versions}, " # noqa: E501 f"Server in Legacy mode : {is_cse_server_running_in_legacy_mode}, " # noqa: E501 f"Selected api version : {cse_server_api_version}.") else: cse_server_api_version = \ sys_info.get(CSE_SERVER_API_VERSION) CLIENT_LOGGER.debug( "Pre CSE 3.1 server detected. Selected api version : " f"{cse_server_api_version}.") profiles.set(CSE_SERVER_API_VERSION, cse_server_api_version) profiles.set(CSE_SERVER_RUNNING, True) profiles.save() except (requests.exceptions.Timeout, CseResponseError) as err: CLIENT_LOGGER.error(err, exc_info=True) CLIENT_LOGGER.debug( "Request to CSE server timed out. Restricting CLI to only TKG operations." ) # noqa: E501 profiles.set(CSE_SERVER_RUNNING, False) restrict_cli_to_tkg_s_operations() ctx.obj['profiles'] = profiles profiles.save() return client = Client(profiles.get('host'), api_version=cse_server_api_version, verify_ssl_certs=profiles.get('verify'), log_file='vcd.log', log_requests=profiles.get('log_request'), log_headers=profiles.get('log_header'), log_bodies=profiles.get('log_body')) client.rehydrate_from_token(profiles.get('token'), profiles.get('is_jwt_token')) # noqa: E501 ctx.obj['client'] = client ctx.obj['profiles'] = profiles
class DefaultBroker(threading.Thread): def __init__(self, config): threading.Thread.__init__(self) self.config = config self.host = config['vcd']['host'] self.username = config['vcd']['username'] self.password = config['vcd']['password'] self.version = config['vcd']['api_version'] self.verify = config['vcd']['verify'] self.log = config['vcd']['log'] def _connect_sysadmin(self): if not self.verify: LOGGER.warning('InsecureRequestWarning: ' 'Unverified HTTPS request is being made. ' 'Adding certificate verification is strongly ' 'advised.') requests.packages.urllib3.disable_warnings() self.client_sysadmin = Client(uri=self.host, api_version=self.version, verify_ssl_certs=self.verify, log_file='sysadmin.log', log_headers=True, log_bodies=True) self.client_sysadmin.set_credentials( BasicLoginCredentials(self.username, 'System', self.password)) def _connect_tenant(self, headers): token = headers.get('x-vcloud-authorization') accept_header = headers.get('Accept') version = accept_header.split('version=')[1] self.client_tenant = Client(uri=self.host, api_version=version, verify_ssl_certs=self.verify, log_file='tenant.log', log_headers=True, log_bodies=True) session = self.client_tenant.rehydrate_from_token(token) return { 'user_name': session.get('user'), 'user_id': session.get('userId'), 'org_name': session.get('org'), 'org_href': self.client_tenant._get_wk_endpoint( _WellKnownEndpoint.LOGGED_IN_ORG) } def update_task(self, status, operation, message=None, error_message=None): if not hasattr(self, 'task'): self.task = Task(self.client_sysadmin) if message is None: message = OP_MESSAGE[operation] if hasattr(self, 't'): task_href = self.t.get('href') else: task_href = None self.t = self.task.update(status.value, 'vcloud.cse', message, operation, '', None, 'urn:cse:cluster:%s' % self.cluster_id, self.cluster_name, 'application/vcloud.cse.cluster+xml', self.tenant_info['user_id'], self.tenant_info['user_name'], org_href=self.tenant_info['org_href'], task_href=task_href, error_message=error_message) def is_valid_name(self, name): """Validates that the cluster name against the pattern. """ if len(name) > MAX_HOST_NAME_LENGTH: return False if name[-1] == '.': name = name[:-1] allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE) return all(allowed.match(x) for x in name.split(".")) def get_template(self, name=None): if name is None: if 'template' in self.body and self.body['template'] is not None: name = self.body['template'] else: name = self.config['broker']['default_template'] for template in self.config['broker']['templates']: if template['name'] == name: return template raise Exception('Template %s not found' % name) def run(self): LOGGER.debug('thread started op=%s' % self.op) if self.op == OP_CREATE_CLUSTER: self.create_cluster_thread() elif self.op == OP_DELETE_CLUSTER: self.delete_cluster_thread() def list_clusters(self, headers, body): result = {} try: result['body'] = [] result['status_code'] = OK self._connect_tenant(headers) clusters = load_from_metadata(self.client_tenant, get_leader_ip=True) result['body'] = clusters except Exception: LOGGER.error(traceback.format_exc()) result['body'] = [] result['status_code'] = INTERNAL_SERVER_ERROR result['message'] = traceback.format_exc() return result def create_cluster(self, headers, body): result = {} result['body'] = {} cluster_name = body['name'] vdc_name = body['vdc'] node_count = body['node_count'] LOGGER.debug('about to create cluster %s on %s with %s nodes, sp=%s', cluster_name, vdc_name, node_count, body['storage_profile']) result['body'] = {'message': 'can\'t create cluster %s' % cluster_name} result['status_code'] = INTERNAL_SERVER_ERROR try: if not self.is_valid_name(cluster_name): raise Exception('Invalid cluster name') self.tenant_info = self._connect_tenant(headers) self.headers = headers self.body = body self.cluster_name = cluster_name self.cluster_id = str(uuid.uuid4()) self.op = OP_CREATE_CLUSTER self._connect_sysadmin() self.update_task(TaskStatus.RUNNING, self.op, message='Creating cluster %s(%s)' % (cluster_name, self.cluster_id)) self.daemon = True self.start() response_body = {} response_body['name'] = self.cluster_name response_body['cluster_id'] = self.cluster_id response_body['task_href'] = self.t.get('href') result['body'] = response_body result['status_code'] = ACCEPTED except Exception as e: result['body'] = {'message': e.message} LOGGER.error(traceback.format_exc()) return result def create_cluster_thread(self): network_name = self.body['network'] try: clusters = load_from_metadata(self.client_tenant, name=self.cluster_name) if len(clusters) != 0: raise Exception('Cluster already exists.') org_resource = self.client_tenant.get_org() org = Org(self.client_tenant, resource=org_resource) vdc_resource = org.get_vdc(self.body['vdc']) vdc = VDC(self.client_tenant, resource=vdc_resource) template = self.get_template() self.update_task(TaskStatus.RUNNING, self.op, message='Creating cluster vApp %s(%s)' % (self.cluster_name, self.cluster_id)) vapp_resource = vdc.create_vapp(self.cluster_name, description='cluster %s' % self.cluster_name, network=network_name, fence_mode='bridged') t = self.client_tenant.get_task_monitor().wait_for_status( task=vapp_resource.Tasks.Task[0], timeout=60, poll_frequency=2, fail_on_status=None, expected_target_statuses=[ TaskStatus.SUCCESS, TaskStatus.ABORTED, TaskStatus.ERROR, TaskStatus.CANCELED ], callback=None) assert t.get('status').lower() == TaskStatus.SUCCESS.value tags = {} tags['cse.cluster.id'] = self.cluster_id tags['cse.version'] = pkg_resources.require( 'container-service-extension')[0].version tags['cse.template'] = template['name'] vapp = VApp(self.client_tenant, href=vapp_resource.get('href')) for k, v in tags.items(): t = vapp.set_metadata('GENERAL', 'READWRITE', k, v) self.client_tenant.get_task_monitor().\ wait_for_status( task=t, timeout=600, poll_frequency=5, fail_on_status=None, expected_target_statuses=[TaskStatus.SUCCESS], callback=None) self.update_task(TaskStatus.RUNNING, self.op, message='Creating master node for %s(%s)' % (self.cluster_name, self.cluster_id)) vapp.reload() add_nodes(1, template, TYPE_MASTER, self.config, self.client_tenant, org, vdc, vapp, self.body, wait=True) self.update_task(TaskStatus.RUNNING, self.op, message='Initializing cluster %s(%s)' % (self.cluster_name, self.cluster_id)) vapp.reload() init_cluster(self.config, vapp, template) master_ip = get_master_ip(self.config, vapp, template) t = vapp.set_metadata('GENERAL', 'READWRITE', 'cse.master.ip', master_ip) self.client_tenant.get_task_monitor().\ wait_for_status( task=t, timeout=600, poll_frequency=5, fail_on_status=None, expected_target_statuses=[TaskStatus.SUCCESS], callback=None) if self.body['node_count'] > 0: self.update_task(TaskStatus.RUNNING, self.op, message='Creating %s node(s) for %s(%s)' % (self.body['node_count'], self.cluster_name, self.cluster_id)) add_nodes(self.body['node_count'], template, TYPE_NODE, self.config, self.client_tenant, org, vdc, vapp, self.body, wait=True) self.update_task(TaskStatus.RUNNING, self.op, message='Adding %s node(s) to %s(%s)' % (self.body['node_count'], self.cluster_name, self.cluster_id)) vapp.reload() join_cluster(self.config, vapp, template) self.update_task(TaskStatus.SUCCESS, self.op, message='Created cluster %s(%s)' % (self.cluster_name, self.cluster_id)) except Exception as e: LOGGER.error(traceback.format_exc()) self.update_task(TaskStatus.ERROR, self.op, error_message=str(e)) def delete_cluster(self, headers, body): result = {} result['body'] = {} LOGGER.debug('about to delete cluster with name: %s' % body['name']) result['status_code'] = INTERNAL_SERVER_ERROR try: self.cluster_name = body['name'] self.tenant_info = self._connect_tenant(headers) self.headers = headers self.body = body self.op = OP_DELETE_CLUSTER self._connect_sysadmin() clusters = load_from_metadata(self.client_tenant, name=self.cluster_name) if len(clusters) != 1: raise Exception('Cluster %s not found.' % self.cluster_name) self.cluster = clusters[0] self.cluster_id = self.cluster['cluster_id'] self.update_task(TaskStatus.RUNNING, self.op, message='Deleting cluster %s(%s)' % (self.cluster_name, self.cluster_id)) self.daemon = True self.start() response_body = {} response_body['cluster_name'] = self.cluster_name response_body['task_href'] = self.t.get('href') result['body'] = response_body result['status_code'] = ACCEPTED except Exception as e: if hasattr(e, 'message'): result['body'] = {'message': e.message} else: result['body'] = {'message': str(e)} LOGGER.error(traceback.format_exc()) return result def delete_cluster_thread(self): LOGGER.debug('about to delete cluster with name: %s', self.cluster_name) try: vdc = VDC(self.client_tenant, href=self.cluster['vdc_href']) delete_task = vdc.delete_vapp(self.cluster['name'], force=True) self.client_tenant.get_task_monitor().\ wait_for_status( task=delete_task, timeout=600, poll_frequency=5, fail_on_status=None, expected_target_statuses=[TaskStatus.SUCCESS], callback=None) self.update_task(TaskStatus.SUCCESS, self.op, message='Deleted cluster %s(%s)' % (self.cluster_name, self.cluster_id)) except Exception as e: LOGGER.error(traceback.format_exc()) self.update_task(self.cluster_name, self.cluster_id, TaskStatus.ERROR, self.op, error_message=str(e)) def get_cluster_config(self, cluster_name, headers): result = {} try: self._connect_tenant(headers) clusters = load_from_metadata(self.client_tenant, name=cluster_name) if len(clusters) != 1: raise Exception('Cluster \'%s\' not found' % cluster_name) vapp = VApp(self.client_tenant, href=clusters[0]['vapp_href']) template = self.get_template(name=clusters[0]['template']) result['body'] = get_cluster_config(self.config, vapp, template['admin_password']) result['status_code'] = OK except Exception as e: result['body'] = str(e) result['status_code'] = INTERNAL_SERVER_ERROR return result
class DefaultBroker(threading.Thread): def __init__(self, config): threading.Thread.__init__(self) self.config = config self.host = config['vcd']['host'] self.username = config['vcd']['username'] self.password = config['vcd']['password'] self.version = config['vcd']['api_version'] self.verify = config['vcd']['verify'] self.log = config['vcd']['log'] def _connect_sysadmin(self): if not self.verify: LOGGER.warning('InsecureRequestWarning: ' 'Unverified HTTPS request is being made. ' 'Adding certificate verification is strongly ' 'advised.') requests.packages.urllib3.disable_warnings() self.client_sysadmin = Client(uri=self.host, api_version=self.version, verify_ssl_certs=self.verify, log_headers=True, log_bodies=True) credentials = BasicLoginCredentials(self.username, SYSTEM_ORG_NAME, self.password) self.client_sysadmin.set_credentials(credentials) def _connect_tenant(self, headers): token = headers.get('x-vcloud-authorization') accept_header = headers.get('Accept') version = accept_header.split('version=')[1] self.client_tenant = Client(uri=self.host, api_version=version, verify_ssl_certs=self.verify, log_headers=True, log_bodies=True) session = self.client_tenant.rehydrate_from_token(token) return { 'user_name': session.get('user'), 'user_id': session.get('userId'), 'org_name': session.get('org'), 'org_href': self.client_tenant._get_wk_endpoint( _WellKnownEndpoint.LOGGED_IN_ORG) } def _to_message(self, e): if hasattr(e, 'message'): return {'message': e.message} else: return {'message': str(e)} def update_task(self, status, message=None, error_message=None): if not hasattr(self, 'task'): self.task = Task(self.client_sysadmin) if message is None: message = OP_MESSAGE[self.op] if hasattr(self, 'task_resource'): task_href = self.task_resource.get('href') else: task_href = None self.task_resource = self.task.update( status.value, 'vcloud.cse', message, self.op, '', None, 'urn:cse:cluster:%s' % self.cluster_id, self.cluster_name, 'application/vcloud.cse.cluster+xml', self.tenant_info['user_id'], self.tenant_info['user_name'], org_href=self.tenant_info['org_href'], task_href=task_href, error_message=error_message) def is_valid_name(self, name): """Validate that the cluster name against the pattern.""" if len(name) > MAX_HOST_NAME_LENGTH: return False if name[-1] == '.': name = name[:-1] allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE) return all(allowed.match(x) for x in name.split(".")) def get_template(self, name=None): if name is None: if 'template' in self.body and self.body['template'] is not None: name = self.body['template'] else: name = self.config['broker']['default_template'] for template in self.config['broker']['templates']: if template['name'] == name: return template raise Exception('Template %s not found' % name) def run(self): LOGGER.debug('thread started op=%s' % self.op) if self.op == OP_CREATE_CLUSTER: self.create_cluster_thread() elif self.op == OP_DELETE_CLUSTER: self.delete_cluster_thread() elif self.op == OP_CREATE_NODES: self.create_nodes_thread() elif self.op == OP_DELETE_NODES: self.delete_nodes_thread() @exception_handler def list_clusters(self, headers, body): result = {} result['body'] = [] result['status_code'] = OK self._connect_tenant(headers) clusters = load_from_metadata(self.client_tenant) result['body'] = clusters return result @exception_handler def get_cluster_info(self, name, headers, body): """Get the info of the cluster. :param cluster_name: (str): Name of the cluster :param headers: (str): Request headers :return: (dict): Info of the cluster. """ result = {} result['body'] = [] result['status_code'] = OK self._connect_tenant(headers) clusters = load_from_metadata(self.client_tenant, name=name) if len(clusters) == 0: raise CseServerError('Cluster \'%s\' not found.' % name) vapp = VApp(self.client_tenant, href=clusters[0]['vapp_href']) vms = vapp.get_all_vms() for vm in vms: node_info = {'name': vm.get('name'), 'ipAddress': ''} try: node_info['ipAddress'] = vapp.get_primary_ip(vm.get('name')) except Exception: LOGGER.debug('cannot get ip address for node %s' % vm.get('name')) if vm.get('name').startswith(TYPE_MASTER): clusters[0].get('master_nodes').append(node_info) elif vm.get('name').startswith(TYPE_NODE): clusters[0].get('nodes').append(node_info) elif vm.get('name').startswith(TYPE_NFS): clusters[0].get('nfs_nodes').append(node_info) result['body'] = clusters[0] return result @exception_handler def get_node_info(self, cluster_name, node_name, headers): """Get the info of a given node in the cluster. :param cluster_name: (str): Name of the cluster :param node_name: (str): Name of the node :param headers: (str): Request headers :return: (dict): Info of the node. """ result = {} result['body'] = [] result['status_code'] = OK self._connect_tenant(headers) clusters = load_from_metadata(self.client_tenant, name=cluster_name) if len(clusters) == 0: raise CseServerError('Cluster \'%s\' not found.' % cluster_name) vapp = VApp(self.client_tenant, href=clusters[0]['vapp_href']) vms = vapp.get_all_vms() node_info = None for vm in vms: if (node_name == vm.get('name')): node_info = { 'name': vm.get('name'), 'numberOfCpus': '', 'memoryMB': '', 'status': VCLOUD_STATUS_MAP.get(int(vm.get('status'))), 'ipAddress': '' } if hasattr(vm, 'VmSpecSection'): node_info['numberOfCpus'] = vm.VmSpecSection.NumCpus.text node_info[ 'memoryMB'] = \ vm.VmSpecSection.MemoryResourceMb.Configured.text try: node_info['ipAddress'] = vapp.get_primary_ip( vm.get('name')) except Exception: LOGGER.debug('cannot get ip address ' 'for node %s' % vm.get('name')) if vm.get('name').startswith(TYPE_MASTER): node_info['node_type'] = 'master' elif vm.get('name').startswith(TYPE_NODE): node_info['node_type'] = 'node' elif vm.get('name').startswith(TYPE_NFS): node_info['node_type'] = 'nfsd' exports = self._get_nfs_exports(node_info['ipAddress'], vapp, vm) node_info['exports'] = exports if node_info is None: raise CseServerError('Node \'%s\' not found in cluster \'%s\'' % (node_name, cluster_name)) result['body'] = node_info return result def _get_nfs_exports(self, ip, vapp, node): """Get the exports from remote NFS server (helper method). :param ip: (str): IP address of the NFS server :param vapp: (pyvcloud.vcd.vapp.VApp): The vApp or cluster to which node belongs :param node: (str): IP address of the NFS server :param node: (`lxml.objectify.StringElement`) object representing the vm resource. :return: (List): List of exports """ # TODO(right template) find a right way to retrieve # the template from which nfs node was created. template = self.config['broker']['templates'][0] script = '#!/usr/bin/env bash\nshowmount -e %s' % ip result = execute_script_in_nodes(self.config, vapp, template['admin_password'], script, nodes=[node], check_tools=False) lines = result[0][1].content.decode().split('\n') exports = [] for index in range(1, len(lines) - 1): export = lines[index].strip().split()[0] exports.append(export) return exports @exception_handler def create_cluster(self, headers, body): result = {} result['body'] = {} cluster_name = body['name'] vdc_name = body['vdc'] node_count = body['node_count'] LOGGER.debug('about to create cluster %s on %s with %s nodes, sp=%s', cluster_name, vdc_name, node_count, body['storage_profile']) result['body'] = { 'message': 'can\'t create cluster \'%s\'' % cluster_name } if not self.is_valid_name(cluster_name): raise CseServerError(f"Invalid cluster name \'{cluster_name}\'") self.tenant_info = self._connect_tenant(headers) self.headers = headers self.body = body self.cluster_name = cluster_name self.cluster_id = str(uuid.uuid4()) self.op = OP_CREATE_CLUSTER self._connect_sysadmin() self.update_task(TaskStatus.RUNNING, message='Creating cluster %s(%s)' % (cluster_name, self.cluster_id)) self.daemon = True self.start() response_body = {} response_body['name'] = self.cluster_name response_body['cluster_id'] = self.cluster_id response_body['task_href'] = self.task_resource.get('href') result['body'] = response_body result['status_code'] = ACCEPTED return result @rollback def create_cluster_thread(self): network_name = self.body['network'] try: clusters = load_from_metadata(self.client_tenant, name=self.cluster_name) if len(clusters) != 0: raise ClusterAlreadyExistsError( f'Cluster {self.cluster_name} already exists.') org_resource = self.client_tenant.get_org() org = Org(self.client_tenant, resource=org_resource) vdc_resource = org.get_vdc(self.body['vdc']) vdc = VDC(self.client_tenant, resource=vdc_resource) template = self.get_template() self.update_task(TaskStatus.RUNNING, message='Creating cluster vApp %s(%s)' % (self.cluster_name, self.cluster_id)) try: vapp_resource = vdc.create_vapp(self.cluster_name, description='cluster %s' % self.cluster_name, network=network_name, fence_mode='bridged') except Exception as e: raise ClusterOperationError('Error while creating vApp:', str(e)) self.client_tenant.get_task_monitor().wait_for_status( vapp_resource.Tasks.Task[0]) tags = {} tags['cse.cluster.id'] = self.cluster_id tags['cse.version'] = pkg_resources.require( 'container-service-extension')[0].version tags['cse.template'] = template['name'] vapp = VApp(self.client_tenant, href=vapp_resource.get('href')) for k, v in tags.items(): task = vapp.set_metadata('GENERAL', 'READWRITE', k, v) self.client_tenant.get_task_monitor().wait_for_status(task) self.update_task(TaskStatus.RUNNING, message='Creating master node for %s(%s)' % (self.cluster_name, self.cluster_id)) vapp.reload() try: add_nodes(1, template, TYPE_MASTER, self.config, self.client_tenant, org, vdc, vapp, self.body) except Exception as e: raise MasterNodeCreationError( "Error while adding master node:", str(e)) self.update_task(TaskStatus.RUNNING, message='Initializing cluster %s(%s)' % (self.cluster_name, self.cluster_id)) vapp.reload() init_cluster(self.config, vapp, template) master_ip = get_master_ip(self.config, vapp, template) task = vapp.set_metadata('GENERAL', 'READWRITE', 'cse.master.ip', master_ip) self.client_tenant.get_task_monitor().wait_for_status(task) if self.body['node_count'] > 0: self.update_task(TaskStatus.RUNNING, message='Creating %s node(s) for %s(%s)' % (self.body['node_count'], self.cluster_name, self.cluster_id)) try: add_nodes(self.body['node_count'], template, TYPE_NODE, self.config, self.client_tenant, org, vdc, vapp, self.body) except Exception as e: raise WorkerNodeCreationError( "Error while creating worker node:", str(e)) self.update_task(TaskStatus.RUNNING, message='Adding %s node(s) to %s(%s)' % (self.body['node_count'], self.cluster_name, self.cluster_id)) vapp.reload() join_cluster(self.config, vapp, template) if self.body['enable_nfs']: self.update_task(TaskStatus.RUNNING, message='Creating NFS node for %s(%s)' % (self.cluster_name, self.cluster_id)) try: add_nodes(1, template, TYPE_NFS, self.config, self.client_tenant, org, vdc, vapp, self.body) except Exception as e: raise NFSNodeCreationError( "Error while creating NFS node:", str(e)) self.update_task(TaskStatus.SUCCESS, message='Created cluster %s(%s)' % (self.cluster_name, self.cluster_id)) except (MasterNodeCreationError, WorkerNodeCreationError, NFSNodeCreationError, ClusterJoiningError, ClusterInitializationError, ClusterOperationError) as e: LOGGER.error(traceback.format_exc()) error_obj = error_to_json(e) self.update_task( TaskStatus.ERROR, error_message=error_obj[ERROR_MESSAGE][ERROR_DESCRIPTION]) raise e except Exception as e: LOGGER.error(traceback.format_exc()) error_obj = error_to_json(e) self.update_task( TaskStatus.ERROR, error_message=error_obj[ERROR_MESSAGE][ERROR_DESCRIPTION]) @exception_handler def delete_cluster(self, headers, body): result = {} result['body'] = {} LOGGER.debug('about to delete cluster with name: %s' % body['name']) result['status_code'] = INTERNAL_SERVER_ERROR self.cluster_name = body['name'] self.tenant_info = self._connect_tenant(headers) self.headers = headers self.body = body self.op = OP_DELETE_CLUSTER self._connect_sysadmin() clusters = load_from_metadata(self.client_tenant, name=self.cluster_name) if len(clusters) != 1: raise CseServerError('Cluster %s not found.' % self.cluster_name) self.cluster = clusters[0] self.cluster_id = self.cluster['cluster_id'] self.update_task(TaskStatus.RUNNING, message='Deleting cluster %s(%s)' % (self.cluster_name, self.cluster_id)) self.daemon = True self.start() response_body = {} response_body['cluster_name'] = self.cluster_name response_body['task_href'] = self.task_resource.get('href') result['body'] = response_body result['status_code'] = ACCEPTED return result def delete_cluster_thread(self): LOGGER.debug('about to delete cluster with name: %s', self.cluster_name) try: vdc = VDC(self.client_tenant, href=self.cluster['vdc_href']) task = vdc.delete_vapp(self.cluster['name'], force=True) self.client_tenant.get_task_monitor().wait_for_status(task) self.update_task(TaskStatus.SUCCESS, message='Deleted cluster %s(%s)' % (self.cluster_name, self.cluster_id)) except Exception as e: LOGGER.error(traceback.format_exc()) self.update_task(TaskStatus.ERROR, error_message=str(e)) @exception_handler def get_cluster_config(self, cluster_name, headers): result = {} self._connect_tenant(headers) clusters = load_from_metadata(self.client_tenant, name=cluster_name) if len(clusters) != 1: raise CseServerError('Cluster \'%s\' not found' % cluster_name) vapp = VApp(self.client_tenant, href=clusters[0]['vapp_href']) template = self.get_template(name=clusters[0]['template']) result['body'] = get_cluster_config(self.config, vapp, template['admin_password']) result['status_code'] = OK return result @exception_handler def create_nodes(self, headers, body): result = {'body': {}} self.cluster_name = body['name'] LOGGER.debug('about to add %s nodes to cluster %s on VDC %s, sp=%s', body['node_count'], self.cluster_name, body['vdc'], body['storage_profile']) if body['node_count'] < 1: raise CseServerError('Invalid node count: %s.' % body['node_count']) self.tenant_info = self._connect_tenant(headers) clusters = load_from_metadata(self.client_tenant, name=self.cluster_name) if len(clusters) != 1: raise CseServerError('Cluster \'%s\' not found.' % self.cluster_name) self.cluster = clusters[0] self.headers = headers self.body = body self.op = OP_CREATE_NODES self._connect_sysadmin() self.cluster_id = self.cluster['cluster_id'] self.update_task( TaskStatus.RUNNING, message='Adding %s node(s) to cluster %s(%s)' % (body['node_count'], self.cluster_name, self.cluster_id)) self.daemon = True self.start() response_body = {} response_body['cluster_name'] = self.cluster_name response_body['task_href'] = self.task_resource.get('href') result['body'] = response_body result['status_code'] = ACCEPTED return result @rollback def create_nodes_thread(self): LOGGER.debug('about to add nodes to cluster with name: %s', self.cluster_name) try: org_resource = self.client_tenant.get_org() org = Org(self.client_tenant, resource=org_resource) vdc = VDC(self.client_tenant, href=self.cluster['vdc_href']) vapp = VApp(self.client_tenant, href=self.cluster['vapp_href']) template = self.get_template() self.update_task( TaskStatus.RUNNING, message='Creating %s node(s) for %s(%s)' % (self.body['node_count'], self.cluster_name, self.cluster_id)) new_nodes = add_nodes(self.body['node_count'], template, self.body['node_type'], self.config, self.client_tenant, org, vdc, vapp, self.body) if self.body['node_type'] == TYPE_NFS: self.update_task(TaskStatus.SUCCESS, message='Created %s node(s) for %s(%s)' % (self.body['node_count'], self.cluster_name, self.cluster_id)) elif self.body['node_type'] == TYPE_NODE: self.update_task(TaskStatus.RUNNING, message='Adding %s node(s) to %s(%s)' % (self.body['node_count'], self.cluster_name, self.cluster_id)) target_nodes = [] for spec in new_nodes['specs']: target_nodes.append(spec['target_vm_name']) vapp.reload() join_cluster(self.config, vapp, template, target_nodes) self.update_task(TaskStatus.SUCCESS, message='Added %s node(s) to cluster %s(%s)' % (self.body['node_count'], self.cluster_name, self.cluster_id)) except NodeCreationError as e: error_obj = error_to_json(e) LOGGER.error(traceback.format_exc()) self.update_task( TaskStatus.ERROR, error_message=error_obj[ERROR_MESSAGE][ERROR_DESCRIPTION]) raise except Exception as e: error_obj = error_to_json(e) LOGGER.error(traceback.format_exc()) self.update_task( TaskStatus.ERROR, error_message=error_obj[ERROR_MESSAGE][ERROR_DESCRIPTION]) @exception_handler def delete_nodes(self, headers, body): result = {'body': {}} self.cluster_name = body['name'] LOGGER.debug('about to delete nodes from cluster with name: %s' % body['name']) if len(body['nodes']) < 1: raise CseServerError('Invalid list of nodes: %s.' % body['nodes']) for node in body['nodes']: if node.startswith(TYPE_MASTER): raise CseServerError('Can\'t delete a master node: \'%s\'.' % node) self.tenant_info = self._connect_tenant(headers) clusters = load_from_metadata(self.client_tenant, name=self.cluster_name) if len(clusters) != 1: raise CseServerError('Cluster \'%s\' not found.' % self.cluster_name) self.cluster = clusters[0] self.headers = headers self.body = body self.op = OP_DELETE_NODES self._connect_sysadmin() self.cluster_id = self.cluster['cluster_id'] self.update_task( TaskStatus.RUNNING, message='Deleting %s node(s) from cluster %s(%s)' % (len(body['nodes']), self.cluster_name, self.cluster_id)) self.daemon = True self.start() response_body = {} response_body['cluster_name'] = self.cluster_name response_body['task_href'] = self.task_resource.get('href') result['body'] = response_body result['status_code'] = ACCEPTED return result def delete_nodes_thread(self): LOGGER.debug('about to delete nodes from cluster with name: %s', self.cluster_name) try: vapp = VApp(self.client_tenant, href=self.cluster['vapp_href']) template = self.get_template() self.update_task( TaskStatus.RUNNING, message='Deleting %s node(s) from %s(%s)' % (len(self.body['nodes']), self.cluster_name, self.cluster_id)) try: delete_nodes_from_cluster(self.config, vapp, template, self.body['nodes'], self.body['force']) except Exception: LOGGER.error("Couldn't delete node %s from cluster:%s" % (self.body['nodes'], self.cluster_name)) self.update_task( TaskStatus.RUNNING, message='Undeploying %s node(s) for %s(%s)' % (len(self.body['nodes']), self.cluster_name, self.cluster_id)) for vm_name in self.body['nodes']: vm = VM(self.client_tenant, resource=vapp.get_vm(vm_name)) try: task = vm.undeploy() self.client_tenant.get_task_monitor().wait_for_status(task) except Exception as e: LOGGER.warning('couldn\'t undeploy VM %s' % vm_name) self.update_task( TaskStatus.RUNNING, message='Deleting %s VM(s) for %s(%s)' % (len(self.body['nodes']), self.cluster_name, self.cluster_id)) task = vapp.delete_vms(self.body['nodes']) self.client_tenant.get_task_monitor().wait_for_status(task) self.update_task( TaskStatus.SUCCESS, message='Deleted %s node(s) to cluster %s(%s)' % (len(self.body['nodes']), self.cluster_name, self.cluster_id)) except Exception as e: LOGGER.error(traceback.format_exc()) self.update_task(TaskStatus.ERROR, error_message=str(e)) def node_rollback(self, node_list): """Implements rollback for node creation failure :param list node_list: faulty nodes to be deleted """ LOGGER.info('About to rollback nodes from cluster with name: %s' % self.cluster_name) LOGGER.info('Node list to be deleted:%s' % node_list) vapp = VApp(self.client_tenant, href=self.cluster['vapp_href']) template = self.get_template() try: delete_nodes_from_cluster(self.config, vapp, template, node_list, force=True) except Exception: LOGGER.warning("Couldn't delete node %s from cluster:%s" % (node_list, self.cluster_name)) for vm_name in node_list: vm = VM(self.client_tenant, resource=vapp.get_vm(vm_name)) try: vm.undeploy() except Exception: LOGGER.warning("Couldn't undeploy VM %s" % vm_name) vapp.delete_vms(node_list) LOGGER.info('Successfully deleted nodes: %s' % node_list) def cluster_rollback(self): """Implements rollback for cluster creation failure""" LOGGER.info('About to rollback cluster with name: %s' % self.cluster_name) clusters = load_from_metadata(self.client_tenant, name=self.cluster_name) if len(clusters) != 1: LOGGER.debug('Cluster %s not found.' % self.cluster_name) return self.cluster = clusters[0] vdc = VDC(self.client_tenant, href=self.cluster['vdc_href']) vdc.delete_vapp(self.cluster['name'], force=True) LOGGER.info('Successfully deleted cluster: %s' % self.cluster_name)