Ejemplo n.º 1
0
class DomainResourceExtractor:
    """
    Create a domain resource file for use with Kubernetes deployment.
    """
    _class_name = "DomainResourceExtractor"

    # the name field keys corresponding to named model elements
    NAME_KEY_MAP = {CHANNELS: 'channelName', CLUSTERS: CLUSTER_NAME}

    def __init__(self, model, model_context, aliases, logger):
        self._model = model
        self._model_context = model_context
        self._aliases = AliasHelper(aliases, logger, ExceptionType.DEPLOY)
        self._logger = logger
        return

    def extract(self):
        """
        Deploy resource model elements at the domain level, including multi-tenant elements.
        """
        _method_name = 'extract'

        resource_file = self._model_context.get_domain_resource_file()
        self._logger.info("WLSDPLY-10000",
                          resource_file,
                          method_name=_method_name,
                          class_name=self._class_name)

        # create the target file directory, if needed
        resource_dir = File(resource_file).getParentFile()
        if (not resource_dir.isDirectory()) and (not resource_dir.mkdirs()):
            mkdir_ex = exception_helper.create_deploy_exception(
                'WLSDPLY-10001', resource_dir)
            raise mkdir_ex

        # build the resource file structure from the kubernetes section of the model
        resource_dict = self._create_domain_resource_dictionary()

        # revise the resource file structure with values from command line, and elsewhere in model
        self._update_resource_dictionary(resource_dict)

        # write the resource file structure to the output file
        writer = PythonToFile(resource_dict)

        writer.write_to_file(resource_file)
        return

    def _create_domain_resource_dictionary(self):
        """
        Build the resource file structure from the kubernetes section of the model.
        :return: the resource file structure
        """
        kubernetes_map = self._model.get_model_kubernetes()

        resource_dict = PyOrderedDict()

        attribute_location = self._aliases.get_model_section_attribute_location(
            KUBERNETES)
        top_attributes_map = self._aliases.get_model_attribute_names_and_types(
            attribute_location)
        top_folders = self._aliases.get_model_section_top_level_folder_names(
            KUBERNETES)

        self._process_fields(kubernetes_map, top_folders, top_attributes_map,
                             LocationContext(), resource_dict)
        return resource_dict

    def _process_fields(self, model_dict, folder_names, attributes_map,
                        location, target_dict):
        """
        Transfer folders and attributes from the model dictionary to the target domain resource dictionary.
        For the top level, the folders and attributes are not derived directly from the location.
        :param model_dict: the source model dictionary
        :param folder_names: the names of the folders at this location
        :param attributes_map: the map of attribute names to types for this location
        :param location: the location used for alias processing
        :param target_dict: the target dictionary for the domain resource file.
       """
        for key, model_value in model_dict.items():
            if key in attributes_map.keys():
                type_name = attributes_map[key]
                target_dict[key] = _get_target_value(model_value, type_name)

            elif key in folder_names:
                child_location = LocationContext(location).append_location(key)

                if self._aliases.supports_multiple_mbean_instances(
                        child_location):
                    target_dict[key] = self._build_dictionary_list(
                        key, model_value, child_location)
                else:
                    if key not in target_dict:
                        target_dict[key] = PyOrderedDict()
                    target_child_dict = target_dict[key]
                    self._process_location_fields(model_value, child_location,
                                                  target_child_dict)
        return

    def _process_location_fields(self, model_dict, location, target_dict):
        """
        Transfer folders and attributes from the model dictionary to the target domain resource dictionary.
        Below the top level, the folders and attributes can be derived from the location.
        :param model_dict: the source model dictionary
        :param location: the location used for alias processing
        :param target_dict: the target dictionary for the domain resource file.
       """
        attributes_map = self._aliases.get_model_attribute_names_and_types(
            location)
        folder_names = self._aliases.get_model_subfolder_names(location)
        self._process_fields(model_dict, folder_names, attributes_map,
                             location, target_dict)
        return

    def _build_dictionary_list(self, model_key, name_dictionary, location):
        """
        Build a dictionary list object based on the name dictionary and location.
        :param name_dictionary: a dictionary containing named levels
        :param location: the location used for alias resolution
        :return:
        """
        child_list = DictionaryList()
        for name in name_dictionary:
            model_named_dict = name_dictionary[name]
            name_key = self._get_name_key(model_key)
            target_list_dict = PyOrderedDict()
            target_list_dict[name_key] = name
            self._process_location_fields(model_named_dict, location,
                                          target_list_dict)
            child_list.append(target_list_dict)
        return child_list

    def _get_name_key(self, key):
        """
        Return the key to be used for the name in a dictionary list element.
        :param key: the folder key in the model
        :return: the name key
        """
        key = dictionary_utils.get_element(self.NAME_KEY_MAP, key)
        if key is not None:
            return key
        return 'name'

    def _update_resource_dictionary(self, resource_dict):
        """
        Revise the resource file structure with values from defaults, command line, and elsewhere in model
        :param resource_dict: the resource file dictionary
        """
        _method_name = '_update_resource_dictionary'

        # add API version if not present
        if API_VERSION not in resource_dict:
            resource_dict[API_VERSION] = DEFAULT_API_VERSION

        # add kind if not present
        if KIND not in resource_dict:
            resource_dict[KIND] = DEFAULT_KIND

        # add a metadata section if not present, since we'll at least add name
        if METADATA not in resource_dict:
            resource_dict[METADATA] = PyOrderedDict()
        metadata_section = resource_dict[METADATA]

        # if metadata name not present, use the domain name from the model, or default
        if K_NAME not in metadata_section:
            domain_name = dictionary_utils.get_element(
                self._model.get_model_topology(), NAME)
            if domain_name is None:
                domain_name = DEFAULT_WLS_DOMAIN_NAME
            metadata_section[K_NAME] = domain_name

        # add a spec section if not present, since we'll at least add domain home
        if SPEC not in resource_dict:
            resource_dict[SPEC] = PyOrderedDict()
        spec_section = resource_dict[SPEC]

        # only set domain home if it is not present in spec section
        if DOMAIN_HOME not in spec_section:
            spec_section[DOMAIN_HOME] = self._model_context.get_domain_home()

        # only set image if it is not present in spec section
        if IMAGE not in spec_section:
            spec_section[IMAGE] = DEFAULT_IMAGE

        # imagePullSecrets is required unless imagePullPolicy is Never
        pull_secrets_required = True
        if IMAGE_PULL_POLICY in spec_section:
            policy = str(spec_section[IMAGE_PULL_POLICY])
            pull_secrets_required = (policy != NEVER)

        # if imagePullSecrets required and not present, add a list with one FIX ME value
        if pull_secrets_required and (IMAGE_PULL_SECRETS not in spec_section):
            secrets_list = DictionaryList()
            secrets_list.append({'name': DEFAULT_IMAGE_PULL_SECRETS})
            spec_section[IMAGE_PULL_SECRETS] = secrets_list

        # if webLogicCredentialsSecret not present, add it using the FIX ME value
        if WEBLOGIC_CREDENTIALS_SECRET not in spec_section:
            spec_section[
                WEBLOGIC_CREDENTIALS_SECRET] = DEFAULT_WEBLOGIC_CREDENTIALS_SECRET

        # only update clusters if section is not present in spec section
        if CLUSTERS not in spec_section:
            topology = self._model.get_model_topology()
            model_clusters = dictionary_utils.get_dictionary_element(
                topology, CLUSTER)
            if len(model_clusters) > 0:
                cluster_list = DictionaryList()
                spec_section[CLUSTERS] = cluster_list
                for cluster_name, cluster_values in model_clusters.items():
                    server_count = k8s_helper.get_server_count(
                        cluster_name, cluster_values, self._model.get_model())
                    cluster_dict = PyOrderedDict()
                    cluster_dict[CLUSTER_NAME] = cluster_name
                    cluster_dict[REPLICAS] = server_count

                    self._logger.info("WLSDPLY-10002",
                                      cluster_name,
                                      server_count,
                                      method_name=_method_name,
                                      class_name=self._class_name)
                    cluster_list.append(cluster_dict)
        return
