-
Notifications
You must be signed in to change notification settings - Fork 0
Implement wait and download action #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
33f0c12
c0a7fb0
186f522
4fa922d
41aef90
9f8e8fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -208,3 +208,6 @@ __marimo__/ | |
|
||
# Streamlit | ||
.streamlit/secrets.toml | ||
|
||
# SDCM credentials | ||
authconfig.json |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,12 @@ | ||
#!/usr/bin/env python3 | ||
|
||
""" | ||
Usage: python3 hw_submission_automation.py [options] [string...] | ||
|
||
Options: | ||
-h, --help show this help message | ||
-t ..., --test_harness parse test_harness, valid value: HLK(default),Attestation | ||
-n ..., --product_name parse product name, eg: "Red Hat VirtIO RNG Drivers for Windows 11" | ||
-a ..., --guest_arch parse specified guest archtechure. Valid value: x86,x64(default),mixed,ARM64 | ||
'mixed': For Win10 packages containing both x86/x64 | ||
-g ..., --guest_names parse specified guest platform. Valid value: | ||
|
||
x86: 10_1511, 10_1607, 10_1703, 10_1709, 10_1803, 10_1809, 10_19H1, 10_2004, 10.all, 10.latest | ||
x64: 10_1511, 10_1607, 10_1703, 10_1709, 10_1803, 10_1809, 10_19H1, 10_2004, 10_21H2, 11_22H2, 11_24H2, 16, 19, 22, | ||
25, 10.all, 11.all, 10.latest, 11.latest | ||
ARM64: 10_1709, 10_1803, 10_19H1, 10_2004, 10_21H2, 11_22H2, 11_24H2, 22, 25, 10.all, 11.all, 10.latest, 11.latest | ||
Examples: | ||
11.latest | ||
10_1803,10_2004 | ||
10.all | ||
|
||
-s ..., --submission_name parse submission name, default value is the same with product_name | ||
-p ..., --package_path parse package file path eg: /home/271_RNG_win11_unsigned.hlkx | ||
-d ..., --announcement_date Parse announcement date (GA) in YYYY-MM-DD format (e.g., 2025-06-24) | ||
|
||
Examples: | ||
python3 hw_submission_automation.py -n test_rng -g 11.latest -p /home/271_RNG_win11_unsigned.hlkx -d 2025-06-24 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, we can keep this example to have a clear usage. |
||
python3 hw_submission_automation.py submit -n test_rng -g 11.latest -p /home/271_RNG_win11_unsigned.hlkx -d 2025-06-24 | ||
""" | ||
|
||
import getopt | ||
import argparse | ||
import json | ||
import os | ||
import re | ||
|
@@ -173,7 +151,7 @@ def _run_sdcm(self, args: List[str]) -> str: | |
error_msg = ( | ||
f"SDCM command failed with code {e.returncode}:\n" | ||
f"Command: {' '.join(command)}\n" | ||
f"Stdout output: {e.output.strip()}" | ||
f"Stdout output: {e.output.strip()}\n" | ||
f"Stderr output: {e.stderr.strip()}" | ||
) | ||
raise RuntimeError(error_msg) from e | ||
|
@@ -306,6 +284,19 @@ def download_results(self, product_id: str, submission_id: str, output_file: str | |
] | ||
) | ||
|
||
def download_metadata(self, product_id: str, submission_id: str, output_file: str) -> str: | ||
"""Download submission metadata""" | ||
return self._run_sdcm( | ||
[ | ||
"--metadata", | ||
output_file, | ||
"--productid", | ||
product_id, | ||
"--submissionid", | ||
submission_id, | ||
] | ||
) | ||
|
||
def list_products(self) -> str: | ||
"""List all products""" | ||
return self._run_sdcm(["--list", "product"]) | ||
|
@@ -324,8 +315,77 @@ def format_date_to_iso(date_str): | |
return datetime.strptime(date_str, "%Y-%m-%d").isoformat() | ||
|
||
|
||
def usage(): | ||
print(__doc__) | ||
def parse_arguments(): | ||
"""Parse command line arguments using argparse""" | ||
parser = argparse.ArgumentParser( | ||
description="Hardware submission automation tool for SDCM", | ||
formatter_class=argparse.RawDescriptionHelpFormatter, | ||
epilog=__doc__ | ||
) | ||
parser.add_argument( | ||
"-t", "--test_harness", | ||
kostyanf14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
default="HLK", | ||
help="parse test_harness, valid value: HLK(default),Attestation" | ||
) | ||
parser.add_argument( | ||
"-n", "--product_name", | ||
help="parse product name, eg: 'Red Hat VirtIO RNG Drivers for Windows 11'" | ||
) | ||
parser.add_argument( | ||
"-a", "--guest_arch", | ||
default="x64", | ||
help="parse specified guest architecture. Valid value: x86,x64(default),mixed,ARM64" | ||
) | ||
parser.add_argument( | ||
"-g", "--guest_names", | ||
help=""" | ||
parse specified guest platform. | ||
Valid value: | ||
x86: 10_1511, 10_1607, 10_1703, 10_1709, 10_1803, 10_1809, 10_19H1, 10_2004, 10.all, 10.latest | ||
x64: 10_1511, 10_1607, 10_1703, 10_1709, 10_1803, 10_1809, 10_19H1, 10_2004, 10_21H2, 11_22H2, 11_24H2, 16, 19, 22, | ||
25, 10.all, 11.all, 10.latest, 11.latest | ||
ARM64: 10_1709, 10_1803, 10_19H1, 10_2004, 10_21H2, 11_22H2, 11_24H2, 22, 25, 10.all, 11.all, 10.latest, 11.latest | ||
Examples: | ||
11.latest | ||
10_1803,10_2004 | ||
10.all""" | ||
) | ||
parser.add_argument( | ||
"-s", "--submission_name", | ||
help="parse submission name, default value is the same with product_name" | ||
) | ||
parser.add_argument( | ||
"-p", "--package_path", | ||
help="parse package file path eg: /home/271_RNG_win11_unsigned.hlkx" | ||
) | ||
parser.add_argument( | ||
"-d", "--announcement_date", | ||
default="2025-01-01", | ||
help="Parse announcement date (GA) in YYYY-MM-DD format (e.g., 2025-06-24)" | ||
) | ||
parser.add_argument( | ||
"action", | ||
nargs="?", | ||
default="submit", | ||
choices=["submit", "wait_download"], | ||
help="Action to perform: submit (default) or wait_download" | ||
) | ||
|
||
parser.add_argument( | ||
"-pid", "--product_id", | ||
help="Parse product ID" | ||
) | ||
|
||
parser.add_argument( | ||
"-sid", "--submission_id", | ||
help="Parse submission ID" | ||
) | ||
|
||
parser.add_argument( | ||
"-o", "--output_file", | ||
help="Parse output file path (e.g., /path/to/file.signed.zip)" | ||
) | ||
return parser.parse_args() | ||
kostyanf14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
def gen_guest_mapping(): | ||
|
@@ -402,54 +462,19 @@ def gen_guest_mapping(): | |
return mapping | ||
|
||
|
||
def main(argv): | ||
test_harness = "HLK" | ||
product_name = None | ||
guest_names = [] | ||
guest_arch = "x64" | ||
submission_name = None | ||
package_path = None | ||
announcement_date = "2025-01-01" | ||
def main_submit(args): | ||
# Validate required arguments for submit action | ||
if not args.product_name: | ||
print("Error: --product_name is required for submit action") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider using argparse.ArgumentParser.error() for consistent error reporting. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will be complicated because "product_name" is not a mandatory option for argparse, because it is required only for the commit action. So, from the argparse point of view, there is no error. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, from this point the view, it is reasonable. |
||
sys.exit(1) | ||
if not args.guest_names: | ||
print("Error: --guest_names is required for submit action") | ||
sys.exit(1) | ||
if not args.package_path: | ||
print("Error: --package_path is required for submit action") | ||
sys.exit(1) | ||
Comment on lines
+466
to
+475
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The manual validation of required arguments for the 'submit' action (and similarly in 'main_wait_download') is functional but can be improved. Using |
||
|
||
marketing_names = [] | ||
try: | ||
opts, args = getopt.getopt( | ||
argv, | ||
"ht:n:g:a:s:p:d:", | ||
[ | ||
"help", | ||
"test_harness=", | ||
"product_name=", | ||
"guest_names=", | ||
"guest_arch=", | ||
"submission_name=", | ||
"package_path=", | ||
"announcement_date=", | ||
], | ||
) | ||
except getopt.GetoptError: | ||
usage() | ||
sys.exit(2) | ||
|
||
for opt, arg in opts: | ||
print(arg) | ||
print(opt) | ||
if opt in ("-h", "--help"): | ||
usage() | ||
sys.exit() | ||
elif opt in ("-t", "--test_harness"): | ||
test_harness = arg | ||
elif opt in ("-n", "--product_name"): | ||
product_name = arg | ||
elif opt in ("-g", "--guest_names"): | ||
guest_names = arg | ||
elif opt in ("-a", "--guest_arch"): | ||
guest_arch = arg | ||
elif opt in ("-s", "--submission_name"): | ||
submission_name = arg | ||
elif opt in ("-p", "--package_path"): | ||
package_path = arg | ||
elif opt in ("-d", "--announcement_date"): | ||
announcement_date = arg | ||
|
||
guest_mapping = gen_guest_mapping() | ||
|
||
|
@@ -460,11 +485,11 @@ def main(argv): | |
requested_signatures = [] | ||
selected_product_types = [] | ||
|
||
for guest_name in guest_names.split(","): | ||
if guest_arch == "mixed": | ||
for guest_name in args.guest_names.split(","): | ||
if args.guest_arch == "mixed": | ||
arch_list = ["x86", "x64"] | ||
else: | ||
arch_list = [guest_arch] | ||
arch_list = [args.guest_arch] | ||
for arch in arch_list: | ||
current_mappings = guest_mapping[arch][guest_name] | ||
for mapping in current_mappings: | ||
|
@@ -474,63 +499,106 @@ def main(argv): | |
requested_signatures = list(set(requested_signatures)) | ||
selected_product_types = list(set(selected_product_types)) | ||
|
||
submission_name = args.submission_name | ||
if not submission_name: | ||
print(f"Submission name is not specified, using product name: {product_name}") | ||
submission_name = product_name | ||
print(f"Submission name is not specified, using product name: {args.product_name}") | ||
submission_name = args.product_name | ||
|
||
marketing_names = [product_name] | ||
marketing_names = [args.product_name] | ||
|
||
if test_harness == "Attestation": | ||
if args.test_harness == "Attestation": | ||
marketing_names = [] | ||
selected_product_types = [] | ||
print("Attestation test harness selected, no marketing names or product types will be set.") | ||
|
||
# we always keep these three value the same when submit manually. | ||
announcement_date = format_date_to_iso(announcement_date) | ||
announcement_date = format_date_to_iso(args.announcement_date) | ||
|
||
print("SDCM product creation parameters:") | ||
print(f" Test harness: {test_harness}") | ||
print(f" Product name: {product_name}") | ||
print(f" Guest names: {guest_names}") | ||
print(f" Guest architecture: {guest_arch}") | ||
print(f" Test harness: {args.test_harness}") | ||
print(f" Product name: {args.product_name}") | ||
print(f" Guest names: {args.guest_names}") | ||
print(f" Guest architecture: {args.guest_arch}") | ||
print(f" - Requested signatures: {requested_signatures}") | ||
print(f" - Selected product types: {selected_product_types}") | ||
print(f" Package path: {package_path}") | ||
print(f" Package path: {args.package_path}") | ||
print(f" Announcement date: {announcement_date}") | ||
print(f" Submission name: {submission_name}") | ||
print(f" Marketing names: {marketing_names}") | ||
|
||
wrapper = SDCMWrapper() | ||
pid = wrapper.create_product( | ||
product_name, | ||
test_harness, | ||
args.product_name, | ||
args.test_harness, | ||
announcement_date, | ||
marketing_names, | ||
selected_product_types, | ||
requested_signatures, | ||
) | ||
if not pid: | ||
print(f"Failed to create product: {product_name}") | ||
print(f"Failed to create product: {args.product_name}") | ||
sys.exit(1) | ||
|
||
sid = wrapper.create_submission(pid, submission_name) | ||
if not sid: | ||
print(f"Failed to create submission: {submission_name}") | ||
sys.exit(1) | ||
|
||
wrapper.upload_package(package_path, pid, sid) | ||
wrapper.upload_package(args.package_path, pid, sid) | ||
wrapper.commit_submission(pid, sid) | ||
|
||
create_results = { | ||
"product_id": pid, | ||
"submission_id": sid, | ||
"product_name": product_name, | ||
"product_name": args.product_name, | ||
"submission_name": submission_name, | ||
} | ||
create_results_file = slugify(f"Result_Product_{(product_name)}.json") | ||
create_results_file = slugify(f"Result_Product_{args.product_name}.json") | ||
with open(create_results_file, "w") as f: | ||
json.dump(create_results, f, indent=4) | ||
|
||
|
||
def main_wait_download(args): | ||
print(f"[INFO] Download action selected") | ||
|
||
if not args.product_id: | ||
print("Error: --product_id is required for download action") | ||
sys.exit(1) | ||
if not args.submission_id: | ||
print("Error: --submission_id is required for download action") | ||
sys.exit(1) | ||
if not args.output_file: | ||
print("Error: --output_file is required for download action") | ||
sys.exit(1) | ||
|
||
output_file = os.path.abspath(args.output_file) | ||
|
||
wrapper = SDCMWrapper() | ||
print(f"Waiting for submission with product ID: {args.product_id} and submission ID: {args.submission_id}") | ||
results = wrapper.wait_for_submission(args.product_id, args.submission_id) | ||
print(f"Submission completed") | ||
|
||
wrapper.download_results(args.product_id, args.submission_id, output_file) | ||
print(f"Results downloaded to: {output_file}") | ||
|
||
if "> driverMetadata Url" in results: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Relying on the string |
||
print(f"Driver metadata URL found in submission results") | ||
metadata_file = output_file + "_metadata.json" | ||
kostyanf14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
wrapper.download_metadata(args.product_id, args.submission_id, metadata_file) | ||
|
||
|
||
if __name__ == "__main__": | ||
main(sys.argv[1:]) | ||
# Parse command line arguments | ||
args = parse_arguments() | ||
|
||
# Check that action is supported | ||
if args.action not in ["submit", "wait_download"]: | ||
print(f"Error: Unsupported action '{args.action}'") | ||
print("Supported actions: submit, wait_download") | ||
sys.exit(1) | ||
kostyanf14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# Call appropriate main function based on action | ||
if args.action == "submit": | ||
main_submit(args) | ||
elif args.action == "wait_download": | ||
main_wait_download(args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We had better keep the available "guest_names" values on the argparse part to make the help message more user-friendly.
eg:
x86: 10_1511, 10_1607, 10_1703, 10_1709, 10_1803, 10_1809, 10_19H1, 10_2004, 10.all, 10.lates