import os
import shutil
import json
import sys
import copy
import re
import tarfile
from conan.tools.files import chdir
from conan.tools.files import copy
from conanbase import ConanBase
required_conan_version = ">=1.60.0"
class AppConan(ConanBase):
@staticmethod
def get_relative_path_paths(json_dir, json_path):
relative_path = os.path.relpath(json_path, json_dir)
if os.sep == '\\':
relative_path = relative_path.replace('\\', '/')
return relative_path.split('/')
@staticmethod
def get_sorted_json_files(json_dir):
json_files = []
for root, _, files in os.walk(json_dir):
for file in files:
if file.endswith(".json") and not os.path.islink(file):
file_path = os.path.join(root, file)
json_files.append(file_path)
json_files.sort()
return json_files
@staticmethod
def set_dict_value(keys, k, v, mapper):
current_dict = mapper
for key in keys:
if key not in current_dict:
current_dict[key] = {}
current_dict = current_dict[key]
current_dict[k] = v
def build(self):
if self.settings.build_type == "Release":
with chdir(self, self.source_folder):
self.run("rm -rf ./interface_config/cli/ipmcset/dft.json")
self.run("rm -rf ./interface_config/cli/ipmcget/dft.json")
super(AppConan, self).build()
with chdir(self, self.source_folder):
tools_path = os.path.join(self.source_folder, "tools")
sys.path.append(tools_path)
from mapper_check import MapperCheck
MapperCheck.check()
schema_check = str(self.options.schema_check)
if schema_check == "true":
from schema_check.schema_checker import SchemaChecker
sc = SchemaChecker()
sc.check()
def custom_ipmc_version(self):
bmc_name = str(self.options.bmc_name)
if bmc_name:
ipmc_version = os.path.join(self.source_folder, "interface_config/cli/echoes/ipmcget/_version")
with open(ipmc_version, "r") as fp:
data = fp.read()
data = data.replace("openUBMC INFO", f"{bmc_name} INFO")
new_bmc = bmc_name.ljust(len("openUBMC"))
data = re.sub(r"\bopenUBMC\b", new_bmc, data)
with open(ipmc_version, "w") as fp:
fp.write(data)
def read_config(self):
config_path = os.path.join(self.source_folder, "interface_config/redfish/config.json")
with open(config_path, 'r') as file:
config = json.load(file)
software_name = config.get('GlobalVariable', {}).get('SoftwareName', 'openUBMC')
sms_name = config.get('GlobalVariable', {}).get('SmsName', 'BMA')
return software_name, sms_name
def replace_strings_in_files(self, software_name, sms_name):
schemastore_path = os.path.join(self.source_folder,
"interface_config/redfish/static_resource/redfish/v1/schemastore/en")
json_schema_dirs = [
os.path.join(schemastore_path, "dmtf/json_schema"),
]
oem_path = os.path.join(schemastore_path, "oem")
if os.path.exists(oem_path):
for oem_vendor in os.listdir(oem_path):
oem_json_schema = os.path.join(oem_path, oem_vendor, "json_schema")
if os.path.exists(oem_json_schema):
json_schema_dirs.append(oem_json_schema)
for json_schema_dir in json_schema_dirs:
if not os.path.exists(json_schema_dir):
continue
for root, _, files in os.walk(json_schema_dir):
for filename in files:
if filename.endswith('.json'):
file_path = os.path.join(root, filename)
with open(file_path, 'r') as file:
content = file.read()
content = content.replace('{{SoftwareName}}', software_name)
content = content.replace('{{SmsName}}', sms_name)
with open(file_path, 'w') as file:
file.write(content)
def create_hard_links(src_dir):
"""在 schemastore/en/ 下创建指向 src_dir 的硬链接"""
src_path = os.path.join(schemastore_path, src_dir)
if not os.path.exists(src_path):
return
for filename in os.listdir(src_path):
if filename.endswith('.json'):
src_file = os.path.join(src_path, filename)
dst_file = os.path.join(schemastore_path, filename)
if os.path.exists(dst_file):
src_stat = os.stat(src_file)
dst_stat = os.stat(dst_file)
if src_stat.st_ino != dst_stat.st_ino:
os.remove(dst_file)
if not os.path.exists(dst_file):
os.link(src_file, dst_file)
for schema_dir in ["dmtf/json_schema", "oem/openubmc/json_schema"]:
create_hard_links(schema_dir)
def json_to_lua_table(self, data, indent=0):
lua = ""
if isinstance(data, dict):
if not data:
return "{}"
lua += "{"
for key, value in data.items():
lua += f'["{key}"]={self.json_to_lua_table(value, indent + 1)},'
lua += f'}}'
elif isinstance(data, list):
if not data:
return "{}"
lua += "{"
for i, value in enumerate(data, start=1):
lua += f'[{i}]={self.json_to_lua_table(value, indent + 1)},'
lua += f'}}'
elif isinstance(data, str):
escaped_str = data.strip('"').replace('\\', '\\\\').replace('"', '\\"')
escaped_str = escaped_str.replace('\n', '\\n').replace('\r', '\\r').replace('\t', '\\t')
lua += f'"{escaped_str}"'
elif isinstance(data, bool):
lua += "true" if data else "false"
elif isinstance(data, (int, float)):
lua += str(data)
elif data is None:
lua += "cjson.null"
else:
raise TypeError(f"Unsupported data type: {type(data)}")
return lua
def json_to_lua(self, intf, json_dir, lua_file_path, lua_mapper_file_path, lua_route_tree_path):
json_files = self.get_sorted_json_files(json_dir)
lua_content = "Input.data="
mapper_content = "Input.data="
lua = {}
mapper = {}
if intf == "cli":
route_tree_content = "Input.data="
route_tree = {}
for json_file in json_files:
with open(json_file, 'r') as f:
data = json.load(f)
relative_path_list = self.get_relative_path_paths(json_dir, json_file)
for resource in data.get("Resources", []):
uri = resource.get("Uri")
if uri is None:
continue
if intf == "redfish":
ignore_etags = resource.get("IgnoreEtags", [])
self.set_dict_value(relative_path_list + [uri], "IgnoreEtags", ignore_etags, mapper)
for key, value in resource.items():
if key in ["Uri", "IgnoreEtags", "Interfaces"]:
continue
self.set_dict_value([uri], key, value, lua)
for interface in resource.get("Interfaces", []):
method_type = interface.get("Type")
if method_type is None:
continue
method_type = method_type.lower()
self.set_dict_value(relative_path_list + [uri], method_type, True, mapper)
if intf == "cli":
self.set_dict_value(uri.split('/')[1:] + ['#methods'], method_type, True, route_tree)
lock_down_allow = interface.get("LockDownAllow")
if lock_down_allow is not None and method_type == 'get':
lock_down_allow = True
if lock_down_allow is not None:
lua_str = self.json_to_lua_table(lock_down_allow, 4)
self.set_dict_value([uri, 'methods', method_type], 'LockDownAllow', lua_str, lua)
rsp_body = interface.get("RspBody")
if rsp_body is not None and intf == 'redfish':
self.set_dict_value([uri, 'methods', method_type], 'RspBody', json.dumps(rsp_body), lua)
elif rsp_body is not None:
self.set_dict_value([uri, 'methods', method_type], 'RspBody', rsp_body, lua)
req_body = interface.get("ReqBody")
if req_body is not None and intf == 'cli':
self.set_dict_value([uri, 'methods', method_type], 'ReqBody', json.dumps(req_body), lua)
elif req_body is not None:
self.set_dict_value([uri, 'methods', method_type], 'ReqBody', req_body, lua)
action_rsp_body = interface.get("ActionResponseBody")
if action_rsp_body is not None:
lua_str = self.json_to_lua_table(json.dumps(action_rsp_body), 4)
self.set_dict_value([uri, 'methods', method_type], 'ActionResponseBody', lua_str, lua)
processing_flow = interface.get("ProcessingFlow")
if processing_flow is not None:
for pf_item in processing_flow:
if "CallIf" in pf_item:
pf_item["CallIf"] = json.dumps(pf_item["CallIf"])
for key, value in interface.items():
if key in ["Type", "RspBody", "ReqBody", "ActionResponseBody", "LockDownAllow"]:
continue
self.set_dict_value([uri, 'methods', method_type], key, value, lua)
mapper_content += f"{self.json_to_lua_table(mapper, 0)}"
lua_content += f"{self.json_to_lua_table(lua, 0)}"
if intf == 'cli':
route_tree_content += f"{self.json_to_lua_table(route_tree, 0)}"
directory = os.path.dirname(lua_route_tree_path)
if not os.path.exists(directory):
os.makedirs(directory)
with open(lua_route_tree_path, 'w') as f:
f.write(route_tree_content)
self.output.info('{} package precompilation route tree file to {} successfully'\
.format(intf, lua_route_tree_path))
directory = os.path.dirname(lua_file_path)
if not os.path.exists(directory):
os.makedirs(directory)
with open(lua_file_path, 'w') as f:
f.write(lua_content)
self.output.info('{} package precompilation config file to {} successfully'.format(intf, lua_file_path))
directory = os.path.dirname(lua_mapper_file_path)
if not os.path.exists(directory):
os.makedirs(directory)
with open(lua_mapper_file_path, 'w') as f:
f.write(mapper_content)
self.output.info('{} package precompilation mapper file to {} successfully'\
.format(intf, lua_mapper_file_path))
def package(self):
copy(self, "permissions.ini", src=os.path.join(self.source_folder, "dist"), dst=self.package_folder)
self.custom_ipmc_version()
software_name, sms_name = self.read_config()
self.replace_strings_in_files(software_name, sms_name)
super(AppConan, self).package()
sys.path.append(self.source_folder)
from custom import mapping_config_patch
interface = {"redfish", "web_backend", "cli", "snmp"}
for intf in interface:
lua_dir = os.path.join(self.source_folder, "interface_config/" + intf)
lua_package_dir = os.path.join(self.package_folder, "opt/bmc/apps/" + intf + "/interface_config")
for root, dirs, files in os.walk(lua_dir):
for file in files:
if file.endswith(".lua") and not os.path.islink(file):
file_path = os.path.join(root, file)
file_package_path = file_path.replace(lua_dir, lua_package_dir)
shutil.copy2(file_path, file_package_path)
config_path = os.path.join(self.source_folder, 'interface_config', intf)
patch_path = os.path.join(self.source_folder, 'oem', str(self.options.oem), intf)
if not os.path.isdir(patch_path):
continue
map_patch = mapping_config_patch.MappingConfigPatch(self, patch_path, config_path)
map_patch.run()
for intf in ["board_info_collector"]:
lua_dir = os.path.join(self.source_folder, "interface_config/" + intf)
lua_package_dir = os.path.join(self.package_folder, "opt/bmc/board_info_collector")
if not os.path.exists(lua_package_dir):
os.mkdir(lua_package_dir)
for root, dirs, files in os.walk(lua_dir):
for file in files:
if file.endswith(".lua") and not os.path.islink(file):
file_path = os.path.join(root, file)
file_package_path = file_path.replace(lua_dir, lua_package_dir)
result =os.path.split(file_package_path)
if not os.path.exists(result[0]):
os.mkdir(result[0])
shutil.copy2(file_path, file_package_path)
config_path = os.path.join(self.source_folder, 'interface_config', intf)
patch_path = os.path.join(self.source_folder, 'oem', str(self.options.oem), intf)
if not os.path.isdir(patch_path):
continue
map_patch = mapping_config_patch.MappingConfigPatch(self, patch_path, config_path)
map_patch.run()
interface_mapper = {
"redfish": ["mapping_config"],
"web_backend": ["mapping_config"],
"cli": ["ipmcget", "ipmcset"],
"snmp": ["mapping_config"],
"board_info_collector": ["config"]
}
for intf, sub_dirs in interface_mapper.items():
for sub_dir in sub_dirs:
json_dir = os.path.join(self.source_folder, "interface_config", intf, sub_dir)
if intf == "board_info_collector":
lua_package_dir = os.path.join(self.package_folder, "opt/bmc/board_info_collector")
else:
lua_package_dir = os.path.join(self.package_folder, "opt/bmc/apps", intf, "interface_config")
lua_file_path = os.path.join(lua_package_dir, sub_dir, "config.lua")
lua_mapper_file_path = os.path.join(lua_package_dir, sub_dir, "mapper.lua")
if intf == "cli":
lua_route_tree_path = os.path.join(lua_package_dir, sub_dir, "route_tree.lua")
self.json_to_lua(intf, json_dir, lua_file_path, lua_mapper_file_path, lua_route_tree_path)
else:
self.json_to_lua(intf, json_dir, lua_file_path, lua_mapper_file_path, None)
config_dir = os.path.join(self.source_folder, "interface_config")
inode_map = {}
for root, dirs, files in os.walk(config_dir):
for file in files:
file_full_path = os.path.join(root, file)
if file.endswith(".json") and not os.path.islink(file_full_path):
file_path = file_full_path
with open(file_path, mode='rb') as fp:
data = json.load(fp)
content = json.dumps(data, separators=(',', ':'))
with open(file_path, mode='wb') as fd:
fd.write(content.encode('utf-8'))
for interface_type in ["web_backend", "redfish", "snmp", "cli"]:
if "interface_config/" + interface_type in file_path:
json_dir = os.path.join(self.source_folder, "interface_config", interface_type)
json_package_dir = os.path.join(self.package_folder, "opt/bmc/apps", interface_type, "interface_config")
for interface_type in ["board_info_collector"]:
if "interface_config/" + interface_type in file_path:
json_dir = os.path.join(self.source_folder, "interface_config", interface_type)
json_package_dir = os.path.join(self.package_folder, "opt/bmc/board_info_collector")
file_package_path = file_path.replace(json_dir, json_package_dir)
result = os.path.split(file_package_path)
if not os.path.exists(result[0]):
os.makedirs(result[0])
file_stat = os.stat(file_path)
inode_key = (file_stat.st_dev, file_stat.st_ino)
if inode_key in inode_map:
if os.path.exists(file_package_path):
src_stat = os.stat(inode_map[inode_key])
try:
dst_stat = os.stat(file_package_path)
if src_stat.st_ino == dst_stat.st_ino:
continue
except OSError:
pass
os.remove(file_package_path)
os.link(inode_map[inode_key], file_package_path)
else:
shutil.copy2(file_path, file_package_path)
inode_map[inode_key] = file_package_path
xml_dir = os.path.join(self.source_folder, "interface_config/redfish/static_resource/redfish/v1")
for root, _, files in os.walk(xml_dir):
for file in files:
file_full_path = os.path.join(root, file)
if file.endswith(".xml") and not os.path.islink(file_full_path):
file_path = file_full_path
with open(file_path, mode='r', encoding='utf-8') as fp:
content = fp.read()
content = self.compress_xml(content)
with open(file_path, mode='w', encoding='utf-8') as fd:
fd.write(content)
xml_package_dir = os.path.join(self.package_folder, "opt/bmc/apps/redfish/interface_config/static_resource/redfish/v1")
file_package_path = file_path.replace(xml_dir, xml_package_dir)
result = os.path.split(file_package_path)
if not os.path.exists(result[0]):
os.makedirs(result[0])
shutil.copy2(file_path, file_package_path)
static_v1_src = os.path.join(self.source_folder,
"interface_config/redfish/static_resource/redfish/v1")
static_v1_dst = os.path.join(self.package_folder,
"opt/bmc/apps/redfish/interface_config/static_resource/redfish/v1")
for dirname in ["jsonschemas", "schemastore"]:
src_dir = os.path.join(static_v1_src, dirname)
if not os.path.exists(src_dir):
continue
tar_name = dirname + ".tar.bz2"
tar_dst_path = os.path.join(static_v1_dst, tar_name)
if not os.path.exists(static_v1_dst):
os.makedirs(static_v1_dst)
with tarfile.open(tar_dst_path, "w:bz2") as tar:
self._add_dir_to_tar_with_hardlinks(tar, src_dir, dirname)
remove_dir = os.path.join(static_v1_dst, dirname)
if os.path.exists(remove_dir):
shutil.rmtree(remove_dir)
def compress_xml(self, xml_content):
xml_content = xml_content.replace('\n', '').replace('\r', '')
xml_content = re.sub(r'\s+', ' ', xml_content)
xml_content = re.sub(r'>\s+', '>', xml_content)
xml_content = re.sub(r'\s+<', '<', xml_content)
return xml_content
def package_info(self):
pass
def _add_dir_to_tar_with_hardlinks(self, tar, src_dir, arcname):
inode_map = {}
for root, dirs, files in os.walk(src_dir):
dirs.sort()
rel_dir = os.path.relpath(root, src_dir)
if rel_dir == '.':
arc_dir = arcname
else:
arc_dir = arcname + '/' + rel_dir.replace(os.sep, '/')
tarinfo = tar.gettarinfo(root, arcname=arc_dir)
tar.addfile(tarinfo)
for filename in sorted(files):
filepath = os.path.join(root, filename)
arc_filepath = arc_dir + '/' + filename
if os.path.islink(filepath):
tarinfo = tar.gettarinfo(filepath, arcname=arc_filepath)
tar.addfile(tarinfo)
continue
stat = os.lstat(filepath)
inode_key = (stat.st_dev, stat.st_ino)
if stat.st_nlink > 1 and inode_key in inode_map:
tarinfo = tarfile.TarInfo(name=arc_filepath)
tarinfo.type = tarfile.LNKTYPE
tarinfo.linkname = inode_map[inode_key]
tarinfo.mtime = stat.st_mtime
tarinfo.mode = stat.st_mode
tarinfo.uid = stat.st_uid
tarinfo.gid = stat.st_gid
tar.addfile(tarinfo)
else:
tarinfo = tar.gettarinfo(filepath, arcname=arc_filepath)
with open(filepath, 'rb') as f:
tar.addfile(tarinfo, f)
if stat.st_nlink > 1:
inode_map[inode_key] = arc_filepath