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 core_utils.ConsoleMessagePrinter msg_update_callback: Callback object. # noqa: E501

    :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
示例#2
0
    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)
示例#3
0
def upload_tkgm_template(client,
                         ova_file_path,
                         catalog_name,
                         catalog_item_name,
                         org_name,
                         force,
                         logger=NULL_LOGGER,
                         msg_update_callback=utils.NullPrinter()):
    """."""
    org = vcd_utils.get_org(client, org_name=org_name)
    if vcd_utils.catalog_item_exists(org=org,
                                     catalog_name=catalog_name,
                                     catalog_item_name=catalog_item_name):
        if not force:
            msg = f"Catalog item '{catalog_item_name}' already exists " \
                  f"in catalog '{catalog_name}'"
            msg_update_callback.info(msg)
            logger.info(msg)
            return False
        else:
            msg = f"Deleting catalog item '{catalog_item_name}' " \
                  f"from catalog '{catalog_name}'"
            msg_update_callback.general(msg)
            logger.info(msg)
            org.delete_catalog_item(catalog_name, catalog_item_name)
            org.reload()

    vcd_utils.upload_ova_to_catalog(client=client,
                                    source_filepath=ova_file_path,
                                    catalog_name=catalog_name,
                                    catalog_item_name=catalog_item_name,
                                    org=org,
                                    logger=logger,
                                    msg_update_callback=msg_update_callback)
    return True
示例#4
0
 def _load_placement_policy_details(
         self, msg_update_callback=utils.NullPrinter()):
     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(api_version=None)
         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
示例#5
0
def get_valid_k8s_local_template_definition(
    client,
    catalog_name,
    org=None,
    org_name=None,
    legacy_mode=False,
    is_tkg_plus_enabled=False,
    logger_debug=logger.NULL_LOGGER,
    msg_update_callback=utils.NullPrinter()):  # noqa: E501
    """Get valid templates as per the current server configuration.

    :param pyvcloud.vcd.Client client: A sys admin client to be used to
        retrieve metadata off the catalog items.
    :param str catalog_name: Name of the catalog where the template resides.
    :param pyvcloud.vcd.Org org: Org object which hosts the catalog.
    :param str org_name: Name of the org that is hosting the catalog. Can be
        provided in lieu of param org, however param org takes precedence.
    :param bool legacy_mode: boolean indicating if the server is configured
        in legacy_mode
    :param bool is_tkg_plus_enabled: Boolean indicating if server is configured
        to work with TKG+
    :param logging.Logger logger_debug: logger to log the results or
        exceptions.
    :param utils.NullPrinter msg_update_callback:

    :return: list of dictionaries containing template data
    :rtype: list of dicts
    """
    all_templates = get_all_k8s_local_template_definition(
        client=client,
        catalog_name=catalog_name,
        org=org,
        org_name=org_name,
        legacy_mode=legacy_mode,
        logger_debug=logger_debug,
        msg_update_callback=msg_update_callback)
    for template in all_templates:
        if not legacy_mode and \
                template[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[LocalTemplateKey.NAME]}' as TKG+ is not enabled"
            logger_debug.debug(msg)
            all_templates.remove(template)
            continue
        msg = f"Found K8 template '{template['name']}' at revision " \
              f"{template['revision']} in catalog '{catalog_name}'"
        msg_update_callback.general(msg)
        logger_debug.info(msg)
    return all_templates
def read_native_template_definition_from_catalog(
        config: ServerConfig,
        msg_update_callback=utils.NullPrinter()
):
    # 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(config.get_value_at('service.log_wire'))
        if log_wire:
            log_filename = logger.SERVER_DEBUG_WIRELOG_FILEPATH

        client = Client(
            uri=config.get_value_at('vcd.host'),
            api_version=config.get_value_at('service.default_api_version'),  # noqa: E501
            verify_ssl_certs=config.get_value_at('vcd.verify'),
            log_file=log_filename,
            log_requests=log_wire,
            log_headers=log_wire,
            log_bodies=log_wire
        )
        credentials = BasicLoginCredentials(
            config.get_value_at('vcd.username'),
            shared_constants.SYSTEM_ORG_NAME,
            config.get_value_at('vcd.password')
        )
        client.set_credentials(credentials)

        legacy_mode = config.get_value_at('service.legacy_mode')
        org_name = config.get_value_at('broker.org')
        catalog_name = config.get_value_at('broker.catalog')

        k8_templates = ltm.get_valid_k8s_local_template_definition(
            client=client, catalog_name=catalog_name, org_name=org_name,
            legacy_mode=legacy_mode,
            is_tkg_plus_enabled=server_utils.is_tkg_plus_enabled(config),
            logger_debug=logger.SERVER_LOGGER,
            msg_update_callback=msg_update_callback)

        return k8_templates
    finally:
        if client:
            client.logout()
