codekingpro/portable-devtools
114k
1##########################################################################2#3# pgAdmin 4 - PostgreSQL Tools4#5# Copyright (C) 2013 - 2024, The pgAdmin Development Team6# This software is released under the PostgreSQL Licence7#8##########################################################################9 10"""Directory comparison"""11 12import copy13import string14from flask import current_app15from flask_babel import gettext16from pgadmin.utils.constants import PGADMIN_STRING_SEPARATOR17 18count = 119 20list_keys_array = ['name', 'colname', 'argid', 'token', 'option', 'conname',21 'member_name', 'label', 'attname', 'fdwoption',22 'fsrvoption', 'umoption']23 24SPECIAL_NODES = ['table', 'view', 'mview']25VIEW_NODES = ['view', 'mview']26 27 28def _get_user_mapping_name(user_mapping_name):29 """30 This function is used to check the pgadmin string separator in the31 specific string and split that.32 """33 mapping_name = user_mapping_name34 35 if mapping_name.find(PGADMIN_STRING_SEPARATOR):36 mapping_name = mapping_name.split(PGADMIN_STRING_SEPARATOR)[0]37 38 return mapping_name39 40 41def _get_source_list(**kwargs):42 """43 Get only source list.44 :param kwargs45 :return: list of source dict.46 """47 added = kwargs.get('added')48 source_dict = kwargs.get('source_dict')49 node = kwargs.get('node')50 source_params = kwargs.get('source_params')51 view_object = kwargs.get('view_object')52 node_label = kwargs.get('node_label')53 group_name = kwargs.get('group_name')54 source_schema_name = kwargs.get('source_schema_name')55 target_schema = kwargs.get('target_schema')56 57 global count58 source_only = []59 for item in added:60 source_object_id = None61 if 'oid' in source_dict[item]:62 source_object_id = source_dict[item]['oid']63 64 if node in SPECIAL_NODES:65 temp_src_params = copy.deepcopy(source_params)66 temp_src_params['tid'] = source_object_id67 temp_src_params['json_resp'] = False68 temp_src_params['add_not_exists_clause'] = True69 if node in VIEW_NODES:70 source_ddl = \71 view_object.get_sql_from_view_diff(**temp_src_params)72 temp_src_params.update({'target_schema': target_schema})73 diff_ddl = (74 view_object.get_sql_from_view_diff(**temp_src_params))75 source_dependencies = \76 view_object.get_view_submodules_dependencies(77 **temp_src_params)78 else:79 source_ddl = \80 view_object.get_sql_from_table_diff(**temp_src_params)81 temp_src_params.update({'target_schema': target_schema})82 diff_ddl = (83 view_object.get_sql_from_table_diff(**temp_src_params))84 source_dependencies = \85 view_object.get_table_submodules_dependencies(86 **temp_src_params)87 else:88 temp_src_params = copy.deepcopy(source_params)89 temp_src_params['oid'] = source_object_id90 # Provide Foreign Data Wrapper ID91 if 'fdwid' in source_dict[item]:92 temp_src_params['fdwid'] = source_dict[item]['fdwid']93 # Provide Foreign Server ID94 if 'fsid' in source_dict[item]:95 temp_src_params['fsid'] = source_dict[item]['fsid']96 97 source_ddl = view_object.get_sql_from_diff(**temp_src_params)98 temp_src_params.update({'target_schema': target_schema})99 diff_ddl = view_object.get_sql_from_diff(**temp_src_params)100 source_dependencies = view_object.get_dependencies(101 view_object.conn, source_object_id, where=None,102 show_system_objects=None, is_schema_diff=True)103 104 title = item105 if node == 'user_mapping':106 title = _get_user_mapping_name(item)107 108 source_only.append({109 'id': count,110 'type': node,111 'label': node_label,112 'title': title,113 'oid': source_object_id,114 'status': gettext('Source Only'),115 'source_ddl': source_ddl,116 'target_ddl': '',117 'diff_ddl': diff_ddl,118 'group_name': group_name,119 'dependencies': source_dependencies,120 'source_schema_name': source_schema_name121 })122 count += 1123 124 return source_only125 126 127def _delete_keys(temp_tgt_params):128 """129 Delete keys from temp target parameters.130 :param temp_tgt_params:131 :type temp_tgt_params:132 :return:133 """134 if 'gid' in temp_tgt_params:135 del temp_tgt_params['gid']136 if 'json_resp' in temp_tgt_params:137 del temp_tgt_params['json_resp']138 if 'add_not_exists_clause' in temp_tgt_params:139 del temp_tgt_params['add_not_exists_clause']140 141 142def _get_target_list(removed, target_dict, node, target_params, view_object,143 node_label, group_name):144 """145 Get only target list.146 :param removed: removed list.147 :param target_dict: target dict.148 :param node: node type.149 :param target_params: target parameters.150 :param view_object: view object for get sql.151 :param node_label: node label.152 :param group_name: group name.153 :return: list of target dict.154 """155 global count156 target_only = []157 for item in removed:158 target_object_id = None159 if 'oid' in target_dict[item]:160 target_object_id = target_dict[item]['oid']161 162 if node in SPECIAL_NODES:163 temp_tgt_params = copy.deepcopy(target_params)164 temp_tgt_params['tid'] = target_object_id165 temp_tgt_params['json_resp'] = False166 temp_tgt_params['add_not_exists_clause'] = True167 if node in VIEW_NODES:168 target_ddl = (169 view_object.get_sql_from_view_diff(**temp_tgt_params))170 temp_tgt_params.update(171 {'drop_sql': True})172 diff_ddl = (173 view_object.get_sql_from_view_diff(**temp_tgt_params))174 else:175 target_ddl = (176 view_object.get_sql_from_table_diff(**temp_tgt_params))177 _delete_keys(temp_tgt_params)178 diff_ddl = view_object.get_drop_sql(**temp_tgt_params)179 else:180 temp_tgt_params = copy.deepcopy(target_params)181 temp_tgt_params['oid'] = target_object_id182 # Provide Foreign Data Wrapper ID183 if 'fdwid' in target_dict[item]:184 temp_tgt_params['fdwid'] = target_dict[item]['fdwid']185 # Provide Foreign Server ID186 if 'fsid' in target_dict[item]:187 temp_tgt_params['fsid'] = target_dict[item]['fsid']188 189 target_ddl = view_object.get_sql_from_diff(**temp_tgt_params)190 temp_tgt_params.update(191 {'drop_sql': True})192 diff_ddl = view_object.get_sql_from_diff(**temp_tgt_params)193 194 title = item195 if node == 'user_mapping':196 title = _get_user_mapping_name(item)197 198 target_only.append({199 'id': count,200 'type': node,201 'label': node_label,202 'title': title,203 'oid': target_object_id,204 'status': gettext('Target Only'),205 'source_ddl': '',206 'target_ddl': target_ddl,207 'diff_ddl': diff_ddl,208 'group_name': group_name,209 'dependencies': []210 })211 count += 1212 213 return target_only214 215 216def _check_add_req_ids(source_dict, target_dict, key, temp_src_params,217 temp_tgt_params):218 """219 Check for Foreign Data Wrapper ID and Foreign Server ID and update it220 in req parameters.221 :param source_dict: Source dict for compare schema.222 :param target_dict: Target dict for compare schema.223 :param key: Key for get obj.224 :param temp_src_params:225 :param temp_tgt_params:226 :return:227 """228 if 'fdwid' in source_dict[key]:229 temp_src_params['fdwid'] = source_dict[key]['fdwid']230 temp_tgt_params['fdwid'] = target_dict[key]['fdwid']231 # Provide Foreign Server ID232 if 'fsid' in source_dict[key]:233 temp_src_params['fsid'] = source_dict[key]['fsid']234 temp_tgt_params['fsid'] = target_dict[key]['fsid']235 236 237def get_source_target_oid(source_dict, target_dict, key):238 """239 Get source and target object ID.240 :param source_dict: Source schema diff data.241 :param target_dict: Target schema diff data.242 :param key: Key.243 :return: source and target object ID.244 """245 source_object_id = None246 target_object_id = None247 if 'oid' in source_dict[key]:248 source_object_id = source_dict[key]['oid']249 target_object_id = target_dict[key]['oid']250 251 return source_object_id, target_object_id252 253 254def _get_identical_and_different_list(intersect_keys, source_dict, target_dict,255 node, node_label, view_object,256 **kwargs):257 """258 get lists of identical and different keys list.259 :param intersect_keys:260 :param source_dict:261 :param target_dict:262 :param node:263 :param node_label:264 :param view_object:265 :param other_param:266 :return: return list of identical and different dict.267 """268 global count269 identical = []270 different = []271 dict1 = kwargs['dict1']272 dict2 = kwargs['dict2']273 ignore_keys = kwargs['ignore_keys']274 source_params = kwargs['source_params']275 target_params = kwargs['target_params']276 group_name = kwargs['group_name']277 target_schema = kwargs.get('target_schema')278 ignore_whitespaces = kwargs.get('ignore_whitespaces')279 ignore_grants = kwargs.get('ignore_grants', False)280 281 for key in intersect_keys:282 source_object_id, target_object_id = \283 get_source_target_oid(source_dict, target_dict, key)284 285 # Recursively Compare the two dictionary286 current_app.logger.debug(287 "Schema Diff: Source Dict: {0}".format(dict1[key]))288 current_app.logger.debug(289 "Schema Diff: Target Dict: {0}".format(dict2[key]))290 291 if are_dictionaries_identical(dict1[key], dict2[key], ignore_keys,292 ignore_whitespaces):293 title = key294 if node == 'user_mapping':295 title = _get_user_mapping_name(key)296 297 identical.append({298 'id': count,299 'type': node,300 'label': node_label,301 'title': title,302 'oid': source_object_id,303 'source_oid': source_object_id,304 'target_oid': target_object_id,305 'status': gettext('Identical'),306 'group_name': group_name,307 'dependencies': [],308 'source_scid': source_params['scid']309 if 'scid' in source_params else 0,310 'target_scid': target_params['scid']311 if 'scid' in target_params else 0,312 })313 else:314 if node in SPECIAL_NODES:315 temp_src_params = copy.deepcopy(source_params)316 temp_tgt_params = copy.deepcopy(target_params)317 # Add submodules into the ignore keys so that directory318 # difference won't include those in added, deleted and changed319 sub_module = ['index', 'rule', 'trigger', 'compound_trigger']320 temp_ignore_keys = ignore_keys + sub_module321 322 diff_dict = directory_diff(323 dict1[key], dict2[key],324 ignore_keys=temp_ignore_keys,325 difference={}326 )327 328 # No need to parse acl if ignore_grants is set to True.329 if not ignore_grants:330 parse_acl(dict1[key], dict2[key], diff_dict)331 332 temp_src_params['tid'] = source_object_id333 temp_tgt_params['tid'] = target_object_id334 335 if node in VIEW_NODES:336 source_ddl = \337 view_object.get_sql_from_view_diff(**temp_src_params)338 diff_dependencies = \339 view_object.get_view_submodules_dependencies(340 **temp_src_params)341 target_ddl = \342 view_object.get_sql_from_view_diff(**temp_tgt_params)343 else:344 temp_src_params['json_resp'] = \345 temp_tgt_params['json_resp'] = False346 347 source_ddl = \348 view_object.get_sql_from_table_diff(**temp_src_params)349 diff_dependencies = \350 view_object.get_table_submodules_dependencies(351 **temp_src_params)352 target_ddl = \353 view_object.get_sql_from_table_diff(**temp_tgt_params)354 355 diff_ddl = view_object.get_sql_from_submodule_diff(356 source_params=temp_src_params,357 target_params=temp_tgt_params,358 source=dict1[key], target=dict2[key], diff_dict=diff_dict,359 target_schema=target_schema,360 ignore_whitespaces=ignore_whitespaces)361 else:362 temp_src_params = copy.deepcopy(source_params)363 temp_tgt_params = copy.deepcopy(target_params)364 diff_dict = directory_diff(365 dict1[key], dict2[key],366 ignore_keys=ignore_keys, difference={}367 )368 369 # No need to parse acl if ignore_grants is set to True.370 if not ignore_grants:371 parse_acl(dict1[key], dict2[key], diff_dict)372 373 temp_src_params['oid'] = source_object_id374 temp_tgt_params['oid'] = target_object_id375 # Provide Foreign Data Wrapper ID376 _check_add_req_ids(source_dict, target_dict, key,377 temp_src_params, temp_tgt_params)378 379 source_ddl = view_object.get_sql_from_diff(**temp_src_params)380 diff_dependencies = view_object.get_dependencies(381 view_object.conn, source_object_id, where=None,382 show_system_objects=None, is_schema_diff=True)383 target_ddl = view_object.get_sql_from_diff(**temp_tgt_params)384 temp_tgt_params.update(385 {'data': diff_dict, 'target_schema': target_schema})386 diff_ddl = view_object.get_sql_from_diff(**temp_tgt_params)387 388 title = key389 if node == 'user_mapping':390 title = _get_user_mapping_name(key)391 392 different.append({393 'id': count,394 'type': node,395 'label': node_label,396 'title': title,397 'oid': source_object_id,398 'source_oid': source_object_id,399 'target_oid': target_object_id,400 'status': gettext('Different'),401 'source_ddl': source_ddl,402 'target_ddl': target_ddl,403 'diff_ddl': diff_ddl,404 'group_name': group_name,405 'dependencies': diff_dependencies406 })407 count += 1408 409 return identical, different410 411 412def compare_dictionaries(**kwargs):413 """414 This function will compare the two dictionaries.415 416 :param kwargs:417 :return:418 """419 view_object = kwargs.get('view_object')420 source_params = kwargs.get('source_params')421 target_params = kwargs.get('target_params')422 target_schema = kwargs.get('target_schema')423 group_name = kwargs.get('group_name')424 source_dict = kwargs.get('source_dict')425 target_dict = kwargs.get('target_dict')426 node = kwargs.get('node')427 node_label = kwargs.get('node_label')428 ignore_keys = kwargs.get('ignore_keys', None)429 source_schema_name = kwargs.get('source_schema_name')430 ignore_owner = kwargs.get('ignore_owner')431 ignore_whitespaces = kwargs.get('ignore_whitespaces')432 ignore_tablespace = kwargs.get('ignore_tablespace')433 ignore_grants = kwargs.get('ignore_grants')434 435 dict1 = copy.deepcopy(source_dict)436 dict2 = copy.deepcopy(target_dict)437 438 # Find the duplicate keys in both the dictionaries439 dict1_keys = set(dict1.keys())440 dict2_keys = set(dict2.keys())441 intersect_keys = dict1_keys.intersection(dict2_keys)442 443 # Add gid to the params444 source_params['gid'] = target_params['gid'] = 1445 446 # Keys that are available in source and missing in target.447 448 added = dict1_keys - dict2_keys449 450 source_only = _get_source_list(added=added, source_dict=source_dict,451 node=node, source_params=source_params,452 view_object=view_object,453 node_label=node_label,454 group_name=group_name,455 source_schema_name=source_schema_name,456 target_schema=target_schema)457 458 target_only = []459 # Keys that are available in target and missing in source.460 removed = dict2_keys - dict1_keys461 target_only = _get_target_list(removed, target_dict, node, target_params,462 view_object, node_label, group_name)463 464 # if ignore_owner is True then add all the possible owner keys to the465 # ignore keys.466 if ignore_owner:467 owner_keys = ['owner', 'eventowner', 'funcowner', 'fdwowner',468 'fsrvowner', 'lanowner', 'relowner', 'seqowner',469 'typeowner']470 ignore_keys = ignore_keys + owner_keys471 472 # if ignore_tablespace is True then add all the possible tablespace keys473 # to the ignore keys.474 if ignore_tablespace:475 tablespace_keys = ['spcname', 'spcoid']476 ignore_keys = ignore_keys + tablespace_keys477 478 # if ignore_grants is True then add all the possible grants keys479 # to the ignore keys.480 if ignore_grants:481 grant_keys = ['relacl', 'acl', 'datacl', 'fdwacl', 'lanacl', 'fsrvacl']482 ignore_keys = ignore_keys + grant_keys483 484 # Compare the values of duplicates keys.485 other_param = {486 "dict1": dict1,487 "dict2": dict2,488 "ignore_keys": ignore_keys,489 "source_params": source_params,490 "target_params": target_params,491 "group_name": group_name,492 "target_schema": target_schema,493 "ignore_whitespaces": ignore_whitespaces,494 "ignore_grants": ignore_grants495 }496 497 identical, different = _get_identical_and_different_list(498 intersect_keys, source_dict, target_dict, node, node_label,499 view_object, **other_param)500 501 return source_only + target_only + different + identical502 503 504def are_lists_identical(source_list, target_list, ignore_keys,505 ignore_whitespaces):506 """507 This function is used to compare two list.508 :param source_list:509 :param target_list:510 :param ignore_keys: ignore keys to compare511 :param ignore_whitespaces:512 :return:513 """514 if source_list is None or target_list is None or \515 len(source_list) != len(target_list):516 return False517 518 for index in range(len(source_list)):519 # Check the type of the value if it is an dictionary then520 # call are_dictionaries_identical() function.521 if isinstance(source_list[index], dict):522 if not are_dictionaries_identical(source_list[index],523 target_list[index],524 ignore_keys,525 ignore_whitespaces):526 return False527 else:528 if source_list[index] != target_list[index]:529 return False530 return True531 532 533def are_dictionaries_identical(source_dict, target_dict, ignore_keys,534 ignore_whitespaces):535 """536 This function is used to recursively compare two dictionaries with537 same keys.538 :param source_dict: source dict539 :param target_dict: target dict540 :param ignore_keys: ignore keys to compare541 :param ignore_whitespaces: ignore whitespaces while comparing542 :return:543 """544 src_keys = set(source_dict.keys())545 tar_keys = set(target_dict.keys())546 igr_keys = set(ignore_keys)547 548 # Keys that are available in source and missing in target.549 src_only = (src_keys - igr_keys) - tar_keys550 # Keys that are available in target and missing in source.551 tar_only = (tar_keys - igr_keys) - src_keys552 553 # If number of keys are different in source and target then554 # return False555 if len(src_only) != len(tar_only):556 current_app.logger.debug("Schema Diff: Number of keys are different "557 "in source and target")558 return False559 560 # If number of keys are same but key is not present in target then561 # return False562 for key in src_only:563 if key not in tar_only:564 current_app.logger.debug(565 "Schema Diff: Number of keys are same but key is not"566 " present in target")567 return False568 569 for key in source_dict.keys():570 # Continue if key is available in ignore_keys571 if key in ignore_keys:572 continue573 574 if isinstance(source_dict[key], dict):575 if not are_dictionaries_identical(source_dict[key],576 target_dict[key],577 ignore_keys,578 ignore_whitespaces):579 return False580 elif isinstance(source_dict[key], list):581 # Sort the source and target list on the basis of582 # list key array.583 source_dict[key], target_dict[key] = sort_list(source_dict[key],584 target_dict[key])585 # Compare the source and target lists586 if not are_lists_identical(source_dict[key], target_dict[key],587 ignore_keys, ignore_whitespaces):588 return False589 else:590 source_value = source_dict[key]591 target_value = target_dict[key]592 # Check if ignore whitespaces or not.593 source_value, target_value = check_for_ignore_whitespaces(594 ignore_whitespaces, source_value, target_value)595 596 # We need a proper solution as sometimes we observe that597 # source_value is '' and target_value is None or vice versa598 # in such situation we shown the comparison as different599 # which is wrong.600 if (source_value == '' and target_value is None) or \601 (source_value is None and target_value == ''):602 continue603 604 if source_value != target_value:605 current_app.logger.debug(606 "Schema Diff: Object name: '{0}', Source Value: '{1}', "607 "Target Value: '{2}', Key: '{3}'".format(608 source_dict['name'] if 'name' in source_dict else '',609 source_value, target_value, key))610 return False611 612 return True613 614 615def check_for_ignore_whitespaces(ignore_whitespaces, source_value,616 target_value):617 """618 If ignore_whitespaces is True then check the source_value and619 target_value if of type string. If the values is of type string620 then using translate function ignore all the whitespaces.621 :param ignore_whitespaces: flag to check ignore whitespace.622 :param source_value: source schema diff value623 :param target_value: target schema diff value624 :return: return source and target values.625 """626 if ignore_whitespaces:627 if isinstance(source_value, str):628 source_value = source_value.translate(629 str.maketrans('', '', string.whitespace))630 if isinstance(target_value, str):631 target_value = target_value.translate(632 str.maketrans('', '', string.whitespace))633 634 return source_value, target_value635 636 637def directory_diff(source_dict, target_dict, ignore_keys=[], difference=None):638 """639 This function is used to recursively compare two dictionaries and640 return the difference.641 The difference is from source to target642 :param source_dict: source dict643 :param target_dict: target dict644 :param ignore_keys: ignore keys to compare645 :param difference:646 """647 648 difference = {} if difference is None else difference649 src_keys = set(source_dict.keys())650 tar_keys = set(target_dict.keys())651 igr_keys = set(ignore_keys)652 653 # Keys that are available in source and missing in target.654 src_only = (src_keys - igr_keys) - tar_keys655 # Keys that are available in target and missing in source.656 tar_only = (tar_keys - igr_keys) - src_keys657 658 for key in source_dict.keys():659 added = []660 deleted = []661 updated = []662 source = None663 664 # ignore the keys if available.665 if key in ignore_keys:666 continue667 elif key in tar_only:668 if isinstance(target_dict[key], list):669 difference[key] = {}670 difference[key]['deleted'] = target_dict[key]671 elif key in src_only:672 # Source only values in the newly added list673 if isinstance(source_dict[key], list):674 difference[key] = {}675 difference[key]['added'] = source_dict[key]676 elif isinstance(source_dict[key], dict):677 directory_diff(source_dict[key], target_dict[key],678 ignore_keys, difference)679 elif isinstance(source_dict[key], list):680 tmp_target = None681 tmp_list = [x for x in source_dict[key]682 if isinstance(x, (list, dict))]683 684 if tmp_list:685 tmp_target = copy.deepcopy(target_dict[key])686 for index in range(len(source_dict[key])):687 source = copy.deepcopy(source_dict[key][index])688 if isinstance(source, list):689 # This block is empty will figure out some scenario690 pass691 elif isinstance(source, dict):692 # Check the above keys are exist in the dictionary693 tmp_key = is_key_exists(list_keys_array, source)694 if tmp_key is not None:695 # Compare the two list by ignoring the keys.696 compare_list_by_ignoring_keys(source, tmp_target,697 added, updated,698 tmp_key, ignore_keys)699 700 difference[key] = {}701 if len(added) > 0:702 difference[key]['added'] = added703 if len(updated) > 0:704 difference[key]['changed'] = updated705 elif target_dict[key] is None or \706 (isinstance(target_dict[key], list) and707 len(target_dict[key]) < index and708 source != target_dict[key][index]):709 difference[key] = source710 elif isinstance(target_dict[key], list) and\711 len(target_dict[key]) > index:712 difference[key] = source713 elif len(source_dict[key]) > 0:714 difference[key] = source_dict[key]715 elif key in target_dict and isinstance(target_dict[key], list):716 # If no element in source dict then check for the element717 # is available in target and the type is of list.718 # Added such elements as a deleted.719 tmp_tar_list = [x for x in target_dict[key]720 if isinstance(x, (list, dict))]721 if tmp_tar_list:722 difference[key] = {'deleted': target_dict[key]}723 724 if isinstance(source, dict) and tmp_target and key in tmp_target \725 and tmp_target[key] and len(tmp_target[key]) > 0:726 if isinstance(tmp_target[key], list) and \727 isinstance(tmp_target[key][0], dict):728 deleted = deleted + tmp_target[key]729 else:730 deleted.append({key: tmp_target[key]})731 difference[key]['deleted'] = deleted732 elif tmp_target and isinstance(tmp_target, list):733 difference[key]['deleted'] = tmp_target734 735 # No point adding empty list into difference.736 if key in difference and len(difference[key]) == 0:737 difference.pop(key)738 else:739 if source_dict[key] != target_dict[key]:740 if (key == 'comment' or key == 'description') and \741 source_dict[key] is None:742 difference[key] = ''743 else:744 difference[key] = source_dict[key]745 746 if len(src_only) == 0 and len(tar_only) > 0:747 for key in tar_only:748 if isinstance(target_dict[key], list):749 difference[key] = {}750 difference[key]['deleted'] = target_dict[key]751 752 return difference753 754 755def is_key_exists(key_list, target_dict):756 """757 This function is used to iterate the key list and check that key is758 present in the given dictionary759 :param key_list:760 :param target_dict:761 :return:762 """763 for key in key_list:764 if key in target_dict:765 return key766 767 return None768 769 770def _check_key_in_source_target(key, acl_keys, target, source):771 """772 Check if key is present in source if not then check it's present in target.773 :param key: key to be checked.774 :param acl_keys: acl keys775 :param target: target object.776 :param source: source object.777 :return: return key.778 """779 if key is None:780 key = is_key_exists(acl_keys, target)781 if key is None:782 key = 'acl'783 elif key is not None and not isinstance(source[key], list):784 key = 'acl'785 786 return key787 788 789def parse_acl(source, target, diff_dict):790 """791 This function is used to parse acl.792 793 :param source: Source Dict794 :param target: Target Dict795 :param diff_dict: Difference Dict796 """797 acl_keys = ['datacl', 'relacl', 'typacl', 'pkgacl', 'fsrvacl']798 key = is_key_exists(acl_keys, source)799 800 # If key is not found in source then check the key is available801 # in target.802 key = _check_key_in_source_target(key, acl_keys, target, source)803 804 tmp_source = source[key] if\805 key in source and source[key] is not None else []806 tmp_target = copy.deepcopy(target[key]) if\807 key in target and target[key] is not None else []808 809 diff = {'added': [], 'deleted': []}810 for acl in tmp_source:811 if acl in tmp_target:812 tmp_target.remove(acl)813 elif acl not in tmp_target:814 diff['added'].append(acl)815 diff['deleted'] = tmp_target816 817 # Update the key if there are some element in added or deleted818 # else remove that key from diff dict819 if len(diff['added']) > 0 or len(diff['deleted']) > 0:820 diff_dict.update({key: diff})821 elif key in diff_dict:822 diff_dict.pop(key)823 824 825def sort_list(source, target):826 """827 This function is used to sort the source and target list on the828 basis of key found in the source and target list.829 :param source:830 :param target:831 :return:832 """833 # Check the above keys are exist in the dictionary834 if source is not None and source and isinstance(source[0], dict):835 tmp_key = is_key_exists(list_keys_array, source[0])836 if tmp_key is not None:837 source = sorted(source, key=lambda k: k[tmp_key])838 839 # Check the above keys are exist in the dictionary840 if target is not None and target and isinstance(target[0], dict):841 tmp_key = is_key_exists(list_keys_array, target[0])842 if tmp_key is not None:843 target = sorted(target, key=lambda k: k[tmp_key])844 845 return source, target846 847 848def compare_list_by_ignoring_keys(source_list, target_list, added, updated,849 key, ignore_keys):850 """851 This function is used to compare the two list by ignoring the keys852 specified in ignore_keys.853 :param source_list:854 :param target_list:855 :param added:856 :param updated:857 :param key:858 :param ignore_keys:859 :return:860 """861 if isinstance(target_list, list) and target_list:862 tmp_target = None863 for item in target_list:864 if key in item and item[key] == source_list[key]:865 tmp_target = copy.deepcopy(item)866 867 if tmp_target is None:868 added.append(source_list)869 else:870 source_with_ignored_keys = copy.deepcopy(source_list)871 target_with_ignored_keys = copy.deepcopy(tmp_target)872 873 # Remove ignore keys from source and target before comparison874 _remove_keys(ignore_keys, source_with_ignored_keys,875 target_with_ignored_keys)876 877 _compare_source_and_target(source_with_ignored_keys,878 target_with_ignored_keys, source_list,879 target_list, updated, tmp_target)880 else:881 added.append(source_list)882 883 884def _remove_keys(ignore_keys, source_with_ignored_keys,885 target_with_ignored_keys):886 """887 Remove non required keys form both source and target object.888 :param ignore_keys: ignore keys list.889 :param source_with_ignored_keys: source keys list.890 :param target_with_ignored_keys: target keys list.891 :return: None892 """893 for ig_key in ignore_keys:894 if ig_key in source_with_ignored_keys:895 del source_with_ignored_keys[ig_key]896 if ig_key in target_with_ignored_keys:897 del target_with_ignored_keys[ig_key]898 899 900def _compare_source_and_target(source_with_ignored_keys,901 target_with_ignored_keys, source_list,902 target_list, updated, tmp_target):903 """904 Compare source and target keys905 :param source_with_ignored_keys:906 :param target_with_ignored_keys:907 :param source_list:908 :param target_list:909 :param updated:910 :param tmp_target:911 :return:912 """913 if source_with_ignored_keys != target_with_ignored_keys:914 updated.append(source_list)915 target_list.remove(tmp_target)916 elif source_with_ignored_keys == target_with_ignored_keys:917 target_list.remove(tmp_target)918 