def _load_placement_policy_details(self, msg_update_callback=utils.NullPrinter()): # noqa: E501 msg = "Loading kubernetes runtime placement policies." logger.SERVER_LOGGER.info(msg) msg_update_callback.general(msg) try: sysadmin_client = vcd_utils.get_sys_admin_client() if float(sysadmin_client.get_api_version()) < compute_policy_manager.GLOBAL_PVDC_COMPUTE_POLICY_MIN_VERSION: # noqa: E501 msg = "Placement policies for kubernetes runtimes not " \ " supported in api version " \ f"{sysadmin_client.get_api_version()}" # noqa: E501 logger.SERVER_LOGGER.debug(msg) msg_update_callback.info(msg) return placement_policy_name_to_href = {} cpm = compute_policy_manager.ComputePolicyManager(sysadmin_client, log_wire=self.config['service'].get('log_wire')) # noqa: E501 for runtime_policy in shared_constants.CLUSTER_RUNTIME_PLACEMENT_POLICIES: # noqa: E501 k8_runtime = shared_constants.RUNTIME_INTERNAL_NAME_TO_DISPLAY_NAME_MAP[runtime_policy] # noqa: E501 try: placement_policy_name_to_href[k8_runtime] = \ compute_policy_manager.get_cse_vdc_compute_policy( cpm, runtime_policy, is_placement_policy=True)['href'] except EntityNotFoundException: pass self.config['placement_policy_hrefs'] = placement_policy_name_to_href # noqa: E501 except Exception as e: msg = f"Failed to load placement policies to server runtime configuration: {str(e)}" # noqa: E501 msg_update_callback.error(msg) logger.SERVER_LOGGER.error(msg) raise
def _process_template_rules(self, msg_update_callback=utils.NullPrinter()): if 'template_rules' not in self.config: return rules = self.config['template_rules'] if not rules: return templates = self.config['broker']['templates'] # process rules msg = "Processing template rules." logger.SERVER_LOGGER.debug(msg) msg_update_callback.general_no_color(msg) for rule_def in rules: rule = TemplateRule(name=rule_def.get('name'), target=rule_def.get('target'), action=rule_def.get('action'), logger=logger.SERVER_LOGGER, msg_update_callback=msg_update_callback) msg = f"Processing rule : {rule}." logger.SERVER_LOGGER.debug(msg) msg_update_callback.general_no_color(msg) # Since the patching is in-place, the changes will reflect back on # the dictionary holding the server runtime configuration. rule.apply(templates) msg = f"Finished processing rule : '{rule.name}'" logger.SERVER_LOGGER.debug(msg) msg_update_callback.general(msg)
def create_cse_service_role(client, msg_update_callback=utils.NullPrinter(), logger_debug=SERVER_CLI_LOGGER): """Create Service Role for CSE operations. The method can only be called by System Administrator user :param client: pyvcloud.vcd.client to interact with VCD HOST :param utils.ConsoleMessagePrinter msg_update_callback: Callback object. :raises pyvcloud.vcd.exceptions.BadRequestException when Role already exist :raises pyvcloud.vcd.exceptions.EntityNotFoundException when Right doesn't exist """ # We can't check if the user is Sysadmin or some other user in system org, # as the values will need to be hardcoded. # For now just check if its system org or not. vcd_utils.raise_error_if_user_not_from_system_org(client) system_org = Org(client, resource=client.get_org()) system_org.create_role(server_constants.CSE_SERVICE_ROLE_NAME, server_constants.CSE_SERVICE_ROLE_DESC, server_constants.CSE_SERVICE_ROLE_RIGHTS) msg = f"Successfully created {server_constants.CSE_SERVICE_ROLE_NAME}" msg_update_callback.general(msg) logger_debug.info(msg) return
def _load_def_schema(self, msg_update_callback=utils.NullPrinter()): """Load cluster interface and cluster entity type to global context. If defined entity framework is supported by vCD api version, load defined entity interface and defined entity type registered during server install :param utils.ConsoleMessagePrinter msg_update_callback """ sysadmin_client = None try: sysadmin_client = vcd_utils.get_sys_admin_client() logger_wire = logger.NULL_LOGGER if utils.str_to_bool( utils.str_to_bool(self.config['service'].get( 'log_wire', False))): # noqa: E501 logger_wire = logger.SERVER_CLOUDAPI_WIRE_LOGGER cloudapi_client = \ vcd_utils.get_cloudapi_client_from_vcd_client(sysadmin_client, logger.SERVER_LOGGER, # noqa: E501 logger_wire) raise_error_if_def_not_supported(cloudapi_client) schema_svc = def_schema_svc.DefSchemaService(cloudapi_client) defKey = def_utils.DefKey keys_map = def_utils.MAP_API_VERSION_TO_KEYS[float( sysadmin_client.get_api_version())] # noqa: E501 interface_id = def_utils.generate_interface_id( vendor=keys_map[defKey.VENDOR], # noqa: E501 nss=keys_map[defKey.INTERFACE_NSS], # noqa: E501 version=keys_map[defKey.INTERFACE_VERSION]) # noqa: E501 entity_type_id = def_utils.generate_entity_type_id( vendor=keys_map[defKey.VENDOR], # noqa: E501 nss=keys_map[defKey.ENTITY_TYPE_NSS], # noqa: E501 version=keys_map[defKey.ENTITY_TYPE_VERSION]) # noqa: E501 self._nativeInterface = schema_svc.get_interface(interface_id) self._nativeEntityType = schema_svc.get_entity_type(entity_type_id) msg = "Successfully loaded defined entity schema to global context" msg_update_callback.general(msg) logger.SERVER_LOGGER.debug(msg) except cse_exception.DefNotSupportedException: msg = "Skipping initialization of defined entity type" \ " and defined entity interface" msg_update_callback.info(msg) logger.SERVER_LOGGER.debug(msg) except cse_exception.DefSchemaServiceError as e: msg = f"Error while loading defined entity schema: {e.minor_error_code}" # noqa: E501 msg_update_callback.error(msg) logger.SERVER_LOGGER.debug(msg) raise e except Exception as e: msg = f"Failed to load defined entity schema to global context: {str(e)}" # noqa: E501 msg_update_callback.error(e) logger.SERVER_LOGGER.error(e) raise (e) finally: if sysadmin_client: sysadmin_client.logout()
def _process_template_compute_policy_compliance(self, msg_update_callback=utils.NullPrinter()): # noqa: E501 msg = "Processing compute policy for k8s templates." logger.SERVER_LOGGER.info(msg) msg_update_callback.general_no_color(msg) org_name = self.config['broker']['org'] catalog_name = self.config['broker']['catalog'] sysadmin_client = None try: sysadmin_client = vcd_utils.get_sys_admin_client() cpm = compute_policy_manager.ComputePolicyManager(sysadmin_client, log_wire=self.config['service'].get('log_wire')) # noqa: E501 for template in self.config['broker']['templates']: policy_name = template[LocalTemplateKey.COMPUTE_POLICY] catalog_item_name = template[LocalTemplateKey.CATALOG_ITEM_NAME] # noqa: E501 # if policy name is not empty, stamp it on the template if policy_name: try: policy = cpm.get_policy(policy_name=policy_name) except EntityNotFoundException: # create the policy if it does not exist msg = f"Creating missing compute policy " \ f"'{policy_name}'." msg_update_callback.info(msg) logger.SERVER_LOGGER.debug(msg) policy = cpm.add_policy(policy_name=policy_name) msg = f"Assigning compute policy '{policy_name}' to " \ f"template '{catalog_item_name}'." msg_update_callback.general(msg) logger.SERVER_LOGGER.debug(msg) cpm.assign_compute_policy_to_vapp_template_vms( compute_policy_href=policy['href'], org_name=org_name, catalog_name=catalog_name, catalog_item_name=catalog_item_name) else: # empty policy name means we should remove policy from # template msg = f"Removing compute policy from template " \ f"'{catalog_item_name}'." msg_update_callback.general(msg) logger.SERVER_LOGGER.debug(msg) cpm.remove_all_compute_policies_from_vapp_template_vms( org_name=org_name, catalog_name=catalog_name, catalog_item_name=catalog_item_name) except OperationNotSupportedException: msg = "Compute policy not supported by vCD. Skipping " \ "assigning/removing it to/from templates." msg_update_callback.info(msg) logger.SERVER_LOGGER.debug(msg) finally: if sysadmin_client is not None: sysadmin_client.logout()
def _register_right(client, right_name, description, category, bundle_key, msg_update_callback=utils.NullPrinter()): """Register a right for CSE. :param pyvcloud.vcd.client.Client client: :param str right_name: the name of the new right to be registered. :param str description: brief description about the new right. :param str category: add the right in existing categories in vCD Roles and Rights or specify a new category name. :param str bundle_key: is used to identify the right name and change its value to different languages using localization bundle. :param utils.ConsoleMessagePrinter msg_update_callback: Callback object. :raises BadRequestException: if a right with given name already exists in vCD. """ ext = APIExtension(client) # Since the client is a sys admin, org will hold a reference to System org system_org = Org(client, resource=client.get_org()) try: right_name_in_vcd = f"{{{server_constants.CSE_SERVICE_NAME}}}:{right_name}" # noqa: E501 # TODO(): When org.get_right_record() is moved outside the org scope in # pyvcloud, update the code below to adhere to the new method names. system_org.get_right_record(right_name_in_vcd) msg = f"Right: {right_name} already exists in vCD" msg_update_callback.general(msg) INSTALL_LOGGER.info(msg) # Presence of the right in vCD is not a guarantee that the right will # be assigned to system org too. rights_in_system = system_org.list_rights_of_org() for dikt in rights_in_system: # TODO(): When localization support comes in, this check should be # ditched for a better one. if dikt['name'] == right_name_in_vcd: msg = f"Right: {right_name} already assigned to System " \ f"organization." msg_update_callback.general(msg) INSTALL_LOGGER.info(msg) return # Since the right is not assigned to system org, we need to add it. msg = f"Assigning Right: {right_name} to System organization." msg_update_callback.general(msg) INSTALL_LOGGER.info(msg) system_org.add_rights([right_name_in_vcd]) except EntityNotFoundException: # Registering a right via api extension end point, auto assigns it to # System org. msg = f"Registering Right: {right_name} in vCD" msg_update_callback.general(msg) INSTALL_LOGGER.info(msg) ext.add_service_right(right_name, server_constants.CSE_SERVICE_NAME, server_constants.CSE_SERVICE_NAMESPACE, description, category, bundle_key)
def _create_amqp_exchange(exchange_name, host, port, vhost, use_ssl, username, password, msg_update_callback=utils.NullPrinter()): """Create the specified AMQP exchange if it does not exist. If specified AMQP exchange exists already, does nothing. :param str exchange_name: The AMQP exchange name to check for or create. :param str host: AMQP host name. :param str password: AMQP password. :param int port: AMQP port number. :param bool use_ssl: Enable ssl. :param str username: AMQP username. :param str vhost: AMQP vhost. :param utils.ConsoleMessagePrinter msg_update_callback: Callback object. :raises cse_exception.AmqpError: if AMQP exchange could not be created. """ msg = f"Checking for AMQP exchange '{exchange_name}'" msg_update_callback.info(msg) INSTALL_LOGGER.info(msg) credentials = pika.PlainCredentials(username, password) parameters = pika.ConnectionParameters(host, port, vhost, credentials, ssl=use_ssl, connection_attempts=3, retry_delay=2, socket_timeout=5) connection = None try: connection = pika.BlockingConnection(parameters) channel = connection.channel() channel.exchange_declare(exchange=exchange_name, exchange_type=server_constants.EXCHANGE_TYPE, durable=True, auto_delete=False) except Exception as err: msg = f"Cannot create AMQP exchange '{exchange_name}'" msg_update_callback.error(msg) INSTALL_LOGGER.error(msg, exc_info=True) raise cse_exception.AmqpError(msg, str(err)) finally: if connection is not None: connection.close() msg = f"AMQP exchange '{exchange_name}' is ready" msg_update_callback.general(msg) INSTALL_LOGGER.info(msg)
def _setup_placement_policies(client, policy_list, msg_update_callback=utils.NullPrinter(), log_wire=False): """ Create placement policies for each cluster type. Create the global pvdc compute policy if not present and create placement policy for each policy in the policy list. This should be done only for vcd api version >= 35 (zeus) :parma client vcdClient.Client :param policy_list str[] """ msg = "Setting up placement policies for cluster types" msg_update_callback.info(msg) INSTALL_LOGGER.debug(msg) computePolicyManager = cpm.ComputePolicyManager(client, log_wire=log_wire) pvdc_compute_policy = None try: try: pvdc_compute_policy = computePolicyManager.get_pvdc_compute_policy( server_constants.CSE_GLOBAL_PVDC_COMPUTE_POLICY_NAME) msg = "Skipping global PVDC compute policy creation. Policy already exists" # noqa: E501 msg_update_callback.general(msg) INSTALL_LOGGER.debug(msg) except EntityNotFoundException: msg = "Creating global PVDC compute policy" msg_update_callback.general(msg) INSTALL_LOGGER.debug(msg) pvdc_compute_policy = computePolicyManager.add_pvdc_compute_policy( server_constants.CSE_GLOBAL_PVDC_COMPUTE_POLICY_NAME, server_constants.CSE_GLOBAL_PVDC_COMPUTE_POLICY_DESCRIPTION) for policy in policy_list: try: computePolicyManager.get_vdc_compute_policy( policy, is_placement_policy=True) # noqa: E501 msg = f"Skipping creating VDC placement policy '{policy}'. Policy already exists" # noqa: E501 msg_update_callback.general(msg) INSTALL_LOGGER.debug(msg) except EntityNotFoundException: msg = f"Creating placement policy '{policy}'" msg_update_callback.general(msg) INSTALL_LOGGER.debug(msg) computePolicyManager.add_vdc_compute_policy( policy, pvdc_compute_policy_id=pvdc_compute_policy['id'] ) # noqa: E501 except cse_exception.GlobalPvdcComputePolicyNotSupported: msg = "Global PVDC compute policies are not supported." \ "Skipping placement policy creation." msg_update_callback.general(msg) INSTALL_LOGGER.debug(msg)
def _register_cse(client, routing_key, exchange, msg_update_callback=utils.NullPrinter()): """Register or update CSE on vCD. :param pyvcloud.vcd.client.Client client: :param pyvcloud.vcd.client.Client client: :param str routing_key: :param str exchange: :param utils.ConsoleMessagePrinter msg_update_callback: Callback object. """ ext = APIExtension(client) patterns = [ f'/api/{server_constants.CSE_SERVICE_NAME}', f'/api/{server_constants.CSE_SERVICE_NAME}/.*', f'/api/{server_constants.CSE_SERVICE_NAME}/.*/.*', f'/api/{server_constants.PKS_SERVICE_NAME}', f'/api/{server_constants.PKS_SERVICE_NAME}/.*', f'/api/{server_constants.PKS_SERVICE_NAME}/.*/.*' ] cse_info = None try: cse_info = ext.get_extension_info( server_constants.CSE_SERVICE_NAME, namespace=server_constants.CSE_SERVICE_NAMESPACE) # noqa: E501 except MissingRecordException: pass if cse_info is None: ext.add_extension( server_constants.CSE_SERVICE_NAME, server_constants.CSE_SERVICE_NAMESPACE, routing_key, # noqa: E501 exchange, patterns) msg = f"Registered {server_constants.CSE_SERVICE_NAME} as an API extension in vCD" # noqa: E501 else: ext.update_extension( server_constants.CSE_SERVICE_NAME, namespace=server_constants.CSE_SERVICE_NAMESPACE, # noqa: E501 routing_key=routing_key, exchange=exchange) msg = f"Updated {server_constants.CSE_SERVICE_NAME} API Extension in vCD" # noqa: E501 msg_update_callback.general(msg) INSTALL_LOGGER.info(msg)
def _load_template_definition_from_catalog( self, msg_update_callback=utils.NullPrinter()): # noqa: E501 msg = "Loading k8s template definition from catalog" logger.SERVER_LOGGER.info(msg) msg_update_callback.general_no_color(msg) client = None try: log_filename = None log_wire = \ utils.str_to_bool(self.config['service'].get('log_wire')) if log_wire: log_filename = logger.SERVER_DEBUG_WIRELOG_FILEPATH client = Client(self.config['vcd']['host'], api_version=self.config['vcd']['api_version'], verify_ssl_certs=self.config['vcd']['verify'], log_file=log_filename, log_requests=log_wire, log_headers=log_wire, log_bodies=log_wire) credentials = BasicLoginCredentials(self.config['vcd']['username'], SYSTEM_ORG_NAME, self.config['vcd']['password']) client.set_credentials(credentials) org_name = self.config['broker']['org'] catalog_name = self.config['broker']['catalog'] k8_templates = ltm.get_all_k8s_local_template_definition( client=client, catalog_name=catalog_name, org_name=org_name) if not k8_templates: msg = "No valid K8 templates were found in catalog " \ f"'{catalog_name}'. Unable to start CSE server." msg_update_callback.error(msg) logger.SERVER_LOGGER.error(msg) sys.exit(1) # Check that default k8s template exists in vCD at the correct # revision default_template_name = \ self.config['broker']['default_template_name'] default_template_revision = \ str(self.config['broker']['default_template_revision']) found_default_template = False for template in k8_templates: if str(template[LocalTemplateKey.REVISION] ) == default_template_revision and template[ LocalTemplateKey. NAME] == default_template_name: # noqa: E501 found_default_template = True msg = f"Found K8 template '{template['name']}' at revision " \ f"{template['revision']} in catalog '{catalog_name}'" msg_update_callback.general(msg) logger.SERVER_LOGGER.info(msg) if not found_default_template: msg = f"Default template {default_template_name} with " \ f"revision {default_template_revision} not found." \ " Unable to start CSE server." msg_update_callback.error(msg) logger.SERVER_LOGGER.error(msg) sys.exit(1) self.config['broker']['templates'] = k8_templates finally: if client: client.logout()
def run(self, msg_update_callback=utils.NullPrinter()): self.config = get_validated_config( self.config_file, pks_config_file_name=self.pks_config_file, skip_config_decryption=self.skip_config_decryption, decryption_password=self.decryption_password, log_wire_file=logger.SERVER_DEBUG_WIRELOG_FILEPATH, logger_debug=logger.SERVER_LOGGER, msg_update_callback=msg_update_callback) populate_vsphere_list(self.config['vcs']) # Load def entity-type and interface self._load_def_schema(msg_update_callback=msg_update_callback) # Read k8s catalog definition from catalog item metadata and append # the same to to server run-time config self._load_template_definition_from_catalog( msg_update_callback=msg_update_callback) if float(self.config['vcd']['api_version']) < float( vCDApiVersion.VERSION_35.value): # noqa: E501 # Read templates rules from config and update template deinfition # in server run-time config self._process_template_rules( msg_update_callback=msg_update_callback) # Make sure that all vms in templates are compliant with the # compute policy specified in template definition (can be affected # by rules). self._process_template_compute_policy_compliance( msg_update_callback=msg_update_callback) else: msg = "Template rules are not supported by CSE for vCD api " \ "version 35.0 or above. Skipping template rule processing." msg_update_callback.info(msg) logger.SERVER_LOGGER.debug(msg) if self.should_check_config: check_cse_installation(self.config, msg_update_callback=msg_update_callback) if self.config.get('pks_config'): pks_config = self.config.get('pks_config') self.pks_cache = PksCache( pks_servers=pks_config.get('pks_api_servers', []), pks_accounts=pks_config.get('pks_accounts', []), pvdcs=pks_config.get('pvdcs', []), orgs=pks_config.get('orgs', []), nsxt_servers=pks_config.get('nsxt_servers', [])) amqp = self.config['amqp'] num_consumers = self.config['service']['listeners'] for n in range(num_consumers): try: c = MessageConsumer(amqp['host'], amqp['port'], amqp['ssl'], amqp['vhost'], amqp['username'], amqp['password'], amqp['exchange'], amqp['routing_key']) name = 'MessageConsumer-%s' % n t = Thread(name=name, target=consumer_thread, args=(c, )) t.daemon = True t.start() msg = f"Started thread '{name} ({t.ident})'" msg_update_callback.general(msg) logger.SERVER_LOGGER.info(msg) self.threads.append(t) self.consumers.append(c) time.sleep(0.25) except KeyboardInterrupt: break except Exception: logger.SERVER_LOGGER.error(traceback.format_exc()) logger.SERVER_LOGGER.info( f"Number of threads started: {len(self.threads)}") # noqa: E501 self._state = ServerState.RUNNING message = f"Container Service Extension for vCloud Director" \ f"\nServer running using config file: {self.config_file}" \ f"\nLog files: {logger.SERVER_INFO_LOG_FILEPATH}, " \ f"{logger.SERVER_DEBUG_LOG_FILEPATH}" \ f"\nwaiting for requests (ctrl+c to close)" signal.signal(signal.SIGINT, signal_handler) msg_update_callback.general_no_color(message) logger.SERVER_LOGGER.info(message) # Record telemetry on user action and details of operation. cse_params = { PayloadKey.WAS_DECRYPTION_SKIPPED: bool(self.skip_config_decryption), # noqa: E501 PayloadKey.WAS_PKS_CONFIG_FILE_PROVIDED: bool(self.pks_config_file), # noqa: E501 PayloadKey.WAS_INSTALLATION_CHECK_SKIPPED: bool(self.should_check_config) # noqa: E501 } record_user_action_details(cse_operation=CseOperation.SERVICE_RUN, cse_params=cse_params) record_user_action(cse_operation=CseOperation.SERVICE_RUN) while True: try: time.sleep(1) if self._state == ServerState.STOPPING and \ self.active_requests_count() == 0: break except KeyboardInterrupt: break except Exception: msg_update_callback.general_no_color(traceback.format_exc()) logger.SERVER_LOGGER.error(traceback.format_exc()) sys.exit(1) logger.SERVER_LOGGER.info("Stop detected") logger.SERVER_LOGGER.info("Closing connections...") for c in self.consumers: try: c.stop() except Exception: logger.SERVER_LOGGER.error(traceback.format_exc()) self._state = ServerState.STOPPED logger.SERVER_LOGGER.info("Done")
def _install_template(client, remote_template_manager, template, org_name, vdc_name, catalog_name, network_name, ip_allocation_mode, storage_profile, force_update, retain_temp_vapp, ssh_key, msg_update_callback=utils.NullPrinter()): localTemplateKey = server_constants.LocalTemplateKey templateBuildKey = server_constants.TemplateBuildKey remote_template_manager.download_template_scripts( template_name=template[server_constants.RemoteTemplateKey.NAME], revision=template[server_constants.RemoteTemplateKey.REVISION], force_overwrite=force_update) catalog_item_name = ltm.get_revisioned_template_name( template[server_constants.RemoteTemplateKey.NAME], template[server_constants.RemoteTemplateKey.REVISION]) # remote template data is a super set of local template data, barring # the key 'catalog_item_name' template_data = dict(template) template_data[localTemplateKey.CATALOG_ITEM_NAME] = catalog_item_name missing_keys = [k for k in localTemplateKey if k not in template_data] if len(missing_keys) > 0: raise ValueError(f"Invalid template data. Missing keys: {missing_keys}" ) # noqa: E501 temp_vm_name = \ f"{template[server_constants.RemoteTemplateKey.OS].replace('.','')}-k8s" \ f"{template[server_constants.RemoteTemplateKey.KUBERNETES_VERSION].replace('.', '')}" \ f"-{template[server_constants.RemoteTemplateKey.CNI]}{template[server_constants.RemoteTemplateKey.CNI_VERSION].replace('.','')}-vm" # noqa: E501 build_params = { templateBuildKey.TEMPLATE_NAME: template[server_constants.RemoteTemplateKey.NAME], # noqa: E501 templateBuildKey.TEMPLATE_REVISION: template[server_constants.RemoteTemplateKey.REVISION], # noqa: E501 templateBuildKey.SOURCE_OVA_NAME: template[ server_constants.RemoteTemplateKey.SOURCE_OVA_NAME], # noqa: E501 templateBuildKey.SOURCE_OVA_HREF: template[ server_constants.RemoteTemplateKey.SOURCE_OVA_HREF], # noqa: E501 templateBuildKey.SOURCE_OVA_SHA256: template[server_constants.RemoteTemplateKey. SOURCE_OVA_SHA256], # noqa: E501 templateBuildKey.ORG_NAME: org_name, templateBuildKey.VDC_NAME: vdc_name, templateBuildKey.CATALOG_NAME: catalog_name, templateBuildKey.CATALOG_ITEM_NAME: catalog_item_name, templateBuildKey.CATALOG_ITEM_DESCRIPTION: template[server_constants.RemoteTemplateKey.DESCRIPTION], # noqa: E501 templateBuildKey.TEMP_VAPP_NAME: template[server_constants.RemoteTemplateKey.NAME] + '_temp', # noqa: E501 templateBuildKey.TEMP_VM_NAME: temp_vm_name, templateBuildKey.CPU: template[server_constants.RemoteTemplateKey.CPU], templateBuildKey.MEMORY: template[server_constants.RemoteTemplateKey.MEMORY], # noqa: E501 templateBuildKey.NETWORK_NAME: network_name, templateBuildKey.IP_ALLOCATION_MODE: ip_allocation_mode, # noqa: E501 templateBuildKey.STORAGE_PROFILE: storage_profile } builder = TemplateBuilder(client, client, build_params, ssh_key=ssh_key, logger=INSTALL_LOGGER, msg_update_callback=msg_update_callback) builder.build(force_recreate=force_update, retain_temp_vapp=retain_temp_vapp) ltm.save_metadata(client, org_name, catalog_name, catalog_item_name, template_data)
def check_cse_installation(config, msg_update_callback=utils.NullPrinter()): """Ensure that CSE is installed on vCD according to the config file. Checks, 1. AMQP exchange exists 2. CSE is registered with vCD, 3. CSE K8 catalog exists :param dict config: config yaml file as a dictionary :param utils.ConsoleMessagePrinter msg_update_callback: Callback object. :raises Exception: if CSE is not registered to vCD as an extension, or if specified catalog does not exist, or if specified template(s) do not exist. """ msg_update_callback.info( "Validating CSE installation according to config file") err_msgs = [] client = None try: log_filename = None log_wire = utils.str_to_bool(config['service'].get('log_wire')) if log_wire: log_filename = SERVER_CLI_WIRELOG_FILEPATH client = Client(config['vcd']['host'], api_version=config['vcd']['api_version'], verify_ssl_certs=config['vcd']['verify'], log_file=log_filename, log_requests=log_wire, log_headers=log_wire, log_bodies=log_wire) credentials = BasicLoginCredentials(config['vcd']['username'], server_constants.SYSTEM_ORG_NAME, config['vcd']['password']) client.set_credentials(credentials) # check that AMQP exchange exists amqp = config['amqp'] credentials = pika.PlainCredentials(amqp['username'], amqp['password']) parameters = pika.ConnectionParameters(amqp['host'], amqp['port'], amqp['vhost'], credentials, ssl=amqp['ssl'], connection_attempts=3, retry_delay=2, socket_timeout=5) connection = None try: connection = pika.BlockingConnection(parameters) channel = connection.channel() try: channel.exchange_declare( exchange=amqp['exchange'], exchange_type=server_constants.EXCHANGE_TYPE, # noqa: E501 durable=True, passive=True, auto_delete=False) msg = f"AMQP exchange '{amqp['exchange']}' exists" msg_update_callback.general(msg) SERVER_CLI_LOGGER.debug(msg) except pika.exceptions.ChannelClosed: msg = f"AMQP exchange '{amqp['exchange']}' does not exist" msg_update_callback.error(msg) SERVER_CLI_LOGGER.error(msg) err_msgs.append(msg) except Exception: # TODO() replace raw exception with specific msg = f"Could not connect to AMQP exchange '{amqp['exchange']}'" msg_update_callback.error(msg) SERVER_CLI_LOGGER.error(msg) err_msgs.append(msg) finally: if connection is not None: connection.close() # check that CSE is registered to vCD correctly ext = APIExtension(client) try: cse_info = ext.get_extension( server_constants.CSE_SERVICE_NAME, namespace=server_constants.CSE_SERVICE_NAMESPACE) # noqa: E501 rkey_matches = cse_info['routingKey'] == amqp['routing_key'] exchange_matches = cse_info['exchange'] == amqp['exchange'] if not rkey_matches or not exchange_matches: msg = "CSE is registered as an extension, but the extension " \ "settings on vCD are not the same as config settings." if not rkey_matches: msg += f"\nvCD-CSE routing key: {cse_info['routingKey']}" \ f"\nCSE config routing key: {amqp['routing_key']}" if not exchange_matches: msg += f"\nvCD-CSE exchange: {cse_info['exchange']}" \ f"\nCSE config exchange: {amqp['exchange']}" msg_update_callback.info(msg) SERVER_CLI_LOGGER.debug(msg) err_msgs.append(msg) if cse_info['enabled'] == 'true': msg = "CSE on vCD is currently enabled" msg_update_callback.general(msg) SERVER_CLI_LOGGER.debug(msg) else: msg = "CSE on vCD is currently disabled" msg_update_callback.info(msg) SERVER_CLI_LOGGER.debug(msg) except MissingRecordException: msg = "CSE is not registered to vCD" msg_update_callback.error(msg) SERVER_CLI_LOGGER.error(msg) err_msgs.append(msg) # check that catalog exists in vCD org_name = config['broker']['org'] org = vcd_utils.get_org(client, org_name=org_name) catalog_name = config['broker']['catalog'] if vcd_utils.catalog_exists(org, catalog_name): msg = f"Found catalog '{catalog_name}'" msg_update_callback.general(msg) SERVER_CLI_LOGGER.debug(msg) else: msg = f"Catalog '{catalog_name}' not found" msg_update_callback.error(msg) SERVER_CLI_LOGGER.error(msg) err_msgs.append(msg) finally: if client: client.logout() if err_msgs: raise Exception(err_msgs) msg = "CSE installation is valid" msg_update_callback.general(msg) SERVER_CLI_LOGGER.debug(msg)
def install_template(template_name, template_revision, config_file_name, force_create, retain_temp_vapp, ssh_key, skip_config_decryption=False, decryption_password=None, msg_update_callback=utils.NullPrinter()): """Install a particular template in CSE. If template_name and revision are wild carded to *, all templates defined in remote template cookbook will be installed. :param str template_name: :param str template_revision: :param str config_file_name: config file name. :param bool force_create: if True and template already exists in vCD, overwrites existing template. :param str ssh_key: public ssh key to place into template vApp(s). :param bool retain_temp_vapp: if True, temporary vApp will not destroyed, so the user can ssh into and debug the vm. :param bool skip_config_decryption: do not decrypt the config file. :param str decryption_password: password to decrypt the config file. :param utils.ConsoleMessagePrinter msg_update_callback: Callback object. """ config = get_validated_config( config_file_name, skip_config_decryption=skip_config_decryption, decryption_password=decryption_password, log_wire_file=INSTALL_WIRELOG_FILEPATH, logger_debug=INSTALL_LOGGER, msg_update_callback=msg_update_callback) populate_vsphere_list(config['vcs']) msg = f"Installing template '{template_name}' at revision " \ f"'{template_revision}' on vCloud Director using config file " \ f"'{config_file_name}'" msg_update_callback.info(msg) INSTALL_LOGGER.info(msg) client = None try: # Telemetry data construction cse_params = { PayloadKey.TEMPLATE_NAME: template_name, PayloadKey.TEMPLATE_REVISION: template_revision, PayloadKey.WAS_DECRYPTION_SKIPPED: bool(skip_config_decryption), PayloadKey.WERE_TEMPLATES_FORCE_UPDATED: bool(force_create), PayloadKey.WAS_TEMP_VAPP_RETAINED: bool(retain_temp_vapp), PayloadKey.WAS_SSH_KEY_SPECIFIED: bool(ssh_key) } # Record telemetry data record_user_action_details( cse_operation=CseOperation.TEMPLATE_INSTALL, cse_params=cse_params, telemetry_settings=config['service']['telemetry']) log_filename = None log_wire = utils.str_to_bool(config['service'].get('log_wire')) if log_wire: log_filename = INSTALL_WIRELOG_FILEPATH client = Client(config['vcd']['host'], api_version=config['vcd']['api_version'], verify_ssl_certs=config['vcd']['verify'], log_file=log_filename, log_requests=log_wire, log_headers=log_wire, log_bodies=log_wire) credentials = BasicLoginCredentials(config['vcd']['username'], server_constants.SYSTEM_ORG_NAME, config['vcd']['password']) client.set_credentials(credentials) msg = f"Connected to vCD as system administrator: " \ f"{config['vcd']['host']}:{config['vcd']['port']}" msg_update_callback.general(msg) INSTALL_LOGGER.info(msg) # read remote template cookbook rtm = RemoteTemplateManager( remote_template_cookbook_url=config['broker'] ['remote_template_cookbook_url'], # noqa: E501 logger=INSTALL_LOGGER, msg_update_callback=msg_update_callback) remote_template_cookbook = rtm.get_remote_template_cookbook() found_template = False for template in remote_template_cookbook['templates']: template_name_matched = template_name in ( template[server_constants.RemoteTemplateKey.NAME], '*' ) # noqa: E501 template_revision_matched = \ str(template_revision) in (str(template[server_constants.RemoteTemplateKey.REVISION]), '*') # noqa: E501 if template_name_matched and template_revision_matched: found_template = True _install_template( client=client, remote_template_manager=rtm, template=template, org_name=config['broker']['org'], vdc_name=config['broker']['vdc'], catalog_name=config['broker']['catalog'], network_name=config['broker']['network'], ip_allocation_mode=config['broker']['ip_allocation_mode'], storage_profile=config['broker']['storage_profile'], force_update=force_create, retain_temp_vapp=retain_temp_vapp, ssh_key=ssh_key, msg_update_callback=msg_update_callback) if not found_template: msg = f"Template '{template_name}' at revision " \ f"'{template_revision}' not found in remote template " \ "cookbook." msg_update_callback.error(msg) INSTALL_LOGGER.error(msg, exc_info=True) raise Exception(msg) # Record telemetry data on successful template install record_user_action( cse_operation=CseOperation.TEMPLATE_INSTALL, status=OperationStatus.SUCCESS, telemetry_settings=config['service']['telemetry']) # noqa: E501 except Exception: msg_update_callback.error( "Template Installation Error. Check CSE install logs") INSTALL_LOGGER.error("Template Installation Error", exc_info=True) # Record telemetry data on template install failure record_user_action(cse_operation=CseOperation.TEMPLATE_INSTALL, status=OperationStatus.FAILED, telemetry_settings=config['service']['telemetry']) finally: if client is not None: client.logout()
def install_cse(config_file_name, skip_template_creation, force_update, ssh_key, retain_temp_vapp, pks_config_file_name=None, skip_config_decryption=False, decryption_password=None, msg_update_callback=utils.NullPrinter()): """Handle logistics for CSE installation. Handles decision making for configuring AMQP exchange/settings, defined entity schema registration for vCD api version >= 35, extension registration, catalog setup and template creation. Also records telemetry data on installation details. :param str config_file_name: config file name. :param bool skip_template_creation: If True, skip creating the templates. :param bool force_update: if True and templates already exist in vCD, overwrites existing templates. :param str ssh_key: public ssh key to place into template vApp(s). :param bool retain_temp_vapp: if True, temporary vApp will not destroyed, so the user can ssh into and debug the vm. :param str pks_config_file_name: pks config file name. :param bool skip_config_decryption: do not decrypt the config file. :param str decryption_password: password to decrypt the config file. :param utils.ConsoleMessagePrinter msg_update_callback: Callback object. :raises cse_exception.AmqpError: if AMQP exchange could not be created. """ config = get_validated_config( config_file_name, pks_config_file_name=pks_config_file_name, skip_config_decryption=skip_config_decryption, decryption_password=decryption_password, log_wire_file=INSTALL_WIRELOG_FILEPATH, logger_debug=INSTALL_LOGGER, msg_update_callback=msg_update_callback) populate_vsphere_list(config['vcs']) msg = f"Installing CSE on vCloud Director using config file " \ f"'{config_file_name}'" msg_update_callback.info(msg) INSTALL_LOGGER.info(msg) client = None try: # Telemetry - Construct telemetry data telemetry_data = { PayloadKey.WAS_DECRYPTION_SKIPPED: bool(skip_config_decryption), # noqa: E501 PayloadKey.WAS_PKS_CONFIG_FILE_PROVIDED: bool(pks_config_file_name), # noqa: E501 PayloadKey.WERE_TEMPLATES_SKIPPED: bool(skip_template_creation), # noqa: E501 PayloadKey.WERE_TEMPLATES_FORCE_UPDATED: bool(force_update), # noqa: E501 PayloadKey.WAS_TEMP_VAPP_RETAINED: bool(retain_temp_vapp), # noqa: E501 PayloadKey.WAS_SSH_KEY_SPECIFIED: bool(ssh_key) # noqa: E501 } # Telemetry - Record detailed telemetry data on install record_user_action_details( CseOperation.SERVICE_INSTALL, telemetry_data, telemetry_settings=config['service']['telemetry']) # noqa: E501 log_filename = None log_wire = utils.str_to_bool(config['service'].get('log_wire')) if log_wire: log_filename = INSTALL_WIRELOG_FILEPATH client = Client(config['vcd']['host'], api_version=config['vcd']['api_version'], verify_ssl_certs=config['vcd']['verify'], log_file=log_filename, log_requests=log_wire, log_headers=log_wire, log_bodies=log_wire) credentials = BasicLoginCredentials(config['vcd']['username'], server_constants.SYSTEM_ORG_NAME, config['vcd']['password']) client.set_credentials(credentials) msg = f"Connected to vCD as system administrator: " \ f"{config['vcd']['host']}:{config['vcd']['port']}" msg_update_callback.general(msg) INSTALL_LOGGER.info(msg) # create amqp exchange if it doesn't exist amqp = config['amqp'] _create_amqp_exchange(amqp['exchange'], amqp['host'], amqp['port'], amqp['vhost'], amqp['ssl'], amqp['username'], amqp['password'], msg_update_callback=msg_update_callback) # register or update cse on vCD _register_cse(client, amqp['routing_key'], amqp['exchange'], msg_update_callback=msg_update_callback) # register cse def schema on VCD # schema should be located at # ~/.cse-schema/api-v<API VERSION>/schema.json _register_def_schema(client, msg_update_callback=msg_update_callback, log_wire=log_wire) # Since we use CSE extension id as our telemetry instance_id, the # validated config won't have the instance_id yet. Now that CSE has # been registered as an extension, we should update the telemetry # config with the correct instance_id if config['service']['telemetry']['enable']: store_telemetry_settings(config) # register rights to vCD # TODO() should also remove rights when unregistering CSE _register_right( client, right_name=server_constants. CSE_NATIVE_DEPLOY_RIGHT_NAME, # noqa: E501 description=server_constants. CSE_NATIVE_DEPLOY_RIGHT_DESCRIPTION, # noqa: E501 category=server_constants. CSE_NATIVE_DEPLOY_RIGHT_CATEGORY, # noqa: E501 bundle_key=server_constants. CSE_NATIVE_DEPLOY_RIGHT_BUNDLE_KEY, # noqa: E501 msg_update_callback=msg_update_callback) _register_right( client, right_name=server_constants. CSE_PKS_DEPLOY_RIGHT_NAME, # noqa: E501 description=server_constants. CSE_PKS_DEPLOY_RIGHT_DESCRIPTION, # noqa: E501 category=server_constants. CSE_PKS_DEPLOY_RIGHT_CATEGORY, # noqa: E501 bundle_key=server_constants. CSE_PKS_DEPLOY_RIGHT_BUNDLE_KEY, # noqa: E501 msg_update_callback=msg_update_callback) # set up placement policies for all types of clusters _setup_placement_policies( client, policy_list=server_constants. CLUSTER_PLACEMENT_POLICIES, # noqa: E501 msg_update_callback=msg_update_callback, log_wire=log_wire) # set up cse catalog org = vcd_utils.get_org(client, org_name=config['broker']['org']) vcd_utils.create_and_share_catalog( org, config['broker']['catalog'], catalog_desc='CSE templates', logger=INSTALL_LOGGER, msg_update_callback=msg_update_callback) if skip_template_creation: msg = "Skipping creation of templates." msg_update_callback.info(msg) INSTALL_LOGGER.warning(msg) else: # read remote template cookbook, download all scripts rtm = RemoteTemplateManager( remote_template_cookbook_url=config['broker'] ['remote_template_cookbook_url'], # noqa: E501 logger=INSTALL_LOGGER, msg_update_callback=msg_update_callback) remote_template_cookbook = rtm.get_remote_template_cookbook() # create all templates defined in cookbook for template in remote_template_cookbook['templates']: # TODO tag created templates with placement policies _install_template( client=client, remote_template_manager=rtm, template=template, org_name=config['broker']['org'], vdc_name=config['broker']['vdc'], catalog_name=config['broker']['catalog'], network_name=config['broker']['network'], ip_allocation_mode=config['broker']['ip_allocation_mode'], storage_profile=config['broker']['storage_profile'], force_update=force_update, retain_temp_vapp=retain_temp_vapp, ssh_key=ssh_key, msg_update_callback=msg_update_callback) # if it's a PKS setup, setup NSX-T constructs if config.get('pks_config'): nsxt_servers = config['pks_config']['nsxt_servers'] wire_logger = NULL_LOGGER if log_wire: wire_logger = SERVER_NSXT_WIRE_LOGGER for nsxt_server in nsxt_servers: msg = f"Configuring NSX-T server ({nsxt_server.get('name')})" \ " for CSE. Please check install logs for details." msg_update_callback.general(msg) INSTALL_LOGGER.info(msg) nsxt_client = NSXTClient(host=nsxt_server.get('host'), username=nsxt_server.get('username'), password=nsxt_server.get('password'), logger_debug=INSTALL_LOGGER, logger_wire=wire_logger, http_proxy=nsxt_server.get('proxy'), https_proxy=nsxt_server.get('proxy'), verify_ssl=nsxt_server.get('verify')) setup_nsxt_constructs( nsxt_client=nsxt_client, nodes_ip_block_id=nsxt_server.get('nodes_ip_block_ids'), pods_ip_block_id=nsxt_server.get('pods_ip_block_ids'), ncp_boundary_firewall_section_anchor_id=nsxt_server.get( 'distributed_firewall_section_anchor_id') ) # noqa: E501 # Telemetry - Record successful install action record_user_action(CseOperation.SERVICE_INSTALL, telemetry_settings=config['service']['telemetry']) except Exception: msg_update_callback.error( "CSE Installation Error. Check CSE install logs") INSTALL_LOGGER.error("CSE Installation Error", exc_info=True) # Telemetry - Record failed install action record_user_action(CseOperation.SERVICE_INSTALL, status=OperationStatus.FAILED, telemetry_settings=config['service']['telemetry']) raise # TODO() need installation relevant exceptions for rollback finally: if client is not None: client.logout()
def _load_template_definition_from_catalog(self, msg_update_callback=utils.NullPrinter()): # noqa: E501 # NOTE: If `enable_tkg_plus` in the config file is set to false, # CSE server will skip loading the TKG+ template this will prevent # users from performing TKG+ related operations. msg = "Loading k8s template definition from catalog" logger.SERVER_LOGGER.info(msg) msg_update_callback.general_no_color(msg) client = None try: log_filename = None log_wire = \ utils.str_to_bool(self.config['service'].get('log_wire')) if log_wire: log_filename = logger.SERVER_DEBUG_WIRELOG_FILEPATH client = Client(self.config['vcd']['host'], api_version=self.config['vcd']['api_version'], verify_ssl_certs=self.config['vcd']['verify'], log_file=log_filename, log_requests=log_wire, log_headers=log_wire, log_bodies=log_wire) credentials = BasicLoginCredentials(self.config['vcd']['username'], server_constants.SYSTEM_ORG_NAME, # noqa: E501 self.config['vcd']['password']) client.set_credentials(credentials) is_tkg_plus_enabled = utils.is_tkg_plus_enabled(self.config) org_name = self.config['broker']['org'] catalog_name = self.config['broker']['catalog'] k8_templates = ltm.get_all_k8s_local_template_definition( client=client, catalog_name=catalog_name, org_name=org_name, logger_debug=logger.SERVER_LOGGER) if not k8_templates: msg = "No valid K8 templates were found in catalog " \ f"'{catalog_name}'. Unable to start CSE server." msg_update_callback.error(msg) logger.SERVER_LOGGER.error(msg) sys.exit(1) # Check that default k8s template exists in vCD at the correct # revision default_template_name = \ self.config['broker']['default_template_name'] default_template_revision = \ str(self.config['broker']['default_template_revision']) found_default_template = False for template in k8_templates: api_version = float(client.get_api_version()) if api_version >= float(vCDApiVersion.VERSION_35.value) and \ template[server_constants.LocalTemplateKey.KIND] == \ shared_constants.ClusterEntityKind.TKG_PLUS.value and \ not is_tkg_plus_enabled: # TKG+ is not enabled on CSE config. Skip the template and # log the relevant information. msg = "Skipping loading template data for " \ f"'{template[server_constants.LocalTemplateKey.NAME]}' as " \ "TKG+ is not enabled" # noqa: E501 logger.SERVER_LOGGER.debug(msg) k8_templates.remove(template) continue if str(template[server_constants.LocalTemplateKey.REVISION]) == default_template_revision and \ template[server_constants.LocalTemplateKey.NAME] == default_template_name: # noqa: E501 found_default_template = True msg = f"Found K8 template '{template['name']}' at revision " \ f"{template['revision']} in catalog '{catalog_name}'" msg_update_callback.general(msg) logger.SERVER_LOGGER.info(msg) if not found_default_template: msg = f"Default template {default_template_name} with " \ f"revision {default_template_revision} not found." \ " Unable to start CSE server." msg_update_callback.error(msg) logger.SERVER_LOGGER.error(msg) sys.exit(1) self.config['broker']['templates'] = k8_templates finally: if client: client.logout()
def _register_def_schema(client: Client, msg_update_callback=utils.NullPrinter(), log_wire=False): """Register defined entity interface and defined entity type. If vCD api version is >= 35, register the vCD api version based defined entity interface and defined entity type. Read the schema present in the location dictated by api version to register the defined entity type. :param pyvcloud.vcd.client.Client client: :param utils.ConsoleMessagePrinter msg_update_callback: Callback object. :param bool log_wire: wire logging enabled """ msg = "Registering defined entity schema" msg_update_callback.info(msg) INSTALL_LOGGER.debug(msg) logger_wire = SERVER_CLOUDAPI_WIRE_LOGGER if log_wire else NULL_LOGGER cloudapi_client = vcd_utils.get_cloudapi_client_from_vcd_client( client=client, # noqa: E501 logger_debug=INSTALL_LOGGER, # noqa: E501 logger_wire=logger_wire) # noqa: E501 schema_file = None try: def_utils.raise_error_if_def_not_supported(cloudapi_client) schema_svc = def_schema_svc.DefSchemaService(cloudapi_client) keys_map = def_utils.MAP_API_VERSION_TO_KEYS[float( client.get_api_version())] # noqa: E501 defKey = def_utils.DefKey native_interface = def_models.\ DefInterface(name=keys_map[defKey.INTERFACE_NAME], vendor=keys_map[defKey.VENDOR], nss=keys_map[defKey.INTERFACE_NSS], version=keys_map[defKey.INTERFACE_VERSION], # noqa: E501 readonly=False) msg = "" try: schema_svc.get_interface(native_interface.get_id()) msg = "defined entity interface already exists." \ " Skipping defined entity interface creation" except HTTPError: # TODO handle this part only if the interface was not found native_interface = schema_svc.create_interface(native_interface) msg = "Successfully created defined entity interface" msg_update_callback.general(msg) INSTALL_LOGGER.debug(msg) # TODO stop-gap fix - find efficient way to import schema import importlib import importlib.resources as pkg_resources schema_module = importlib.import_module( f'{def_utils.DEF_SCHEMA_DIRECTORY}.{keys_map[defKey.ENTITY_TYPE_SCHEMA_VERSION]}' ) # noqa: E501 schema_file = pkg_resources.open_text( schema_module, def_utils.DEF_ENTITY_TYPE_SCHEMA_FILE) # noqa: E501 native_entity_type = def_models.\ DefEntityType(name=keys_map[defKey.ENTITY_TYPE_NAME], description='', vendor=keys_map[defKey.VENDOR], nss=keys_map[defKey.ENTITY_TYPE_NSS], version=keys_map[defKey.ENTITY_TYPE_VERSION], schema=json.load(schema_file), interfaces=[native_interface.get_id()], readonly=False) msg = "" try: schema_svc.get_entity_type(native_entity_type.get_id()) msg = "defined entity type already exists." \ " Skipping defined entity type creation" except HTTPError: # TODO handle this part only if the entity type was not found native_entity_type = schema_svc.create_entity_type( native_entity_type) # noqa: E501 msg = "Successfully registered defined entity type" msg_update_callback.general(msg) INSTALL_LOGGER.debug(msg) except cse_exception.DefNotSupportedException: msg = "Skipping defined entity type and defined entity interface" \ " registration" msg_update_callback.general(msg) INSTALL_LOGGER.debug(msg) except Exception as e: msg = f"Error occured while registering defined entity schema: {str(e)}" # noqa: E501 msg_update_callback.error(msg) INSTALL_LOGGER.error(msg) raise (e) finally: try: schema_file.close() except Exception: pass
def run(self, msg_update_callback=utils.NullPrinter()): sysadmin_client = None try: sysadmin_client = vcd_utils.get_sys_admin_client() verify_version_compatibility(sysadmin_client, self.config['vcd']['api_version'], utils.should_use_mqtt_protocol(self.config)) # noqa: E501 except Exception as err: logger.SERVER_LOGGER.info(err) raise finally: if sysadmin_client: sysadmin_client.logout() if utils.should_use_mqtt_protocol(self.config): # Store/setup MQTT extension, api filter, and token info try: sysadmin_client = vcd_utils.get_sys_admin_client() mqtt_ext_manager = MQTTExtensionManager(sysadmin_client) ext_info = mqtt_ext_manager.get_extension_info( ext_name=server_constants.CSE_SERVICE_NAME, ext_version=server_constants.MQTT_EXTENSION_VERSION, ext_vendor=server_constants.MQTT_EXTENSION_VENDOR) ext_urn_id = ext_info[MQTTExtKey.EXT_URN_ID] ext_uuid = mqtt_ext_manager.get_extension_uuid(ext_urn_id) api_filters_status = mqtt_ext_manager.check_api_filters_setup( ext_uuid, configure_cse.API_FILTER_PATTERNS) if not api_filters_status: msg = 'MQTT Api filter is not set up' logger.SERVER_LOGGER.error(msg) raise cse_exception.MQTTExtensionError(msg) token_info = mqtt_ext_manager.create_extension_token( token_name=server_constants.MQTT_TOKEN_NAME, ext_urn_id=ext_urn_id) self.config['mqtt'].update(ext_info) self.config['mqtt'].update(token_info) self.config['mqtt'][MQTTExtKey.EXT_UUID] = ext_uuid except Exception as err: msg = f'MQTT extension setup error: {err}' logger.SERVER_LOGGER.error(msg) raise err finally: if sysadmin_client: sysadmin_client.logout() populate_vsphere_list(self.config['vcs']) # Load def entity-type and interface self._load_def_schema(msg_update_callback=msg_update_callback) # Read k8s catalog definition from catalog item metadata and append # the same to to server run-time config self._load_template_definition_from_catalog( msg_update_callback=msg_update_callback) self._load_placement_policy_details( msg_update_callback=msg_update_callback) if float(self.config['vcd']['api_version']) < float(vCDApiVersion.VERSION_35.value): # noqa: E501 # Read templates rules from config and update template deinfition # in server run-time config self._process_template_rules( msg_update_callback=msg_update_callback) # Make sure that all vms in templates are compliant with the # compute policy specified in template definition (can be affected # by rules). self._process_template_compute_policy_compliance( msg_update_callback=msg_update_callback) else: msg = "Template rules are not supported by CSE for vCD api " \ "version 35.0 or above. Skipping template rule processing." msg_update_callback.info(msg) logger.SERVER_LOGGER.debug(msg) if self.should_check_config: configure_cse.check_cse_installation( self.config, msg_update_callback=msg_update_callback) if self.config.get('pks_config'): pks_config = self.config.get('pks_config') self.pks_cache = PksCache( pks_servers=pks_config.get('pks_api_servers', []), pks_accounts=pks_config.get('pks_accounts', []), pvdcs=pks_config.get('pvdcs', []), orgs=pks_config.get('orgs', []), nsxt_servers=pks_config.get('nsxt_servers', [])) num_processors = self.config['service']['processors'] try: self.consumer = MessageConsumer(self.config, num_processors) name = server_constants.MESSAGE_CONSUMER_THREAD consumer_thread = Thread(name=name, target=consumer_thread_run, args=(self.consumer, )) consumer_thread.daemon = True consumer_thread.start() self.consumer_thread = consumer_thread msg = f"Started thread '{name}' ({consumer_thread.ident})" msg_update_callback.general(msg) logger.SERVER_LOGGER.info(msg) except KeyboardInterrupt: if self.consumer: self.consumer.stop() interrupt_msg = f"\nKeyboard interrupt when starting thread " \ f"'{name}'" logger.SERVER_LOGGER.debug(interrupt_msg) raise Exception(interrupt_msg) except Exception: if self.consumer: self.consumer.stop() logger.SERVER_LOGGER.error(traceback.format_exc()) # Updating state to Running before starting watchdog because watchdog # exits when server is not Running self._state = ServerState.RUNNING # Start consumer watchdog name = server_constants.WATCHDOG_THREAD consumer_watchdog = Thread(name=name, target=watchdog_thread_run, args=(self, num_processors)) consumer_watchdog.daemon = True consumer_watchdog.start() self._consumer_watchdog = consumer_watchdog msg = f"Started thread '{name}' ({consumer_watchdog.ident})" msg_update_callback.general(msg) logger.SERVER_LOGGER.info(msg) message = f"Container Service Extension for vCloud Director" \ f"\nServer running using config file: {self.config_file}" \ f"\nLog files: {logger.SERVER_INFO_LOG_FILEPATH}, " \ f"{logger.SERVER_DEBUG_LOG_FILEPATH}" \ f"\nwaiting for requests (ctrl+c to close)" signal.signal(signal.SIGINT, signal_handler) msg_update_callback.general_no_color(message) logger.SERVER_LOGGER.info(message) # Record telemetry on user action and details of operation. cse_params = { PayloadKey.WAS_DECRYPTION_SKIPPED: bool(self.skip_config_decryption), # noqa: E501 PayloadKey.WAS_PKS_CONFIG_FILE_PROVIDED: bool(self.pks_config_file), # noqa: E501 PayloadKey.WAS_INSTALLATION_CHECK_SKIPPED: bool(self.should_check_config) # noqa: E501 } record_user_action_details(cse_operation=CseOperation.SERVICE_RUN, cse_params=cse_params) record_user_action(cse_operation=CseOperation.SERVICE_RUN) while True: try: time.sleep(1) if self._state == ServerState.STOPPING and \ self.active_requests_count() == 0: break except KeyboardInterrupt: break except Exception: msg_update_callback.general_no_color( traceback.format_exc()) logger.SERVER_LOGGER.error(traceback.format_exc()) sys.exit(1) logger.SERVER_LOGGER.info("Stop detected") logger.SERVER_LOGGER.info("Closing connections...") self._state = ServerState.STOPPING try: self.consumer.stop() except Exception: logger.SERVER_LOGGER.error(traceback.format_exc()) self._state = ServerState.STOPPED logger.SERVER_LOGGER.info("Done")