Skip to content

Commit 1ab70d3

Browse files
author
Jeny Sadadia
committed
Implement results issues command
`kci-dev results issues` command utilities: - Fetch KCIDB issues from the dashboard - Get new issues for checkout with `--new` option - Get failed/inconclusive builds and boots without any associated issues with `--missing` option Signed-off-by: Jeny Sadadia <[email protected]>
1 parent 1a2f165 commit 1ab70d3

File tree

3 files changed

+286
-15
lines changed

3 files changed

+286
-15
lines changed

kcidev/libs/dashboard.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -285,23 +285,24 @@ def dashboard_fetch_hardware_tests(name, origin, use_json):
285285
)
286286

287287

288-
def dashboard_fetch_build_issues(build_id, use_json):
288+
def dashboard_fetch_build_issues(build_id, use_json, error_verbose):
289289
endpoint = f"build/{build_id}/issues"
290290
logging.info(f"Fetching build issues for build ID: {build_id}")
291-
return dashboard_api_fetch(endpoint, {}, use_json)
291+
return dashboard_api_fetch(endpoint, {}, use_json, error_verbose=error_verbose)
292292

293293

294-
def dashboard_fetch_boot_issues(test_id, use_json):
294+
def dashboard_fetch_boot_issues(test_id, use_json, error_verbose):
295295
endpoint = f"test/{test_id}/issues"
296296
logging.info(f"Fetching test issues for test ID: {test_id}")
297-
return dashboard_api_fetch(endpoint, {}, use_json)
297+
return dashboard_api_fetch(endpoint, {}, use_json, error_verbose=error_verbose)
298298

299299

300300
def dashboard_fetch_issue_list(origin, days, use_json):
301301
params = {
302-
"filter_origin": origin,
303302
"interval_in_days": days,
304303
}
304+
if origin:
305+
params["filter_origin"] = origin
305306
logging.info(f"Fetching issue list for origin: {origin}")
306307
return dashboard_api_fetch("issue/", params, use_json)
307308

kcidev/subcommands/results/__init__.py

Lines changed: 223 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import click
77
from tabulate import tabulate
88

9-
from kcidev.libs.common import kci_msg, kci_msg_green, kci_msg_red
9+
from kcidev.libs.common import kci_msg, kci_msg_bold, kci_msg_green, kci_msg_red
1010
from kcidev.libs.dashboard import (
1111
dashboard_fetch_boot_issues,
1212
dashboard_fetch_boots,
@@ -16,6 +16,7 @@
1616
dashboard_fetch_commits_history,
1717
dashboard_fetch_issue,
1818
dashboard_fetch_issue_builds,
19+
dashboard_fetch_issue_list,
1920
dashboard_fetch_issue_tests,
2021
dashboard_fetch_issues_extra,
2122
dashboard_fetch_summary,
@@ -41,6 +42,7 @@
4142
cmd_tests,
4243
print_issue,
4344
print_issues,
45+
print_missing_data,
4446
)
4547

4648

@@ -473,21 +475,22 @@ def print_stats(data, headers, max_col_width, table_fmt):
473475
)
474476

475477

476-
def get_new_issues(origin, tree_name, giturl, branch, commit, arch):
478+
def get_new_issues(origin, tree_name, giturl, branch, commit, arch, use_json=False):
477479
"""Get new KCIDB issue for a checkout"""
478480
try:
481+
kci_msg_green(f"{tree_name}/{branch}:{commit}")
479482
data = dashboard_fetch_summary(origin, giturl, branch, commit, arch, True)
480483
tree_summary = data["summary"]
481484
items = ["builds", "boots", "tests"]
482-
all_issues = []
483485
new_issues = []
486+
issue_id_version = []
484487
for item in items:
485488
for i in tree_summary[item]["issues"]:
486-
all_issues.append([i["id"], i["version"]])
487-
if not all_issues:
488-
kci_msg_red(f"{tree_name}/{branch}:{commit} No issues found")
489+
issue_id_version.append([i["id"], i["version"]])
490+
if not issue_id_version:
491+
kci_msg("No issues found")
489492
return
490-
issue_extras = dashboard_fetch_issues_extra(all_issues, True)["issues"]
493+
issue_extras = dashboard_fetch_issues_extra(issue_id_version, True)["issues"]
491494
for issue_id, extras in issue_extras.items():
492495
first_incident = extras.get("first_incident")
493496
if first_incident:
@@ -496,13 +499,14 @@ def get_new_issues(origin, tree_name, giturl, branch, commit, arch):
496499
first_incident["git_commit_hash"] == commit,
497500
first_incident["git_repository_url"] == giturl,
498501
first_incident["git_repository_branch"] == branch,
502+
first_incident["tree_name"] == tree_name,
499503
]
500504
):
501-
new_issues.append(issue_id)
505+
new_issues.append(dashboard_fetch_issue(issue_id, False))
502506
if not new_issues:
503-
kci_msg_red(f"{tree_name}/{branch}:{commit} No new issues found")
507+
kci_msg("No new issues found")
504508
else:
505-
kci_msg_green(f"{tree_name}/{branch}:{commit} New issues: {new_issues}")
509+
print_issues(new_issues)
506510
except click.ClickException as e:
507511
print("Exception:", e.message)
508512