def read_tkgm_template_definition_from_catalog(
        config: ServerConfig,
        msg_update_callback=utils.NullPrinter()
):
    msg = "Loading TKGm 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(
            config.get_value_at('service.log_wire')
        )
        if log_wire:
            log_filename = logger.SERVER_DEBUG_WIRELOG_FILEPATH

        client = Client(
            uri=config.get_value_at('vcd.host'),
            api_version=config.get_value_at('service.default_api_version'),  # noqa: E501
            verify_ssl_certs=config.get_value_at('vcd.verify'),
            log_file=log_filename,
            log_requests=log_wire,
            log_headers=log_wire,
            log_bodies=log_wire
        )
        credentials = BasicLoginCredentials(
            config.get_value_at('vcd.username'),
            shared_constants.SYSTEM_ORG_NAME,
            config.get_value_at('vcd.password')
        )
        client.set_credentials(credentials)

        org_name = config.get_value_at('broker.org')
        catalog_name = config.get_value_at('broker.catalog')

        tkgm_templates = ttm.read_all_tkgm_template(
            client=client,
            org_name=org_name,
            catalog_name=catalog_name,
            logger=logger.SERVER_LOGGER,
            msg_update_callback=msg_update_callback
        )

        return tkgm_templates
    finally:
        if client:
            client.logout()
示例#8
0
def read_all_tkgm_template(client,
                           org_name,
                           catalog_name,
                           logger=NULL_LOGGER,
                           msg_update_callback=utils.NullPrinter()):
    """."""
    org = vcd_utils.get_org(client, org_name=org_name)
    catalog_item_names = [
        entry['name'] for entry in org.list_catalog_items(catalog_name)
    ]

    templates = []
    for item_name in catalog_item_names:
        md = org.get_all_metadata_from_catalog_item(catalog_name=catalog_name,
                                                    item_name=item_name)
        metadata_dict = metadata_to_dict(md)
        filtered_dict = {}
        kind = metadata_dict.get(server_constants.TKGmTemplateKey.KIND)
        if kind == shared_constants.ClusterEntityKind.TKG_M.value:
            msg = f"Found catalog item `{item_name}`."
            logger.debug(msg)
            msg_update_callback.general(msg)
            for entry in server_constants.TKGmTemplateKey:
                key = entry.value
                value = metadata_dict.get(key, '')
                # If catalog item has been renamed directly in VCD,
                # update the `name` metadata field.
                if entry == server_constants.TKGmTemplateKey.NAME and value != item_name:  # noqa: E501
                    msg = f"Template `{value}` has been " \
                          "renamed outside of CSE. Attempting to fix."
                    logger.debug(msg)
                    msg_update_callback.general(msg)
                    org.set_metadata_on_catalog_item(catalog_name=catalog_name,
                                                     item_name=item_name,
                                                     key=key,
                                                     value=item_name)
                    value = item_name
                filtered_dict[key] = value
            msg = f"Template `{item_name}` successfully loaded."
            logger.debug(msg)
            msg_update_callback.general(msg)
            templates.append(filtered_dict)

    return templates
    def __init__(
            self,
            remote_template_cookbook_url,
            legacy_mode: bool = False,
            cookbook_version=None,
            logger: logging.Logger = NULL_LOGGER,
            msg_update_callback: Union[utils.NullPrinter, utils.ConsoleMessagePrinter] = utils.NullPrinter()  # noqa: E501
    ):
        """.

        :param str remote_template_cookbook_url:
        :param bool legacy_mode:
        :param semantic_version.Version cookbook_version: Use this parameter
            to optionally set the value for cookbook_version. This value is
            automatically filled by get_filtered_cookbook() or
            get_unfiltered_cookbook()
        :param logging.Logger logger: logger to log with.
        :param utils.ConsoleMessagePrinter msg_update_callback:
            Callback object.
        """
        self.legacy_mode = legacy_mode
        self.url = remote_template_cookbook_url
        self.logger = logger
        self.msg_update_callback = msg_update_callback
        self.filtered_cookbook = None
        self.unfiltered_cookbook = None
        self.cookbook_version: semantic_version.Version = cookbook_version
        self.scripts_directory_path: Optional[str] = None
