32
32
from oracle .weblogic .deploy .validate import ValidateException
33
33
34
34
import oracle .weblogic .deploy .util .TranslateException as TranslateException
35
+ from wlsdeploy .aliases import alias_utils
36
+ from wlsdeploy .aliases .alias_constants import ALIAS_LIST_TYPES
35
37
from wlsdeploy .aliases .aliases import Aliases
36
38
from wlsdeploy .aliases .location_context import LocationContext
37
39
from wlsdeploy .aliases .wlst_modes import WlstModes
42
44
from wlsdeploy .logging .platform_logger import PlatformLogger
43
45
from wlsdeploy .tool .validate .validator import Validator
44
46
from wlsdeploy .util import cla_helper
47
+ from wlsdeploy .util import dictionary_utils
48
+ from wlsdeploy .util import model_helper
45
49
from wlsdeploy .util import variables
46
50
from wlsdeploy .util .cla_utils import CommandLineArgUtil
47
51
from wlsdeploy .util .model_context import ModelContext
@@ -87,7 +91,8 @@ def __process_args(args):
87
91
88
92
class ModelDiffer :
89
93
90
- def __init__ (self , current_dict , past_dict ):
94
+ def __init__ (self , current_dict , past_dict , aliases ):
95
+ self .aliases = aliases
91
96
self .final_changed_model = PyOrderedDict ()
92
97
self .current_dict = current_dict
93
98
self .past_dict = past_dict
@@ -136,7 +141,7 @@ def recursive_changed_detail(self, key, token, root):
136
141
"""
137
142
debug ("DEBUG: Entering recursive_changed_detail key=%s token=%s root=%s" , key , token , root )
138
143
139
- a = ModelDiffer (self .current_dict [key ], self .past_dict [key ])
144
+ a = ModelDiffer (self .current_dict [key ], self .past_dict [key ], self . aliases )
140
145
diff = a .changed ()
141
146
added = a .added ()
142
147
removed = a .removed ()
@@ -243,112 +248,130 @@ def calculate_changed_model(self):
243
248
_logger .throwing (ex , class_name = _class_name , method_name = _method_name )
244
249
raise ex
245
250
246
- def _is_alias_folder (self , path ):
251
+ def _parse_change_path (self , path ):
247
252
"""
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
251
256
"""
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'
258
258
259
- found = True
259
+ location = LocationContext ()
260
+ attribute_name = None
260
261
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
+
261
266
for path_token in path_tokens [1 :]:
267
+ attribute_names = self .aliases .get_model_attribute_names (location )
268
+
262
269
if name_token_next :
263
- token_name = aliases .get_name_token (location )
270
+ token_name = self . aliases .get_name_token (location )
264
271
location .add_name_token (token_name , path_token )
265
272
name_token_next = False
266
- else :
273
+ elif path_token in folder_names :
267
274
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
276
292
277
- return found
293
+ return location , attribute_name
278
294
279
- def _add_results (self , ar_changes , is_delete = False , is_change = False ):
295
+ def _add_results (self , change_paths , is_delete = False ):
280
296
"""
281
297
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
283
300
"""
284
- # The ar_changes is the keys of changes in the piped format
285
- # 'resources|JDBCSystemResource|Generic2|JdbcResource|JDBCConnectionPoolParams|TestConnectionsOnReserve
286
- #
287
301
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 ])
310
331
else :
311
- result = tmp
312
- walked .append (splitted [0 ])
332
+ change_tree = tmp_folder
333
+ change_tokens .append (splitted [0 ])
313
334
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
336
359
else :
337
- leaf [ splitted [ 0 ] ] = None
360
+ change_folder [ key ] = None
338
361
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 )
340
364
341
365
# if it is a deletion then go back and update with '!'
342
366
343
367
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 )
346
369
# allowable_delete_length = len(allowable_delete.split(PATH_TOKEN))
347
370
split_delete_length = len (split_delete )
348
371
if is_folder_path :
349
372
app_key = split_delete [split_delete_length - 1 ]
350
373
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 )
352
375
pointer_dict = self .final_changed_model
353
376
for k_item in split_delete :
354
377
if k_item == parent_key :
@@ -364,6 +387,44 @@ def _add_results(self, ar_changes, is_delete=False, is_change=False):
364
387
else :
365
388
pointer_dict [parent_key ]['!' + app_key ] = PyOrderedDict ()
366
389
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
+
367
428
def merge_dictionaries (self , dictionary , new_dictionary ):
368
429
"""
369
430
Merge the values from the new dictionary to the existing one.
@@ -432,7 +493,8 @@ def compare(self):
432
493
433
494
self .model_context .set_validation_method ('lax' )
434
495
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 )
436
498
437
499
validator = Validator (self .model_context , aliases , wlst_mode = WlstModes .OFFLINE )
438
500
@@ -498,7 +560,7 @@ def compare(self):
498
560
_logger .throwing (ex , class_name = _class_name , method_name = _method_name )
499
561
return VALIDATION_FAIL
500
562
501
- obj = ModelDiffer (current_dict , past_dict )
563
+ obj = ModelDiffer (current_dict , past_dict , aliases )
502
564
obj .calculate_changed_model ()
503
565
net_diff = obj .get_final_changed_model ()
504
566
0 commit comments