"""
# **********************************************************************************
# Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
# [openeuler-jenkins] is licensed under the Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
# http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# Author:
# Create: 2023-07-18
# Description: Generate hot patch metadata files
# **********************************************************************************
"""
import argparse
import os
import re
import sys
import traceback
from xml.etree.ElementTree import Element
import xml.etree.ElementTree as ET
from xml.dom import minidom
from src.logger import logger
from src.proxy.gitee_proxy import GiteeProxy
def init_metadata_xml(filename):
try:
doc = minidom.Document()
root = doc.createElement('hotpatchdoc')
root.setAttribute('xmlns', meta_url)
root.setAttribute('xmlns:cvrf', meta_url)
doc.appendChild(root)
with open(filename, 'w', encoding='utf-8') as xml_file:
doc.writexml(xml_file, encoding="utf-8")
except IOError:
logger.error('init metadata.xml error: %s', traceback.format_exc())
sys.exit(1)
def update_hotpatch(xml_root, hotpatch_issue):
version, release = get_version(xml_root)
hotpatch = Element('hotpatch')
if mode == "ACC":
hotpatch.attrib['version'] = version
hotpatch.attrib['release'] = str(int(release) + 1)
elif mode == "SGL":
name = xml_root.find('.//hotpatch[@name="SGL-%s"]' % cve_issue_id)
logger.warning("name=SGL-%s result:%s", cve_issue_id, name)
if name:
err_info = "修复issue:%s 的SGL补丁pr已经创建,请勿重复提交" % cve_issue_id
comment_source_pr(err_info)
hotpatch.attrib['name'] = "SGL-%s" % cve_issue_id
hotpatch.attrib['version'] = "1"
hotpatch.attrib['release'] = "1"
hotpatch.attrib['type'] = patch_type
hotpatch.attrib['inherit'] = "0"
hotpatch.attrib['status'] = "unconfirmed"
src_link = Element('SRC_RPM')
src_link.text = src_url
hotpatch.append(src_link)
x86_debug_link = Element('Debug_RPM_X86_64')
x86_debug_link.text = x86_url
hotpatch.append(x86_debug_link)
aarch64_debug_link = Element('Debug_RPM_Aarch64')
aarch64_debug_link.text = arm_url
hotpatch.append(aarch64_debug_link)
for patch_file in args.patch.split(","):
patch = Element('patch')
patch.text = patch_file
hotpatch.append(patch)
issue_id = Element('issue')
issue_id.attrib['id'] = cve_issue_id
issue_id.attrib['issue_href'] = cve_issue_url
hotpatch.append(issue_id)
hotpatch_issue_link = Element('hotpatch_issue_link')
hotpatch_issue_link.text = hotpatch_issue
hotpatch.append(hotpatch_issue_link)
p = xml_root.find('.//Package[@name="%s-%s"]' % (args.repo, repo_version))
p.append(hotpatch)
return xml_root
def new_metadata(xml_root, hotpatch_issue):
logger.info("src_url=%s", src_url)
logger.info("x86_url=%s", x86_url)
logger.info("arm_url=%s", arm_url)
DocumentTitle = Element('DocumentTitle')
DocumentTitle.attrib['xml:lang'] = "en"
DocumentTitle.attrib['type'] = mode
DocumentTitle.text = "Managing Hot Patch Metadata"
xml_root.append(DocumentTitle)
HotPatchList = Element('HotPatchList')
xml_root.append(HotPatchList)
Package = Element('Package')
Package.attrib['name'] = "%s-%s" % (args.repo, repo_version)
HotPatchList.append(Package)
hotpatch = Element('hotpatch')
if mode == "SGL":
hotpatch.attrib['name'] = "SGL-%s" % cve_issue_id
hotpatch.attrib['version'] = "1"
hotpatch.attrib['release'] = "1"
hotpatch.attrib['type'] = patch_type
hotpatch.attrib['inherit'] = "0"
hotpatch.attrib['status'] = "unconfirmed"
logger.info("patch_type=%s", patch_type)
Package.append(hotpatch)
src_link = Element('SRC_RPM')
src_link.text = src_url
hotpatch.append(src_link)
x86_debug_link = Element('Debug_RPM_X86_64')
x86_debug_link.text = x86_url
hotpatch.append(x86_debug_link)
aarch64_debug_link = Element('Debug_RPM_Aarch64')
aarch64_debug_link.text = arm_url
hotpatch.append(aarch64_debug_link)
logger.info("patch=%s", args.patch)
for patch_file in args.patch.split(","):
patch = Element('patch')
patch.text = patch_file
logger.info("patch_file=%s", patch_file)
hotpatch.append(patch)
issue_id = Element('issue')
issue_id.attrib['id'] = cve_issue_id
issue_id.attrib['issue_href'] = cve_issue_url
hotpatch.append(issue_id)
logger.info("cve_issue_id=%s", cve_issue_id)
logger.info("cve_issue_url=%s", cve_issue_url)
hotpatch_issue_link = Element('hotpatch_issue_link')
hotpatch_issue_link.text = hotpatch_issue
hotpatch.append(hotpatch_issue_link)
logger.info("hotpatch_issue_url=%s", hotpatch_issue)
return xml_root
def get_version(xml_root):
hotpatch = xml_root.iter('hotpatch')
version = 0
release = 0
for child in hotpatch:
version = child.attrib["version"]
release = child.attrib["release"]
logger.info("version: %s, release: %s", version, release)
return version, release
def del_hotpatch(xml_root):
hotpatch_issue_number = ''
del_version, del_release = get_version(xml_root)
Package = xml_root.find('.//Package[@name="%s-%s"]' % (args.repo, repo_version))
if mode == "SGL":
p = xml_root.find('.//hotpatch[@name="SGL-%s"][@version="%s"][@release="%s"]' % (
cve_issue_id, del_version, del_release))
else:
p = xml_root.find('.//hotpatch[@version="%s"][@release="%s"]' % (del_version, del_release))
issue_url = p.find('hotpatch_issue_link').text
logger.info("hotpatch_issue_url exist: %s" % issue_url)
Package.remove(p)
if issue_url:
hotpatch_issue_number = issue_url.split("/")[-1]
return hotpatch_issue_number
def add_hotmetadata_xml():
if os.path.exists(metadata_file):
tree = ET.parse(metadata_file)
root = tree.getroot()
root = update_hotpatch(root, hotpatch_issue_url)
else:
init_metadata_xml(metadata_file)
tree = ET.ElementTree(file=metadata_file)
root = tree.getroot()
root = new_metadata(root, hotpatch_issue_url)
write_to_hotmetadata_xml(root)
def write_to_hotmetadata_xml(xml_root):
"""
Convert the ElementTree into a formated string, and write it to the output path.
"""
logger.info("---------------------")
raw_text = ET.tostring(xml_root)
dom = minidom.parseString(raw_text)
pretty_text = dom.toprettyxml(indent='\t')
new_pretty_text = re.sub('\n[\s|]*\n', '\n', pretty_text)
with open(metadata_file, 'w') as meta_f:
meta_f.write(new_pretty_text)
def get_issue_info(issue):
enterprises = "open_euler"
if patch_type not in ["cve", "bugfix", "feature"]:
comment_source_pr("修复的问题类型%s不是支持的类型,可选类型[cve/bugfix/feature],请重新指定\n" % patch_type)
resp = gp.get_issue(issue, enterprises)
html_url = resp.get("html_url")
if not resp:
err_info = "获取issue: %s 报错,请检查该issue是否存在" % issue
comment_source_pr(err_info)
if patch_type == "cve":
issue_id = resp.get("title")
if not issue_id.lower().startswith("cve-"):
command_format = r"""makerhotpatch命令格式如下:
```text
/makehotpatch <version> <mode> <patch_name> <patch_type> <issue_id> <os_branch>
version: 源码包版本号,必填
mode: 热补丁包演进方式<ACC/SGL>, 必填
patch_name: 冷补丁包名,支持多patch包按顺序传入,以逗号隔开,src-openeuler下的仓库必填;kernel仓库不用填,门禁会自动打包patch文件
patch_type: 修复的问题类型<cve/bugfix/feature>,必填
issue_id: 修复问题的issue id,必填
os_branch: 本次热补丁基于哪个分支做,必填
```"""
comment_source_pr("issue %s 不是cve类型,请重新指定\n %s" % (issue, command_format))
else:
issue_id = issue
return issue_id, html_url
def comment_source_pr(err_info):
body_str = "In response to this:\n > %s \n\n命令执行结果:\n %s" % (args.comment, err_info)
gp.comment_pr(args.prid, body_str)
logger.error(err_info)
sys.exit(1)
def create_update_issue(number=""):
owner = "openeuler"
title = "[hotpatch][{}-{}]fix {}".format(args.repo, repo_version, cve_issue)
if mode == "SGL":
hotpatch_metadata = "{}/blob/master/{}/{}/{}/hotmetadata_SGL.xml".format(
meta_url, args.branch, args.repo, repo_version)
else:
hotpatch_metadata = "{}/blob/master/{}/{}/{}/hotmetadata_ACC.xml".format(
meta_url, args.branch, args.repo, repo_version)
body = "问题类别:{}\n热补丁元数据:{}\n".format(patch_type, hotpatch_metadata)
if args.func == "add":
data = {"access_token": args.token, "repo": "hotpatch_meta", "title": title, "issue_type": "hotpatch",
"body": body}
resp = gp.create_issue(owner, data)
if not resp:
err_info = "创建热补丁issue失败,请重试"
comment_source_pr(err_info)
elif args.func == "update":
data = {"access_token": args.token, "repo": "hotpatch_meta", "title": title, "body": body}
resp = gp.update_issue(owner, data, number)
if not resp:
err_info = "更新热补丁issue:%s 失败,请重试" % number
comment_source_pr(err_info)
resp_issue_number = resp.get("html_url")
logger.info("issue_number=%s", resp_issue_number)
return resp_issue_number
def get_patch_list(metadata):
patch_list = []
patch = []
with open(metadata, "r") as xml_file:
try:
lines = xml_file.readlines()
package = lines[4].strip()
patch_list.append(package)
for line in lines[5:-3]:
line = line.strip()
if line.startswith("<hotpatch "):
patch = []
patch.append(line)
elif line.startswith("</hotpatch>"):
patch.append(line.strip())
patch_list.append("\n".join(patch))
else:
patch.append(line)
except IndexError:
logger.error('metadata.xml format error: %s', traceback.format_exc())
sys.exit(1)
logger.info("patch_list: %s", patch_list)
return patch_list
def init_args():
"""
init args
:return:
"""
parser = argparse.ArgumentParser(description='generate hotmetadata.xml')
parser.add_argument("-c", type=str, dest="community", default="src-openeuler",
help="src-openeuler or openeuler")
parser.add_argument("-t", type=str, dest="token", help="gitee api token")
parser.add_argument("-r", type=str, dest="repo", help="repo name")
parser.add_argument("-o", type=str, dest="output", help="output file to save result")
parser.add_argument("-m", type=str, dest="comment", help="trigger comment")
parser.add_argument("-b", type=str, dest="branch", help="src branch")
parser.add_argument("-pr", type=str, dest="prid", help="src prid")
parser.add_argument("-p", type=str, dest="patch", help="patch_list")
parser.add_argument("-f", type=str, dest="func", help="add or update")
parser.add_argument("-i", type=str, dest="hotpatch_file", help="hotpatch issue file")
parser.add_argument("-l", type=str, dest="download_link", help="src and debuginfo download link")
return parser.parse_args()
if __name__ == '__main__':
args = init_args()
if len(args.comment.split()) == 6:
_, repo_version, mode, patch_type, cve_issue, os_branch = args.comment.split()
elif len(args.comment.split()) == 7:
_, repo_version, mode, patch_name, patch_type, cve_issue, os_branch = args.comment.split()
else:
logger.error(
"commond error, please keep it consistent: /makehotpatch <version> <mode> <patch_name> "
"<patch_type> <cve_issue_id> <os_branch>")
sys.exit(1)
issue_number = ""
metadata_path = args.output
src_url, x86_url, arm_url = args.download_link.split(",")
meta_url = "https://gitee.com/openeuler/hotpatch_meta"
patch_path = os.path.join(metadata_path, args.branch, args.repo, repo_version, "patch")
if not os.path.exists(patch_path):
os.makedirs(patch_path)
version_path = os.path.join(metadata_path, args.branch, args.repo, repo_version)
if mode == "SGL":
metadata_file = os.path.join(version_path, "hotmetadata_SGL.xml")
else:
metadata_file = os.path.join(version_path, "hotmetadata_ACC.xml")
logger.info("new_metadata_patch: %s", metadata_file)
gp = GiteeProxy(args.community, args.repo, args.token)
cve_issue_id, cve_issue_url = get_issue_info(cve_issue)
if args.func == "update" and os.path.exists(metadata_file):
del_tree = ET.parse(metadata_file)
del_root = del_tree.getroot()
issue_number = del_hotpatch(del_root)
write_to_hotmetadata_xml(del_root)
hotpatch_issue_url = create_update_issue(issue_number)
with open(args.hotpatch_file, "w") as f:
f.write(hotpatch_issue_url)
add_hotmetadata_xml()
logger.info("success: hotmetadata.xml has been generated.")
sys.exit(0)