示例#10
0
    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(api_version=None)
            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[server_constants.LegacyLocalTemplateKey.
                                       COMPUTE_POLICY]  # noqa: E501
                catalog_item_name = template[
                    server_constants.LegacyLocalTemplateKey.
                    CATALOG_ITEM_NAME]  # noqa: E501
                # if policy name is not empty, stamp it on the template
                if policy_name:
                    try:
                        policy = \
                            compute_policy_manager.get_cse_vdc_compute_policy(
                                cpm, policy_name)  # noqa: E501
                    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 = \
                            compute_policy_manager.add_cse_vdc_compute_policy(
                                cpm,
                                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_vdc_sizing_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_vdc_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()
示例#11
0
    def _load_template_definition_from_catalog(
        self, msg_update_callback=utils.NullPrinter()):
        # 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

            # Since the config param has been read from file by
            # get_validated_config method, we can safely use the
            # default_api_version key, it will be set to the highest api
            # version supported by VCD and CSE.
            client = Client(
                self.config['vcd']['host'],
                api_version=self.config['service']['default_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'],
                shared_constants.SYSTEM_ORG_NAME,  # noqa: E501
                self.config['vcd']['password'])
            client.set_credentials(credentials)

            is_tkg_plus_enabled = server_utils.is_tkg_plus_enabled(self.config)
            legacy_mode = self.config['service']['legacy_mode']
            org_name = self.config['broker']['org']
            catalog_name = self.config['broker']['catalog']
            k8_templates = ltm.get_valid_k8s_local_template_definition(
                client=client,
                catalog_name=catalog_name,
                org_name=org_name,
                legacy_mode=legacy_mode,
                is_tkg_plus_enabled=is_tkg_plus_enabled,
                logger_debug=logger.SERVER_LOGGER,
                msg_update_callback=msg_update_callback)

            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[server_constants.LocalTemplateKey.REVISION]) == default_template_revision and \
                        template[server_constants.LocalTemplateKey.NAME] == default_template_name:  # noqa: E501
                    found_default_template = True

            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()
