import os
import shutil
import json
import copy
import stat
import re
permission_dict = {
"redfish": {"rd": 550, "r": 440, "user": [104, 104]},
"web_backend": {"rd": 550, "r": 440, "user": [104, 104]},
"snmp": {"rd": 550, "r": 440, "user": [95, 95]},
"cli": {"rd": 555, "r": 444, "user": [0, 0]}
}
def is_valid_path(path, prefix, check_is_exist):
real_path = os.path.realpath(path)
if not real_path.startswith(prefix):
print(f"无效的路径:{path}")
return False
if check_is_exist and not os.path.exists(path):
print(f"无效的路径,指定的文件/目录不存在:{path}")
return False
return True
def load_json_file(json_file):
with open(json_file, "r") as f:
return json.load(f)
def save_json_file(json_file, json_dict):
print(f"保存 json 文件到: {json_file}")
with os.fdopen(os.open(json_file, os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO), 'w') as fp:
json.dump(json_dict, fp, separators=(',', ':'))
def split_prop_name(prop):
array = prop.split("/")
for i, x in enumerate(array):
try:
num = int(x)
array[i] = num - 1
except ValueError:
pass
return array
def remove_json_prop(obj, array, depth):
if not isinstance(obj, (dict, list)):
return
size = len(array) - 1
if depth > size:
return
elif depth == size:
if isinstance(obj, dict) and array[depth] in obj:
del obj[array[depth]]
elif isinstance(obj, list) and array[depth] < len(obj):
del obj[array[depth]]
else:
node = None
if isinstance(obj, dict) and obj.get(array[depth]) is not None:
node = obj.get(array[depth])
elif isinstance(obj, list) and array[depth] < len(obj):
node = obj[array[depth]]
if node is not None:
remove_json_prop(node, array, depth + 1)
def merge_json(obj1, obj2):
if not isinstance(obj1, type(obj2)) or not isinstance(obj1, (dict, list)):
return
if isinstance(obj1, dict):
for key, value in obj2.items():
if not obj1.get(key) or not isinstance(obj1[key], type(value)) or not isinstance(value, (dict, list)):
obj1[key] = copy.deepcopy(value)
else:
merge_json(obj1[key], obj2[key])
if isinstance(obj1, list):
for item in obj2:
obj1.append(copy.deepcopy(item))
def rename_json_prop(obj, array, depth, new_key):
if not isinstance(obj, (dict, list)):
return
size = len(array) - 1
if depth > size:
return
elif depth == size:
if isinstance(obj, dict) and obj.get(array[depth]) is not None:
obj[new_key] = obj.pop(array[depth])
elif isinstance(obj, list) and array[depth] < len(obj):
return
else:
node = None
if isinstance(obj, dict) and obj.get(array[depth]) is not None:
node = obj.get(array[depth])
elif isinstance(obj, list) and array[depth] < len(obj):
node = obj[array[depth]]
if node is not None:
remove_json_prop(node, array, depth + 1, new_key)
class MappingConfigPatch():
def __init__(self, appconan, patch_path, config_path):
self.appconan = appconan
self.config_path = config_path
self.config_patch_path = patch_path
def run_command(self, command, ignore_error=False, sudo=False, **kwargs):
print(self.appconan.build_folder)
return self.appconan.run(command)
def uri_remove_in_file(self, target_path, target_uri, target_method):
target_object = load_json_file(target_path)
resources = target_object.get("Resources")
if resources is None:
print(f"Uri移除场景,{target_path}无Resources配置")
return
flag = False
for i in range(len(resources) - 1, -1, -1):
uri = resources[i].get("Uri")
if uri != target_uri:
continue
if target_method is None:
flag = True
del resources[i]
continue
interfaces = resources[i].get("Interfaces", [])
for j in range(len(interfaces) - 1, -1, -1):
method = interfaces[j].get("Type", "").lower()
if method in target_method:
flag = True
del interfaces[j]
if len(interfaces) == 0 and flag:
del resources[i]
if flag:
save_json_file(target_path, target_object)
else:
print(f"Uri移除场景,没有找到Uri({target_uri})或Method({target_method})指定的配置")
def uri_remove(self, actions):
for action in actions:
target = action.get("Target")
target_uri = action.get("Uri")
target_method = action.get("Method")
if target is None:
print(f"Uri移除场景,Target参数必须存在")
continue
target_path = os.path.join(self.config_path, target)
if target_method is not None:
target_method = [method.lower() for method in target_method]
if not is_valid_path(target_path, self.config_path, True):
continue
if os.path.isdir(target_path):
if target_uri is not None or target_method is not None:
print(f"Uri移除场景,删除整个路径时,无需Uri和Method配置,路径为{target_path}")
self.run_command(f"rm -rf {target_path}", sudo=True)
continue
if os.path.isfile(target_path):
if target_uri is None:
self.run_command(f"rm -rf {target_path}", sudo=True)
continue
self.uri_remove_in_file(target_path, target_uri, target_method)
continue
print(f"Uri移除场景,没有Target{target}对应的文件或目录")
def uri_copy(self, actions):
for action in actions:
source = action.get("Source")
target = action.get("Target")
if source is None or target is None:
print(f"Uri增加场景,Source和Target参数必须存在")
continue
source_path = os.path.join(self.config_patch_path, source)
target_path = os.path.join(self.config_path, target)
if not is_valid_path(source_path, self.config_patch_path, True) or\
not is_valid_path(target_path, self.config_path, False):
continue
if os.path.exists(target_path):
print(f"Uri增加场景,Target({target})指定的文件或目录已存在,删除原有的")
self.run_command(f"rm -rf {target_path}", sudo=True, command_echo=False)
if os.path.isfile(source_path):
dir_name = os.path.dirname(target_path)
if not os.path.exists(dir_name):
os.makedirs(dir_name)
shutil.copyfile(source_path, target_path)
elif os.path.isdir(source_path):
shutil.copytree(source_path, target_path)
print(f"复制 {source_path} 到 {target_path}")
def uri_rename(self, actions):
for action in actions:
target = action.get("Target")
origin_uri = action.get("OriginUri")
new_uri = action.get("NewUri")
if target is None or origin_uri is None or new_uri is None:
print(f"Uri重命名场景,Target、OriginUri、NewUri参数必须存在")
continue
target_path = os.path.join(self.config_path, target)
if not is_valid_path(target_path, self.config_path, True):
continue
target_object = load_json_file(target_path)
resources = target_object.get("Resources")
if resources is None:
print(f"Uri重命名场景,文件{target_path}无Resources配置")
continue
flag = False
for resource in resources:
uri = resource.get("Uri")
if uri == origin_uri:
flag = True
resource["Uri"] = new_uri
if flag:
save_json_file(target_path, target_object)
else:
print(f"Uri重命名场景,没有找到Uri({origin_uri})指定的配置")
def uri_actions(self, actions):
self.uri_remove(actions.get("Remove", []))
self.uri_copy(actions.get("Copy", []))
self.uri_rename(actions.get("Rename", []))
def adjust_processingflow_sequence(self, base, actions):
if base is None or len(base) == 0:
return
base = len(base)
def replace_processing_flow(s):
pattern = r"ProcessingFlow\[(\d+)\]"
def replace(match):
n = int(match.group(1))
return f"ProcessingFlow[{n + base}]"
t = re.sub(pattern, replace, s)
return s == t, t
def traversal(obj):
if isinstance(obj, dict):
for key, value in obj.items():
is_equal, new = replace_processing_flow(key)
if not is_equal:
obj[new] = obj[key].pop()
if isinstance(value, (dict, list)):
traversal(value)
continue
if not isinstance(value, str):
continue
is_equal, new = replace_processing_flow(value)
if not is_equal:
obj[key] = new
elif isinstance(obj, list):
for i, _ in enumerate(obj):
if isinstance(obj[i], (dict, list)):
traversal(obj[i])
continue
if not isinstance(obj[i], str):
continue
is_equal, new = replace_processing_flow(obj[i])
if not is_equal:
obj[i] = new
traversal(actions)
def property_remove(self, interface, actions):
for prop in actions:
remove_json_prop(interface, split_prop_name(prop), 0)
def property_modify(self, interface, actions):
self.adjust_processingflow_sequence(interface.get("ProcessingFlow"), actions)
merge_json(interface, actions)
def property_rename(self, interface, actions):
for key, value in actions.items():
rename_json_prop(interface, split_prop_name(key), 0, value)
def property_action(self, resources, target_method, target_uri, action):
flag = False
for resource in resources:
uri = resource.get("Uri")
if uri != target_uri:
continue
interfaces = resource.get("Interfaces", [])
for interface in interfaces:
method = interface.get("Type", "")
if method.lower() != target_method.lower():
continue
flag = True
self.property_remove(interface, action.get("Remove", []))
self.property_modify(interface, action.get("Modify", {}))
self.property_rename(interface, action.get("Rename", {}))
return flag
def property_actions(self, actions):
for action in actions:
target = action.get("Target")
target_uri = action.get("Uri")
target_method = action.get("Method")
if target is None or target_uri is None or target_method is None:
print(f"属性变更场景,Target、Uri、Method参数必须存在")
continue
target_path = os.path.join(self.config_path, target)
if not is_valid_path(target_path, self.config_path, True):
continue
target_object = load_json_file(target_path)
resources = target_object.get("Resources")
if resources is None:
print(f"属性变更场景,文件{target_path}无Resources配置")
continue
flag = self.property_action(resources, target_method, target_uri, action)
if flag:
save_json_file(target_path, target_object)
else:
print(f"属性变更场景,没有找到Uri({target_uri})指定的配置")
def global_variables(self, global_variable):
if global_variable is None:
return
target_path = os.path.join(self.config_path, "config.json")
if not os.path.exists(target_path):
return
target_object = load_json_file(target_path)
target_global_variable = target_object.get("GlobalVariable")
if target_global_variable is None:
print(f"平台层文件{target_path}无GlobalVariable配置")
return
merge_json(target_global_variable, global_variable)
save_json_file(target_path, target_object)
def restore_permission(self):
p = {"rd": 550, "r": 440, "user": [0, 0]}
for key, value in permission_dict.items():
app = "opt/bmc/apps/" + key
if app in self.config_path:
p = value
self.pipe_command([f"sudo find {self.config_path} -type d", f"sudo xargs -P 0 -i. chmod {p['rd']} ."])
self.pipe_command([f"sudo find {self.config_path} -type f", f"sudo xargs -P 0 -i. chmod {p['r']} ."])
self.pipe_command([f"sudo find {self.config_path} -type d",
f"sudo xargs -P 0 -i. chown {p['user'][0]}:{p['user'][1]} ."])
self.pipe_command([f"sudo find {self.config_path} -type f",
f"sudo xargs -P 0 -i. chown {p['user'][0]}:{p['user'][1]} ."])
def config_patch(self):
patch_file = os.path.join(self.config_patch_path, 'config.json')
print(patch_file)
if not os.path.isfile(patch_file):
return
patch_object = load_json_file(patch_file)
self.uri_actions(patch_object.get("Uri.Actions", {}))
self.property_actions(patch_object.get("Property.Actions", []))
self.global_variables(patch_object.get("GlobalVariable", {}))
def run(self):
if not os.path.isdir(self.config_patch_path):
return
self.config_patch()