From bc63aac79a4640f951b050c537ba040bb231449b Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Thu, 16 Feb 2023 18:37:12 +0100 Subject: dashboard: display the subsystem list Take the counts from the cache, include links to the filtered bug views. --- dashboard/app/handler.go | 2 ++ dashboard/app/main.go | 64 +++++++++++++++++++++++++++++++++++++++++++ dashboard/app/main_test.go | 27 ++++++++++++++++++ dashboard/app/subsystems.html | 41 +++++++++++++++++++++++++++ dashboard/app/templates.html | 4 +++ pkg/html/pages/style.css | 4 +-- pkg/subsystem/service.go | 8 ++++++ 7 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 dashboard/app/subsystems.html diff --git a/dashboard/app/handler.go b/dashboard/app/handler.go index cb998a516..333d7847d 100644 --- a/dashboard/app/handler.go +++ b/dashboard/app/handler.go @@ -129,6 +129,7 @@ type uiHeader struct { Namespace string BugCounts *CachedBugStats Namespaces []uiNamespace + ShowSubsystems bool } type uiNamespace struct { @@ -203,6 +204,7 @@ func commonHeader(c context.Context, r *http.Request, w http.ResponseWriter, ns } if ns != adminPage { h.Namespace = ns + h.ShowSubsystems = getSubsystemService(c, ns) != nil cookie.Namespace = ns encodeCookie(w, cookie) cached, err := CacheGet(c, r, ns) diff --git a/dashboard/app/main.go b/dashboard/app/main.go index 661d7e992..fa31a7ab2 100644 --- a/dashboard/app/main.go +++ b/dashboard/app/main.go @@ -60,6 +60,7 @@ func initHTTPHandlers() { http.Handle("/"+ns+"/graph/crashes", handlerWrapper(handleGraphCrashes)) http.Handle("/"+ns+"/repos", handlerWrapper(handleRepos)) http.Handle("/"+ns+"/bug-stats", handlerWrapper(handleBugStats)) + http.Handle("/"+ns+"/subsystems", handlerWrapper(handleSubsystemsList)) } http.HandleFunc("/cron/cache_update", cacheUpdate) http.HandleFunc("/cron/deprecate_assets", handleDeprecateAssets) @@ -144,6 +145,25 @@ type uiRepo struct { Alias string } +type uiSubsystemsPage struct { + Header *uiHeader + List []*uiSubsystem +} + +type uiSubsystem struct { + Name string + Lists string + Maintainers string + Open uiSubsystemStats + Fixed uiSubsystemStats + Invalid uiSubsystemStats +} + +type uiSubsystemStats struct { + Count int + Link string +} + type uiAdminPage struct { Header *uiHeader Log []byte @@ -770,6 +790,50 @@ func getUIJob(c context.Context, bug *Bug, jobType JobType) (*uiJob, error) { return makeUIJob(job, jobKey, bug, crash, build), nil } +func handleSubsystemsList(c context.Context, w http.ResponseWriter, r *http.Request) error { + hdr, err := commonHeader(c, r, w, "") + if err != nil { + return err + } + cached, err := CacheGet(c, r, hdr.Namespace) + if err != nil { + return err + } + service := getSubsystemService(c, hdr.Namespace) + if service == nil { + return fmt.Errorf("the namespace does not have subsystems") + } + list := []*uiSubsystem{} + for _, item := range service.List() { + stats := cached.Subsystems[item.Name] + list = append(list, &uiSubsystem{ + Name: item.Name, + Lists: strings.Join(item.Lists, ", "), + Maintainers: strings.Join(item.Maintainers, ", "), + Open: uiSubsystemStats{ + Count: stats.Open, + Link: html.AmendURL("/"+hdr.Namespace, + "subsystem", item.Name), + }, + Fixed: uiSubsystemStats{ + Count: stats.Fixed, + Link: html.AmendURL("/"+hdr.Namespace+"/fixed", + "subsystem", item.Name), + }, + Invalid: uiSubsystemStats{ + Count: stats.Invalid, + Link: html.AmendURL("/"+hdr.Namespace+"/invalid", + "subsystem", item.Name), + }, + }) + } + sort.Slice(list, func(i, j int) bool { return list[i].Name < list[j].Name }) + return serveTemplate(w, "subsystems.html", &uiSubsystemsPage{ + Header: hdr, + List: list, + }) +} + // handleText serves plain text blobs (crash logs, reports, reproducers, etc). func handleTextImpl(c context.Context, w http.ResponseWriter, r *http.Request, tag string) error { var id int64 diff --git a/dashboard/app/main_test.go b/dashboard/app/main_test.go index 88cb0dcf5..e69dd0275 100644 --- a/dashboard/app/main_test.go +++ b/dashboard/app/main_test.go @@ -183,3 +183,30 @@ func TestMainBugFilters(t *testing.T) { assert.NotContains(t, string(reply), build1.Manager) // managers are hidden assert.Contains(t, string(reply), "Applied filters") // we're seeing a prompt to disable the filter } + +func TestSubsystemsList(t *testing.T) { + c := NewCtx(t) + defer c.Close() + + client := c.client + build := testBuild(1) + client.UploadBuild(build) + + crash1 := testCrash(build, 1) + crash1.GuiltyFiles = []string{"a.c"} + client.ReportCrash(crash1) + client.pollBug() + + crash2 := testCrash(build, 2) + crash2.GuiltyFiles = []string{"b.c"} + client.ReportCrash(crash2) + client.updateBug(client.pollBug().ID, dashapi.BugStatusInvalid, "") + + _, err := c.AuthGET(AccessUser, "/cron/refresh_subsystems") + c.expectOK(err) + + reply, err := c.AuthGET(AccessAdmin, "/test1/subsystems") + c.expectOK(err) + assert.Contains(t, string(reply), "subsystemA") + assert.Contains(t, string(reply), "subsystemB") +} diff --git a/dashboard/app/subsystems.html b/dashboard/app/subsystems.html new file mode 100644 index 000000000..254a113fe --- /dev/null +++ b/dashboard/app/subsystems.html @@ -0,0 +1,41 @@ +{{/* +Copyright 2023 syzkaller project authors. All rights reserved. +Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +The list of polled trees. +*/}} + + + + + {{template "head" .Header}} + syzbot + + + {{template "header" .Header}} +

The list of subsystems


+ (*) Note that the numbers below do not represent the latest data. They are updated once an hour.
+ (**) If you have any ideas/suggestions on how to improve this list, feel free to contact us at syzkaller@googlegroups.com. + + + + + + + + + + + {{range $item := .List}} + + + + + + + + + {{end}} +
Subsystems list
NameListsMaintainersOpen bugsFixedInvalid
{{$item.Name}}{{$item.Lists}}{{$item.Maintainers}}{{link $item.Open.Link (printf "%d" $item.Open.Count)}}{{link $item.Fixed.Link (printf "%d" $item.Fixed.Count)}}{{link $item.Invalid.Link (printf "%d" $item.Invalid.Count)}}
+ + diff --git a/dashboard/app/templates.html b/dashboard/app/templates.html index 2b7ef61c1..f40361f85 100644 --- a/dashboard/app/templates.html +++ b/dashboard/app/templates.html @@ -63,6 +63,10 @@ Use of this source code is governed by Apache 2 LICENSE that can be found in the 🐞 Fixed [{{$.BugCounts.Fixed}}] 🐞 Invalid [{{$.BugCounts.Invalid}}] + {{if .ShowSubsystems}} + + Subsystems + {{end}} 📈 Kernel Health diff --git a/pkg/html/pages/style.css b/pkg/html/pages/style.css index aecde7554..ebeb7d012 100644 --- a/pkg/html/pages/style.css +++ b/pkg/html/pages/style.css @@ -140,8 +140,8 @@ table td, table th { } .list_table .maintainers { - width: 150pt; - max-width: 150pt; + max-width: 300pt; + white-space: normal; } .list_table .result { diff --git a/pkg/subsystem/service.go b/pkg/subsystem/service.go index b27200c30..d8c144cc4 100644 --- a/pkg/subsystem/service.go +++ b/pkg/subsystem/service.go @@ -45,3 +45,11 @@ func MakeService(list []*Subsystem) (*Service, error) { func (s *Service) ByName(name string) *Subsystem { return s.perName[name] } + +func (s *Service) List() []*Subsystem { + ret := []*Subsystem{} + for _, item := range s.perName { + ret = append(ret, item) + } + return ret +} -- cgit mrf-deployment