示例#12
0
    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.NullMessagePrinter msg_update_callback:
        """
        sysadmin_client = None
        try:
            sysadmin_client = vcd_utils.get_sys_admin_client(api_version=None)
            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)

            server_rde_version = server_utils.get_rde_version_in_use()
            msg_update_callback.general(
                f"Using RDE version: {server_rde_version}")  # noqa: E501

            schema_svc = def_schema_svc.DefSchemaService(cloudapi_client)
            def_metadata_dict: dict = def_utils.get_rde_metadata(
                server_rde_version)  # noqa: E501
            entity_type: common_models.DefEntityType = \
                def_metadata_dict[def_constants.RDEMetadataKey.ENTITY_TYPE]  # noqa: E501
            interfaces: List[common_models.DefInterface] = \
                def_metadata_dict[def_constants.RDEMetadataKey.INTERFACES]  # noqa: E501

            for interface in interfaces:
                # TODO change _kubernetesInterface to an array once additional
                # interface for CSE is added.
                self._kubernetesInterface = \
                    schema_svc.get_interface(interface.get_id())

            self._nativeEntityType = \
                schema_svc.get_entity_type(entity_type.get_id())

            msg = f"Successfully loaded defined entity schema " \
                  f"{entity_type.get_id()} 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.error_message}"  # noqa: E501
            msg_update_callback.error(msg)
            logger.SERVER_LOGGER.debug(msg)
            raise
        except Exception as e:
            msg = f"Failed to load defined entity schema to global context: {str(e)}"  # noqa: E501
            msg_update_callback.error(msg)
            logger.SERVER_LOGGER.error(msg)
            raise
        finally:
            if sysadmin_client:
                sysadmin_client.logout()
示例#13
0
    def run(self, msg_update_callback=utils.NullPrinter()):
        sysadmin_client = None
        try:
            sysadmin_client = vcd_utils.get_sys_admin_client(api_version=None)
            verify_version_compatibility(
                sysadmin_client,
                should_cse_run_in_legacy_mode=self.config['service']
                ['legacy_mode'],  # noqa: E501
                is_mqtt_extension=server_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 server_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(api_version=None)
                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[server_constants.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.setup_extension_token(
                    token_name=server_constants.MQTT_TOKEN_NAME,
                    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_urn_id)

                self.config['mqtt'].update(ext_info)
                self.config['mqtt'].update(token_info)
                self.config['mqtt'][server_constants.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 self.config['service']['legacy_mode']:
            # Read templates rules from config and update template definition
            # 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']
        name = server_constants.MESSAGE_CONSUMER_THREAD
        try:
            self.consumer = MessageConsumer(self.config, num_processors)
            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")
示例#14
0
def get_all_k8s_local_template_definition(
    client,
    catalog_name,
    org=None,
    org_name=None,
    legacy_mode=False,
    logger_debug=logger.NULL_LOGGER,
    msg_update_callback=utils.NullPrinter()):  # noqa: E501
    """Fetch all CSE k8s templates in a catalog.

    A CSE k8s template is a catalog item that has all the necessary metadata
    stamped onto it. If only partial metadata is present on a catalog item,
    that catalog item will be disqualified from the result.

    :param pyvcloud.vcd.Client client: A sys admin client to be used to
        retrieve metadata off the catalog items.
    :param str catalog_name: Name of the catalog where the template resides.
    :param pyvcloud.vcd.Org org: Org object which hosts the catalog.
    :param str org_name: Name of the org that is hosting the catalog. Can be
        provided in lieu of param org, however param org takes precedence.
    :param bool legacy_mode: True, if CSE is running in legacy mode
    :param logging.Logger logger_debug:
    :param utils.NullPrinter msg_update_callback:

    :return: list of dictionaries containing template data

    :rtype: list of dicts
    """
    if not org:
        org = get_org(client, org_name=org_name)
    catalog_item_names = [
        entry['name'] for entry in org.list_catalog_items(catalog_name)
    ]
    templates = []

    # Select the right Key enum based on legacy_mode flag
    localTemplateKey = LocalTemplateKey
    if legacy_mode:
        # if template is loaded in legacy mode, make sure to avoid the keys
        # min_cse_version and max_cse_version
        localTemplateKey = LegacyLocalTemplateKey

    for item_name in catalog_item_names:
        md = org.get_all_metadata_from_catalog_item(catalog_name=catalog_name,
                                                    item_name=item_name)
        metadata_dict = metadata_to_dict(md)

        # if catalog item doesn't have all the required metadata keys,
        # CSE should not recognize it as a template
        expected_metadata_keys = \
            set([entry.value for entry in localTemplateKey])
        missing_metadata_keys = expected_metadata_keys - metadata_dict.keys()
        num_missing_metadata_keys = len(missing_metadata_keys)
        if num_missing_metadata_keys == len(expected_metadata_keys):
            # This catalog item has no CSE related metadata, so skip it.
            continue
        if num_missing_metadata_keys > 0:
            # This catalog item has partial CSE metadata, so skip it but also
            # log relevant information.
            msg = f"Catalog item '{item_name}' missing " \
                  f"{num_missing_metadata_keys} metadata: " \
                  f"{missing_metadata_keys}"  # noqa: F841
            logger_debug.debug(msg)
            msg_update_callback.info(msg)
            continue

        if not legacy_mode:
            # Do not load the template in non-legacy_mode if
            # min_cse_version and max_cse_version are not present
            # in the metadata_dict
            curr_cse_version = server_utils.get_installed_cse_version()
            valid_cse_versions = semantic_version.SimpleSpec(
                f">={metadata_dict[localTemplateKey.MIN_CSE_VERSION]},"
                f"<={metadata_dict[localTemplateKey.MAX_CSE_VERSION]}")
            if not valid_cse_versions.match(curr_cse_version):
                template_name = \
                    metadata_dict.get(localTemplateKey.NAME, "Unknown")
                template_revision = \
                    metadata_dict.get(localTemplateKey.REVISION, "Unknown")
                msg = f"Template '{template_name}' at " \
                      f"revision '{template_revision}' exists but is " \
                      f"not valid for CSE {curr_cse_version}"
                logger_debug.debug(msg)
                msg_update_callback.info(msg)
                continue

        # non-string metadata is written to the dictionary as a string
        # when 'upgrade_from' metadata is empty, vcd returns it as: "['']"
        # when 'upgrade_from' metadata is not empty, vcd returns it as an array
        # coerce "['']" to the more usable empty array []
        if isinstance(metadata_dict[localTemplateKey.UPGRADE_FROM], str):
            metadata_dict[localTemplateKey.UPGRADE_FROM] = \
                ast.literal_eval(metadata_dict[localTemplateKey.UPGRADE_FROM])
        if metadata_dict[localTemplateKey.UPGRADE_FROM] == ['']:
            metadata_dict[localTemplateKey.UPGRADE_FROM] = []

        templates.append(metadata_dict)

    return templates
示例#15
0
    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 = server_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()