@@ -682,6 +686,215 @@ def detect(
682686
print_stats(stats, headers, max_col_width, table_fmt)
683687

684688

689+
def get_missing_issue_items(
690+
ctx, origin, item_type, giturl, branch, commit, tree_name, arch
691+
):
692+
"""Get information of failed or inconclusive builds/boots for which KCIDB
693+
issues don't exist"""
694+
try:
695+
if item_type == "builds":
696+
results_cmd = builds
697+
dashboard_func = dashboard_fetch_build_issues
698+
elif item_type == "boots":
699+
results_cmd = boots
700+
dashboard_func = dashboard_fetch_boot_issues
701+
else:
702+
kci_msg_red("Please specify 'builds' or 'boots' as items type")
703+
return []
704+
705+
dashboard_items = ctx.invoke(
706+
results_cmd,
707+
origin=origin,
708+
giturl=giturl,
709+
branch=branch,
710+
commit=commit,
711+
status="all",
712+
count=True,
713+
verbose=False,
714+
arch=arch,
715+
)
716+
717+
missing_ids = []
718+
for item in dashboard_items:
719+
# Exclude passed builds/boots
720+
if item["status"] == "PASS":
721+
continue
722+
item_id = item["id"]
723+
try:
724+
_ = dashboard_func(item_id, False, error_verbose=False)
725+
except click.ClickException as e:
726+
if "No issues" in e.message:
727+
missing_ids.append({item_id: item["status"]})
728+
if missing_ids:
729+
return [f"{tree_name}/{branch}", commit, missing_ids]
730+
return []
731+
except click.Abort:
732+
kci_msg_red(
733+
f"{tree_name}/{branch}: Aborted while fetching dashboard builds/boots"
734+
)
735+
return []
736+
except click.ClickException as e:
737+
kci_msg_red(f"{tree_name}/{branch}: {e.message}")
738+
return []
739+
740+
741+
@results.command(
742+
name="issues",
743+
help="""Fetch KCIDB issues from the dashboard
744+
745+
\b
746+
Examples:
747+
# Get issues
748+
kci-dev results issues --origin <origin> --days <number-of-days>
749+
# Get new issues for all checkouts
750+
kci-dev issues --new --days <number-of-days> --origin <origin>
751+
# Get new issues for a checkout
752+
kci-dev issues --new --giturl <git-url> --branch <git-branch> --commit <commit> --origin <origin>
753+
# Get failed or inconclusive builds and boots without any issue for all checkouts
754+
kci-dev issues --missing
755+
# Get failed or inconclusive builds and boots without any issue for specific checkout
756+
kci-dev issues --missing --giturl <git-url> --branch <git-branch> --commit <commit> --origin <origin>
757+
# Get failed/inconclusive builds without issues
758+
kci-dev issues --missing --builds
759+
# Get failed/inconclusive boots without issues
760+
kci-dev issues --missing --boots
761+
""",
762+
)
763+
@click.option(
764+
"--origin",
765+
help="Select KCIDB origin",
766+
default="maestro",
767+
)
768+
@click.option(
769+
"--days",
770+
help="Provide a period of time in days to get results for",
771+
type=int,
772+
default="5",
773+
)
774+
@click.option(
775+
"--new",
776+
is_flag=True,
777+
help="Fetch new KCIDB issues for a checkout",
778+
)
779+
@click.option(
780+
"--giturl",
781+
help="Git URL of kernel tree",
782+
)
783+
@click.option(
784+
"--branch",
785+
help="Branch to get results for",
786+
)
787+
@click.option(
788+
"--commit",
789+
help="Commit or tag to get results for",
790+
)
791+
@click.option(
792+
"--git-folder",
793+
help="Path of git repository folder",
794+
type=click.Path(exists=True, file_okay=False, dir_okay=True),
795+
)
796+
@click.option(
797+
"--latest",
798+
is_flag=True,
799+
help="Select latest results available",
800+
)
801+
@click.option(
802+
"--missing",
803+
is_flag=True,
804+
help="List all failed/incomplete builds and boots without any issue associated to them",
805+
)
806+
@click.option(
807+
"--builds",
808+
is_flag=True,
809+
help="The option can be used along with '--missing' to list only builds",
810+
)
811+
@click.option(
812+
"--boots",
813+
is_flag=True,
814+
help="The option can be used along with '--missing' to list only boots",
815+
)
816+
@click.option("--arch", help="Filter by arch")
817+
@click.pass_context
818+
@results_display_options
819+
def issues(
820+
ctx,
821+
origin,
822+
days,
823+
new,
824+
giturl,
825+
branch,
826+
commit,
827+
use_json,
828+
latest,
829+
git_folder,
830+
arch,
831+
missing,
832+
builds,
833+
boots,
834+
):
835+
"""Issues command handler"""
836+
if not new and not missing:
837+
data = dashboard_fetch_issue_list(origin, days, use_json)
838+
print_issues(data)
839+
if new:
840+
if not all([giturl, branch, commit]):
841+
kci_msg("Fetching new issues for all checkouts...")
842+
trees_list = ctx.invoke(trees, origin=origin, days=days, verbose=False)
843+
for tree in trees_list:
844+
giturl = tree["git_repository_url"]
845+
branch = tree["git_repository_branch"]
846+
commit = tree["git_commit_hash"]
847+
tree_name = tree["tree_name"]
848+
get_new_issues(
849+
origin, tree_name, giturl, branch, commit, arch, use_json
850+
)
851+
return
852+
kci_msg("Fetching new issues for the checkout...")
853+
giturl, branch, commit = set_giturl_branch_commit(
854+
origin, giturl, branch, commit, latest, git_folder
855+
)
856+
tree_name = get_tree_name(origin, giturl, branch)
857+
get_new_issues(origin, tree_name, giturl, branch, commit, arch, use_json)
858+
if missing:
859+
if builds:
860+
item_types = ["builds"]
861+
elif boots:
862+
item_types = ["boots"]
863+
else:
864+
item_types = ["builds", "boots"]
865+
866+
if all([giturl, branch, commit]):
867+
tree_name = get_tree_name(origin, giturl, branch)
868+
for item_type in item_types:
869+
final_stats = []
870+
kci_msg_green(f"Fetching data for {item_type}...")
871+
stats = get_missing_issue_items(
872+
ctx, origin, item_type, giturl, branch, commit, tree_name, arch
873+
)
874+
if stats:
875+
final_stats.append(stats)
876+
if final_stats:
877+
print_missing_data(item_type, final_stats)
878+
879+
else:
880+
trees_list = ctx.invoke(trees, origin=origin, days=days, verbose=False)
881+
for item_type in item_types:
882+
kci_msg_green(f"Fetching data for {item_type}...")
883+
final_stats = []
884+
for tree in trees_list:
885+
giturl = tree["git_repository_url"]
886+
branch = tree["git_repository_branch"]
887+
commit = tree["git_commit_hash"]
888+
tree_name = tree["tree_name"]
889+
stats = get_missing_issue_items(
890+
ctx, origin, item_type, giturl, branch, commit, tree_name, arch
891+
)
892+
if stats:
893+
final_stats.append(stats)
894+
if final_stats:
895+
print_missing_data(item_type, final_stats)
896+
897+
685898
@results.command(
686899
name="issue",
687900
help="""Fetch KCIDB issue matching provided ID.

kcidev/subcommands/results/parser.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,3 +986,60 @@ def print_issue(issue):
986986
kci_msg_nonl(" First incident seen on ")
987987
kci_msg_bold(tree_branch)
988988
kci_msg("")
989+
990+
991+
def print_issues(data):
992+
"""Print a list of issues with formatting
993+
data (dict): provide dictionary with 'issues' and 'extras' keys"""
994+
parsed = urlparse(DASHBOARD_API)
995+
dashboard_url = f"{parsed.scheme}://{parsed.netloc}"
996+
997+
if not data.get("issues") or not data.get("extras"):
998+
kci_err("Please provide dictionary with 'issues' and 'extras' keys")
999+
return
1000+
1001+
issues = data["issues"]
1002+
extras = data["extras"]
1003+
for issue in issues:
1004+
print_issue_information(issue, dashboard_url)
1005+
1006+
extra = extras.get(issue.get("id"))
1007+
if extra:
1008+
tree_branch = (
1009+
f'{extra.get("tree_name")}/{extra.get("git_repository_branch")}'
1010+
)
1011+
kci_msg_nonl(" First incident seen on ")
1012+
kci_msg_bold(tree_branch)
1013+
kci_msg("")
1014+
1015+
1016+
def print_missing_data(item_type, data):
1017+
"""Print builds/tests for which KCIDB issues are missing"""
1018+
from collections import defaultdict
1019+
1020+
tree_groups = defaultdict(list)
1021+
1022+
for row in data:
1023+
try:
1024+
tree_branch = row[0]
1025+
commit = row[1]
1026+
missing_ids = row[2]
1027+
except IndexError:
1028+
kci_msg_red("Failed to extract data for list view")
1029+
continue
1030+
tree_groups[tree_branch].append({"commit": commit, "missing_ids": missing_ids})
1031+
1032+
parsed = urlparse(DASHBOARD_API)
1033+
dashboard_url = f"{parsed.scheme}://{parsed.netloc}"
1034+
if item_type == "builds":
1035+
endpoint = "build"
1036+
else:
1037+
endpoint = "test"
1038+
for tree_branch, commits in tree_groups.items():
1039+
kci_msg_bold(f"{tree_branch}: ")
1040+
for commit in commits:
1041+
kci_msg(f" - Commit:{commit['commit']}")
1042+
for item in missing_ids:
1043+
for item_id, status in item.items():
1044+
kci_msg_nonl(f" {dashboard_url}/{endpoint}/{item_id} status: ")
1045+
kci_msg_red(f"{status}")

0 commit comments

Comments
 (0)