blob: abbfb478a417883fae91ff1852931b35a1ab32a9 [file] [log] [blame]
# Copyright 2013 - 2017 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import os
import yaml
def get_nested_key(data, path=None):
if type(path) is not list:
raise("Use 'list' object with key names for 'path'")
for key in path:
value = data.get(key, None)
if value is not None:
data = value
else:
return None
return data
def create_nested_key(data, path=None, value=None):
if type(data) is not dict:
raise("Use 'dict' object for 'data'")
if type(path) is not list:
raise("Use 'list' object with key names for 'path'")
for key in path[:-1]:
if key not in data:
data[key] = {}
data = data[key]
data[path[-1]] = value
def remove_nested_key(data, path=None):
if type(path) is not list:
raise("Use 'list' object with key names for 'path'")
# Remove the value from the specified key
val = get_nested_key(data, path[:-1])
val[path[-1]] = None
# Clear parent keys if empty
while path:
val = get_nested_key(data, path)
if val is not None:
# Non-empty value, nothing to do
return
else:
get_nested_key(data, path[:-1]).pop(path[-1])
path = path[:-1]
def yaml_read(yaml_file):
if os.path.isfile(yaml_file):
with open(yaml_file, 'r') as f:
return yaml.load(f)
else:
print("\'{}\' is not a file!".format(yaml_file))
def json_read(yaml_file):
if os.path.isfile(yaml_file):
with open(yaml_file, 'r') as f:
return json.load(f)
else:
print("\'{}\' is not a file!".format(yaml_file))
def merge_nested_objects(obj_1, obj_2):
"""Merge two objects with optional key overwrites
Original : https://stackoverflow.com/a/17860173
- Merges dicts and lists
- If a dict key has the suffix '__overwrite__' and boolean value,
then the key is assumed as a special keyword for merging:
<key>__overwrite__: True # Overwrite the existing <key> content
# with <key> from obj_2
<key>__overwrite__: False # Keep the existing <key> content from obj_1
Case #1: Merge dicts and lists, overwrite other types with latest value
dict_a = {
'host': '1.1.1.1',
'ssh': {
'login': 'user'
}
}
dict_b = {
'host': '2.2.2.2',
'ssh': {
'password': 'pass'
}
}
print(merge_nested_objects(dict_a, dict_b))
{
'host': '2.2.2.2',
'ssh': {
'login': 'user',
'password': 'pass',
}
}
Case #2: Use <key>__overwrite__: True to remove previous key content
dict_a = {
'host': '1.1.1.1'
'ssh': {
'login': 'user'
}
}
dict_b = {
'ssh__overwrite__': True
'ssh': {
'password': 'pass'
}
}
print(merge_nested_objects(dict_a, dict_b))
{
'host': '1.1.1.1',
'ssh': {
'password': 'pass',
}
}
Case #3: Use <key>__overwrite__: False to skip merging key
if already exists
dict_a = {
'host': '1.1.1.1'
'ssh': {
'login': 'user'
}
}
dict_b = {
'host__overwrite__': False
'host': '2.2.2.2'
'ssh': {
'login__overwrite__': False
'login': 'new_user'
'password': 'pass'
}
}
print(merge_nested_objects(dict_a, dict_b))
{
'host': '1.1.1.1',
'ssh': {
'login': 'user',
'password': 'pass'
}
}
"""
# Merge two dicts
if isinstance(obj_1, dict) and isinstance(obj_2, dict):
result = {}
for key, value in obj_1.iteritems():
if key not in obj_2:
result[key] = value
else:
overwrite_key = key + '__overwrite__'
if overwrite_key in obj_2 and obj_2[overwrite_key] is True:
result[key] = obj_2[key]
elif overwrite_key in obj_2 and obj_2[overwrite_key] is False:
result[key] = value
else:
result[key] = merge_nested_objects(value, obj_2[key])
for key, value in obj_2.iteritems():
if key not in obj_1:
result[key] = value
return result
# Add two lists
if isinstance(obj_1, list) and isinstance(obj_2, list):
return obj_1 + list(set(obj_2) - set(obj_1))
# Overwrite a value with new one
return obj_2