diff --git a/core/src/main/java/oracle/weblogic/deploy/util/CustomBeanUtils.java b/core/src/main/java/oracle/weblogic/deploy/create/CustomBeanUtils.java similarity index 99% rename from core/src/main/java/oracle/weblogic/deploy/util/CustomBeanUtils.java rename to core/src/main/java/oracle/weblogic/deploy/create/CustomBeanUtils.java index 8c005b848b..a17a8bb512 100644 --- a/core/src/main/java/oracle/weblogic/deploy/util/CustomBeanUtils.java +++ b/core/src/main/java/oracle/weblogic/deploy/create/CustomBeanUtils.java @@ -2,7 +2,7 @@ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. */ -package oracle.weblogic.deploy.util; +package oracle.weblogic.deploy.create; import oracle.weblogic.deploy.aliases.TypeUtils; import oracle.weblogic.deploy.exception.ExceptionHelper; diff --git a/core/src/main/python/wlsdeploy/aliases/alias_constants.py b/core/src/main/python/wlsdeploy/aliases/alias_constants.py index 3cd89c483a..c99cb9d224 100644 --- a/core/src/main/python/wlsdeploy/aliases/alias_constants.py +++ b/core/src/main/python/wlsdeploy/aliases/alias_constants.py @@ -82,6 +82,7 @@ SEMI_COLON_DELIMITED_STRING = 'delimited_string[semicolon]' SPACE_DELIMITED_STRING = 'delimited_string[space]' STRING = 'string' +MASKED = '' ALIAS_DELIMITED_TYPES = [ COMMA_DELIMITED_STRING, @@ -113,6 +114,12 @@ STRING ] +ALIAS_NUMERIC_DATA_TYPES = [ + DOUBLE, + INTEGER, + LONG +] + ALIAS_DATA_TYPES = list() ALIAS_DATA_TYPES.extend(ALIAS_PRIMITIVE_DATA_TYPES) ALIAS_DATA_TYPES.extend(ALIAS_LIST_TYPES) diff --git a/core/src/main/python/wlsdeploy/tool/create/creator.py b/core/src/main/python/wlsdeploy/tool/create/creator.py index f2c9412819..d3c4ac4b0a 100644 --- a/core/src/main/python/wlsdeploy/tool/create/creator.py +++ b/core/src/main/python/wlsdeploy/tool/create/creator.py @@ -10,10 +10,10 @@ from wlsdeploy.exception import exception_helper from wlsdeploy.exception.expection_types import ExceptionType from wlsdeploy.logging.platform_logger import PlatformLogger -from wlsdeploy.util import dictionary_utils +from wlsdeploy.tool.create.custom_folder_helper import CustomFolderHelper from wlsdeploy.tool.util.alias_helper import AliasHelper from wlsdeploy.tool.util.attribute_setter import AttributeSetter -from wlsdeploy.tool.util.custom_folder_helper import CustomFolderHelper +from wlsdeploy.util import dictionary_utils from wlsdeploy.tool.util.wlst_helper import WlstHelper from wlsdeploy.util.model import Model from wlsdeploy.util.weblogic_helper import WebLogicHelper diff --git a/core/src/main/python/wlsdeploy/tool/util/custom_folder_helper.py b/core/src/main/python/wlsdeploy/tool/create/custom_folder_helper.py similarity index 99% rename from core/src/main/python/wlsdeploy/tool/util/custom_folder_helper.py rename to core/src/main/python/wlsdeploy/tool/create/custom_folder_helper.py index 2cac42d7d5..cc96cd1536 100644 --- a/core/src/main/python/wlsdeploy/tool/util/custom_folder_helper.py +++ b/core/src/main/python/wlsdeploy/tool/create/custom_folder_helper.py @@ -5,7 +5,7 @@ from java.lang import IllegalArgumentException from java.lang import IllegalAccessException from java.lang.reflect import InvocationTargetException -from oracle.weblogic.deploy.util import CustomBeanUtils +from oracle.weblogic.deploy.create import CustomBeanUtils from wlsdeploy.aliases.location_context import LocationContext from wlsdeploy.exception import exception_helper diff --git a/core/src/main/python/wlsdeploy/tool/discover/custom_discoverer.py b/core/src/main/python/wlsdeploy/tool/discover/custom_discoverer.py new file mode 100644 index 0000000000..956f661827 --- /dev/null +++ b/core/src/main/python/wlsdeploy/tool/discover/custom_discoverer.py @@ -0,0 +1,6 @@ +""" +Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. +The Universal Permissive License (UPL), Version 1.0 +""" + +_class_name = 'custom_discoverer' diff --git a/core/src/main/python/wlsdeploy/tool/discover/custom_folder_helper.py b/core/src/main/python/wlsdeploy/tool/discover/custom_folder_helper.py new file mode 100644 index 0000000000..b29deca43d --- /dev/null +++ b/core/src/main/python/wlsdeploy/tool/discover/custom_folder_helper.py @@ -0,0 +1,525 @@ +""" +Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. +The Universal Permissive License (UPL), Version 1.0 +""" + +import java.lang.Boolean as Boolean +import java.lang.Double as Double +import java.lang.Enum as Enum +import java.lang.Integer as Integer +import java.lang.Long as Long +import java.lang.NumberFormatException as NumberFormatException +import java.lang.String as String +import java.math.BigInteger as BigInteger +import java.util.Map as Map +import java.util.Properties as Properties +import javax.management.ObjectName as ObjectName + +import oracle.weblogic.deploy.util.PyOrderedDict as PyOrderedDict + +import wlsdeploy.aliases.alias_constants as alias_constants +from wlsdeploy.aliases.location_context import LocationContext +from wlsdeploy.logging.platform_logger import PlatformLogger +from wlsdeploy.tool.util.alias_helper import AliasHelper +from wlsdeploy.tool.util.mbean_utils import MBeanUtils +from wlsdeploy.tool.util.wlst_helper import WlstHelper +from wlsdeploy.util.weblogic_helper import WebLogicHelper + + +_logger = PlatformLogger('wlsdeploy.discover') +_class_name = 'CustomFolderHelper' + + +class CustomFolderHelper(object): + """ + Helper for locating the custom MBeans and its attributes. + + These require special handling, since they do not have alias definitions. + Discover the MBean attributes using the information provided by the MBeanAttributes + wrapper class. + """ + + def __init__(self, aliases, logger, model_context, exception_type): + global _logger + self._exception_type = exception_type + self._model_context = model_context + if logger is not None: + _logger = logger + self._alias_helper = AliasHelper(aliases, _logger, self._exception_type) + self._weblogic_helper = WebLogicHelper(_logger) + self._wlst_helper = WlstHelper(_logger, self._exception_type) + self._info_helper = MBeanUtils(self._model_context, self._alias_helper, self._exception_type) + + def discover_custom_mbean(self, base_location, model_type, mbean_name): + """ + Discover the Custom MBean attributes using its MBeanInfo. + :param base_location: the current context for the location + :param model_type: The parent type of the custom MBean + :param mbean_name: the name of the custom MBean instance + :raises: BundleAwareException of the specified type: if an error occurs + """ + _method_name = 'discover_custom_mbean' + _logger.entering(base_location.get_folder_path(), model_type, mbean_name, + class_name=_class_name, method_name=_method_name) + location = LocationContext(base_location) + subfolder_result = PyOrderedDict() + + attribute_helper = self._info_helper.get_info_attribute_helper(location) + if attribute_helper is None: + _logger.warning('WLSDPLY-06753', model_type, str(attribute_helper), mbean_name, + class_name=_class_name, method_name=_method_name) + else: + subfolder_result[mbean_name] = PyOrderedDict() + _logger.finer('WLSDPLY-06757', attribute_helper.mbean_string(), + class_name=_class_name, method_name=_method_name) + short_name = attribute_helper.get_mbean_name() + # This is not like other custom interface names and should be changed to be more flexible + interface_name = security_provider_interface_name(attribute_helper.get_mbean_interface_name()) + subfolder_result[mbean_name][interface_name] = PyOrderedDict() + _logger.info('WLSDPLY-06751', model_type, short_name, class_name=_class_name, method_name=_method_name) + _logger.info('WLSDPLY-06752', mbean_name, model_type, short_name, + class_name=_class_name, method_name=_method_name) + subfolder_result[mbean_name][interface_name] = self.get_model_attribute_map(attribute_helper) + _logger.exiting(class_name=_class_name, method_name=_method_name) + return subfolder_result + + def get_model_attribute_map(self, attribute_helper): + """ + Return a map of the MBean's attributes, in model format, which do not have default values. + :param attribute_helper: context for the current MBean + :return: model ready dictionary of the discovered MBean + """ + _method_name = 'get_model_attribute_map' + _logger.entering(str(attribute_helper), class_name=_class_name, method_name=_method_name) + mbean_attributes = PyOrderedDict() + for attribute_name in attribute_helper.get_mbean_attributes(): + model_value = self.ge,t_model_attribute_value(attribute_helper, attribute_name) + if model_value is not None: + mbean_attributes[attribute_name] = model_value + _logger.exiting(class_name=_class_name, method_name=_method_name) + return mbean_attributes + + def get_model_attribute_value(self, attribute_helper, attribute_name, wlst_value='unknown'): + """ + Retrieve the WLST value for the attribute and convert the value into a model appropriate format. + If the attribute is read only, or the value is empty, or the value is the default, return None + :param attribute_helper: context for the current MBean + :param attribute_name: current MBean attribute being processed + :param wlst_value: if provided, use this WLST value for the attribute instead of from the MBean instance + :return: Converted model attribute value + """ + _method_name = 'get_model_attribute_value' + add_value = None + mbean_string = attribute_helper.mbean_string() + if attribute_helper.is_read_only(attribute_name): + _logger.finer('WLDSPLY-06776', mbean_string, attribute_name, + class_name=_class_name, method_name=_method_name) + else: + model_type, model_value = self.__convert_to_type(attribute_helper, attribute_name, wlst_value) + if model_type is not None: + print_conv = model_value + if model_type == alias_constants.PASSWORD: + print_orig = alias_constants.MASKED + print_conv = print_orig + _logger.finer('WLSDPLY-06770', mbean_string, attribute_name, model_type, str(print_conv), + class_name=_class_name, method_name=_method_name) + default_value = self.__get_default_value(attribute_helper, attribute_name) + if not is_empty(model_value): + if is_empty(default_value) or not self.is_default(model_value, model_type, default_value): + add_value = model_value + if add_value is not None and model_type == alias_constants.PASSWORD: + add_value = alias_constants.PASSWORD_TOKEN + else: + _logger.finer('WLSDPLY-06771', mbean_string, attribute_name, attribute_helper.get_type(attribute_name), + class_name=_class_name, method_name=_method_name) + + return add_value + + def convert(self, value, value_type): + """ + Public function to convert the value with value_type to a model compatible value. + :param value: Value to be converted into the appropriate model data type + :param value_type: data type of the value + :return: converted data type and value + """ + _method_name = 'convert_method' + converted_type = None + converted = None + try: + if value_type == '[B': + converted_type = alias_constants.PASSWORD + converted = convert_byte_buffer(value) + elif value_type == 'int' or value_type == 'java.lang.Integer': + converted_type = alias_constants.INTEGER + converted = convert_numeric(Integer, value) + elif value_type == 'long' or value_type == 'java.lang.Long': + converted_type = alias_constants.LONG + converted = convert_numeric(Long, value) + elif value_type == 'double' or value_type == 'java.lang.Double': + converted_type = alias_constants.DOUBLE + converted = convert_numeric(Double, value) + elif value_type == 'float' or value_type == 'java.lang.Float': + _logger.info('WLSDPLY-06766', value_type, class_name=_class_name, method_name=_method_name) + elif value_type == 'java.math.BigInteger': + converted_type, converted = convert_big_integer(value) + _logger.fine('WLSDPLY-06767', converted, converted_type, + class_name=_class_name, method_name=_method_name) + elif value_type == 'str' or value_type == 'java.lang.String' or value_type == 'string': + converted_type = alias_constants.STRING + converted = convert_string(value) + elif value_type == 'bool' or value_type == 'boolean' or value_type == 'java.lang.Boolean': + converted_type = alias_constants.BOOLEAN + converted = convert_boolean(value) + elif value_type == 'dict' or value_type == 'java.util.Properties' or value_type == 'java.util.Map': + converted_type = alias_constants.PROPERTIES + converted = create_dictionary(value) + elif value_type.endswith('Enum'): + converted_type = alias_constants.STRING + if value is not None: + converted = value.toString() + elif value_type == 'PyArray' or value_type.startswith('[L'): + converted = create_array(value) + if converted is not None: + converted_type = alias_constants.JARRAY + elif value_type == 'list': + converted = create_array(value) + if converted is not None: + converted_type = alias_constants.LIST + else: + converted_type, converted = convert_value(value) + _logger.fine('WLSDPLY-06768', value_type, converted_type, + class_name=_class_name, method_name=_method_name) + except Exception, e: + _logger.fine('WLSDPLY-06769', value_type, converted_type, str(e), + class_name=_class_name, method_name=_method_name) + return converted_type, converted + + def is_default(self, model_value, model_type, default_value): + """ + Compare the model value to the model default value to determine if it is a default. + If this is running in offline Discover then the default value might differ from the MBeanInfo value, + which is geared towards online. If it is offline and the default value is not empty but the + WLST value indicates empty (i.e. zero length string or zero in a numeric field) then return True. + :param model_value: WLST value converted to model value + :param model_type: data type of the model value using the alias_constants nomenclature + :param default_value: WLST default value converted to model value + :return: True if the model value is the default value + """ + _method_name = 'is_default' + mvalue = model_value + dvalue = default_value + if model_type == alias_constants.PASSWORD: + mvalue = alias_constants.MASKED + dvalue = alias_constants.MASKED + _logger.finest('WLSDPLY-06772', mvalue, model_type, dvalue, + class_name=_class_name, method_name=_method_name) + is_default = False + if model_type == alias_constants.LIST: + is_default = equal_lists(model_value, default_value) + elif model_type == alias_constants.PROPERTIES: + is_default = equal_dictionary(model_value, default_value) + elif model_type == alias_constants.JARRAY: + is_default = equal_jarrays(model_value, default_value) + elif model_type == alias_constants.STRING: + is_default = model_value == default_value or self.__offline_default(model_value, model_type, default_value) + elif model_type == alias_constants.OBJECT: + is_default = model_value.equals(default_value) + elif model_type in alias_constants.ALIAS_NUMERIC_DATA_TYPES: + is_default = model_value == default_value or \ + self.__offline_default_numeric(model_value, model_type, default_value) + elif model_type != alias_constants.PASSWORD: + is_default = model_value == default_value + _logger.finest('WLSDPLY-06773', Boolean(is_default), + class_name=_class_name, method_name=_method_name) + return is_default + + def __convert_to_type(self, attribute_helper, attr_name, wlst_value): + """ + Convert the provided value from WLST type to model type. + :param attribute_helper: helper from which to retrieve the WLST attribute type + :param attr_name: attribute name + :param wlst_value: WLST value to convert for model + :return: converted value + """ + _method_name = '__convert_to_type' + mbean_string = attribute_helper.mbean_string() + attr_type = attribute_type(attribute_helper, attr_name) + _logger.finest('WLSDPLY-06763', mbean_string, attr_name, attr_type, + class_name=_class_name, method_name=_method_name) + + if attribute_helper.is_clear_text_encrypted(attr_name): + _logger.fine('WLSDPLY-06777', mbean_string, attr_name, + class_name=_class_name, method_name=_method_name) + converted_type = None + converted = None + else: + if wlst_value == 'unknown': + wlst_value = attribute_helper.get_value(attr_name) + converted_type, converted = self.convert(wlst_value, attr_type) + + return converted_type, converted + + def __get_default_value(self, attribute_helper, attribute_name): + """ + Retrieve the default value for the attribute through the attribute helper. + :param attribute_helper: Helper to generically provide attribute information + :param attribute_name: Name of the attribute from which to retrieve the default value + :return: default value converted to model type + """ + _method_name = '__get_default_value' + default = attribute_helper.get_default_value(attribute_name) + _logger.finest('WLSDPLY-06762', attribute_helper.mbean_string(), attribute_name, default, + class_name=_class_name, method_name=_method_name) + converted_default = None + if not is_empty(default): + __, converted_default = self.__convert_to_type(attribute_helper, attribute_name, default) + return converted_default + + def __offline_default(self, model_value, model_type, default_value): + _method_name = '__offline_default' + if self._model_context.is_wlst_offline() and is_empty(model_value) and not is_empty(default_value): + _logger.fine('WLSDPLY-06775', model_type, str(model_value), str(default_value), + class_name=_class_name, method_name=_method_name) + return True + return False + + def __offline_default_numeric(self, model_value, model_type, default_value): + _method_name = '__offline_default_numeric' + if self._model_context.is_wlst_offline() and \ + (model_value is None or model_value == 0) and default_value != 0: + _logger.fine('WLSDPLY-06775', model_type, str(model_value), str(default_value), + class_name=_class_name, method_name=_method_name) + return True + return False + + +def equal_dictionary(dict1, dict2): + if dict1 is not None and dict2 is not None: + dict1_keys = dict1.keys() + if equal_lists(dict1_keys, dict2.keys()): + for key in dict1_keys: + if dict1[key] != dict2[key]: + return False + return True + return False + + +def equal_lists(list1, list2): + """ + Compare the two lists for values that are in the first or second but not both + :param list1: first list of values + :param list2: second list of values + :return: True if all values in each list are in the other list + """ + if list1 is not None and list2 is not None and len(list1) == len(list2): + return (len([item for item in list1 if item not in list2]) + + len([item for item in list2 if item not in list1])) == 0 + return False + + +def equal_jarrays(array1, array2): + """ + Compare the two jarrays for values that are in the first or second but not both + :param array1: first jarray of values + :param array2: second jarray of values + :return: True if the values in the first jarray are the exact values in the second jarray + """ + if len(array1) == len(array2): + for item1 in array1: + found = False + for item2 in array2: + if item1 == item2: + found = True + break + if not found: + return False + return True + return False + + +def security_provider_interface_name(mbean_interface): + """ + Return the name that is used to look up the custom Security Provider MBeanInfo. + + This is too tightly coupled to be in this class. + This needs something more to differentiate Security Provider Interface which is formatted differently from other + custom MBean Interface names. + :param mbean_interface: interface for the MBean + :return: massaged name specific to the Scurity Provider + """ + result = mbean_interface + idx = mbean_interface.rfind('MBean') + if idx > 0: + result = result[:idx] + return result + + +def attribute_type(attribute_helper, attribute_name): + """ + Use the attribute helper to return the attribute type. + :param attribute_helper: wrapper Class is a helper to extract attribute information + :param attribute_name: name of the attribute to type + :return: data type of the attribute + """ + attr_type = None + check_type = attribute_helper.get_type(attribute_name) + if check_type is not None: + attr_type = str(check_type) + return attr_type + + +def create_enumeration_list(enumeration): + """ + The attribute value is a Java Enumeration class. Convert the iterable to string values. + :param enumeration: Enumeration iterable + :return: string list of values + """ + enumeration_list = list() + if not is_empty(enumeration): + while enumeration.hasMoreElements(): + enumeration_list.append(enumeration.nextElement()) + return enumeration_list + + +def convert_numeric(class_type, number): + """ + Convert the numeric to appropriate model value using the Java Class representing the number. + :param class_type: Java Class to convert into a model type + :param number: value to be converted + :return: model value from WLST value + """ + if number is None: + return None + try: + converted = class_type.valueOf(number) + except NumberFormatException: + converted = None + return converted + + +def convert_big_integer(value): + """ + Convert the big integer value to long. + :param value: big integer string or number value + :return: converted to long + """ + _method_name = 'convert_big_integer' + converted_type = alias_constants.LONG + converted = None + if value is not None: + try: + converted = BigInteger(value).longValue() + except NumberFormatException, nfe: + _logger.fine('WLSDPLY-06774', value, type(value), nfe.getMessage(), + class_name=_class_name, method_name=_method_name) + return converted_type, converted + + +def is_empty(value): + """ + Determine if the provided value is empty. + :param value: attribute value to test for empty + :return: True if the attribute does not contain a value + """ + return value is None or (type(value) in [list, dict] and len(value) == 0) or \ + ((type(value) == str or isinstance(value, String)) and + (len(value) == 0 or value == '[]' or value == 'null' or value == 'None')) + + +def convert_string(value): + """ + Convert the provided value to a python string. + :param value: value to convert to string + :return: string form of value or None if the value does not contain a value + """ + converted = None + if not is_empty(value): + converted = str(value) + return converted + + +def convert_boolean(boolean_value): + if boolean_value is not None: + return Boolean(boolean_value).toString() + return None + + +def convert_byte_buffer(buffer_value): + if buffer_value is not None: + return str(String(buffer_value)) + return None + + +def create_array(iterable): + """ + Create an array from the jarray or list objects. + :param iterable: a List object or other iterable type + :return: an array or a string containing the converted contents from the provided iterable + """ + my_array = None + if is_iterable(iterable): + my_array = list() + for element in iterable: + __, converted = convert_value(element) + my_array.append(converted) + elif iterable is None: + my_array = list() + return my_array + + +def create_dictionary(value): + """ + Convert the value that is a dict, properties or map type to a python dictionary. + :param value: value to be converted to python dictionary + :return: python dictionary of key, value from original type + """ + my_dict = PyOrderedDict() + if not is_empty(value): + if isinstance(value, dict) or isinstance(value, PyOrderedDict): + for key, item in value.items(): + my_dict[key] = item + elif isinstance(value, Properties): + for key in value.stringPropertyNames(): + my_dict[key] = value.getProperty(key) + elif isinstance(value, Map): + for result in value.entrySet(): + my_dict[result.getKey()] = result.getValue() + return my_dict + + +def convert_value(value): + """ + Convert the value that does not have a well known data type with the information directly from the value. + Select the appropriate data type from the conversion. + :param value: value to convert which does not have well-known converted value + :return: converted value and type of the converted value + """ + converted = None + converted_type = None + if not is_empty(value): + if isinstance(value, ObjectName): + converted_type = alias_constants.STRING + converted = value.getKeyProperty('Name') + elif isinstance(value, String): + converted_type = alias_constants.STRING + converted = str(value) + elif isinstance(value, Enum): + converted_type = alias_constants.STRING + converted = str(value.toString()) + else: + converted_type = alias_constants.OBJECT + converted = value + return converted_type, converted + + +def is_iterable(iterable): + """ + Determine if the provided object is an iterable type. + :param iterable: Object to test + :return: True if the object is an instance of an iterable data type + """ + try: + iter(iterable) + return True + except TypeError: + return False diff --git a/core/src/main/python/wlsdeploy/tool/discover/discoverer.py b/core/src/main/python/wlsdeploy/tool/discover/discoverer.py index 29808d5b55..8b0115ceee 100644 --- a/core/src/main/python/wlsdeploy/tool/discover/discoverer.py +++ b/core/src/main/python/wlsdeploy/tool/discover/discoverer.py @@ -16,7 +16,9 @@ from wlsdeploy.exception import exception_helper from wlsdeploy.exception.expection_types import ExceptionType from wlsdeploy.logging.platform_logger import PlatformLogger + from wlsdeploy.tool.util.mbean_utils import MBeanUtils +from wlsdeploy.tool.discover.custom_folder_helper import CustomFolderHelper from wlsdeploy.tool.util.alias_helper import AliasHelper from wlsdeploy.tool.util.wlst_helper import WlstHelper from wlsdeploy.util import path_utils @@ -49,10 +51,11 @@ def __init__(self, model_context, base_location, wlst_mode, aliases=None): self._aliases = Aliases(self._model_context, wlst_mode=self._wlst_mode) self._alias_helper = AliasHelper(self._aliases, _logger, ExceptionType.DISCOVER) self._att_handler_map = OrderedDict() + self._custom_folder = CustomFolderHelper(self._aliases, _logger, self._model_context, ExceptionType.DISCOVER) self._weblogic_helper = WebLogicHelper(_logger) - self._wls_version = self._weblogic_helper.get_actual_weblogic_version() self._wlst_helper = WlstHelper(_logger, ExceptionType.DISCOVER) self._mbean_utils = MBeanUtils(self._model_context, self._alias_helper, ExceptionType.DISCOVER) + self._wls_version = self._weblogic_helper.get_actual_weblogic_version() # methods for use only by the subclasses @@ -356,19 +359,19 @@ def _discover_subfolder_with_single_name(self, model_subfolder_name, location, n _logger.exiting(class_name=_class_name, method_name=_method_name) return result - def _discover_artificial_folder(self, model_subfolder_name, location, name_token): + def _discover_artificial_folder(self, model_subfolder_type, location, name_token): """ Discover the subfolder that has an artificial connection; the subfolder contains multiple different types under one MBean. The model must contain the subfolder type, the artificial type that specifies which it is, and the name of the subfolder. This folder is only one layer deep. No need to continue to discover additional subfolders - :param model_subfolder_name: type of the model subfolder + :param model_subfolder_type: type of the model subfolder :param location: context containing the current location information :param name_token: for use in the location to contain the folder name :return: dictionary containing the discovered folder attributes """ _method_name = '_discover_artifical_folder' - _logger.entering(model_subfolder_name, str(location), name_token, class_name=_class_name, + _logger.entering(model_subfolder_type, str(location), name_token, class_name=_class_name, method_name=_method_name) subfolder_result = OrderedDict() names = self._find_names_in_folder(location) @@ -378,10 +381,16 @@ def _discover_artificial_folder(self, model_subfolder_name, location, name_token location.add_name_token(name_token, massaged) artificial = self._get_artificial_type(location) if artificial is None: - _logger.warning('WLSDPLY-06123', self._alias_helper.get_model_folder_path(location), - class_name=_class_name, method_name=_method_name) + if self._alias_helper.is_custom_folder_allowed(location): + _logger.fine('WLSDPLY-06148', model_subfolder_type, massaged, location.get_folder_path(), + class_name=_class_name, method_name=_method_name) + subfolder_result = self._custom_folder.discover_custom_mbean(location, model_subfolder_type, + massaged) + else: + _logger.warning('WLSDPLY-06123', self._alias_helper.get_model_folder_path(location), + class_name=_class_name, method_name=_method_name) else: - _logger.finer('WLSDPLY-06120', artificial, massaged, model_subfolder_name, class_name=_class_name, + _logger.finer('WLSDPLY-06120', artificial, massaged, model_subfolder_type, class_name=_class_name, method_name=_method_name) location.append_location(artificial) subfolder_result[massaged] = OrderedDict() @@ -665,9 +674,6 @@ def _inspect_security_folder_name(self, folder_name, location): self._alias_helper.is_security_provider_type(location) and 'Provider' == folder_name: raise exception_helper.create_discover_exception('WLSDPLY-06201', folder_name, location.get_folder_path()) - _logger.fine('version {0} mode {1} type? {2} provider {3}', not self._weblogic_helper.is_version_in_12c(), - self._wlst_mode == WlstModes.OFFLINE, self._alias_helper.is_security_provider_type(location), - 'Provider' == folder_name) return folder_name diff --git a/core/src/main/python/wlsdeploy/tool/util/beaninfo_constants.py b/core/src/main/python/wlsdeploy/tool/util/beaninfo_constants.py new file mode 100644 index 0000000000..26bad75dc1 --- /dev/null +++ b/core/src/main/python/wlsdeploy/tool/util/beaninfo_constants.py @@ -0,0 +1,13 @@ +""" +Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. +The Universal Permissive License (UPL), Version 1.0 +""" + +# Constant values representing the properties in the WebLogic Server Class java.beans.PropertyDescriptor +ATTRIBUTE_DEFAULT = 'default' +ATTRIBUTE_ENCRYPTED = 'encrypted' +ATTRIBUTE_GETTER = 'readMethod' +ATTRIBUTE_NAME = 'name' +ATTRIBUTE_SETTER = 'writeMethod' +ATTRIBUTE_TYPE = 'propertyType' + diff --git a/core/src/main/python/wlsdeploy/tool/util/mbean_utils.py b/core/src/main/python/wlsdeploy/tool/util/mbean_utils.py index 2403ed4020..5e6e7852fb 100644 --- a/core/src/main/python/wlsdeploy/tool/util/mbean_utils.py +++ b/core/src/main/python/wlsdeploy/tool/util/mbean_utils.py @@ -9,11 +9,9 @@ from oracle.weblogic.deploy.exception import BundleAwareException -from wlsdeploy.aliases.aliases import Aliases from wlsdeploy.aliases.wlst_modes import WlstModes from wlsdeploy.exception import exception_helper from wlsdeploy.logging.platform_logger import PlatformLogger -from wlsdeploy.tool.util.alias_helper import AliasHelper from wlsdeploy.tool.util.wlst_helper import WlstHelper from wlsdeploy.util.weblogic_helper import WebLogicHelper @@ -23,9 +21,9 @@ class MBeanUtils(object): """ - Utility class used to provide information about WLST attributes as retrieved from the MBean MBeanInfo or Interface + Utility class used to provide information about WLST attributes as retrieved from the MBeans MBeanInfo or Interface methods. This class has methods to provide the information stored in different combinations. All methods that want - to combine the information from the MBean helpers into different combinations are located in this class. + to combine the information from the MBeans helpers into different combinations are located in this class. """ def __init__(self, model_context, alias_helper, exception_type): @@ -36,14 +34,13 @@ def __init__(self, model_context, alias_helper, exception_type): self.__helper = self.__get_helper() self.__ignore_list = None - def get_attributes_not_in_lsa_map(self, location, lsa_map=None): """ - Return a list of all attributes from the MBean MBeanInfo or Interface methods that are not contained in the LSA + Return a list of all attributes from the MBeans MBeanInfo or Interface methods that are not contained in the LSA attributes for the location in the location context. :param location: current location context of the MBean :param lsa_map: map returned from WLST ls('a') or None to get the LSA map from the current location context - :return: Any additional attributes or empty list if none found + :return: Any additional attributes or empty list if None found """ _method_name = 'get_attributes_not_in_lsa_map' _logger.entering(location.get_folder_path(), class_name=self.__class__.__name__, method_name=_method_name) @@ -54,23 +51,65 @@ def get_attributes_not_in_lsa_map(self, location, lsa_map=None): _logger.exiting(class_name=self.__class__.__name__, method_name=_method_name, result=loose_attributes) return loose_attributes + def get_info_attribute_helper(self, location): + """ + Get a wrapper for the MBeanInfo attribute information for the current MBean designated in the location context. + :param location: containing the current MBean context + :return: MBeanAttributes class wrapping the MBeanInfo attributes with convenience methods + """ + return self.__get_info_helper(location) + def get_mbean_info_attributes(self, location=None, helper=None): + """ + Get a list of attribute names for the MBean using the MBeanInfoAttributes wrapper class. + If the helper is not provided, use the location arg to create an instance of the helper + for the MBean designated in the location context. + :param location: If helper is None, the location is required to create the helper instance + :param helper: If not None, the provided helper is used to return the attribute information. The + location arg is not required if helper is provided. + :return: List of attribute names for the MBean + """ if helper is None: - helper = self.__get_info_helper(location) - return helper.get_mbean_info_attributes() + helper = self.get_info_attribute_helper(location) + return self.get_mbean_attributes(helper) + + def get_interface_attribute_helper(self, location): + """ + Return an instance of the InterfaceAttributes helper class for the MBean indicated in the location context. + :param location: context for the current MBean location + :return: InterfaceAttributes helper instance + """ + return self.__get_interface_helper(location) def get_interface_attributes(self, location=None, helper=None): + """ + Get a list of attribute names for the MBean using the InterfaceAttributes wrapper class. + If the helper is not provided, use the location arg to create an instance of the helper + for the MBean designated in the location context. + :param location: If helper is None, the location is required to create the helper instance + :param helper: If not None, the provided helper is used to return the attribute information. The + location arg is not required if helper is provided. + :return: List of attribute names for the MBean + """ if helper is None: - helper = self.__get_interface_helper(location) - return helper.get_interface_attributes() + helper = self.get_interface_attribute_helper(location) + return self.get_mbean_attributes(helper) + + def get_mbean_attributes(self, helper): + """ + Return a list of the MBean attribute names through the MBean attribute helper. + :param helper: MBeanAttributes helper class + :return: list of MBean attribute names + """ + return helper.get_mbean_attributes() def __collapse_attributes(self, location): _method_name = '__filter_attributes' info_helper = self.__get_info_helper(location) - info_attributes = self.get_mbean_info_attributes(helper=info_helper) + info_attributes = self.get_mbean_attributes(info_helper) interface_helper = self.__get_interface_helper(location) - interface_attributes = self.get_interface_attributes(helper=interface_helper) + interface_attributes = self.get_mbean_attributes(interface_helper) self.__remove_duplicates(interface_attributes, str(interface_helper), info_attributes, str(info_helper)) # This is the main list to drive from @@ -95,10 +134,10 @@ def __slim_list(self, attributes, attribute_helper, remove_readonly=False): )] def __get_info_helper(self, location): - return MBeanInfoAttributes(self.__model_context, self.__exception_type, location) + return MBeanInfoAttributes(self.__model_context, self.__alias_helper, self.__exception_type, location) def __get_interface_helper(self, location): - return InterfaceAttributes(self.__model_context, self.__exception_type, location) + return InterfaceAttributes(self.__model_context, self.__alias_helper, self.__exception_type, location) def __remove_duplicates(self, check_list, check_list_type, main_list, main_list_type): _method_name = '__remove_duplicates' @@ -260,7 +299,7 @@ def is_attribute_in_lsa_map(self, attribute, lsa_attributes): def is_lsa_in_attributes(self, lsa_attribute, attribute_list): """ - Look for the offline WLST LSA attribute name in one of the MBean helper lists. The names + Look for the offline WLST LSA attribute name in one of the MBean's helper lists. The names can differ between the LSA attribute and the MBean attribute lists. Attempt to match the LSA attribute using different representations of the name. :param lsa_attribute: attribute from the offline WLST LSA list @@ -303,47 +342,69 @@ class MBeanAttributes(object): __interface_matcher = re.compile('Bean$') - def __init__(self, model_context, exception_type, location): + def __init__(self, model_context, alias_helper, exception_type, location, mbean_interface_name): self.__model_context = model_context self.__exception_type = exception_type self.__location = location - self.__aliases = Aliases(self.__model_context, wlst_mode=self.__model_context.get_target_wlst_mode()) - self.__alias_helper = AliasHelper(self.__aliases, _logger, exception_type) + self.__alias_helper = alias_helper + self.__mbean_interface = mbean_interface_name self.__wlst_helper = WlstHelper(_logger, exception_type) self.__mbean_instance = None self.__mbean_name = '' + def mbean_string(self): + """ + Return a string representing the MBean encapsulated by the helper class. + :return: Printable string identifying the MBean + """ + return 'MBean %s at location %s' % (self.get_mbean_name(), self._get_mbean_path()) + + def get_mbean_name(self): + """ + Return the MBean "type" (i.e. JDBCSystemResource) + :return: mbean type + """ + return self.__mbean_name + + def get_mbean_interface_name(self): + """ + Return the full name of the MBean interface class. + :return: Interface name + """ + return self._get_mbean_interface() + def _get_mbean_instance(self): _method_name = '_get_mbean_instance' if self.__mbean_instance is None: attribute_path = self.__alias_helper.get_wlst_attributes_path(self.__location) self.__mbean_instance = self.__wlst_helper.get_mbean(attribute_path) if self.__mbean_instance is None: - ex = exception_helper.create_exception(self.__exception_type, 'WLSDPLY-01775', attribute_path) + ex = exception_helper.create_exception(self._get_exception_type(), 'WLSDPLY-01775', attribute_path) _logger.throwing(ex, class_name=self.__class__.__name__, method_name=_method_name) raise ex return self.__mbean_instance def _get_mbean_interface(self): _method_name = '__get_mbean_interface' - _logger.entering(class_name=self.__class__.__name__, method_name=_method_name) - interfaces = [str(interface) for interface in self._get_mbean_interfaces() - if re.search(self.__interface_matcher, str(interface)) is not None] - if len(interfaces) == 0: - ex = exception_helper.create_exception(self.__exception_type, 'WLSDPLY-01777', - str(self._get_mbean_interfaces()), - self._get_mbean_instance()) - _logger.throwing(ex, class_name=self.__class__.__name__, method_name=_method_name) - raise ex - else: - if len(interfaces) > 1: - _logger.fine('WLSDPLY-01770', interfaces, self._get_mbean_instance(), - class_name=self.__class__.__name__, method_name=_method_name) - mbean_interface = interfaces[0] - self.__mbean_name = self._get_mbean_interfaces()[0].getSimpleName() + if self.__mbean_interface is None: + _logger.entering(class_name=self.__class__.__name__, method_name=_method_name) + interfaces = [interface for interface in self._get_mbean_interfaces() + if re.search(self.__interface_matcher, str(interface)) is not None] + if len(interfaces) == 0: + ex = exception_helper.create_exception(self._get_exception_type(), 'WLSDPLY-01777', + self._get_mbean_instance()) + _logger.throwing(ex, class_name=self.__class__.__name__, method_name=_method_name) + raise ex + else: + if len(interfaces) > 1: + _logger.fine('WLSDPLY-01770', interfaces, self._get_mbean_instance(), + class_name=self.__class__.__name__, method_name=_method_name) + interface = interfaces[0] + self.__mbean_name = interface.getSimpleName() + self.__mbean_interface = str(interface) + _logger.exiting(class_name=self.__class__.__name__, method_name=_method_name, result=self.__mbean_interface) - _logger.exiting(class_name=self.__class__.__name__, method_name=_method_name, result=mbean_interface) - return mbean_interface + return self.__mbean_interface def _get_mbean_methods(self): return self.__get_mbean_class().getDeclaredMethods() @@ -354,6 +415,9 @@ def _get_mbean_name(self): def _get_mbean_interfaces(self): return self.__get_mbean_class().getInterfaces() + def _get_exception_type(self): + return self.__exception_type + def __get_mbean_class(self): _method_name = '__get_mbean_class' mbean_class = None @@ -364,7 +428,7 @@ def __get_mbean_class(self): except AttributeError: pass if mbean_class is None: - ex = exception_helper.create_exception(self.__exception_type, 'WLSDPLY-01776', mbean_instance) + ex = exception_helper.create_exception(self._get_exception_type(), 'WLSDPLY-01776', mbean_instance) _logger.throwing(ex, class_name=self.__class__.__name__, method_name=_method_name) raise ex return mbean_class @@ -388,6 +452,13 @@ def _get_from_bean_proxy(self, getter): class_name=self.__class__.__name__, method_name=_method_name) return success, value + def _get_mbean_path(self): + return self.__location.get_folder_path() + + +def _is_empty(value): + return value is None or len(value) == 0 or value == '[]' or value == 'null' + class InterfaceAttributes(MBeanAttributes): """ @@ -395,18 +466,18 @@ class InterfaceAttributes(MBeanAttributes): attribute methods on the WLST CMO instance. """ - def __init__(self, model_context, exception_type, location): - MBeanAttributes.__init__(self, model_context, exception_type, location) + def __init__(self, model_context, alias_helper, exception_type, location, mbean_interface_name=None): + MBeanAttributes.__init__(self, model_context, alias_helper, exception_type, location, mbean_interface_name) self.__interface_methods_list = None self.__interface_method_names_list = None self.__interface_attribute_map = None - def get_interface_attributes(self): + def get_mbean_attributes(self): """ Return the sorted list of interface attribute names including child MBeans, - as compiled from MBean interface getter methods. - :return: list of attribute names from the MBean interface or empty list if the MBean has no attributes. + as compiled from an MBean's interface getter methods. + :return: list of attribute names from the MBean's interface or empty list if the MBean has no attributes. """ _method_name = 'get_interface_attribute_list' _logger.entering(class_name=self.__class__.__name__, method_name=_method_name) @@ -419,18 +490,18 @@ def get_interface_attributes(self): def exists(self, attribute_name): """ - Determine if the attribute name exists for the MBean in MBean Interface. - :param attribute_name: to search for in the MBean Interface - :return: True if the attribute is found in the MBean Interface + Determine if the attribute name exists for the MBean using the methods in the MBean's Interface. + :param attribute_name: to search for in the MBean's Interface + :return: True if the attribute is found in the MBean's Interface """ return attribute_name in self.__get_interface_map() def is_child_mbean(self, attribute_name): """ - Determine if the attribute exists in MBean Interface and if the attribute is a child MBean. + Determine if the attribute exists in the MBean's Interface and if the attribute is a child MBean. - :param attribute_name: to search for in the MBean Interface - :return: True if the attribute is a child MBean or None if the attribute is not found in MBean Interface + :param attribute_name: to search for in the MBean's Interface + :return: True if the attribute is a child MBean or None if the attribute is not found in the MBean's Interface """ _method_name = 'is_child_mbean' if self.exists(attribute_name): @@ -447,9 +518,9 @@ def is_child_mbean(self, attribute_name): def is_read_only(self, attribute_name): """ - Determine if the attribute exists in MBean Interface and if the attribute is readonly. - :param attribute_name: to search for in the MBean Interface - :return: True if the attribute is readonly or None if the attribute does not exist in MBean Interface + Determine if the attribute exists in the MBean's Interface and if the attribute is readonly. + :param attribute_name: to search for in the MBean's Interface + :return: True if the attribute is readonly or None if the attribute does not exist in the MBean's Interface """ if self.exists(attribute_name): return self.setter(attribute_name) is None @@ -457,9 +528,9 @@ def is_read_only(self, attribute_name): def getter(self, attribute_name): """ - Return the read method string for the attribute in the MBean Interface. - :param attribute_name: to search for in the MBean Interface - :return: attribute getter or None if the attribute does not exist in the MBean Interface + Return the read method string for the attribute in the MBean's Interface. + :param attribute_name: to search for in the MBean's Interface + :return: attribute getter or None if the attribute does not exist in the MBean's Interface """ method_list = self.__get_mbean_attribute(attribute_name) if method_list is not None: @@ -468,7 +539,7 @@ def getter(self, attribute_name): def is_valid_getter(self, attribute_name): """ - Try to invoke the getter method on the mbean_instance. Some of the methods listed + Try to invoke the Interface getter method on the mbean instance. Some of the methods listed on the Interface will fail. :return: True if can invoke the getter on the mbean instance """ @@ -484,8 +555,8 @@ def is_valid_getter(self, attribute_name): def setter(self, attribute_name): """ - Return the setter Method for the attribute in the MBean interface - :param attribute_name: to search for in the MBean interface + Return the setter Method for the attribute in the MBean's Interface + :param attribute_name: to search for in the MBean's Interface :return: setter Method or None if the attribute is readonly or the attribute does not exist in the Interface """ method_list = self.__get_mbean_attribute(attribute_name) @@ -496,7 +567,7 @@ def setter(self, attribute_name): def is_encrypted(self, attribute_name): """ Determine if the property is encrypted by checking for a byte array return type on the getter. - :param attribute_name: to search for in the MBean interface + :param attribute_name: to search for in the MBean's Interface :return: True if the attribute is an encrypted type or None if the attribute does not exist in the Interface """ return_type = self.get_type(attribute_name) @@ -506,11 +577,22 @@ def is_encrypted(self, attribute_name): return False return None + def is_clear_text_encrypted(self, attribute_name): + """ + The tool does not discover encrypted attributes that are clear text. Determine if the attribute is a + this type of attribute. + :param attribute_name: name of the attribute to test + :return: True if the attribute is an encrypted clear text attribute + """ + if self.is_encrypted(attribute_name + 'Encrypted'): + return True + return False + def get_type(self, attribute_name): """ - Return the type of the attribute value if the attribute exists in MBean Interface. - :param attribute_name: to search for in the MBean Interface - :return: Type of the property attribute or None if the attribute does not exist in the MBean Interface + Return the type of the attribute value if the attribute exists in the MBean's Interface. + :param attribute_name: to search for in the MBean's Interface + :return: Type of the property attribute or None if the attribute does not exist in the MBean's Interface """ method_list = self.__get_mbean_attribute(attribute_name) if method_list is not None: @@ -518,7 +600,24 @@ def get_type(self, attribute_name): return None def get_default_value(self, attribute_name): - pass + """ + Unable to determine the default value when using only the MBean Interface. + :param attribute_name: attribute name + :return: None to indicate no default information available + """ + return None + + def get_value(self, attribute_name): + """ + Return the attribute value from the MBean instance. + :param attribute_name: name of the attribute + :return: value of the MBean attribute in the format retrieved from the MBean instance + """ + value = None + getter = self.getter(attribute_name) + if getter is not None: + __, value = self._get_from_bean_proxy(getter) + return value def __get_interface_map(self): if self.__interface_attribute_map is None: @@ -613,26 +712,26 @@ def _get_index_of_name_in_list(search_list_for, name): class MBeanInfoAttributes(MBeanAttributes): """ - This MBeanAttributes class type encapsulates the attribute information found from the + MBeanInfoAttributes extends the MBeanAttributes class. It encapsulates the attribute information found from the PropertyDescriptors in the MBeanInfo for the MBean type. """ __class_name = 'MBeanInfoAttributes' - def __init__(self, model_context, exception_type, location): - MBeanAttributes.__init__(self, model_context, exception_type, location) + def __init__(self, model_context, alias_helper, exception_type, location, mbean_interface_name=None): + MBeanAttributes.__init__(self, model_context, alias_helper, exception_type, location, mbean_interface_name) self.__weblogic_helper = WebLogicHelper(_logger) self.__mbean_info_descriptors = None self.__mbean_info_map = None - def get_mbean_info_attributes(self): + def get_mbean_attributes(self): """ Return the sorted list of attributes compiled from the MBeanInfo PropertyDescriptors including the child MBeans. :return: list of all attributes from the MBeanInfo property descriptors, or an empty list if none """ - _method_name = 'get_mbean_info_attribute_list' + _method_name = 'get_mbean_attributes' _logger.entering(class_name=self.__class__.__name__, method_name=_method_name) map_to_list = list() attributes = self.__get_mbean_info_map() @@ -693,8 +792,8 @@ def getter(self, attribute_name): def is_valid_getter(self, attribute_name): """ Try to invoke the getter method on the mbean_instance. Some of the attributes in the PropertyDescriptors - have read methods that cannot be invoked on the mbean instance. - :return: True if can invoke the getter on the mbean instance + have read methods that cannot be invoked on the MBean instance. + :return: True if can invoke the getter on the MBean instance """ _method_name = 'is_valid_getter' _logger.entering(attribute_name, class_name=self.__class__.__name__, method_name=_method_name) @@ -721,15 +820,24 @@ def setter(self, attribute_name): def is_encrypted(self, attribute_name): """ - Determine if the property is encrypted. + Determine if the property is an encrypted attribute. :param attribute_name: to search for in the MBeanInfo - :return: True if is an encrypted property or None if the attribute does not exist in the MBeanInfo + :return: True if it is an encrypted attribute or None if the attribute does not exist in the MBeanInfo """ descriptor = self.__get_mbean_attribute(attribute_name) if descriptor is not None: - return descriptor.getPropertyType() + return descriptor.getValue('encrypted') is True return None + def is_clear_text_encrypted(self, attribute_name): + """ + The tool does not discover security attributes that are clear text. Determine if the attribute is a + an attribute returning clear text form of the matching encrypted attribute and skip if True. + :param attribute_name: name of the attribute to test + :return: True if the attribute is a clear text security attribute + """ + return self.is_encrypted(attribute_name) and str(self.get_type(attribute_name)) == 'java.lang.String' + def get_type(self, attribute_name): """ Return the type of the attribute value if the attribute exists in MBeanInfo. @@ -742,7 +850,30 @@ def get_type(self, attribute_name): return None def get_default_value(self, attribute_name): - pass + """ + Return the default value if the attribute exists in MBeanInfo + :param attribute_name: to search for in the MBeanInfo + :return: The default value for the attribute + """ + descriptor = self.__get_mbean_attribute(attribute_name) + values = _get_descriptor_values_keys(descriptor) + if 'defaultValueNull' in values and descriptor.getValue('defaultValueNull') is True: + default = None + else: + default = descriptor.getValue('default') + return default + + def get_value(self, attribute_name): + """ + Return the attribute value from the mbean instance. + :param attribute_name: name of the attribute + :return: Value of the MBean attribute in the format retrieved from the mbean instance + """ + value = None + getter = self.getter(attribute_name) + if getter is not None: + __, value = self._get_from_bean_proxy(getter) + return value def __get_mbean_info_map(self): if self.__mbean_info_map is None: @@ -757,8 +888,8 @@ def __get_mbean_descriptors(self): if self.__mbean_info_descriptors is None: mbean_info = self.__weblogic_helper.get_bean_info_for_interface(self._get_mbean_interface()) if mbean_info is None: - ex = exception_helper.create_exception(self.__exception_type, 'WLSDPLY-01774', - self._get_mbean_interface(), self._get_mbean_instance()) + ex = exception_helper.create_exception(self._get_exception_type(), 'WLSDPLY-01774', + self._get_mbean_interface(), self.mbean_string()) _logger.throwing(ex, class_name=self.__class__.__name__, method_name=_method_name) raise ex self.__mbean_info_descriptors = mbean_info.getPropertyDescriptors() @@ -772,3 +903,20 @@ def __get_mbean_attribute(self, attribute): def __str__(self): return self.__class__.__name__ + self._get_mbean_name() + + +def _get_descriptor_values_keys(descriptor): + """ + Return a list of keys from the PropertyDescriptor "values" map. + :param descriptor: MBeanInfo PropertyDescriptor with the values array + :return: list of keys from the "values" key=value map + """ + enumerations = descriptor.attributeNames() + return _create_enumeration_list(enumerations) + + +def _create_enumeration_list(enumeration): + enumeration_list = list() + while enumeration.hasMoreElements(): + enumeration_list.append(enumeration.nextElement()) + return enumeration_list diff --git a/core/src/main/python/wlsdeploy/util/wlst_helper.py b/core/src/main/python/wlsdeploy/util/wlst_helper.py index 6afc4d08ce..f1c4d67ed4 100644 --- a/core/src/main/python/wlsdeploy/util/wlst_helper.py +++ b/core/src/main/python/wlsdeploy/util/wlst_helper.py @@ -463,13 +463,13 @@ def get_mbean(wlst_path): mbean_path = wlst_path if mbean_path is None: mbean_path = current_dir - _logger.finest('WLSDPLY-00096', mbean_path, class_name=_class_name, method_name=_method_name) + _logger.finest('WLSDPLY-00097', mbean_path, class_name=_class_name, method_name=_method_name) cd(current_dir) cmo = get_cmo() if cmo is None: cmo = get_mbean_for_wlst_path(mbean_path) if cmo is None: - pwe = exception_helper.create_pywlst_exception('WLSDPLY-00095', mbean_path) + pwe = exception_helper.create_pywlst_exception('WLSDPLY-00096', mbean_path) _logger.throwing(class_name=_class_name, method_name=_method_name, error=pwe) raise pwe if wlst_path is None: diff --git a/core/src/main/resources/oracle/weblogic/deploy/messages/wlsdeploy_rb.properties b/core/src/main/resources/oracle/weblogic/deploy/messages/wlsdeploy_rb.properties index d987ad7c7a..ece4bb7047 100644 --- a/core/src/main/resources/oracle/weblogic/deploy/messages/wlsdeploy_rb.properties +++ b/core/src/main/resources/oracle/weblogic/deploy/messages/wlsdeploy_rb.properties @@ -100,7 +100,8 @@ WLSDPLY-00092=In the wrong state to activate saved changes for the edit session WLSDPLY-00093=In the wrong state to change to start an edit session : {0} WLSDPLY-00094=Checking the editor in online with current configuration manager failed: {0} WLSDPLY-00095=wlst.selectCustomTemplate({0}) failed : {1} - +WLSDPLY-00096=Unable to obtain the cmo or MBean proxy for the current location {0}. +WLSDPLY-0097=Look for MBean instance for location {0}. ############################################################################### # Util messages (1000 - 3999) # @@ -301,16 +302,16 @@ WLSDPLY-01772=The list of methods for the MBean interface {0} is {1} WLSDPLY-01773=Working on method name {0} WLSDPLY-01774=Unable to locate MBeanInfo for interface {0} from the MBean instance {1} WLSDPLY-01775=Failed to locate the MBean instance for location {0} -WLSDPLY-01776=Unable to find the mbean class for the mbean instance {0} -WLSDPLY-01777=Unable to find a valid mbean interface in the interface list {0} for the mbean instance {1} +WLSDPLY-01776=Unable to find the MBean Interface for the MBean instance {0} +WLSDPLY-01777=Unable to find a valid MBean Interface in the Class list {0} of the MBean instance {1} WLSDPLY-01779=MBean attributes found in this alias definition list {0} will be ignored. WLSDPLY-01780=The MBeanInfo PropertyDescriptor indicates attribute {0} is a child MBean WLSDPLY-01781=The getter method {0} for attribute {1} indicates the attribute is a child MBean WLSDPLY-01782=Check to see if the child MBean create method {0} is for attribute {1} WLSDPLY-01783=List of attributes not in LSA map at location {0} is {1} -WLSDPLY-01784=Getter method {0} successfully invoked on the mbean instance for MBean type {1} -WLSDPLY-01785=Failure invoking MBean {0} getter {1} cannot be invoked on the mbean_instance : {2} -WLSDPLY-01786=MBean {0} getter {1} is not an attribute on the mbean instance +WLSDPLY-01784=MBean {1} Getter method {0} successfully invoked on the MBean instance for MBean type +WLSDPLY-01785=Failure invoking MBean {0} getter {1} cannot be invoked on the MBean instance : {2} +WLSDPLY-01786=MBean {0} getter {1} is not an attribute on the MBean instance WLSDPLY-01787=The list of attributes to discover that are not in the LSA map {0} WLSDPLY-01788=Attribute {0} from {1} not found in {2} @@ -492,16 +493,16 @@ WLSDPLY-06106=Unable to add {0} from location {1} to the model : {2} WLSDPLY-06107=Added attribute {0} value {1} to model WLSDPLY-06108=Attribute {0} has default value and will not be added to the model WLSDPLY-06109=Problem with the model folder {0} for the model definition at location {1} : {2} -WLSDPLY-06110=The mbean type {0} is not defined for the model and will not be added to the model +WLSDPLY-06110=The MBean type {0} is not defined for the model and will not be added to the model WLSDPLY-06111=Locate WLST MBean names at location {0} -WLSDPLY-06112=The mbean type {0} for {1} is marked as singleton but has {2} name entries +WLSDPLY-06112=The MBean type {0} for {1} is marked as singleton but has {2} name entries WLSDPLY-06113=Adding entity with name {0} to {1} WLSDPLY-06114=Discover current location context {0} WLSDPLY-06115=Discovering {0} at {1} WLSDPLY-06116=Check to see if the model folder {0} is a named folder while discovering {1}. Returned location token {2} -WLSDPLY-06117=Locate model subfolder name for wlst mbean {0} while discovering {1} -WLSDPLY-06118=Model name {0} returned for wlst name {1} -WLSDPLY-06119=The mbean type {0} for is not present in {1} mode for wls version {2} +WLSDPLY-06117=Locate model subfolder name for WLST MBean {0} while discovering {1} +WLSDPLY-06118=Model name {0} returned for WLST name {1} +WLSDPLY-06119=The MBean type {0} for is not present in {1} mode for wls version {2} WLSDPLY-06120=Add artificial folder type {0} with name {1} to {2} WLSDPLY-06121=Invalid location returned from wlst.cd() to {0} WLSDPLY-06122=Invalid security type MBean interface {0} : {1} @@ -509,7 +510,7 @@ WLSDPLY-06123=Unable to determine the security type for {0}. This security entit WLSDPLY-06124=Entity at location {0} is a proxy that does not have a registered MBean interface name : {1} WLSDPLY-06125=Unable to locate model name for MBean interface name {0} for {1} WLSDPLY-06126=Locate model name for MBean interface {0} for {1} -WLSDPLY-06127=Unable to discover wlst attribute {0} at location {1} : {2} +WLSDPLY-06127=Unable to discover WLST attribute {0} at location {1} : {2} WLSDPLY-06130=Unexpected exception attempting to discover MBean entries at {0} will prevent the discovery \ of attributes at this location : {1} WLSDPLY-06131=Attribute {0} retrieved from lsa() map @@ -519,12 +520,13 @@ WLSDPLY-06141=Add online attribute {0} and value to the current attribute lsa() WLSDPLY-06142=The online attribute {0} is in the lsa() list but is a folder not an attribute WLSDPLY-06143=Attribute {0} at location {1} is not writable and is deprecated WLSDPLY-06144=Folder {0} at location {1} is not an online folder. Removing from online subfolder list -WLSDPLY-06145=Subfolder list {0} at location {1} does not match the mbi containment folder list {2} +WLSDPLY-06145=Subfolder list {0} at location {1} does not match the MBI containment folder list {2} WLSDPLY-06146=Discovered WLST MBean names {0} at location {1} -WLSDPLY-06147=Call method {0} to get the value for wlst attribute {1} at wlst path {2} -WLSDPLY-06149=Additional attributes that are not in the LSA attributes at location {0} are {1} +WLSDPLY-06147=Call method {0} to get the value for WLST attribute {1} at wlst path {2} +WLSDPLY-06148=MBean {0} of type {1} at location {2} is a custom MBean +WLSDPLY-06149=Additional attributes that are not in the LSA attributes for location {0} are {1} WLSDPLY-06150=Unable to determine if additional attributes are available for {0} at location {1} : {2} -WLSDPLY-06151=Additional attribute {0} for model folder at location {1} requires getter on mbean instance +WLSDPLY-06151=Additional attribute {0} for model folder at location {1} requires getter on MBean instance WLSDPLY-06152=Attribute {0} for model folder at location {1} requires the CMO getter to retrieve the attribute value WLSDPLY-06153=Attribute {0} for model folder at location {1} is not in the lsa map and is not defined in the \ alias definitions @@ -707,6 +709,43 @@ WLSDPLY-06709=Discover Multi-tenant Topology WLSDPLY-06710=Discovering {0} Virtual Targets WLSDPLY-06711=Adding {0} to Virtual Targets +# custom_folder_helper.py and +WLSDPLY-06750=Unable to determine the provider type for the Custom Security Provider {0} MBean {1} +WLSDPLY-06751=Discover {0} Custom MBean {1} +WLSDPLY-06752=Adding {0} to {1} the Custom MBean {2} +WLSDPLY-06753=Unable to discover {0} Custom MBean {1} with name {2}. The MBean was not loaded \ + into the current WLST session. +WLSDPLY-06754=Attribute {0} for the Custom MBean {1} is read-only +WLSDPLY-06755=Attribute {0} value is default value {2} for the Custom MBean {1} +WLSDPLY-06756={0}={1} for Custom MBean {2} +WLSDPLY-06757=The Custom MBean type is {0} +WLSDPLY-06758=Custom MBean {0} property descriptor attribute {1} +WLSDPLY-06759=Custom MBean {0} attribute {1} does not have a valid getter +WLSDPLY-06760=Custom MBean {0} attribute {1} getter is {2} +WLSDPLY-06761=MBean instance for location {0} was not returned from WLST request : {1} +WLSDPLY-06762={0} attribute {1} has default value of {2} +WLSDPLY-06763={0} attribute {1} data type is {2} +WLSDPLY-06764=Clear text password attribute with data type {0} will return None +WLSDPLY-06765=Unable to convert encrypted attribute with data type {0} +WLSDPLY-06766=Data type is {0} which is not supported. +WLSDPLY-06767=Java data type BigInteger with value {0} was converted to data type {1}. +WLSDPLY-06768=Value with data type {0} was converted to data type {0} +WLSDPLY-06769=Problem converting value with data type {0} to data type {1} : {2} +WLSDPLY-06770={0} Attribute {1} converted to model value {3} as data type {2} +WLSDPLY-06771={0} attribute {1} with data type {2} will be skipped +WLSDPLY-06772=Compare model value {0} with data type of {1} to default value {2} +WLSDPLY-06773=Model value equal to default value is {0} +WLSDPLY-06774=Unable to convert value {0} python type {1} to BigInteger : {2} +WLSDPLY-06775=If WLST offline model type {0} has an empty value {0} but the default value {1} \ +is not empty, consider the model value a default_value +WLSDPLY-06776={0} attribute {1} read-only and will not be added to the model +WLSDPLY-06777={0} attribute {1} is clear text encrypted and will not be added to the model + +#oracle.weblogic.deploy.discover.CustomBeanUtils +WLSDPLY-06800=Byte buffer for encrypted field contains an invalid multi-string encrypted value + +WLSDPLY-06801=Unable to convert encrypted byte array to String value + ############################################################################### # Aliases messages (08000 - 08999) # ############################################################################### diff --git a/core/src/test/java/oracle/weblogic/deploy/util/CustomBeanUtilsTest.java b/core/src/test/java/oracle/weblogic/deploy/create/CustomBeanUtilsTest.java similarity index 97% rename from core/src/test/java/oracle/weblogic/deploy/util/CustomBeanUtilsTest.java rename to core/src/test/java/oracle/weblogic/deploy/create/CustomBeanUtilsTest.java index bb53e05242..9645c3849f 100644 --- a/core/src/test/java/oracle/weblogic/deploy/util/CustomBeanUtilsTest.java +++ b/core/src/test/java/oracle/weblogic/deploy/create/CustomBeanUtilsTest.java @@ -2,8 +2,9 @@ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. */ -package oracle.weblogic.deploy.util; +package oracle.weblogic.deploy.create; +import oracle.weblogic.deploy.create.CustomBeanUtils; import org.junit.Assert; import org.junit.Test; diff --git a/core/src/test/python/custom_folder_helper_test.py b/core/src/test/python/custom_folder_helper_test.py new file mode 100644 index 0000000000..6d843979a1 --- /dev/null +++ b/core/src/test/python/custom_folder_helper_test.py @@ -0,0 +1,290 @@ +""" +Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. +The Universal Permissive License (UPL), Version 1.0 +""" + +import unittest + +from org.python.modules import jarray + +from java.lang import Boolean +from java.util import HashMap +from java.lang import String +from java.io import ByteArrayOutputStream +from java.io import DataOutputStream +from java.io import IOException +from java.util import Properties + +from oracle.weblogic.deploy.util import PyOrderedDict + +import wlsdeploy.aliases.alias_constants as alias_constants +import wlsdeploy.logging.platform_logger as platform_logger +from wlsdeploy.aliases.aliases import Aliases +from wlsdeploy.aliases.wlst_modes import WlstModes +from wlsdeploy.exception.expection_types import ExceptionType +from wlsdeploy.tool.discover.custom_folder_helper import CustomFolderHelper +from wlsdeploy.util.cla_utils import CommandLineArgUtil as CLA +from wlsdeploy.util.model_context import ModelContext + + +class CustomFolderHelperTestCase(unittest.TestCase): + """ + + Test the Custom MBean attribute conversion routines which convert from WLST value to Model value. + Test comparison of converted model value to default value. + """ + + _custom_helper = None + _logger = platform_logger.PlatformLogger('wlsdeploy.unittest') + _aliases = None + _model_context = None + + _wls_version = '12.2.1.3' + + def setUp(self): + arg_map = dict() + arg_map[CLA.ORACLE_HOME_SWITCH] = '/my/path/to/oracle' + arg_map[CLA.TARGET_MODE_SWITCH] = 'offline' + + self._model_context = ModelContext("test", arg_map) + self._aliases = Aliases(model_context=self._model_context, wlst_mode=WlstModes.OFFLINE, + wls_version=self._wls_version) + self._custom_helper = CustomFolderHelper(self._aliases, self._logger, self._model_context, + ExceptionType.DISCOVER) + return + + def testEncryptedPassword(self): + credential_string = 'AES}0vlIcO+I+VWV9aQ1wzQUa1qtByh4D9d0I1dJHa7HsdE=' + try: + bos = ByteArrayOutputStream() + dos = DataOutputStream(bos) + dos.writeBytes(credential_string) + byte_array = bos.toByteArray() + dos.close() + except IOException, ioe: + self.fail('Unexpected exception writing out credential : ', str(ioe)) + converted_type, converted_value = self._custom_helper.convert(byte_array, '[B') + self.common_test_converted(alias_constants.PASSWORD_TOKEN, alias_constants.PASSWORD, + converted_value, converted_type) + + def testListEqualsDefault(self): + list_value = ['foo', 'bar'] + list_default = list(list_value) + converted_type, converted_value = self._custom_helper.convert(list_value, 'list') + self.common_test_converted(list_value, alias_constants.LIST, converted_value, converted_type) + self.common_test_default(list_default, 'list', converted_value, converted_type, True) + + def testListNotDefaultLengthDiff(self): + list_value = ['foo', 'bar'] + list_default = ['bar'] + converted_type, converted_value = self._custom_helper.convert(list_value, 'list') + self.common_test_converted(list_value, alias_constants.LIST, converted_value, converted_type) + self.common_test_default(list_default, 'list', converted_value, converted_type, False) + + def testListNotDefaultElementsDiff(self): + list_value = ['abc', 'xyz'] + list_default = ['bar', 'foo'] + converted_type, converted_value = self._custom_helper.convert(list_value, 'list') + self.common_test_converted(list_value, alias_constants.LIST, converted_value, converted_type) + self.common_test_default(list_default, 'list', converted_value, converted_type, False) + + def testJarrayEqualsDefault(self): + jarray_value = jarray.array(['Idcs_user_assertion', 'idcs_user_assertion'], String) + expected_value = ['Idcs_user_assertion', 'idcs_user_assertion'] + jarray_default = ['idcs_user_assertion', 'Idcs_user_assertion'] + converted_type, converted_value = self._custom_helper.convert(jarray_value, '[L') + self.common_test_converted(expected_value, alias_constants.JARRAY, converted_value, converted_type) + self.common_test_default(jarray_default, str(type(jarray_default)), converted_value, converted_type, True) + + def testJarryDoesNotEqualDefaultDiffLength(self): + jarray_value = jarray.array(['idcs_user_assertion'], String) + expected_value = ['idcs_user_assertion'] + jarray_default = ['idcs_user_assertion', 'Idcs_user_assertion'] + converted_type, converted_value = self._custom_helper.convert(jarray_value, 'PyArray') + self.common_test_converted(expected_value, alias_constants.JARRAY, converted_value, converted_type) + self.common_test_default(jarray_default, 'PyArray', converted_value, converted_type, False) + + def testJarryDoesNotEqualDefaultDiffValue(self): + jarray_value = jarray.array(['nonmatch', 'idcs_user_assertion'], String) + expected_value = ['nomatch', 'idcs_user_assertion'] + jarray_default = ['idcs_user_assertion', 'Idcs_user_assertion'] + converted_type, converted_value = self._custom_helper.convert(jarray_value, 'PyArray') + self.common_test_converted(expected_value, alias_constants.JARRAY, converted_value, converted_type) + self.common_test_default(jarray_default, str(type(jarray_default)), converted_value, converted_type, False) + + def testMatchesEmptyJarray(self): + jarray_value = None + expected_value = [] + jarray_default = jarray.array([], String) + converted_type, converted_value = self._custom_helper.convert(jarray_value, '[L') + self.common_test_converted(expected_value, alias_constants.JARRAY, converted_value, converted_type) + self.common_test_default(jarray_default, 'PyArray', converted_value, converted_type, True) + + def testBooleanDefault(self): + bool_value = Boolean(False) + expected_value = 'false' + expected_default = expected_value + converted_type, converted_value = self._custom_helper.convert(bool_value, 'java.lang.Boolean') + self.common_test_converted(expected_value, alias_constants.BOOLEAN, converted_value, converted_type) + self.common_test_default(expected_default, 'bool', converted_value, converted_type, True) + + def testBooleanNotDefault(self): + bool_value = False + expected_value = 'false' + expected_default = True + converted_type, converted_value = self._custom_helper.convert(bool_value, 'bool') + self.common_test_converted(expected_value, alias_constants.BOOLEAN, converted_value, converted_type) + self.common_test_default(expected_default, 'bool', converted_value, converted_type, False) + + def testDictionaryDefault(self): + dict_value = {'integer1': 111, 'integer2': 112} + dict_default = {'integer2': 112, 'integer1': 111} + converted_type, converted_value = self._custom_helper.convert(dict_value, 'dict') + self.common_test_converted(dict_value, alias_constants.PROPERTIES, converted_value, converted_type) + self.assertEquals(PyOrderedDict, type(converted_value)) + self.common_test_default(dict_default, 'dict', converted_value, converted_type, True) + + def testDictionaryNotDefault(self): + dict_value = {'integer1': 111, 'integer2': 112} + dict_default = dict() + converted_type, converted_value = self._custom_helper.convert(dict_value, 'dict') + self.common_test_converted(dict_value, alias_constants.PROPERTIES, converted_value, converted_type) + self.assertEquals(PyOrderedDict, type(converted_value)) + self.common_test_default(dict_default, 'dict', converted_value, converted_type, False) + + def testPropertiesDefault(self): + prop_value = Properties() + prop_value.setProperty('value1', 'foo') + prop_value.setProperty('value2', 'bar') + prop_value.setProperty('value3', 'equal') + prop_default = Properties(prop_value) + expected_value = {'value3': 'equal', 'value1': 'foo', 'value2': 'bar'} + converted_type, converted_value = self._custom_helper.convert(prop_value, 'java.util.Properties') + self.common_test_converted(expected_value, alias_constants.PROPERTIES, converted_value, converted_type) + self.common_test_default(prop_default, 'java.util.Properties', converted_value, converted_type, True) + + def testPropertiesNotDefault(self): + prop_value = Properties() + prop_value.setProperty('value1', 'foo') + prop_value.setProperty('value2', 'bar') + prop_value.setProperty('value3', 'equal') + expected_value = {'value1': 'foo', 'value2': 'bar', 'value3': 'equal'} + prop_default = {'value2': 'foo', 'value1': 'bar', 'value3': 'equal'} + converted_type, converted_value = self._custom_helper.convert(prop_value, 'java.util.Properties') + self.common_test_converted(expected_value, alias_constants.PROPERTIES, converted_value, converted_type) + self.assertEquals(PyOrderedDict, type(converted_value)) + self.common_test_default(prop_default, 'dict', converted_value, converted_type, False) + + def testMapDefault(self): + map_value = HashMap() + map_value.put('value1', 'foo') + map_value.put('value2', 'bar') + map_value.put('value3', 'equal') + expected_value = {'value1': 'foo', 'value2': 'bar', 'value3': 'equal'} + map_default = HashMap() + map_default.put('value1', 'foo') + map_default.put('value2', 'bar') + map_default.put('value3', 'equal') + + converted_type, converted_value = self._custom_helper.convert(map_value, 'java.util.Map') + self.common_test_converted(expected_value, alias_constants.PROPERTIES, converted_value, converted_type) + self.assertEquals(PyOrderedDict, type(converted_value)) + self.common_test_default(map_default, 'java.util.Map', converted_value, converted_type, True) + + def testMapNotDefault(self): + map_value = HashMap() + map_value.put('value1', 'foo') + map_value.put('value2', 'bar') + map_value.put('value3', 'equal') + expected_value = {'value1': 'foo', 'value2': 'bar', 'value3': 'equal'} + + map_default = dict() + map_default['value1'] = 'eightball' + map_default['value2'] = 'bar' + map_default['value3'] = 'equal' + + converted_type, converted_value = self._custom_helper.convert(map_value, 'java.util.Map') + self.common_test_converted(expected_value, alias_constants.PROPERTIES, converted_value, converted_type) + self.assertEquals(alias_constants.PROPERTIES, converted_type) + self.common_test_default(map_default, 'dict', converted_value, converted_type, False) + + def testIntegerDefault(self): + port_value = '0' + expected_value = 0 + default_value = expected_value + converted_type, converted_value = self._custom_helper.convert(port_value, 'java.lang.Integer') + self.common_test_converted(expected_value, alias_constants.INTEGER, converted_value, converted_type) + self.common_test_default(default_value, 'int', converted_value, converted_type, True) + + def testIntegerNotDefault(self): + port_value = 1443 + expected_value = port_value + default_value = 0 + converted_type, converted_value = self._custom_helper.convert(port_value, 'int') + self.common_test_converted(expected_value, alias_constants.INTEGER, converted_value, converted_type) + self.common_test_default(default_value, 'int', converted_value, converted_type, False) + + def testBigIntegerConvert(self): + big_value = '67999' + expected_value = 67999L + default_value = 0 + converted_type, converted_value = self._custom_helper.convert(big_value, 'java.math.BigInteger') + self.common_test_converted(expected_value, alias_constants.LONG, converted_value, converted_type) + self.common_test_default(default_value, 'long', converted_value, converted_type, False) + + def testDoubleConvert(self): + double_value = '67999' + expected_value = 67999 + default_value = expected_value + converted_type, converted_value = self._custom_helper.convert(double_value, 'java.lang.Double') + self.common_test_converted(expected_value, alias_constants.DOUBLE, converted_value, converted_type) + self.common_test_default(default_value, 'double', converted_value, converted_type, True) + + def testFloatConvert(self): + float_value = 4.2e-4 + expected_value = None + converted_type, converted_value = self._custom_helper.convert(float_value, 'float') + self.common_test_converted(expected_value, None, converted_value, converted_type) + + def common_test_default(self, default_value, default_type, model_value, model_type, expected): + converted_type, converted_default = self._custom_helper.convert(default_value, default_type) + return_result = self._custom_helper.is_default(model_value, model_type, converted_default) + self.assertEquals(expected, return_result) + + def common_test_converted(self, expected_value, expected_type, model_value, model_type): + + self.assertEquals(expected_type, model_type) + if expected_type == alias_constants.LIST: + self.is_expected_list(expected_value, model_value) + elif expected_type == alias_constants.PROPERTIES: + self.is_expected_dict(expected_value, model_value) + else: + self.is_expected(expected_value, model_value) + + def is_expected(self, expected_value, model_value): + return expected_value is not None and model_value is not None and expected_value == model_value + + def is_expected_list(self, expected_list, converted_list): + return expected_list is not None and converted_list is not None and \ + self.roll_list(expected_list, converted_list) and self.roll_list(converted_list, expected_list) + + def roll_list(self, list1, list2): + for item in list1: + if item not in list2: + return False + return True + + def is_expected_dict(self, expected_dict, converted_dict): + return expected_dict is not None and converted_dict is not None and \ + self.roll_dict(expected_dict, converted_dict) and self.roll_dict(converted_dict, expected_dict) + + def roll_dict(self, dict1, dict2): + dict1_keys = dict1.keys() + for key in dict2.keys(): + if key not in dict1_keys or dict2[key] != dict1[key]: + return False + return True + + +if __name__ == '__main__': + unittest.main()