|
6 | 6 | """Tool to manage the KernelCI YAML pipeline configuration"""
|
7 | 7 |
|
8 | 8 | import os
|
| 9 | +import sys |
9 | 10 |
|
10 | 11 | import click
|
11 | 12 | import yaml
|
12 | 13 |
|
13 | 14 | import kernelci.config
|
| 15 | +import kernelci.api.helper |
14 | 16 | from . import Args, kci
|
15 | 17 |
|
16 | 18 |
|
@@ -75,3 +77,127 @@ def dump(section, config, indent, recursive):
|
75 | 77 | else:
|
76 | 78 | echo = click.echo_via_pager if recursive else click.echo
|
77 | 79 | echo(yaml.dump(data, indent=indent))
|
| 80 | + |
| 81 | + |
| 82 | +def validate_rules(node, rules): |
| 83 | + helper = kernelci.api.helper.APIHelper(None) |
| 84 | + if helper.should_create_node(rules, node): |
| 85 | + return True |
| 86 | + else: |
| 87 | + return False |
| 88 | + |
| 89 | + |
| 90 | +def compare_builds(merged_data): |
| 91 | + """ |
| 92 | + Compare kbuilds and print builds with identical params |
| 93 | + """ |
| 94 | + r = "" |
| 95 | + jobs = merged_data.get("jobs") |
| 96 | + if not jobs: |
| 97 | + click.echo("No jobs found in the merged data, " |
| 98 | + "maybe you need to add parameter " |
| 99 | + "-c path/kernelci-pipeline/config?") |
| 100 | + sys.exit(1) |
| 101 | + kbuilds_list = [] |
| 102 | + for job in jobs: |
| 103 | + if jobs[job].get("kind") == "kbuild": |
| 104 | + kbuilds_list.append(job) |
| 105 | + |
| 106 | + kbuilds_dict = {} |
| 107 | + import json |
| 108 | + for kbuild in kbuilds_list: |
| 109 | + params = jobs[kbuild].get("params", {}) |
| 110 | + # Convert params to a hashable type by serializing to JSON |
| 111 | + key = json.dumps(params, sort_keys=True) |
| 112 | + if key not in kbuilds_dict: |
| 113 | + kbuilds_dict[key] = [] |
| 114 | + kbuilds_dict[key].append(kbuild) |
| 115 | + |
| 116 | + # print builds with identical params |
| 117 | + for params, kbuild_list in kbuilds_dict.items(): |
| 118 | + if len(kbuild_list) > 1: |
| 119 | + r += f"Params {params}: {kbuild_list}," |
| 120 | + |
| 121 | + return r |
| 122 | + |
| 123 | + |
| 124 | +# pylint: disable=too-many-branches disable=too-many-locals |
| 125 | +def do_forecast(merged_data): |
| 126 | + """ |
| 127 | + We will simulate checkout event on each tree/branch |
| 128 | + and try to build list of builds/tests it will run |
| 129 | + """ |
| 130 | + checkouts = [] |
| 131 | + build_configs = merged_data.get("build_configs", {}) |
| 132 | + for bcfg in build_configs: |
| 133 | + data = build_configs[bcfg] |
| 134 | + if not data.get("architectures"): |
| 135 | + data["architectures"] = None |
| 136 | + checkouts.append(data) |
| 137 | + |
| 138 | + # sort checkouts by tree and branch |
| 139 | + checkouts.sort(key=lambda x: (x.get("tree", ""), x.get("branch", ""))) |
| 140 | + |
| 141 | + # iterate over checkouts |
| 142 | + for checkout in checkouts: |
| 143 | + checkout["kbuilds"] = [] |
| 144 | + # iterate over events (jobs) |
| 145 | + jobs = merged_data.get("scheduler", []) |
| 146 | + for job in jobs: |
| 147 | + kind = job.get("event", {}).get("kind") |
| 148 | + if kind != "checkout": |
| 149 | + continue |
| 150 | + job_name = job.get("job") |
| 151 | + job_kind = merged_data.get("jobs", {}).get(job_name, {}).get("kind") |
| 152 | + if job_kind == "kbuild": |
| 153 | + # check "params" "arch" |
| 154 | + job_params = merged_data.get("jobs", {}).get(job_name, {}).get("params", {}) |
| 155 | + arch = job_params.get("arch") |
| 156 | + if checkout.get("architectures") and arch not in checkout.get("architectures"): |
| 157 | + continue |
| 158 | + scheduler_rules = job.get("rules", []) |
| 159 | + job = merged_data.get("jobs", {}).get(job_name, {}) |
| 160 | + job_rules = job.get("rules", []) |
| 161 | + node = { |
| 162 | + "kind": "checkout", |
| 163 | + "data": { |
| 164 | + "kernel_revision": { |
| 165 | + "tree": checkout.get("tree"), |
| 166 | + "branch": checkout.get("branch"), |
| 167 | + "version": { |
| 168 | + "version": 6, |
| 169 | + "patchlevel": 16, |
| 170 | + "extra": "-rc3-973-gb7d1bbd97f77" |
| 171 | + }, |
| 172 | + } |
| 173 | + }, |
| 174 | + } |
| 175 | + if not validate_rules(node, job_rules) or not validate_rules(node, scheduler_rules): |
| 176 | + continue |
| 177 | + checkout["kbuilds"].append(job_name) |
| 178 | + checkout["kbuilds_identical"] = compare_builds(merged_data) |
| 179 | + |
| 180 | + # print the results |
| 181 | + for checkout in checkouts: |
| 182 | + print(f"Checkout: {checkout.get('tree')}:{checkout.get('branch')}") |
| 183 | + if checkout.get("kbuilds_identical"): |
| 184 | + print(f" Identical builds: {checkout['kbuilds_identical']}") |
| 185 | + if checkout.get("kbuilds"): |
| 186 | + num_builds = len(checkout["kbuilds"]) |
| 187 | + print(f" Number of builds: {num_builds}") |
| 188 | + print(" Builds:") |
| 189 | + for build in checkout["kbuilds"]: |
| 190 | + print(f" - {build}") |
| 191 | + else: |
| 192 | + print(" No builds found for this checkout") |
| 193 | + |
| 194 | + |
| 195 | +@kci_config.command |
| 196 | +@Args.config |
| 197 | +def forecast(config): |
| 198 | + """Dump entries from the SECTION of the pipeline YAML configuration""" |
| 199 | + config_paths = kernelci.config.get_config_paths(config) |
| 200 | + if not config_paths: |
| 201 | + return |
| 202 | + data = kernelci.config.load_yaml(config_paths) |
| 203 | + do_forecast(data) |
0 commit comments