If you need to enhance what modify_cache_cluster() considers when deciding what is to be
    (or can be) updated, add it to 'modifiable' below.  It's a dict mapping the param as used
    in modify_cache_cluster() to that in describe_cache_clusters().  Any data fiddlery that
    needs to be done to make the mappings meaningful should be done in the munging section
    below as well.

    This function will ONLY touch settings that are explicitly called out in 'desired' - any
    settings which might have previously been changed from their 'default' values will not be
    changed back simply by leaving them out of 'desired'.  This is both intentional, and
    much, much easier to code :)

def _diff_cache_cluster(current, desired):
    '''
    If you need to enhance what modify_cache_cluster() considers when deciding what is to be
    (or can be) updated, add it to 'modifiable' below.  It's a dict mapping the param as used
    in modify_cache_cluster() to that in describe_cache_clusters().  Any data fiddlery that
    needs to be done to make the mappings meaningful should be done in the munging section
    below as well.

    This function will ONLY touch settings that are explicitly called out in 'desired' - any
    settings which might have previously been changed from their 'default' values will not be
    changed back simply by leaving them out of 'desired'.  This is both intentional, and
    much, much easier to code :)
    '''
    ### The data formats are annoyingly (and as far as I can can tell, unnecessarily)
    ### different - we have to munge to a common format to compare...
    if current.get('SecurityGroups') is not None:
        current['SecurityGroupIds'] = [s['SecurityGroupId'] for s in current['SecurityGroups']]
    if current.get('CacheSecurityGroups') is not None:
        current['CacheSecurityGroupNames'] = [c['CacheSecurityGroupName'] for c in current['CacheSecurityGroups']]
    if current.get('NotificationConfiguration') is not None:
        current['NotificationTopicArn'] = current['NotificationConfiguration']['TopicArn']
        current['NotificationTopicStatus'] = current['NotificationConfiguration']['TopicStatus']
    if current.get('CacheParameterGroup') is not None:
        current['CacheParameterGroupName'] = current['CacheParameterGroup']['CacheParameterGroupName']

    modifiable = {
        'AutoMinorVersionUpgrade': 'AutoMinorVersionUpgrade',
        'AZMode': 'AZMode',
        'CacheNodeType': 'CacheNodeType',
        'CacheNodeIdsToRemove': None,
        'CacheParameterGroupName': 'CacheParameterGroupName',
        'CacheSecurityGroupNames': 'CacheSecurityGroupNames',
        'EngineVersion': 'EngineVersion',
        'NewAvailabilityZones': None,
        'NotificationTopicArn': 'NotificationTopicArn',
        'NotificationTopicStatus': 'NotificationTopicStatus',
        'NumCacheNodes': 'NumCacheNodes',
        'PreferredMaintenanceWindow': 'PreferredMaintenanceWindow',
        'SecurityGroupIds': 'SecurityGroupIds',
        'SnapshotRetentionLimit': 'SnapshotRetentionLimit',
        'SnapshotWindow': 'SnapshotWindow'
    }

    need_update = {}
    for m, o in modifiable.items():
        if m in desired:
            if not o:
                # Always pass these through - let AWS do the math...
                need_update[m] = desired[m]
            else:
                if m in current:
                    # Equivalence testing works fine for current simple type comparisons
                    # This might need enhancement if more complex structures enter the picture
                    if current[m] != desired[m]:
                        need_update[m] = desired[m]
    return need_update