Ejemplo n.º 2
0
class ModelHelpPrinter(object):
    """
    Class for printing the recognized model metadata to STDOUT.
    """
    def __init__(self, aliases, logger):
        """
        :param aliases: A reference to an Aliases class instance
        :param logger: A reference to the platform logger to write to, if a log entry needs to be made
        """
        self._logger = logger
        self._alias_helper = AliasHelper(aliases, self._logger,
                                         ExceptionType.CLA)

    def print_model_help(self, model_path, control_option, as_sample=False):
        """
        Prints out the help information for a given '''model_path'''. '''model_path''' needs to be specified
        using the following pattern:

                <model_section>[:/<section_folder>[/<section_subfolder>|...]]

        Examples:
                'domainInfo', 'domainInfo:' or 'domainInfo:/' (all 3 are equivalent)
                'topology:/Server'
                'resources:/JDBCSystemResource/JdbcResource/JDBCDriverParams'
                'appDeployments:/Application'

        :param model_path: a formatted string containing the model path
        :param control_option: a command-line switch that controls what is output
        :param as_sample: specifies that a model sample should be output
        :raises CLAException: if a problem is encountered
        """

        # print filter information, if not NORMAL
        if control_option == ControlOptions.RECURSIVE:
            print
            print _format_message('WLSDPLY-10102')
        elif control_option == ControlOptions.FOLDERS_ONLY:
            print
            print _format_message('WLSDPLY-10103')
        elif control_option == ControlOptions.ATTRIBUTES_ONLY:
            print
            print _format_message('WLSDPLY-10104')

        model_path_tokens = self._parse_model_path(model_path)

        section_name = model_path_tokens[0]
        valid_section_folder_keys = self._alias_helper.get_model_section_top_level_folder_names(
            section_name)

        if as_sample:
            sample_printer = ModelSamplePrinter(self._alias_helper,
                                                self._logger)
            sample_printer.print_model_sample(model_path_tokens,
                                              control_option)
        else:
            if model_path_tokens[0] == 'top':
                self._print_model_top_level_help()
            elif len(model_path_tokens) == 1:
                self._print_model_section_help(section_name,
                                               valid_section_folder_keys,
                                               control_option)
            else:
                self._print_model_folder_help(model_path_tokens,
                                              valid_section_folder_keys,
                                              control_option)

    def _parse_model_path(self, model_path):
        """
        Parse the specified model_path into a Python list of elements.
        The first element will be the section name.
        If not specified, the section name will be derived from the first path element.
        :param model_path: a string contain the model path to parse
        :return: a python list containing the section name, then each folder element
        :raises: CLAException if the path cannot be parsed, or the section name is invalid
        """
        _method_name = '_parse_model_path'

        self._logger.entering(model_path,
                              class_name=_class_name,
                              method_name=_method_name)

        match = MODEL_PATH_PATTERN.match(model_path)
        if match is None:
            ex = exception_helper.create_cla_exception('WLSDPLY-10108',
                                                       model_path)
            self._logger.throwing(ex,
                                  class_name=_class_name,
                                  method_name=_method_name)
            raise ex

        self._logger.finest('match.group(1)={0}, match.group(2)={1}',
                            str(match.group(1)),
                            str(match.group(2)),
                            class_name=_class_name,
                            method_name=_method_name)

        section = match.group(1)
        path = match.group(2)

        folders = []
        if path is not None:
            for folder in path.split('/'):
                # trailing or duplicate slashes may cause empty folder name
                if len(folder) > 0:
                    folders.append(folder)

        if section is None:
            section = self._get_section_for_folder_list(folders)

        section = section.replace(':', '')

        top_level_keys = model.get_model_top_level_keys()

        # 'top' is a special case for listing the sections
        all_section_keys = ['top']
        all_section_keys.extend(top_level_keys)

        if section not in all_section_keys:
            ex = exception_helper.create_cla_exception(
                'WLSDPLY-10109', section, str(', '.join(top_level_keys)))
            self._logger.throwing(ex,
                                  class_name=_class_name,
                                  method_name=_method_name)
            raise ex

        model_path_tokens = [section]
        model_path_tokens.extend(folders)
        return model_path_tokens

    def _get_section_for_folder_list(self, folder_list):
        """
        Derive the section name from the first folder in the specified list.
        :param folder_list: the list of folders in the model path
        :return: the section name
        :raises: CLAException if the section name cannot be determined
        """
        _method_name = '_get_section_for_folder_list'
        top_folder = folder_list[0]

        for section in KNOWN_TOPLEVEL_MODEL_SECTIONS:
            folder_keys = self._alias_helper.get_model_section_top_level_folder_names(
                section)
            if top_folder in folder_keys:
                return section

        ex = exception_helper.create_cla_exception('WLSDPLY-10101', top_folder)
        self._logger.throwing(ex,
                              class_name=_class_name,
                              method_name=_method_name)
        raise ex

    def _print_model_top_level_help(self):
        """
        Prints out all the valid section names for a model.
        The -recursive flag is disregarded for this case.
        """
        _method_name = '_print_model_top_level_help'
        self._logger.finest('sections={0}',
                            KNOWN_TOPLEVEL_MODEL_SECTIONS,
                            class_name=_class_name,
                            method_name=_method_name)

        title = _format_message('WLSDPLY-10113')

        # Print 'All Sections:' header
        print
        _print_indent(title, 0)
        print

        for section in KNOWN_TOPLEVEL_MODEL_SECTIONS:
            _print_indent(section, 1)

    def _print_model_section_help(self, section_name,
                                  valid_section_folder_keys, control_option):
        """
        Prints the help for a section of a model, when just the section_name[:] is provided
        :param section_name: the name of the model section
        :param valid_section_folder_keys: list of the valid top folders in the specified section
        :param control_option: A command-line switch that controls what is output to STDOUT
        """
        _method_name = '_print_model_section_help'

        self._logger.finest('1 section_name={0}',
                            section_name,
                            class_name=_class_name,
                            method_name=_method_name)

        location_path = '%s:' % section_name
        self._logger.finest('1 location_path={0}',
                            location_path,
                            class_name=_class_name,
                            method_name=_method_name)

        model_section = _format_message('WLSDPLY-10106', location_path)

        # Print 'Section: <model_section>' label and field
        print
        _print_indent(model_section, 0)

        if model_help_utils.show_attributes(control_option):
            attributes_location = self._alias_helper.get_model_section_attribute_location(
                section_name)
            if attributes_location is not None:
                self._print_attributes_help(attributes_location, 1)

        if model_help_utils.show_folders(control_option):
            print
            _print_indent(_format_message('WLSDPLY-10107'), 1)
            valid_section_folder_keys.sort()

            for section_folder_key in valid_section_folder_keys:
                _print_indent(section_folder_key, 2)

                if control_option == ControlOptions.RECURSIVE:
                    model_location = LocationContext().append_location(
                        section_folder_key)
                    self._print_subfolders_help(model_location, control_option,
                                                2)

    def _print_model_folder_help(self, model_path_tokens,
                                 valid_section_folder_keys, control_option):
        """
        Prints the help for a folder in a model, when more than just the section_name[:] is provided.
        The section name in the first token was already validated.
        :param model_path_tokens: a Python list of path elements built from model path
        :param valid_section_folder_keys: A list of valid folder names for the model section in the path
        :param control_option: A command-line switch that controls what is output to STDOUT
        """
        _method_name = '_print_model_folder_help'

        self._logger.finest(
            '1 model_path_tokens={0}, control_option={1}, valid_section_folder_keys={0}',
            str(model_path_tokens),
            ControlOptions.from_value(control_option),
            str(valid_section_folder_keys),
            class_name=_class_name,
            method_name=_method_name)

        section_name = model_path_tokens[0]
        top_folder = model_path_tokens[1]
        if top_folder not in valid_section_folder_keys:
            ex = exception_helper.create_cla_exception(
                'WLSDPLY-10110', section_name + ':', top_folder,
                ', '.join(valid_section_folder_keys))
            self._logger.throwing(ex,
                                  class_name=_class_name,
                                  method_name=_method_name)
            raise ex

        # Populate the location context using model_path_tokens[1:]
        model_location = LocationContext()
        for folder_key in model_path_tokens[1:]:
            model_location.append_location(folder_key)
            name_token = self._alias_helper.get_name_token(model_location)
            if name_token is not None:
                model_location.add_name_token(name_token, '%s-0' % folder_key)

            self._logger.finest('2 model_location={0}',
                                model_location,
                                class_name=_class_name,
                                method_name=_method_name)

        folder_path = '/'.join(model_path_tokens[1:])
        model_path = _format_message('WLSDPLY-10105',
                                     '%s:/%s' % (section_name, folder_path))
        type_name = self._get_folder_type_name(model_location)
        if type_name is not None:
            model_path += " (" + type_name + ")"

        # Print 'Path: <model_section>' header
        print
        _print_indent(model_path, 0)

        if model_help_utils.show_attributes(control_option):
            # Print the attributes associated with location context
            self._print_attributes_help(model_location, 1)

        if model_help_utils.show_folders(control_option):
            # Print the folders associated with location context
            print
            _print_indent(_format_message('WLSDPLY-10107'), 1)
            self._print_subfolders_help(model_location, control_option, 1)

        self._logger.exiting(class_name=_class_name, method_name=_method_name)
        return

    def _print_subfolders_help(self, model_location, control_option,
                               indent_level):
        """
        Prints the help for the folders in a model location, without header or leading space.
        :param model_location: the model location being worked on
        :param control_option: a command-line switch that controls what is output to STDOUT
        :param indent_level: the level to indent by, before printing output
        """
        _method_name = '_print_subfolders_help'

        valid_subfolder_keys = self._alias_helper.get_model_subfolder_names(
            model_location)
        self._logger.finest(
            '3 aliases.get_model_subfolder_names(model_location) returned: {0}',
            str(valid_subfolder_keys),
            class_name=_class_name,
            method_name=_method_name)

        if not valid_subfolder_keys:
            return

        valid_subfolder_keys.sort()

        for key in valid_subfolder_keys:
            model_location.append_location(key)
            name_token = self._alias_helper.get_name_token(model_location)
            if name_token is not None:
                model_location.add_name_token(name_token, '%s-0' % key)

            self._logger.finest('3 model_location={0}',
                                model_location,
                                class_name=_class_name,
                                method_name=_method_name)

            text = key
            type_name = self._get_folder_type_name(model_location)
            if type_name is not None:
                text += " (" + type_name + ")"

            _print_indent(text, indent_level + 1)

            if control_option == ControlOptions.RECURSIVE:
                # Call this method recursively
                self._print_subfolders_help(model_location, control_option,
                                            indent_level + 1)

            model_location.pop_location()

    def _print_attributes_help(self, model_location, indent_level):
        """
        Prints out the help for the attributes in a model location
        :param model_location: An object containing data about the model location being worked on
        :param indent_level: The level to indent by, before printing output
        """
        _method_name = '_print_attributes_help'

        attr_infos = self._alias_helper.get_model_attribute_names_and_types(
            model_location)
        self._logger.finer('WLSDPLY-05012',
                           str(model_location),
                           str(attr_infos),
                           class_name=_class_name,
                           method_name=_method_name)

        # Print 'Valid Attributes:' area label
        print
        _print_indent(_format_message('WLSDPLY-10111'), indent_level)

        if attr_infos:
            maxlen = 0
            for key in attr_infos:
                if len(key) > maxlen:
                    maxlen = len(key)
            formatted_string = '%-' + str(maxlen) + 's\t%s'

            attr_list = attr_infos.keys()
            attr_list.sort()
            for attr_name in attr_list:
                msg = formatted_string % (attr_name, attr_infos[attr_name])
                _print_indent(msg, indent_level + 1)

    def _get_folder_type_name(self, location):
        """
        Return text indicating the type of a folder, such as "multiple".
        :param location: the location to be checked
        :return: name of the folder type to be displayed, or None
        """
        if self._alias_helper.is_artificial_type_folder(location):
            return None
        if self._alias_helper.supports_multiple_mbean_instances(location):
            return "multiple"
        return None