def _follow_task(op_ctx: ctx.OperationContext, task_href: str, ovdc_id: str): try: task = vcd_task.Task(client=op_ctx.sysadmin_client) session = op_ctx.sysadmin_client.get_vcloud_session() vdc = vcd_utils.get_vdc(op_ctx.sysadmin_client, vdc_id=ovdc_id) org = vcd_utils.get_org(op_ctx.sysadmin_client) user_name = session.get('user') user_href = org.get_user(user_name).get('href') msg = "Remove ovdc compute policy" # TODO(pyvcloud): Add method to retireve task from task href t = task.update(status=vcd_task.TaskStatus.RUNNING.value, namespace='vcloud.cse', operation=msg, operation_name=msg, details='', progress=None, owner_href=vdc.href, owner_name=vdc.name, owner_type=vcd_client.EntityType.VDC.value, user_href=user_href, user_name=user_name, org_href=op_ctx.user.org_href, task_href=task_href) op_ctx.sysadmin_client.get_task_monitor().wait_for_status(t) except Exception as err: logger.SERVER_LOGGER.error(f"{err}") finally: if op_ctx.sysadmin_client: op_ctx.end()
def org_vdc_list(request_data, op_ctx: ctx.OperationContext): """Request handler for orgvdc list operation. This handler returns a paginated response. :return: Dictionary containing list of org VDC K8s provider metadata :rtype: dict """ # NOTE: Response sent out by this function should be paginated data = req_utils.flatten_request_data(request_data, [RequestKey.QUERY_PARAMS]) defaults = { PaginationKey.PAGE_NUMBER: CSE_PAGINATION_FIRST_PAGE_NUMBER, PaginationKey.PAGE_SIZE: CSE_PAGINATION_DEFAULT_PAGE_SIZE } validated_data = {**defaults, **data} page_number = int(validated_data[PaginationKey.PAGE_NUMBER]) page_size = int(validated_data[PaginationKey.PAGE_SIZE]) # Record telemetry data # TODO: enhance telemetry to record the page number and page size data. cse_params = copy.deepcopy(validated_data) cse_params[PayloadKey. SOURCE_DESCRIPTION] = thread_local_data.get_thread_local_data( ThreadLocalData.USER_AGENT) # noqa: E501 record_user_action_details(cse_operation=CseOperation.OVDC_LIST, cse_params=cse_params) client_v33 = op_ctx.get_client(api_version=DEFAULT_API_VERSION) result = vcd_utils.get_ovdcs_by_page(client_v33, page=page_number, page_size=page_size) org_vdcs = result[PaginationKey.VALUES] result_total = result[PaginationKey.RESULT_TOTAL] next_page_uri = result.get(PaginationKey.NEXT_PAGE_URI) prev_page_uri = result.get(PaginationKey.PREV_PAGE_URI) sysadmin_client_v33 = \ op_ctx.get_sysadmin_client(api_version=DEFAULT_API_VERSION) ovdcs = _get_cse_ovdc_list(sysadmin_client_v33, org_vdcs) api_path = CseServerOperationInfo.ORG_VDC_LIST.api_path_format next_page_uri = vcd_utils.create_cse_page_uri(client_v33, api_path, vcd_uri=next_page_uri) prev_page_uri = vcd_utils.create_cse_page_uri(client_v33, api_path, vcd_uri=prev_page_uri) return server_utils.construct_paginated_response( values=ovdcs, result_total=result_total, page_number=page_number, page_size=page_size, next_page_uri=next_page_uri, prev_page_uri=prev_page_uri)
def ovdc_compute_policy_list( request_data, op_ctx: ctx.OperationContext): """Request handler for ovdc compute-policy list operation. Required data: ovdc_id :return: Dictionary with task href. """ required = [ RequestKey.OVDC_ID ] req_utils.validate_payload(request_data, required) config = server_utils.get_server_runtime_config() sysadmin_client_v33 = \ op_ctx.get_sysadmin_client(api_version=DEFAULT_API_VERSION) cpm = compute_policy_manager.ComputePolicyManager( sysadmin_client_v33, log_wire=utils.str_to_bool(config.get_value_at('service.log_wire'))) compute_policies = [] for cp in \ compute_policy_manager.list_cse_sizing_policies_on_vdc( cpm, request_data[RequestKey.OVDC_ID]): policy = { 'name': cp['display_name'], 'id': cp['id'], 'href': cp['href'] } compute_policies.append(policy) return compute_policies
def cluster_config(data: dict, op_ctx: ctx.OperationContext): """Request handler for cluster config operation. Required data: cluster_id :return: Dict """ cluster_id = data[RequestKey.CLUSTER_ID] def_entity_service = entity_service.DefEntityService( op_ctx.cloudapi_client) # noqa: E501 def_entity: common_models.DefEntity = def_entity_service.get_entity( cluster_id) # noqa: E501 telemetry_handler.record_user_action_details( cse_operation=telemetry_constants.CseOperation.V36_CLUSTER_CONFIG, cse_params={ server_constants.CLUSTER_ENTITY: def_entity, telemetry_constants.PayloadKey.SOURCE_DESCRIPTION: thread_local_data.get_thread_local_data( ThreadLocalData.USER_AGENT) # noqa: E501 }) op_ctx.entity_id = cluster_id # hack for passing entity id svc = cluster_service_factory.ClusterServiceFactory( _get_request_context(op_ctx)).get_cluster_service() # noqa: E501 config_dict = svc.get_cluster_config(cluster_id) config: str = config_dict.get( server_constants.BEHAVIOR_TASK_RESPONSE_RESULT_MESSAGE_KEY, {}).get( server_constants.BEHAVIOR_TASK_RESPONSE_RESULT_CONTENT_MESSAGE_KEY ) # noqa: E501 return_dict = {"message": config} return return_dict
def cluster_acl_info(data: dict, op_ctx: ctx.OperationContext): """Request handler for cluster acl list operation.""" cluster_id = data[RequestKey.CLUSTER_ID] def_entity_service = entity_service.DefEntityService( op_ctx.cloudapi_client) # noqa: E501 def_entity = def_entity_service.get_entity(cluster_id) rde_utils.raise_error_if_tkgm_cluster_operation( def_entity.entity.kind) # noqa: E501 query = data.get(RequestKey.QUERY_PARAMS, {}) page = int( query.get(PaginationKey.PAGE_NUMBER, CSE_PAGINATION_FIRST_PAGE_NUMBER)) # noqa: E501 page_size = int( query.get(PaginationKey.PAGE_SIZE, CSE_PAGINATION_DEFAULT_PAGE_SIZE)) # noqa: E501 op_ctx.entity_id = data[ RequestKey.CLUSTER_ID] # hack for passing entity id # noqa: E501 svc = cluster_service_factory.ClusterServiceFactory( _get_request_context(op_ctx)).get_cluster_service() # noqa: E501 result: dict = svc.get_cluster_acl_info(cluster_id, page, page_size) # remove duplicate /api path while forming the endpoint url uri = f"{op_ctx.client.get_api_uri().strip('/api')}{data['url']}" return server_utils.create_links_and_construct_paginated_result( base_uri=uri, values=result.get(PaginationKey.VALUES, []), result_total=result.get(PaginationKey.RESULT_TOTAL, 0), page_number=page, page_size=page_size)
def update_ovdc(operation_context: ctx.OperationContext, ovdc_id: str, ovdc_spec: common_models.Ovdc) -> dict: # noqa: 501 """Update ovdc with the updated k8s runtimes list. :param ctx.OperationContext operation_context: context for the request :param common_models.Ovdc ovdc_spec: Ovdc object having the updated k8s runtime list :return: dictionary containing the task href for the update operation :rtype: dict """ # NOTE: For CSE 3.0, if `enable_tkg_plus` flag in config is set to false, # Prevent enable/disable of OVDC for TKG+ k8s runtime by throwing an # exception msg = "Updating OVDC placement policies" task = vcd_task.Task(operation_context.sysadmin_client) org = vcd_utils.get_org(operation_context.client) user_href = org.get_user(operation_context.user.name).get('href') vdc = vcd_utils.get_vdc(operation_context.sysadmin_client, vdc_id=ovdc_id, # noqa: E501 is_admin_operation=True) logger.SERVER_LOGGER.debug(msg) task_resource = task.update( status=vcd_client.TaskStatus.RUNNING.value, namespace='vcloud.cse', operation=msg, operation_name='OVDC Update', details='', progress=None, owner_href=vdc.href, owner_name=vdc.name, owner_type=vcd_client.EntityType.VDC.value, user_href=user_href, user_name=operation_context.user.name, org_href=operation_context.user.org_href, task_href=None, error_message=None, stack_trace=None) task_href = task_resource.get('href') operation_context.is_async = True # NOTE: Telemetry is currently handled in the async function as it is not # possible to know the operation (enable/disable) without comparing it to # current k8s runtimes. if ClusterEntityKind.TKG_PLUS.value in ovdc_spec.k8s_runtime and \ not server_utils.is_tkg_plus_enabled(): msg = "TKG+ is not enabled on CSE server. Please enable TKG+ in the " \ "server and try again." logger.SERVER_LOGGER.debug(msg) raise Exception(msg) policy_list = [RUNTIME_DISPLAY_NAME_TO_INTERNAL_NAME_MAP[p] for p in ovdc_spec.k8s_runtime] # noqa: E501 _update_ovdc_using_placement_policy_async(operation_context=operation_context, # noqa:E501 task=task, task_href=task_href, user_href=user_href, policy_list=policy_list, # noqa:E501 ovdc_id=ovdc_id, vdc=vdc, org_name=ovdc_spec.org_name, remove_cp_from_vms_on_disable=ovdc_spec.remove_cp_from_vms_on_disable) # noqa:E501 return {'task_href': task_href}
def process_behavior_request(msg_json, mqtt_publisher: MQTTPublisher): # Extracting contents from headers task_id: str = msg_json['headers']['taskId'] entity_id: str = msg_json['headers']['entityId'] behavior_id: str = msg_json['headers']['behaviorId'] usr_ctx: BehaviorUserContext = BehaviorUserContext(**msg_json['headers']['context']) # noqa: E501 # Extracting contents from the input payload payload: dict = json.loads(msg_json['payload']) entity: dict = payload['entity'] entity_type_id: str = payload['typeId'] api_version: str = payload['_metadata']['apiVersion'] # This is needed to enable unified API endpoint workflows. Unified API # endpoint is currently exposed at 37.0.0-alpha (VCD 10.3 Andromeda). if api_version == API_VERSION_37_ALPHA: api_version = ApiVersion.VERSION_36.value auth_token: str = payload['_metadata']['actAsToken'] request_id: str = payload['_metadata']['requestId'] arguments: dict = payload['arguments'] # Initializing Behavior operation context op_ctx = OperationContext(auth_token=auth_token, request_id=request_id) # noqa: E501 behavior_ctx = RequestContext( behavior_id=behavior_id, task_id=task_id, entity_id=entity_id, payload=payload, api_version=float(api_version), entity=entity, user_context=usr_ctx, entity_type_id=entity_type_id, request_id=request_id, op_ctx=op_ctx, mqtt_publisher=mqtt_publisher, arguments=arguments ) # Invoke the handler method and return the response in the string format. try: return MAP_BEHAVIOR_ID_TO_HANDLER_METHOD[behavior_id](behavior_ctx) except CseRequestError as e: error_details = asdict(BehaviorError(majorErrorCode=e.status_code, minorErrorCode=e.minor_error_code, message=e.error_message)) payload = mqtt_publisher. \ construct_behavior_payload(status=BehaviorTaskStatus.ERROR.value, error_details=error_details) LOGGER.error(f"Error while executing handler: {error_details}", exc_info=True) # noqa: E501 return payload except Exception as e: error_details = asdict(BehaviorError(majorErrorCode='500', message=str(e))) payload = mqtt_publisher. \ construct_behavior_payload(status=BehaviorTaskStatus.ERROR.value, error_details=error_details) LOGGER.error(f"Error while executing handler: {error_details}", exc_info=True) # noqa: E501 return payload
def cluster_upgrade_plan(data, op_ctx: ctx.OperationContext): """Request handler for cluster upgrade-plan operation. :return: List[Tuple(str, str)] """ op_ctx.entity_id = data[ RequestKey.CLUSTER_ID] # hack for passing entity id # noqa: E501 svc = cluster_service_factory.ClusterServiceFactory( _get_request_context(op_ctx)).get_cluster_service() # noqa: E501 return svc.get_cluster_upgrade_plan(data[RequestKey.CLUSTER_ID])
def ovdc_list(request_data, op_ctx: ctx.OperationContext): """Request handler for ovdc list operation. :return: List of dictionaries with org VDC k8s provider metadata. """ # NOTE: Response sent out by this function should not be # paginated data = req_utils.flatten_request_data( request_data, [RequestKey.QUERY_PARAMS]) # Record telemetry data cse_params = copy.deepcopy(data) record_user_action_details(cse_operation=CseOperation.OVDC_LIST, cse_params=cse_params) client_v33 = op_ctx.get_client(api_version=DEFAULT_API_VERSION) sysadmin_client_v33 = \ op_ctx.get_sysadmin_client(api_version=DEFAULT_API_VERSION) org_vdcs = vcd_utils.get_all_ovdcs(client_v33) return _get_cse_ovdc_list(sysadmin_client_v33, org_vdcs)
def process_behavior_request(msg_json, mqtt_publisher: MQTTPublisher): # Extracting contents from headers task_id: str = msg_json['headers']['taskId'] entity_id: str = msg_json['headers']['entityId'] behavior_id: str = msg_json['headers']['behaviorId'] usr_ctx: BehaviorUserContext = BehaviorUserContext( **msg_json['headers']['context']) # noqa: E501 # Extracting contents from the input payload payload: dict = json.loads(msg_json['payload']) entity: dict = payload['entity'] entity_type_id: str = payload['typeId'] api_version: str = payload['_metadata']['apiVersion'] auth_token: str = payload['_metadata']['actAsToken'] request_id: str = payload['_metadata']['requestId'] # Initializing Behavior operation context op_ctx = OperationContext(auth_token=auth_token, is_jwt=True, request_id=request_id) # noqa: E501 behavior_ctx = BehaviorRequestContext(behavior_id=behavior_id, task_id=task_id, entity_id=entity_id, payload=payload, api_version=float(api_version), entity=entity, user_context=usr_ctx, entity_type_id=entity_type_id, request_id=request_id, op_ctx=op_ctx, mqtt_publisher=mqtt_publisher) # Invoke the handler method and return the response in the string format. try: return MAP_BEHAVIOR_ID_TO_HANDLER_METHOD[behavior_id]( behavior_ctx) # noqa: E501 except CseRequestError as e: error_details = asdict( BehaviorError(majorErrorCode=e.status_code, minorErrorCode=e.minor_error_code, message=e.error_message)) payload = mqtt_publisher. \ construct_behavior_payload(status=BehaviorTaskStatus.ERROR.value, error_details=error_details) return payload except Exception as e: error_details = asdict( BehaviorError(majorErrorCode='500', message=str(e))) payload = mqtt_publisher. \ construct_behavior_payload(status=BehaviorTaskStatus.ERROR.value, error_details=error_details) return payload
def ovdc_update(request_data, op_ctx: ctx.OperationContext): """Request handler for ovdc enable, disable operations. Required data: org_name, ovdc_name, k8s_provider Conditional data: if k8s_provider is 'ent-pks': pks_plan_name, pks_cluster_domain :return: Dictionary with org VDC update task href. """ # TODO the data flow here should be better understood. # org_name and ovdc_name seem redundant if we already have ovdc_id data = req_utils.flatten_request_data( request_data, [RequestKey.INPUT_SPEC, RequestKey.QUERY_PARAMS]) required = [ RequestKey.ORG_NAME, RequestKey.OVDC_NAME, RequestKey.K8S_PROVIDER, RequestKey.OVDC_ID ] validated_data = data req_utils.validate_payload(validated_data, required) k8s_provider = validated_data[RequestKey.K8S_PROVIDER] k8s_provider_info = {K8S_PROVIDER_KEY: k8s_provider} # Record the telemetry data cse_params = copy.deepcopy(validated_data) cse_params[PayloadKey. SOURCE_DESCRIPTION] = thread_local_data.get_thread_local_data( ThreadLocalData.USER_AGENT) # noqa: E501 cse_operation = CseOperation.OVDC_DISABLE if k8s_provider == K8sProvider.NONE else CseOperation.OVDC_ENABLE # noqa: E501 record_user_action_details(cse_operation=cse_operation, cse_params=cse_params) # noqa: E501 try: sysadmin_client_v33 = \ op_ctx.get_sysadmin_client(api_version=DEFAULT_API_VERSION) task = ovdc_utils.update_ovdc_k8s_provider_metadata( sysadmin_client_v33, validated_data[RequestKey.OVDC_ID], k8s_provider_data=k8s_provider_info, k8s_provider=k8s_provider) # Telemetry - Record successful enabling/disabling of ovdc record_user_action(cse_operation, status=OperationStatus.SUCCESS) return {'task_href': task.get('href')} except Exception as err: # Telemetry - Record failed enabling/disabling of ovdc record_user_action(cse_operation, status=OperationStatus.FAILED) raise err
def cluster_acl_update(data: dict, op_ctx: ctx.OperationContext): """Request handler for cluster acl update operation.""" cluster_id = data[RequestKey.CLUSTER_ID] def_entity_service = entity_service.DefEntityService( op_ctx.cloudapi_client) # noqa: E501 def_entity = def_entity_service.get_entity(cluster_id) rde_utils.raise_error_if_tkgm_cluster_operation( def_entity.entity.kind) # noqa: E501 op_ctx.entity_id = data[ RequestKey.CLUSTER_ID] # hack for passing entity id # noqa: E501 svc = cluster_service_factory.ClusterServiceFactory( _get_request_context(op_ctx)).get_cluster_service() # noqa: E501 update_acl_entries = data.get(RequestKey.INPUT_SPEC, {}).get( ClusterAclKey.ACCESS_SETTING) # noqa: E501 svc.update_cluster_acl(cluster_id, update_acl_entries)
def cluster_info(data: dict, op_ctx: ctx.OperationContext): """Request handler for cluster info operation. Required data: cluster_name Optional data and default values: org_name=None, ovdc_name=None (data validation handled in broker) :return: Dict """ op_ctx.entity_id = data[ RequestKey.CLUSTER_ID] # hack for passing entity id # noqa: E501 svc = cluster_service_factory.ClusterServiceFactory( _get_request_context(op_ctx)).get_cluster_service() # noqa: E501 cluster_id = data[RequestKey.CLUSTER_ID] return svc.get_cluster_info(cluster_id).to_dict()
def cluster_create(request_data, op_ctx: ctx.OperationContext): """Request handler for cluster create operation. Required data: org_name, ovdc_name, cluster_name (data validation handled in broker) :return: Dict """ _raise_error_if_pks_not_enabled() data = req_utils.flatten_request_data(request_data, [RequestKey.INPUT_SPEC]) required = [RequestKey.CLUSTER_NAME] req_utils.validate_payload(data, required) cluster_name = data[RequestKey.CLUSTER_NAME] data['is_org_admin_search'] = True try: _get_cluster_and_broker(data, op_ctx, telemetry=False) raise ClusterAlreadyExistsError(f"Cluster {cluster_name} " f"already exists.") except ClusterNotFoundError: pass sysadmin_client_v33 = op_ctx.get_sysadmin_client( api_version=DEFAULT_API_VERSION) k8s_metadata = \ ovdc_utils.get_ovdc_k8s_provider_metadata( sysadmin_client_v33, org_name=data[RequestKey.ORG_NAME], ovdc_name=data[RequestKey.OVDC_NAME], include_credentials=True, include_nsxt_info=True) broker = _get_broker_from_k8s_metadata(k8s_metadata, op_ctx) data[RequestKey.PKS_PLAN_NAME] = k8s_metadata[PKS_PLANS_KEY][0] data[RequestKey.PKS_EXT_HOST] = \ f"{cluster_name}.{k8s_metadata[PKS_CLUSTER_DOMAIN_KEY]}" cluster = broker.create_cluster(data=data) # Record telemetry data telemetry_handler.record_user_action_details( cse_operation=CseOperation.PKS_CLUSTER_CREATE, cse_params=_get_telemetry_data(data, cluster)) return cluster
def cluster_force_delete(data: dict, op_ctx: ctx.OperationContext): """Request handler for force cluster delete operation. Required data: cluster_name Optional data and default values: org_name=None, ovdc_name=None (data validation handled in broker) :return: Dict """ cluster_id = data[RequestKey.CLUSTER_ID] def_entity_service = entity_service.DefEntityService( op_ctx.cloudapi_client) # noqa: E501 def_entity: common_models.DefEntity = def_entity_service.get_entity( cluster_id) # noqa: E501 op_ctx.entity_id = cluster_id svc = cluster_service_factory.ClusterServiceFactory( _get_request_context(op_ctx)).get_cluster_service() # noqa: E501 task_href = svc.force_delete_cluster(cluster_id) def_entity.entity.status.task_href = task_href return def_entity.to_dict()
def ovdc_info(request_data, op_ctx: ctx.OperationContext): """Request handler for ovdc info operation. Required data: org_name, ovdc_name :return: Dictionary with org VDC k8s provider metadata. """ required = [RequestKey.OVDC_ID] req_utils.validate_payload(request_data, required) # Record telemetry data cse_params = copy.deepcopy(request_data) cse_params[PayloadKey. SOURCE_DESCRIPTION] = thread_local_data.get_thread_local_data( ThreadLocalData.USER_AGENT) # noqa: E501 record_user_action_details(cse_operation=CseOperation.OVDC_INFO, cse_params=cse_params) sysadmin_client_v33 = \ op_ctx.get_sysadmin_client(api_version=DEFAULT_API_VERSION) return ovdc_utils.get_ovdc_k8s_provider_metadata( sysadmin_client_v33, ovdc_id=request_data[RequestKey.OVDC_ID])
def _update_ovdc_using_placement_policy_async( operation_context: ctx.OperationContext, # noqa: E501 task: vcd_task.Task, task_href, user_href, policy_list, ovdc_id, vdc, org_name, remove_cp_from_vms_on_disable=False): # noqa: E501 """Enable ovdc using placement policies. :param ctx.OperationContext operation_context: operation context object :param vcd_task.Task task: Task resource to track progress :param str task_href: href of the task :param str user_href: :param List[str] policy_list: The new list of policies associated with the ovdc :param str ovdc_id: :param pyvcloud.vcd.vdc.VDC vdc: VDC object :param str org_name: name of the organization that vdc provides resource :param bool remove_cp_from_vms_on_disable: Set to true if placement policies need to be removed from the vms before removing from the VDC. """ operation_name = "Update OVDC with placement policies" k8s_runtimes_added = '' k8s_runtimes_deleted = '' try: config = server_utils.get_server_runtime_config() log_wire = utils.str_to_bool(config.get('service', {}).get('log_wire')) cpm = compute_policy_manager.ComputePolicyManager( operation_context.sysadmin_client, log_wire=log_wire) existing_policies = [] for cse_policy in \ compute_policy_manager.list_cse_placement_policies_on_vdc(cpm, ovdc_id): # noqa: E501 existing_policies.append(cse_policy['display_name']) logger.SERVER_LOGGER.debug(policy_list) logger.SERVER_LOGGER.debug(existing_policies) policies_to_add = set(policy_list) - set(existing_policies) policies_to_delete = set(existing_policies) - set(policy_list) # Telemetry for 'vcd cse ovdc enable' command # TODO: Update telemetry request to handle 'k8s_runtime' array k8s_runtimes_added = ','.join(policies_to_add) if k8s_runtimes_added: cse_params = { RequestKey.K8S_PROVIDER: k8s_runtimes_added, RequestKey.OVDC_ID: ovdc_id, RequestKey.ORG_NAME: org_name, PayloadKey.SOURCE_DESCRIPTION: thread_local_data.get_thread_local_data( ThreadLocalData.USER_AGENT) # noqa: E501 } telemetry_handler.record_user_action_details( cse_operation=CseOperation.OVDC_ENABLE, # noqa: E501 cse_params=cse_params) # Telemetry for 'vcd cse ovdc enable' command # TODO: Update telemetry request to handle 'k8s_runtime' array k8s_runtimes_deleted = '.'.join(policies_to_delete) if k8s_runtimes_deleted: cse_params = { RequestKey.K8S_PROVIDER: k8s_runtimes_deleted, RequestKey.OVDC_ID: ovdc_id, RequestKey.ORG_NAME: org_name, RequestKey.REMOVE_COMPUTE_POLICY_FROM_VMS: remove_cp_from_vms_on_disable, # noqa: E501 PayloadKey.SOURCE_DESCRIPTION: thread_local_data.get_thread_local_data( ThreadLocalData.USER_AGENT) # noqa: E501 } telemetry_handler.record_user_action_details( cse_operation=CseOperation.OVDC_DISABLE, # noqa: E501 cse_params=cse_params) for cp_name in policies_to_add: msg = f"Adding k8s provider {cp_name} to OVDC {vdc.name}" logger.SERVER_LOGGER.debug(msg) task.update(status=vcd_client.TaskStatus.RUNNING.value, namespace='vcloud.cse', operation=msg, operation_name=operation_name, details='', progress=None, owner_href=vdc.href, owner_name=vdc.name, owner_type=vcd_client.EntityType.VDC.value, user_href=user_href, user_name=operation_context.user.name, task_href=task_href, org_href=operation_context.user.org_href) policy = compute_policy_manager.get_cse_vdc_compute_policy( cpm, cp_name, is_placement_policy=True) cpm.add_compute_policy_to_vdc(vdc_id=ovdc_id, compute_policy_href=policy['href']) for cp_name in policies_to_delete: msg = f"Removing k8s provider {RUNTIME_INTERNAL_NAME_TO_DISPLAY_NAME_MAP[cp_name]} from OVDC {ovdc_id}" # noqa: E501 logger.SERVER_LOGGER.debug(msg) task_resource = \ task.update(status=vcd_client.TaskStatus.RUNNING.value, namespace='vcloud.cse', operation=msg, operation_name=operation_name, details='', progress=None, owner_href=vdc.href, owner_name=vdc.name, owner_type=vcd_client.EntityType.VDC.value, user_href=user_href, user_name=operation_context.user.name, task_href=task_href, org_href=operation_context.user.org_href) policy = compute_policy_manager.get_cse_vdc_compute_policy( cpm, cp_name, is_placement_policy=True) # noqa: E501 cpm.remove_compute_policy_from_vdc_sync( vdc=vdc, compute_policy_href=policy['href'], # noqa: E501 force=remove_cp_from_vms_on_disable, # noqa: E501 is_placement_policy=True, task_resource=task_resource) # noqa: E501 msg = f"Successfully updated OVDC: {vdc.name}" logger.SERVER_LOGGER.debug(msg) task.update(status=vcd_client.TaskStatus.SUCCESS.value, namespace='vcloud.cse', operation="Operation success", operation_name=operation_name, details=msg, progress=None, owner_href=vdc.href, owner_name=vdc.name, owner_type=vcd_client.EntityType.VDC.value, user_href=user_href, user_name=operation_context.user.name, task_href=task_href, org_href=operation_context.user.org_href) # Record telemetry if k8s_runtimes_added: telemetry_handler.record_user_action( CseOperation.OVDC_ENABLE, status=OperationStatus.SUCCESS) # noqa: E501 if k8s_runtimes_deleted: telemetry_handler.record_user_action( CseOperation.OVDC_DISABLE, status=OperationStatus.SUCCESS) # noqa: E501 except Exception as err: # Record telemetry if k8s_runtimes_added: telemetry_handler.record_user_action(CseOperation.OVDC_ENABLE, status=OperationStatus.FAILED) if k8s_runtimes_deleted: telemetry_handler.record_user_action(CseOperation.OVDC_DISABLE, status=OperationStatus.FAILED) logger.SERVER_LOGGER.error(err) task.update(status=vcd_client.TaskStatus.ERROR.value, namespace='vcloud.cse', operation='Failed to update OVDC', operation_name=operation_name, details=f'Failed with error: {err}', progress=None, owner_href=vdc.href, owner_name=vdc.name, owner_type=vcd_client.EntityType.VDC.value, user_href=user_href, user_name=operation_context.user.name, task_href=task_href, org_href=operation_context.user.org_href, error_message=f"{err}") finally: if operation_context.sysadmin_client: operation_context.end()
def ovdc_update(request_data, op_ctx: ctx.OperationContext): """Request handler for ovdc enable, disable operations. Required data: org_name, ovdc_name, k8s_provider Conditional data: if k8s_provider is 'ent-pks': pks_plan_name, pks_cluster_domain :return: Dictionary with org VDC update task href. """ # TODO the data flow here should be better understood. # org_name and ovdc_name seem redundant if we already have ovdc_id data = req_utils.flatten_request_data( request_data, [RequestKey.INPUT_SPEC]) required = [ RequestKey.ORG_NAME, RequestKey.OVDC_NAME, RequestKey.K8S_PROVIDER, RequestKey.OVDC_ID ] validated_data = data req_utils.validate_payload(validated_data, required) k8s_provider = validated_data[RequestKey.K8S_PROVIDER] k8s_provider_info = {K8S_PROVIDER_KEY: k8s_provider} # Record the telemetry data cse_params = copy.deepcopy(validated_data) cse_params[PayloadKey.SOURCE_DESCRIPTION] = thread_local_data.get_thread_local_data(ThreadLocalData.USER_AGENT) # noqa: E501 cse_operation = CseOperation.OVDC_DISABLE if k8s_provider == K8sProvider.NONE else CseOperation.OVDC_ENABLE # noqa: E501 record_user_action_details(cse_operation=cse_operation, cse_params=cse_params) # noqa: E501 sysadmin_client_v33 = \ op_ctx.get_sysadmin_client(api_version=DEFAULT_API_VERSION) try: if k8s_provider == K8sProvider.PKS: if not server_utils.is_pks_enabled(): raise e.CseServerError('CSE server is not ' 'configured to work with PKS.') required = [ RequestKey.PKS_PLAN_NAME, RequestKey.PKS_CLUSTER_DOMAIN ] req_utils.validate_payload(validated_data, required) # Check if target ovdc is not already enabled for other non PKS k8 providers # noqa: E501 ovdc_metadata = ovdc_utils.get_ovdc_k8s_provider_metadata( sysadmin_client_v33, ovdc_id=validated_data[RequestKey.OVDC_ID]) ovdc_k8_provider = ovdc_metadata.get(K8S_PROVIDER_KEY) if ovdc_k8_provider != K8sProvider.NONE and \ ovdc_k8_provider != k8s_provider: raise e.CseServerError("Ovdc already enabled for different K8 provider") # noqa: E501 k8s_provider_info = ovdc_utils.construct_k8s_metadata_from_pks_cache( # noqa: E501 sysadmin_client_v33, ovdc_id=validated_data[RequestKey.OVDC_ID], org_name=validated_data[RequestKey.ORG_NAME], pks_plans=validated_data[RequestKey.PKS_PLAN_NAME], pks_cluster_domain=validated_data[RequestKey.PKS_CLUSTER_DOMAIN], # noqa: E501 k8s_provider=k8s_provider) ovdc_utils.create_pks_compute_profile(validated_data, op_ctx, k8s_provider_info) task = ovdc_utils.update_ovdc_k8s_provider_metadata( sysadmin_client_v33, validated_data[RequestKey.OVDC_ID], k8s_provider_data=k8s_provider_info, k8s_provider=k8s_provider) # Telemetry - Record successful enabling/disabling of ovdc record_user_action(cse_operation, status=OperationStatus.SUCCESS) return {'task_href': task.get('href')} except Exception as err: logger.SERVER_LOGGER.error(f"Error while updating OVDC: {str(err)}") # Telemetry - Record failed enabling/disabling of ovdc record_user_action(cse_operation, status=OperationStatus.FAILED) raise err
def ovdc_compute_policy_update(request_data, op_ctx: ctx.OperationContext): """Request handler for ovdc compute-policy update operation. Required data: ovdc_id, compute_policy_action, compute_policy_names :return: Dictionary with task href. """ required = [ RequestKey.OVDC_ID, RequestKey.COMPUTE_POLICY_ACTION, RequestKey.COMPUTE_POLICY_NAME ] defaults = { RequestKey.REMOVE_COMPUTE_POLICY_FROM_VMS: False, } validated_data = {**defaults, **request_data} req_utils.validate_payload(validated_data, required) action = validated_data[RequestKey.COMPUTE_POLICY_ACTION] cp_name = validated_data[RequestKey.COMPUTE_POLICY_NAME] ovdc_id = validated_data[RequestKey.OVDC_ID] remove_compute_policy_from_vms = validated_data[ RequestKey.REMOVE_COMPUTE_POLICY_FROM_VMS] # noqa: E501 try: config = server_utils.get_server_runtime_config() cpm = compute_policy_manager.ComputePolicyManager( op_ctx.sysadmin_client, log_wire=utils.str_to_bool( config['service'].get('log_wire'))) # noqa: E501 cp_href = None cp_id = None if cp_name == SYSTEM_DEFAULT_COMPUTE_POLICY_NAME: for _cp in cpm.list_compute_policies_on_vdc(ovdc_id): if _cp['name'] == cp_name: cp_href = _cp['href'] cp_id = _cp['id'] else: try: _cp = compute_policy_manager.get_cse_vdc_compute_policy( cpm, cp_name) # noqa: E501 cp_href = _cp['href'] cp_id = _cp['id'] except vcd_e.EntityNotFoundException: pass if cp_href is None: raise e.BadRequestError(f"Compute policy '{cp_name}' not found.") if action == ComputePolicyAction.ADD: cpm.add_compute_policy_to_vdc(ovdc_id, cp_href) # Record telemetry data record_user_action(CseOperation.OVDC_COMPUTE_POLICY_ADD) return f"Added compute policy '{cp_name}' ({cp_id}) to ovdc " \ f"({ovdc_id})" if action == ComputePolicyAction.REMOVE: # TODO: fix remove_compute_policy by implementing a proper way # for calling async methods without having to pass op_ctx # outside handlers. op_ctx.is_async = True response = cpm.remove_vdc_compute_policy_from_vdc( ovdc_id, cp_href, force=remove_compute_policy_from_vms) # Follow task_href to completion in a different thread and end # operation context _follow_task(op_ctx, response['task_href'], ovdc_id) # Record telemetry data record_user_action(CseOperation.OVDC_COMPUTE_POLICY_REMOVE) return response raise e.BadRequestError("Unsupported compute policy action") except Exception as err: # Record telemetry data failure` if action == ComputePolicyAction.ADD: record_user_action(CseOperation.OVDC_COMPUTE_POLICY_ADD, status=OperationStatus.FAILED) elif action == ComputePolicyAction.REMOVE: record_user_action(CseOperation.OVDC_COMPUTE_POLICY_REMOVE, status=OperationStatus.FAILED) raise err
def ovdc_list(request_data, op_ctx: ctx.OperationContext): """Request handler for ovdc list operation. :return: List of dictionaries with org VDC k8s provider metadata. :rtype: list """ # NOTE: response sent out by this handler should not be paginated data = req_utils.flatten_request_data( request_data, [RequestKey.QUERY_PARAMS]) defaults = { RequestKey.LIST_PKS_PLANS: False, } validated_data = {**defaults, **data} list_pks_plans = utils.str_to_bool(validated_data[RequestKey.LIST_PKS_PLANS]) # noqa: E501 # Record telemetry data cse_params = copy.deepcopy(validated_data) cse_params[RequestKey.LIST_PKS_PLANS] = list_pks_plans cse_params[PayloadKey.SOURCE_DESCRIPTION] = thread_local_data.get_thread_local_data(ThreadLocalData.USER_AGENT) # noqa: E501 record_user_action_details(cse_operation=CseOperation.OVDC_LIST, cse_params=cse_params) client_v33 = op_ctx.get_client(api_version=DEFAULT_API_VERSION) if list_pks_plans and not client_v33.is_sysadmin(): raise e.UnauthorizedRequestError( 'Operation denied. Enterprise PKS plans visible only ' 'to System Administrators.') ovdcs = [] org_vdcs = vcd_utils.get_all_ovdcs(client_v33) sysadmin_client_v33 = \ op_ctx.get_sysadmin_client(api_version=DEFAULT_API_VERSION) for ovdc in org_vdcs: ovdc_name = ovdc.get('name') org_name = ovdc.get('orgName') ovdc_id = vcd_utils.extract_id(ovdc.get('id')) k8s_metadata = ovdc_utils.get_ovdc_k8s_provider_metadata( sysadmin_client_v33, ovdc_id=ovdc_id, ovdc_name=ovdc_name, org_name=org_name) k8s_provider = k8s_metadata[K8S_PROVIDER_KEY] ovdc_dict = { OvdcInfoKey.OVDC_NAME: ovdc_name, OvdcInfoKey.ORG_NAME: org_name, OvdcInfoKey.K8S_PROVIDER: k8s_provider } if list_pks_plans: pks_plans = '' pks_server = '' if k8s_provider == K8sProvider.PKS: # vc name for vdc can only be found using typed query qfilter = f"name=={urllib.parse.quote(ovdc_name)};" \ f"orgName=={urllib.parse.quote(org_name)}" q = client_v33.get_typed_query( vcd_client.ResourceType.ADMIN_ORG_VDC.value, query_result_format=vcd_client.QueryResultFormat.RECORDS, # noqa: E501 qfilter=qfilter) # should only ever be one element in the generator ovdc_records = list(q.execute()) if len(ovdc_records) == 0: raise vcd_e.EntityNotFoundException( f"Org VDC {ovdc_name} not found in org {org_name}") ovdc_record = None for record in ovdc_records: ovdc_record = pyvcd_utils.to_dict( record, resource_type=vcd_client.ResourceType.ADMIN_ORG_VDC.value) # noqa: E501 break vc_to_pks_plans_map = {} pks_contexts = pksbroker_manager.create_pks_context_for_all_accounts_in_org(op_ctx) # noqa: E501 for pks_context in pks_contexts: if pks_context['vc'] in vc_to_pks_plans_map: continue pks_broker = pksbroker.PksBroker(pks_context, op_ctx) plans = pks_broker.list_plans() plan_names = [plan.get('name') for plan in plans] vc_to_pks_plans_map[pks_context['vc']] = \ [plan_names, pks_context['host']] pks_plan_and_server_info = vc_to_pks_plans_map.get( ovdc_record['vcName'], []) if len(pks_plan_and_server_info) > 0: pks_plans = pks_plan_and_server_info[0] pks_server = pks_plan_and_server_info[1] ovdc_dict[PKSOvdcInfoKey.PKS_API_SERVER] = pks_server ovdc_dict[PKSOvdcInfoKey.AVAILABLE_PKS_PLANS] = pks_plans ovdcs.append(ovdc_dict) return ovdcs
def create_pks_context_for_all_accounts_in_org( op_ctx: ctx.OperationContext): # noqa: E501 """Create PKS context for accounts in a given Org. If user is Sysadmin Creates PKS contexts for all PKS accounts defined in the entire system. else Creates PKS contexts for all PKS accounts assigned to the org. However if separate service accounts for each org hasn't been configured by admin via pks.yaml, then PKS accounts of the PKS server corresponding to the vCenters powering the individual orgVDC of the org will be picked up for creating the PKS contexts. :return: list of dict, where each dictionary is a PKS context :rtype: list """ pks_cache = server_utils.get_pks_cache() if pks_cache is None: return [] client_v33 = op_ctx.get_client(api_version=DEFAULT_API_VERSION) if client_v33.is_sysadmin(): all_pks_account_info = pks_cache.get_all_pks_account_info_in_system() pks_ctx_list = [ ovdc_utils.construct_pks_context(pks_account_info, credentials_required=True) for pks_account_info in all_pks_account_info ] # noqa: E501 return pks_ctx_list if pks_cache.do_orgs_have_exclusive_pks_account(): pks_account_infos = pks_cache.get_exclusive_pks_accounts_info_for_org( op_ctx.user.org_name) # noqa: E501 pks_ctx_list = [ ovdc_utils.construct_pks_context(pks_account_info, credentials_required=True) for pks_account_info in pks_account_infos ] # noqa: E501 return pks_ctx_list org_resource = client_v33.get_org() org = Org(client_v33, resource=org_resource) vdc_names = [vdc['name'] for vdc in org.list_vdcs()] # Constructing dict instead of list to avoid duplicates # TODO() figure out a way to add pks contexts to a set directly pks_ctx_dict = {} sysadmin_client_v33 = \ op_ctx.get_sysadmin_client(api_version=DEFAULT_API_VERSION) for vdc_name in vdc_names: # this is a full blown pks_account_info + pvdc_info + # compute_profile_name dictionary k8s_metadata = ovdc_utils.get_ovdc_k8s_provider_metadata( sysadmin_client_v33, ovdc_name=vdc_name, org_name=op_ctx.user.org_name, include_credentials=True) if k8s_metadata[K8S_PROVIDER_KEY] == K8sProvider.PKS: pks_ctx_dict[k8s_metadata['vc']] = k8s_metadata return list(pks_ctx_dict.values())