3232from oracle .weblogic .deploy .validate import ValidateException
3333
3434import oracle .weblogic .deploy .util .TranslateException as TranslateException
35+ from wlsdeploy .aliases import alias_utils
36+ from wlsdeploy .aliases .alias_constants import ALIAS_LIST_TYPES
3537from wlsdeploy .aliases .aliases import Aliases
3638from wlsdeploy .aliases .location_context import LocationContext
3739from wlsdeploy .aliases .wlst_modes import WlstModes
4244from wlsdeploy .logging .platform_logger import PlatformLogger
4345from wlsdeploy .tool .validate .validator import Validator
4446from wlsdeploy .util import cla_helper
47+ from wlsdeploy .util import dictionary_utils
48+ from wlsdeploy .util import model_helper
4549from wlsdeploy .util import variables
4650from wlsdeploy .util .cla_utils import CommandLineArgUtil
4751from wlsdeploy .util .model_context import ModelContext
@@ -87,7 +91,8 @@ def __process_args(args):
8791
8892class ModelDiffer :
8993
90- def __init__ (self , current_dict , past_dict ):
94+ def __init__ (self , current_dict , past_dict , aliases ):
95+ self .aliases = aliases
9196 self .final_changed_model = PyOrderedDict ()
9297 self .current_dict = current_dict
9398 self .past_dict = past_dict
@@ -136,7 +141,7 @@ def recursive_changed_detail(self, key, token, root):
136141 """
137142 debug ("DEBUG: Entering recursive_changed_detail key=%s token=%s root=%s" , key , token , root )
138143
139- a = ModelDiffer (self .current_dict [key ], self .past_dict [key ])
144+ a = ModelDiffer (self .current_dict [key ], self .past_dict [key ], self . aliases )
140145 diff = a .changed ()
141146 added = a .added ()
142147 removed = a .removed ()
@@ -243,112 +248,130 @@ def calculate_changed_model(self):
243248 _logger .throwing (ex , class_name = _class_name , method_name = _method_name )
244249 raise ex
245250
246- def _is_alias_folder (self , path ):
251+ def _parse_change_path (self , path ):
247252 """
248- Check if the delimited path is a folder or attribute
249- :param path: '|' delimited path
250- :return: true if it is a folder otherwise false
253+ Determine the location and attribute name (if specified) for the specified change path
254+ :param path: delimited change path, such as "resources|JDBCSystemResource|Generic2|JdbcResource"
255+ :return: tuple - location for path, attribute name from path or None
251256 """
252- debug ("DEBUG: Entering is_alias_folder %s" , path )
253- path_tokens = path .split (PATH_TOKEN )
254- model_context = ModelContext ("test" , {})
255- location = LocationContext ()
256- last_token = path_tokens [- 1 ]
257- aliases = Aliases (model_context , wlst_mode = WlstModes .OFFLINE , exception_type = ExceptionType .COMPARE )
257+ _method_name = '_parse_change_path'
258258
259- found = True
259+ location = LocationContext ()
260+ attribute_name = None
260261 name_token_next = False
262+
263+ path_tokens = path .split (PATH_TOKEN )
264+ folder_names = self .aliases .get_model_section_top_level_folder_names (path_tokens [0 ])
265+
261266 for path_token in path_tokens [1 :]:
267+ attribute_names = self .aliases .get_model_attribute_names (location )
268+
262269 if name_token_next :
263- token_name = aliases .get_name_token (location )
270+ token_name = self . aliases .get_name_token (location )
264271 location .add_name_token (token_name , path_token )
265272 name_token_next = False
266- else :
273+ elif path_token in folder_names :
267274 location .append_location (path_token )
268- if last_token == path_token :
269- break
270- name_token_next = aliases .supports_multiple_mbean_instances (location )
271- attrib_names = aliases .get_model_attribute_names (location )
272- if last_token in attrib_names :
273- found = False
274-
275- debug ("DEBUG: is_alias_folder %s %s" , path , found )
275+ folder_names = self .aliases .get_model_subfolder_names (location )
276+ regular_type = not self .aliases .is_artificial_type_folder (location )
277+ security_type = regular_type and self .aliases .is_security_provider_type (location )
278+ multiple_type = regular_type and self .aliases .supports_multiple_mbean_instances (location )
279+ if multiple_type or security_type :
280+ name_token_next = True
281+ else :
282+ token_name = self .aliases .get_name_token (location )
283+ if not location .get_name_for_token (token_name ):
284+ location .add_name_token (token_name , "TOKEN" )
285+ elif path_token in attribute_names :
286+ attribute_name = path_token
287+ name_token_next = False
288+ else :
289+ ex = exception_helper .create_compare_exception ('WLSDPLY-05712' , path_token , path )
290+ _logger .throwing (ex , class_name = _class_name , method_name = _method_name )
291+ raise ex
276292
277- return found
293+ return location , attribute_name
278294
279- def _add_results (self , ar_changes , is_delete = False , is_change = False ):
295+ def _add_results (self , change_paths , is_delete = False ):
280296 """
281297 Update the differences in the final model dictionary with the changes
282- :param ar_changes: Array of changes in delimited format
298+ :param change_paths: Array of changes in delimited format
299+ :param is_delete: flag indicating to delete paths
283300 """
284- # The ar_changes is the keys of changes in the piped format
285- # 'resources|JDBCSystemResource|Generic2|JdbcResource|JDBCConnectionPoolParams|TestConnectionsOnReserve
286- #
287301 parent_index = - 2
288- for item in ar_changes :
289- if is_delete :
290- # Skipp adding if it is a delete of an attribute
291- found_in_allowable_delete = self ._is_alias_folder (item )
292- if not found_in_allowable_delete :
293- compare_msgs .add (('WLSDPLY-05701' , item ))
294- continue
295- splitted = item .split (PATH_TOKEN , 1 )
296- n = len (splitted )
297- result = PyOrderedDict ()
298- walked = []
299-
300- while n > 1 :
301- tmp = PyOrderedDict ()
302- tmp [splitted [0 ]] = PyOrderedDict ()
303- if len (result ) > 0 :
304- # traverse to the leaf
305- leaf = result
306- for k in walked :
307- leaf = leaf [k ]
308- leaf [splitted [0 ]] = PyOrderedDict ()
309- walked .append (splitted [0 ])
302+ for change_path in change_paths :
303+ # change_path is the keys of changes in the piped format, such as:
304+ # resources|JDBCSystemResource|Generic2|JdbcResource|JDBCConnectionPoolParams|TestConnectionsOnReserve
305+ location , attribute_name = self ._parse_change_path (change_path )
306+ is_folder_path = attribute_name is None
307+
308+ if is_delete and not is_folder_path :
309+ # Skip adding if it is a delete of an attribute
310+ compare_msgs .add (('WLSDPLY-05701' , change_path ))
311+ continue
312+
313+ # splitted is a tuple containing the next token, and a delimited string of remaining tokens
314+ splitted = change_path .split (PATH_TOKEN , 1 )
315+
316+ # change_tree will be a nested dictionary containing the change path parent elements.
317+ # change_tokens is a list of parent tokens in change_tree.
318+ change_tree = PyOrderedDict ()
319+ change_tokens = []
320+
321+ while len (splitted ) > 1 :
322+ tmp_folder = PyOrderedDict ()
323+ tmp_folder [splitted [0 ]] = PyOrderedDict ()
324+ if len (change_tree ) > 0 :
325+ # traverse to the leaf folder
326+ change_folder = change_tree
327+ for token in change_tokens :
328+ change_folder = change_folder [token ]
329+ change_folder [splitted [0 ]] = PyOrderedDict ()
330+ change_tokens .append (splitted [0 ])
310331 else :
311- result = tmp
312- walked .append (splitted [0 ])
332+ change_tree = tmp_folder
333+ change_tokens .append (splitted [0 ])
313334 splitted = splitted [1 ].split (PATH_TOKEN , 1 )
314- n = len (splitted )
315- #
316- # result is the dictionary format
317- #
318- leaf = result
319- if is_change :
320- value_tree = self .past_dict
321- else :
322- value_tree = self .current_dict
323- for k in walked :
324- leaf = leaf [k ]
325- value_tree = value_tree [k ]
326- #
327- # walk the current dictionary and set the value
328- #
329- if value_tree :
330- if is_change :
331- leaf [COMMENT_MATCH + splitted [0 ]] = value_tree [splitted [0 ]]
332- else :
333- if value_tree [splitted [0 ]] is not None and not isinstance (value_tree [splitted [0 ]], PyOrderedDict ):
334- self ._add_results (ar_changes , is_delete , is_change = True )
335- leaf [splitted [0 ]] = value_tree [splitted [0 ]]
335+
336+ # key is the last name in the change path
337+ key = splitted [0 ]
338+
339+ # find the specified folder in the change tree and in the current and previous models
340+ change_folder = change_tree
341+ current_folder = self .current_dict
342+ previous_folder = self .past_dict
343+ for token in change_tokens :
344+ change_folder = change_folder [token ]
345+ current_folder = current_folder [token ]
346+ previous_folder = dictionary_utils .get_dictionary_element (previous_folder , token )
347+
348+ # set the value in the change folder if present.
349+ # merge new and previous values if relevant.
350+ # add a comment if the previous value was found.
351+ if current_folder :
352+ current_value = current_folder [key ]
353+ previous_value = dictionary_utils .get_element (previous_folder , key )
354+ change_value , comment = self ._get_change_info (current_value , previous_value , location , attribute_name )
355+
356+ if comment :
357+ change_folder [COMMENT_MATCH ] = comment
358+ change_folder [key ] = change_value
336359 else :
337- leaf [ splitted [ 0 ] ] = None
360+ change_folder [ key ] = None
338361
339- self .merge_dictionaries (self .final_changed_model , result )
362+ # merge the change tree into the final model
363+ self .merge_dictionaries (self .final_changed_model , change_tree )
340364
341365 # if it is a deletion then go back and update with '!'
342366
343367 if is_delete :
344- is_folder_path = self ._is_alias_folder (item )
345- split_delete = item .split (PATH_TOKEN )
368+ split_delete = change_path .split (PATH_TOKEN )
346369 # allowable_delete_length = len(allowable_delete.split(PATH_TOKEN))
347370 split_delete_length = len (split_delete )
348371 if is_folder_path :
349372 app_key = split_delete [split_delete_length - 1 ]
350373 parent_key = split_delete [parent_index ]
351- debug ("DEBUG: deleting folder %s from the model: key %s " , item , app_key )
374+ debug ("DEBUG: deleting folder %s from the model: key %s " , change_path , app_key )
352375 pointer_dict = self .final_changed_model
353376 for k_item in split_delete :
354377 if k_item == parent_key :
@@ -364,6 +387,44 @@ def _add_results(self, ar_changes, is_delete=False, is_change=False):
364387 else :
365388 pointer_dict [parent_key ]['!' + app_key ] = PyOrderedDict ()
366389
390+ def _get_change_info (self , current_value , previous_value , location , attribute_name ):
391+ """
392+ Determine the value and comment to put in the change model based on the supplied arguments.
393+ :param current_value: the current value from the new model
394+ :param previous_value: the previous value from the old model
395+ :param location: the location of the value in the model
396+ :param attribute_name: the name of the attribute, or None if this is a folder path
397+ :return: a tuple with the change value and comment, either can be None
398+ """
399+ change_value = current_value
400+ comment = None
401+
402+ if attribute_name and (previous_value is not None ):
403+ attribute_type = self .aliases .get_model_attribute_type (location , attribute_name )
404+ if attribute_type in ALIAS_LIST_TYPES :
405+ current_list = alias_utils .create_list (current_value , 'WLSDPLY-08001' )
406+ previous_list = alias_utils .create_list (previous_value , 'WLSDPLY-08000' )
407+
408+ change_list = list (previous_list )
409+ for item in current_list :
410+ if item in previous_list :
411+ change_list .remove (item )
412+ else :
413+ change_list .append (item )
414+ for item in previous_list :
415+ if item not in current_list :
416+ change_list .remove (item )
417+ change_list .append (model_helper .get_delete_name (item ))
418+ change_value = ',' .join (change_list )
419+
420+ current_text = ',' .join (current_list )
421+ previous_text = ',' .join (previous_list )
422+ comment = attribute_name + ": '" + previous_text + "' -> '" + current_text + "'"
423+ elif not isinstance (previous_value , dict ):
424+ comment = attribute_name + ": '" + str (previous_value ) + "'"
425+
426+ return change_value , comment
427+
367428 def merge_dictionaries (self , dictionary , new_dictionary ):
368429 """
369430 Merge the values from the new dictionary to the existing one.
@@ -432,7 +493,8 @@ def compare(self):
432493
433494 self .model_context .set_validation_method ('lax' )
434495
435- aliases = Aliases (model_context = self .model_context , wlst_mode = WlstModes .OFFLINE )
496+ aliases = Aliases (model_context = self .model_context , wlst_mode = WlstModes .OFFLINE ,
497+ exception_type = ExceptionType .COMPARE )
436498
437499 validator = Validator (self .model_context , aliases , wlst_mode = WlstModes .OFFLINE )
438500
@@ -498,7 +560,7 @@ def compare(self):
498560 _logger .throwing (ex , class_name = _class_name , method_name = _method_name )
499561 return VALIDATION_FAIL
500562
501- obj = ModelDiffer (current_dict , past_dict )
563+ obj = ModelDiffer (current_dict , past_dict , aliases )
502564 obj .calculate_changed_model ()
503565 net_diff = obj .get_final_changed_model ()
504566
0 commit comments