mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-12-26 13:29:05 +01:00
Merge branch 'master' into hotfix: Bring branch up to date
This commit is contained in:
commit
45a3346783
@ -275,7 +275,8 @@ func main() {
|
|||||||
rw.WriteHeader(http.StatusUnauthorized)
|
rw.WriteHeader(http.StatusUnauthorized)
|
||||||
web.RenderTemplate(rw, r, "login.tmpl", &web.Page{
|
web.RenderTemplate(rw, r, "login.tmpl", &web.Page{
|
||||||
Title: "Login failed - ClusterCockpit",
|
Title: "Login failed - ClusterCockpit",
|
||||||
Error: err.Error(),
|
MsgType: "alert-warning",
|
||||||
|
Message: err.Error(),
|
||||||
Build: buildInfo,
|
Build: buildInfo,
|
||||||
})
|
})
|
||||||
})).Methods(http.MethodPost)
|
})).Methods(http.MethodPost)
|
||||||
@ -285,7 +286,8 @@ func main() {
|
|||||||
rw.WriteHeader(http.StatusOK)
|
rw.WriteHeader(http.StatusOK)
|
||||||
web.RenderTemplate(rw, r, "login.tmpl", &web.Page{
|
web.RenderTemplate(rw, r, "login.tmpl", &web.Page{
|
||||||
Title: "Bye - ClusterCockpit",
|
Title: "Bye - ClusterCockpit",
|
||||||
Info: "Logout sucessful",
|
MsgType: "alert-info",
|
||||||
|
Message: "Logout successful",
|
||||||
Build: buildInfo,
|
Build: buildInfo,
|
||||||
})
|
})
|
||||||
}))).Methods(http.MethodPost)
|
}))).Methods(http.MethodPost)
|
||||||
@ -300,7 +302,8 @@ func main() {
|
|||||||
rw.WriteHeader(http.StatusUnauthorized)
|
rw.WriteHeader(http.StatusUnauthorized)
|
||||||
web.RenderTemplate(rw, r, "login.tmpl", &web.Page{
|
web.RenderTemplate(rw, r, "login.tmpl", &web.Page{
|
||||||
Title: "Authentication failed - ClusterCockpit",
|
Title: "Authentication failed - ClusterCockpit",
|
||||||
Error: err.Error(),
|
MsgType: "alert-danger",
|
||||||
|
Message: err.Error(),
|
||||||
Build: buildInfo,
|
Build: buildInfo,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -316,11 +319,11 @@ func main() {
|
|||||||
|
|
||||||
// Send a searchId and then reply with a redirect to a user, or directly send query to job table for jobid and project.
|
// Send a searchId and then reply with a redirect to a user, or directly send query to job table for jobid and project.
|
||||||
secured.HandleFunc("/search", func(rw http.ResponseWriter, r *http.Request) {
|
secured.HandleFunc("/search", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
routerConfig.HandleSearchBar(rw, r, api)
|
routerConfig.HandleSearchBar(rw, r, buildInfo)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Mount all /monitoring/... and /api/... routes.
|
// Mount all /monitoring/... and /api/... routes.
|
||||||
routerConfig.SetupRoutes(secured, version, commit, date)
|
routerConfig.SetupRoutes(secured, buildInfo)
|
||||||
api.MountRoutes(secured)
|
api.MountRoutes(secured)
|
||||||
|
|
||||||
if config.Keys.EmbedStaticFiles {
|
if config.Keys.EmbedStaticFiles {
|
||||||
@ -345,7 +348,7 @@ func main() {
|
|||||||
handlers.AllowedOrigins([]string{"*"})))
|
handlers.AllowedOrigins([]string{"*"})))
|
||||||
handler := handlers.CustomLoggingHandler(io.Discard, r, func(_ io.Writer, params handlers.LogFormatterParams) {
|
handler := handlers.CustomLoggingHandler(io.Discard, r, func(_ io.Writer, params handlers.LogFormatterParams) {
|
||||||
if strings.HasPrefix(params.Request.RequestURI, "/api/") {
|
if strings.HasPrefix(params.Request.RequestURI, "/api/") {
|
||||||
log.Infof("%s %s (%d, %.02fkb, %dms)",
|
log.Debugf("%s %s (%d, %.02fkb, %dms)",
|
||||||
params.Request.Method, params.URL.RequestURI(),
|
params.Request.Method, params.URL.RequestURI(),
|
||||||
params.StatusCode, float32(params.Size)/1024,
|
params.StatusCode, float32(params.Size)/1024,
|
||||||
time.Since(params.TimeStamp).Milliseconds())
|
time.Since(params.TimeStamp).Milliseconds())
|
||||||
|
@ -20,11 +20,11 @@
|
|||||||
* JobName: Job-Table (Allows multiple identical matches, e.g. JobNames from different clusters)
|
* JobName: Job-Table (Allows multiple identical matches, e.g. JobNames from different clusters)
|
||||||
* ProjectId: Job-Table
|
* ProjectId: Job-Table
|
||||||
* Username: Users-Table
|
* Username: Users-Table
|
||||||
* **Please Note**: Only users with jobs will be shown in table! I.e., Users without jobs will be missing in table.
|
* **Please Note**: Only users with jobs will be shown in table! I.e., Users without jobs will be missing in table. Also, a `Last 30 Days` is active by default and might filter out expected users.
|
||||||
* Name: Users-Table
|
* Name: Users-Table
|
||||||
* **Please Note**: Only users with jobs will be shown in table! I.e., Users without jobs will be missing in table.
|
* **Please Note**: Only users with jobs will be shown in table! I.e., Users without jobs will be missing in table. Also, a `Last 30 Days` is active by default and might filter out expected users.
|
||||||
* Best guess search always redirects to Job-Table or `/monitoring/user/$USER` (first username match)
|
* Best guess search always redirects to Job-Table or `/monitoring/user/$USER` (first username match)
|
||||||
* Unprocessable queries will redirect to `/monitoring/jobs/?`
|
* Unprocessable queries will display messages detailing the cause (Info, Warning, Error)
|
||||||
* Spaces trimmed (both for searchTag and queryString)
|
* Spaces trimmed (both for searchTag and queryString)
|
||||||
* ` job12` == `job12`
|
* ` job12` == `job12`
|
||||||
* `projectID : abcd ` == `projectId:abcd`
|
* `projectID : abcd ` == `projectId:abcd`
|
||||||
|
@ -523,7 +523,7 @@ func (api *RestApi) startJob(rw http.ResponseWriter, r *http.Request) {
|
|||||||
} else if err == nil {
|
} else if err == nil {
|
||||||
for _, job := range jobs {
|
for _, job := range jobs {
|
||||||
if (req.StartTime - job.StartTimeUnix) < 86400 {
|
if (req.StartTime - job.StartTimeUnix) < 86400 {
|
||||||
handleError(fmt.Errorf("a job with that jobId, cluster and startTime already exists: dbid: %d", job.ID), http.StatusUnprocessableEntity, rw)
|
handleError(fmt.Errorf("a job with that jobId, cluster and startTime already exists: dbid: %d, jobid: %d", job.ID, job.JobID), http.StatusUnprocessableEntity, rw)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -364,7 +364,7 @@ func (auth *Authentication) Login(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Warn("login failed: no authenticator applied")
|
log.Debugf("login failed: no authenticator applied")
|
||||||
onfailure(rw, r, err)
|
onfailure(rw, r, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -380,7 +380,7 @@ func (auth *Authentication) Auth(
|
|||||||
for _, authenticator := range auth.authenticators {
|
for _, authenticator := range auth.authenticators {
|
||||||
user, err := authenticator.Auth(rw, r)
|
user, err := authenticator.Auth(rw, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("authentication failed: %s", err.Error())
|
log.Infof("authentication failed: %s", err.Error())
|
||||||
http.Error(rw, err.Error(), http.StatusUnauthorized)
|
http.Error(rw, err.Error(), http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -393,7 +393,7 @@ func (auth *Authentication) Auth(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Warnf("authentication failed: %s", "no authenticator applied")
|
log.Debugf("authentication failed: %s", "no authenticator applied")
|
||||||
// http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
// http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||||
onfailure(rw, r, errors.New("unauthorized (login first or use a token)"))
|
onfailure(rw, r, errors.New("unauthorized (login first or use a token)"))
|
||||||
})
|
})
|
||||||
|
@ -30,13 +30,13 @@ var Keys schema.ProgramConfig = schema.ProgramConfig{
|
|||||||
"analysis_view_histogramMetrics": []string{"flops_any", "mem_bw", "mem_used"},
|
"analysis_view_histogramMetrics": []string{"flops_any", "mem_bw", "mem_used"},
|
||||||
"analysis_view_scatterPlotMetrics": [][]string{{"flops_any", "mem_bw"}, {"flops_any", "cpu_load"}, {"cpu_load", "mem_bw"}},
|
"analysis_view_scatterPlotMetrics": [][]string{{"flops_any", "mem_bw"}, {"flops_any", "cpu_load"}, {"cpu_load", "mem_bw"}},
|
||||||
"job_view_nodestats_selectedMetrics": []string{"flops_any", "mem_bw", "mem_used"},
|
"job_view_nodestats_selectedMetrics": []string{"flops_any", "mem_bw", "mem_used"},
|
||||||
"job_view_polarPlotMetrics": []string{"flops_any", "mem_bw", "mem_used", "net_bw", "file_bw"},
|
"job_view_polarPlotMetrics": []string{"flops_any", "mem_bw", "mem_used"},
|
||||||
"job_view_selectedMetrics": []string{"flops_any", "mem_bw", "mem_used"},
|
"job_view_selectedMetrics": []string{"flops_any", "mem_bw", "mem_used"},
|
||||||
"plot_general_colorBackground": true,
|
"plot_general_colorBackground": true,
|
||||||
"plot_general_colorscheme": []string{"#00bfff", "#0000ff", "#ff00ff", "#ff0000", "#ff8000", "#ffff00", "#80ff00"},
|
"plot_general_colorscheme": []string{"#00bfff", "#0000ff", "#ff00ff", "#ff0000", "#ff8000", "#ffff00", "#80ff00"},
|
||||||
"plot_general_lineWidth": 3,
|
"plot_general_lineWidth": 3,
|
||||||
"plot_list_jobsPerPage": 50,
|
"plot_list_jobsPerPage": 50,
|
||||||
"plot_list_selectedMetrics": []string{"cpu_load", "ipc", "mem_used", "flops_any", "mem_bw"},
|
"plot_list_selectedMetrics": []string{"cpu_load", "mem_used", "flops_any", "mem_bw"},
|
||||||
"plot_view_plotsPerRow": 3,
|
"plot_view_plotsPerRow": 3,
|
||||||
"plot_view_showPolarplot": true,
|
"plot_view_showPolarplot": true,
|
||||||
"plot_view_showRoofline": true,
|
"plot_view_showRoofline": true,
|
||||||
|
@ -87,10 +87,13 @@ func HandleImportFlag(flag string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Other metrics...
|
// TODO: Other metrics...
|
||||||
|
job.LoadAvg = loadJobStat(&jobMeta, "cpu_load")
|
||||||
job.FlopsAnyAvg = loadJobStat(&jobMeta, "flops_any")
|
job.FlopsAnyAvg = loadJobStat(&jobMeta, "flops_any")
|
||||||
|
job.MemUsedMax = loadJobStat(&jobMeta, "mem_used")
|
||||||
job.MemBwAvg = loadJobStat(&jobMeta, "mem_bw")
|
job.MemBwAvg = loadJobStat(&jobMeta, "mem_bw")
|
||||||
job.NetBwAvg = loadJobStat(&jobMeta, "net_bw")
|
job.NetBwAvg = loadJobStat(&jobMeta, "net_bw")
|
||||||
job.FileBwAvg = loadJobStat(&jobMeta, "file_bw")
|
job.FileBwAvg = loadJobStat(&jobMeta, "file_bw")
|
||||||
|
|
||||||
job.RawResources, err = json.Marshal(job.Resources)
|
job.RawResources, err = json.Marshal(job.Resources)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Error while marshaling job resources")
|
log.Warn("Error while marshaling job resources")
|
||||||
|
@ -61,7 +61,9 @@ func InitDB() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Other metrics...
|
// TODO: Other metrics...
|
||||||
|
job.LoadAvg = loadJobStat(jobMeta, "cpu_load")
|
||||||
job.FlopsAnyAvg = loadJobStat(jobMeta, "flops_any")
|
job.FlopsAnyAvg = loadJobStat(jobMeta, "flops_any")
|
||||||
|
job.MemUsedMax = loadJobStat(jobMeta, "mem_used")
|
||||||
job.MemBwAvg = loadJobStat(jobMeta, "mem_bw")
|
job.MemBwAvg = loadJobStat(jobMeta, "mem_bw")
|
||||||
job.NetBwAvg = loadJobStat(jobMeta, "net_bw")
|
job.NetBwAvg = loadJobStat(jobMeta, "net_bw")
|
||||||
job.FileBwAvg = loadJobStat(jobMeta, "file_bw")
|
job.FileBwAvg = loadJobStat(jobMeta, "file_bw")
|
||||||
@ -150,8 +152,12 @@ func SanityChecks(job *schema.BaseJob) error {
|
|||||||
|
|
||||||
func loadJobStat(job *schema.JobMeta, metric string) float64 {
|
func loadJobStat(job *schema.JobMeta, metric string) float64 {
|
||||||
if stats, ok := job.Statistics[metric]; ok {
|
if stats, ok := job.Statistics[metric]; ok {
|
||||||
|
if metric == "mem_used" {
|
||||||
|
return stats.Max
|
||||||
|
} else {
|
||||||
return stats.Avg
|
return stats.Avg
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
|
@ -16,13 +16,13 @@ type Hooks struct{}
|
|||||||
|
|
||||||
// Before hook will print the query with it's args and return the context with the timestamp
|
// Before hook will print the query with it's args and return the context with the timestamp
|
||||||
func (h *Hooks) Before(ctx context.Context, query string, args ...interface{}) (context.Context, error) {
|
func (h *Hooks) Before(ctx context.Context, query string, args ...interface{}) (context.Context, error) {
|
||||||
log.Infof("SQL query %s %q", query, args)
|
log.Debugf("SQL query %s %q", query, args)
|
||||||
return context.WithValue(ctx, "begin", time.Now()), nil
|
return context.WithValue(ctx, "begin", time.Now()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// After hook will get the timestamp registered on the Before hook and print the elapsed time
|
// After hook will get the timestamp registered on the Before hook and print the elapsed time
|
||||||
func (h *Hooks) After(ctx context.Context, query string, args ...interface{}) (context.Context, error) {
|
func (h *Hooks) After(ctx context.Context, query string, args ...interface{}) (context.Context, error) {
|
||||||
begin := ctx.Value("begin").(time.Time)
|
begin := ctx.Value("begin").(time.Time)
|
||||||
log.Infof("Took: %s\n", time.Since(begin))
|
log.Debugf("Took: %s\n", time.Since(begin))
|
||||||
return ctx, nil
|
return ctx, nil
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,6 @@ func GetJobRepository() *JobRepository {
|
|||||||
// start archiving worker
|
// start archiving worker
|
||||||
go jobRepoInstance.archivingWorker()
|
go jobRepoInstance.archivingWorker()
|
||||||
})
|
})
|
||||||
|
|
||||||
return jobRepoInstance
|
return jobRepoInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +177,7 @@ func (r *JobRepository) FetchMetadata(job *schema.Job) (map[string]string, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
r.cache.Put(cachekey, job.MetaData, len(job.RawMetaData), 24*time.Hour)
|
r.cache.Put(cachekey, job.MetaData, len(job.RawMetaData), 24*time.Hour)
|
||||||
log.Infof("Timer FetchMetadata %s", time.Since(start))
|
log.Debugf("Timer FetchMetadata %s", time.Since(start))
|
||||||
return job.MetaData, nil
|
return job.MetaData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,7 +237,7 @@ func (r *JobRepository) Find(
|
|||||||
q = q.Where("job.start_time = ?", *startTime)
|
q = q.Where("job.start_time = ?", *startTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Timer Find %s", time.Since(start))
|
log.Debugf("Timer Find %s", time.Since(start))
|
||||||
return scanJob(q.RunWith(r.stmtCache).QueryRow())
|
return scanJob(q.RunWith(r.stmtCache).QueryRow())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,7 +277,7 @@ func (r *JobRepository) FindAll(
|
|||||||
}
|
}
|
||||||
jobs = append(jobs, job)
|
jobs = append(jobs, job)
|
||||||
}
|
}
|
||||||
log.Infof("Timer FindAll %s", time.Since(start))
|
log.Debugf("Timer FindAll %s", time.Since(start))
|
||||||
return jobs, nil
|
return jobs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,7 +343,7 @@ func (r *JobRepository) DeleteJobsBefore(startTime int64) (int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(" DeleteJobsBefore(%d): error %#v", startTime, err)
|
log.Errorf(" DeleteJobsBefore(%d): error %#v", startTime, err)
|
||||||
} else {
|
} else {
|
||||||
log.Infof("DeleteJobsBefore(%d): Deleted %d jobs", startTime, cnt)
|
log.Debugf("DeleteJobsBefore(%d): Deleted %d jobs", startTime, cnt)
|
||||||
}
|
}
|
||||||
return cnt, err
|
return cnt, err
|
||||||
}
|
}
|
||||||
@ -354,7 +353,7 @@ func (r *JobRepository) DeleteJobById(id int64) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("DeleteJobById(%d): error %#v", id, err)
|
log.Errorf("DeleteJobById(%d): error %#v", id, err)
|
||||||
} else {
|
} else {
|
||||||
log.Infof("DeleteJobById(%d): Success", id)
|
log.Debugf("DeleteJobById(%d): Success", id)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -383,7 +382,7 @@ func (r *JobRepository) CountGroupedJobs(
|
|||||||
count = fmt.Sprintf(`sum(job.num_nodes * (CASE WHEN job.job_state = "running" THEN %d - job.start_time ELSE job.duration END)) as count`, now)
|
count = fmt.Sprintf(`sum(job.num_nodes * (CASE WHEN job.job_state = "running" THEN %d - job.start_time ELSE job.duration END)) as count`, now)
|
||||||
runner = r.DB
|
runner = r.DB
|
||||||
default:
|
default:
|
||||||
log.Infof("CountGroupedJobs() Weight %v unknown.", *weight)
|
log.Debugf("CountGroupedJobs() Weight %v unknown.", *weight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,7 +417,7 @@ func (r *JobRepository) CountGroupedJobs(
|
|||||||
counts[group] = count
|
counts[group] = count
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Timer CountGroupedJobs %s", time.Since(start))
|
log.Debugf("Timer CountGroupedJobs %s", time.Since(start))
|
||||||
return counts, nil
|
return counts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,13 +449,14 @@ func (r *JobRepository) MarkArchived(
|
|||||||
case "mem_bw":
|
case "mem_bw":
|
||||||
stmt = stmt.Set("mem_bw_avg", stats.Avg)
|
stmt = stmt.Set("mem_bw_avg", stats.Avg)
|
||||||
case "load":
|
case "load":
|
||||||
|
case "cpu_load":
|
||||||
stmt = stmt.Set("load_avg", stats.Avg)
|
stmt = stmt.Set("load_avg", stats.Avg)
|
||||||
case "net_bw":
|
case "net_bw":
|
||||||
stmt = stmt.Set("net_bw_avg", stats.Avg)
|
stmt = stmt.Set("net_bw_avg", stats.Avg)
|
||||||
case "file_bw":
|
case "file_bw":
|
||||||
stmt = stmt.Set("file_bw_avg", stats.Avg)
|
stmt = stmt.Set("file_bw_avg", stats.Avg)
|
||||||
default:
|
default:
|
||||||
log.Infof("MarkArchived() Metric '%v' unknown", metric)
|
log.Debugf("MarkArchived() Metric '%v' unknown", metric)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,6 +475,7 @@ func (r *JobRepository) archivingWorker() {
|
|||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
start := time.Now()
|
||||||
// not using meta data, called to load JobMeta into Cache?
|
// not using meta data, called to load JobMeta into Cache?
|
||||||
// will fail if job meta not in repository
|
// will fail if job meta not in repository
|
||||||
if _, err := r.FetchMetadata(job); err != nil {
|
if _, err := r.FetchMetadata(job); err != nil {
|
||||||
@ -497,7 +498,7 @@ func (r *JobRepository) archivingWorker() {
|
|||||||
log.Errorf("archiving job (dbid: %d) failed: %s", job.ID, err.Error())
|
log.Errorf("archiving job (dbid: %d) failed: %s", job.ID, err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
log.Debugf("archiving job %d took %s", job.JobID, time.Since(start))
|
||||||
log.Printf("archiving job (dbid: %d) successful", job.ID)
|
log.Printf("archiving job (dbid: %d) successful", job.ID)
|
||||||
r.archivePending.Done()
|
r.archivePending.Done()
|
||||||
}
|
}
|
||||||
@ -523,11 +524,10 @@ var ErrForbidden = errors.New("not authorized")
|
|||||||
// If query is found to be an integer (= conversion to INT datatype succeeds), skip back to parent call
|
// If query is found to be an integer (= conversion to INT datatype succeeds), skip back to parent call
|
||||||
// If nothing matches the search, `ErrNotFound` is returned.
|
// If nothing matches the search, `ErrNotFound` is returned.
|
||||||
|
|
||||||
func (r *JobRepository) FindUserOrProjectOrJobname(ctx context.Context, searchterm string) (username string, project string, metasnip string, err error) {
|
func (r *JobRepository) FindUserOrProjectOrJobname(user *auth.User, searchterm string) (username string, project string, metasnip string, err error) {
|
||||||
if _, err := strconv.Atoi(searchterm); err == nil { // Return empty on successful conversion: parent method will redirect for integer jobId
|
if _, err := strconv.Atoi(searchterm); err == nil { // Return empty on successful conversion: parent method will redirect for integer jobId
|
||||||
return "", "", "", nil
|
return "", "", "", nil
|
||||||
} else { // Has to have letters and logged-in user for other guesses
|
} else { // Has to have letters and logged-in user for other guesses
|
||||||
user := auth.GetUser(ctx)
|
|
||||||
if user != nil {
|
if user != nil {
|
||||||
// Find username in jobs (match)
|
// Find username in jobs (match)
|
||||||
uresult, _ := r.FindColumnValue(user, searchterm, "job", "user", "user", false)
|
uresult, _ := r.FindColumnValue(user, searchterm, "job", "user", "user", false)
|
||||||
@ -634,7 +634,7 @@ func (r *JobRepository) Partitions(cluster string) ([]string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Infof("Timer Partitions %s", time.Since(start))
|
log.Debugf("Timer Partitions %s", time.Since(start))
|
||||||
return partitions.([]string), nil
|
return partitions.([]string), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -679,7 +679,7 @@ func (r *JobRepository) AllocatedNodes(cluster string) (map[string]map[string]in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Timer AllocatedNodes %s", time.Since(start))
|
log.Debugf("Timer AllocatedNodes %s", time.Since(start))
|
||||||
return subclusters, nil
|
return subclusters, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -708,7 +708,7 @@ func (r *JobRepository) StopJobsExceedingWalltimeBy(seconds int) error {
|
|||||||
if rowsAffected > 0 {
|
if rowsAffected > 0 {
|
||||||
log.Infof("%d jobs have been marked as failed due to running too long", rowsAffected)
|
log.Infof("%d jobs have been marked as failed due to running too long", rowsAffected)
|
||||||
}
|
}
|
||||||
log.Infof("Timer StopJobsExceedingWalltimeBy %s", time.Since(start))
|
log.Debugf("Timer StopJobsExceedingWalltimeBy %s", time.Since(start))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ func SecurityCheck(ctx context.Context, query sq.SelectBuilder) (sq.SelectBuilde
|
|||||||
if len(user.Projects) != 0 {
|
if len(user.Projects) != 0 {
|
||||||
return query.Where(sq.Or{sq.Eq{"job.project": user.Projects}, sq.Eq{"job.user": user.Username}}), nil
|
return query.Where(sq.Or{sq.Eq{"job.project": user.Projects}, sq.Eq{"job.user": user.Username}}), nil
|
||||||
} else {
|
} else {
|
||||||
log.Infof("Manager-User '%s' has no defined projects to lookup! Query only personal jobs ...", user.Username)
|
log.Debugf("Manager-User '%s' has no defined projects to lookup! Query only personal jobs ...", user.Username)
|
||||||
return query.Where("job.user = ?", user.Username), nil
|
return query.Where("job.user = ?", user.Username), nil
|
||||||
}
|
}
|
||||||
} else if user.HasRole(auth.RoleUser) { // User : Only personal jobs
|
} else if user.HasRole(auth.RoleUser) { // User : Only personal jobs
|
||||||
|
@ -171,7 +171,7 @@ func (r *JobRepository) JobsStatsGrouped(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Timer JobsStatsGrouped %s", time.Since(start))
|
log.Debugf("Timer JobsStatsGrouped %s", time.Since(start))
|
||||||
return stats, nil
|
return stats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +212,7 @@ func (r *JobRepository) JobsStats(
|
|||||||
TotalAccHours: totalAccHours})
|
TotalAccHours: totalAccHours})
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Timer JobStats %s", time.Since(start))
|
log.Debugf("Timer JobStats %s", time.Since(start))
|
||||||
return stats, nil
|
return stats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +251,7 @@ func (r *JobRepository) JobCountGrouped(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Timer JobCountGrouped %s", time.Since(start))
|
log.Debugf("Timer JobCountGrouped %s", time.Since(start))
|
||||||
return stats, nil
|
return stats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +300,7 @@ func (r *JobRepository) AddJobCountGrouped(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Timer AddJobCountGrouped %s", time.Since(start))
|
log.Debugf("Timer AddJobCountGrouped %s", time.Since(start))
|
||||||
return stats, nil
|
return stats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,7 +343,7 @@ func (r *JobRepository) AddJobCount(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Timer JobJobCount %s", time.Since(start))
|
log.Debugf("Timer JobJobCount %s", time.Since(start))
|
||||||
return stats, nil
|
return stats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,7 +368,7 @@ func (r *JobRepository) AddHistograms(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Timer AddHistograms %s", time.Since(start))
|
log.Debugf("Timer AddHistograms %s", time.Since(start))
|
||||||
return stat, nil
|
return stat, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,6 +406,6 @@ func (r *JobRepository) jobsStatisticsHistogram(
|
|||||||
|
|
||||||
points = append(points, &point)
|
points = append(points, &point)
|
||||||
}
|
}
|
||||||
log.Infof("Timer jobsStatisticsHistogram %s", time.Since(start))
|
log.Debugf("Timer jobsStatisticsHistogram %s", time.Since(start))
|
||||||
return points, nil
|
return points, nil
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ func TestGetUIConfig(t *testing.T) {
|
|||||||
tmp := cfg["plot_list_selectedMetrics"]
|
tmp := cfg["plot_list_selectedMetrics"]
|
||||||
metrics := tmp.([]string)
|
metrics := tmp.([]string)
|
||||||
str := metrics[2]
|
str := metrics[2]
|
||||||
if str != "mem_used" {
|
if str != "flops_any" {
|
||||||
t.Errorf("wrong config\ngot: %s \nwant: mem_bw", str)
|
t.Errorf("wrong config\ngot: %s \nwant: flops_any", str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ClusterCockpit/cc-backend/internal/api"
|
|
||||||
"github.com/ClusterCockpit/cc-backend/internal/auth"
|
"github.com/ClusterCockpit/cc-backend/internal/auth"
|
||||||
"github.com/ClusterCockpit/cc-backend/internal/graph/model"
|
"github.com/ClusterCockpit/cc-backend/internal/graph/model"
|
||||||
"github.com/ClusterCockpit/cc-backend/internal/repository"
|
"github.com/ClusterCockpit/cc-backend/internal/repository"
|
||||||
@ -230,7 +229,7 @@ func buildFilterPresets(query url.Values) map[string]interface{} {
|
|||||||
return filterPresets
|
return filterPresets
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupRoutes(router *mux.Router, version string, hash string, buildTime string) {
|
func SetupRoutes(router *mux.Router, buildInfo web.Build) {
|
||||||
userCfgRepo := repository.GetUserCfgRepo()
|
userCfgRepo := repository.GetUserCfgRepo()
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
route := route
|
route := route
|
||||||
@ -256,7 +255,7 @@ func SetupRoutes(router *mux.Router, version string, hash string, buildTime stri
|
|||||||
Title: title,
|
Title: title,
|
||||||
User: *user,
|
User: *user,
|
||||||
Roles: availableRoles,
|
Roles: availableRoles,
|
||||||
Build: web.Build{Version: version, Hash: hash, Buildtime: buildTime},
|
Build: buildInfo,
|
||||||
Config: conf,
|
Config: conf,
|
||||||
Infos: infos,
|
Infos: infos,
|
||||||
}
|
}
|
||||||
@ -270,66 +269,64 @@ func SetupRoutes(router *mux.Router, version string, hash string, buildTime stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleSearchBar(rw http.ResponseWriter, r *http.Request, api *api.RestApi) {
|
func HandleSearchBar(rw http.ResponseWriter, r *http.Request, buildInfo web.Build) {
|
||||||
if search := r.URL.Query().Get("searchId"); search != "" {
|
|
||||||
user := auth.GetUser(r.Context())
|
user := auth.GetUser(r.Context())
|
||||||
|
availableRoles, _ := auth.GetValidRolesMap(user)
|
||||||
|
|
||||||
|
if search := r.URL.Query().Get("searchId"); search != "" {
|
||||||
|
repo := repository.GetJobRepository()
|
||||||
splitSearch := strings.Split(search, ":")
|
splitSearch := strings.Split(search, ":")
|
||||||
|
|
||||||
if len(splitSearch) == 2 {
|
if len(splitSearch) == 2 {
|
||||||
switch strings.Trim(splitSearch[0], " ") {
|
switch strings.Trim(splitSearch[0], " ") {
|
||||||
case "jobId":
|
case "jobId":
|
||||||
http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery
|
http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusFound) // All Users: Redirect to Tablequery
|
||||||
case "jobName":
|
case "jobName":
|
||||||
http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery
|
http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusFound) // All Users: Redirect to Tablequery
|
||||||
case "projectId":
|
case "projectId":
|
||||||
http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery
|
http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusFound) // All Users: Redirect to Tablequery
|
||||||
case "username":
|
case "username":
|
||||||
if user.HasAnyRole([]auth.Role{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) {
|
if user.HasAnyRole([]auth.Role{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) {
|
||||||
http.Redirect(rw, r, "/monitoring/users/?user="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect)
|
http.Redirect(rw, r, "/monitoring/users/?user="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusFound)
|
||||||
} else {
|
} else {
|
||||||
http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Users: Redirect to Tablequery
|
web.RenderTemplate(rw, r, "message.tmpl", &web.Page{Title: "Error", MsgType: "alert-danger", Message: "Missing Access Rights", User: *user, Roles: availableRoles, Build: buildInfo})
|
||||||
}
|
}
|
||||||
case "name":
|
case "name":
|
||||||
usernames, _ := api.JobRepository.FindColumnValues(user, strings.Trim(splitSearch[1], " "), "user", "username", "name")
|
usernames, _ := repo.FindColumnValues(user, strings.Trim(splitSearch[1], " "), "user", "username", "name")
|
||||||
if len(usernames) != 0 {
|
if len(usernames) != 0 {
|
||||||
joinedNames := strings.Join(usernames, "&user=")
|
joinedNames := strings.Join(usernames, "&user=")
|
||||||
http.Redirect(rw, r, "/monitoring/users/?user="+joinedNames, http.StatusTemporaryRedirect)
|
http.Redirect(rw, r, "/monitoring/users/?user="+joinedNames, http.StatusFound)
|
||||||
} else {
|
} else {
|
||||||
if user.HasAnyRole([]auth.Role{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) {
|
if user.HasAnyRole([]auth.Role{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) {
|
||||||
http.Redirect(rw, r, "/monitoring/users/?user=NoUserNameFound", http.StatusTemporaryRedirect)
|
http.Redirect(rw, r, "/monitoring/users/?user=NoUserNameFound", http.StatusPermanentRedirect)
|
||||||
} else {
|
} else {
|
||||||
http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Users: Redirect to Tablequery
|
web.RenderTemplate(rw, r, "message.tmpl", &web.Page{Title: "Error", MsgType: "alert-danger", Message: "Missing Access Rights", User: *user, Roles: availableRoles, Build: buildInfo})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
log.Warnf("Searchbar type parameter '%s' unknown", strings.Trim(splitSearch[0], " "))
|
web.RenderTemplate(rw, r, "message.tmpl", &web.Page{Title: "Warning", MsgType: "alert-warning", Message: fmt.Sprintf("Unknown search type: %s", strings.Trim(splitSearch[0], " ")), User: *user, Roles: availableRoles, Build: buildInfo})
|
||||||
http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Unknown: Redirect to Tablequery
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if len(splitSearch) == 1 {
|
} else if len(splitSearch) == 1 {
|
||||||
username, project, jobname, err := api.JobRepository.FindUserOrProjectOrJobname(r.Context(), strings.Trim(search, " "))
|
|
||||||
|
|
||||||
|
username, project, jobname, err := repo.FindUserOrProjectOrJobname(user, strings.Trim(search, " "))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error while searchbar best guess: %v", err.Error())
|
web.RenderTemplate(rw, r, "message.tmpl", &web.Page{Title: "Info", MsgType: "alert-info", Message: "Search without result", User: *user, Roles: availableRoles, Build: buildInfo})
|
||||||
http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Unknown: Redirect to Tablequery
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if username != "" {
|
if username != "" {
|
||||||
http.Redirect(rw, r, "/monitoring/user/"+username, http.StatusTemporaryRedirect) // User: Redirect to user page
|
http.Redirect(rw, r, "/monitoring/user/"+username, http.StatusFound) // User: Redirect to user page
|
||||||
} else if project != "" {
|
} else if project != "" {
|
||||||
http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(strings.Trim(search, " ")), http.StatusTemporaryRedirect) // projectId (equal)
|
http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(strings.Trim(search, " ")), http.StatusFound) // projectId (equal)
|
||||||
} else if jobname != "" {
|
} else if jobname != "" {
|
||||||
http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(strings.Trim(search, " ")), http.StatusTemporaryRedirect) // JobName (contains)
|
http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(strings.Trim(search, " ")), http.StatusFound) // JobName (contains)
|
||||||
} else {
|
} else {
|
||||||
http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(strings.Trim(search, " ")), http.StatusTemporaryRedirect) // No Result: Probably jobId
|
http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(strings.Trim(search, " ")), http.StatusFound) // No Result: Probably jobId
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log.Warnf("Searchbar query parameters malformed: %v", search)
|
web.RenderTemplate(rw, r, "message.tmpl", &web.Page{Title: "Error", MsgType: "alert-danger", Message: "Searchbar query parameters malformed", User: *user, Roles: availableRoles, Build: buildInfo})
|
||||||
http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Unknown: Redirect to Tablequery
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect)
|
web.RenderTemplate(rw, r, "message.tmpl", &web.Page{Title: "Warning", MsgType: "alert-warning", Message: "Empty search", User: *user, Roles: availableRoles, Build: buildInfo})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -480,6 +480,7 @@
|
|||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"cpu_user",
|
"cpu_user",
|
||||||
|
"cpu_load",
|
||||||
"mem_used",
|
"mem_used",
|
||||||
"flops_any",
|
"flops_any",
|
||||||
"mem_bw",
|
"mem_bw",
|
||||||
|
@ -327,6 +327,7 @@
|
|||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"cpu_user",
|
"cpu_user",
|
||||||
|
"cpu_load",
|
||||||
"mem_used",
|
"mem_used",
|
||||||
"flops_any",
|
"flops_any",
|
||||||
"mem_bw"
|
"mem_bw"
|
||||||
|
@ -17,15 +17,9 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4 mx-auto">
|
<div class="col-4 mx-auto">
|
||||||
{{if .Error}}
|
{{if .MsgType}}
|
||||||
<div class="alert alert-warning" role="alert">
|
<div class="alert {{.MsgType}}" role="alert">
|
||||||
{{.Error}}
|
{{.Message}}
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if .Info}}
|
|
||||||
<div class="alert alert-success" role="alert">
|
|
||||||
{{.Info}}
|
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
17
web/templates/message.tmpl
Normal file
17
web/templates/message.tmpl
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
{{define "content"}}
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-4">
|
||||||
|
<div class="alert {{.MsgType}} p-3 text-center fs-3" role="alert">
|
||||||
|
{{if eq .MsgType "alert-info"}}
|
||||||
|
<i class="bi-info-circle-fill me-3"></i>
|
||||||
|
{{else if eq .MsgType "alert-warning"}}
|
||||||
|
<i class="bi-question-circle-fill me-3"></i>
|
||||||
|
{{else if eq .MsgType "alert-danger"}}
|
||||||
|
<i class="bi-exclamation-circle-fill me-3"></i>
|
||||||
|
{{end}}
|
||||||
|
{{.Message}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
@ -90,8 +90,8 @@ type Build struct {
|
|||||||
|
|
||||||
type Page struct {
|
type Page struct {
|
||||||
Title string // Page title
|
Title string // Page title
|
||||||
Error string // For generic use (e.g. the exact error message on /login)
|
MsgType string // For generic use in message boxes
|
||||||
Info string // For generic use (e.g. "Logout successfull" on /login)
|
Message string // For generic use in message boxes
|
||||||
User auth.User // Information about the currently logged in user (Full User Info)
|
User auth.User // Information about the currently logged in user (Full User Info)
|
||||||
Roles map[string]auth.Role // Available roles for frontend render checks
|
Roles map[string]auth.Role // Available roles for frontend render checks
|
||||||
Build Build // Latest information about the application
|
Build Build // Latest information about the application
|
||||||
@ -104,8 +104,7 @@ type Page struct {
|
|||||||
func RenderTemplate(rw http.ResponseWriter, r *http.Request, file string, page *Page) {
|
func RenderTemplate(rw http.ResponseWriter, r *http.Request, file string, page *Page) {
|
||||||
t, ok := templates[file]
|
t, ok := templates[file]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Fatalf("WEB/WEB > template '%s' not found", file)
|
log.Errorf("WEB/WEB > template '%s' not found", file)
|
||||||
panic("template not found")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if page.Clusters == nil {
|
if page.Clusters == nil {
|
||||||
@ -114,7 +113,7 @@ func RenderTemplate(rw http.ResponseWriter, r *http.Request, file string, page *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Page config : %v\n", page.Config)
|
log.Debugf("Page config : %v\n", page.Config)
|
||||||
if err := t.Execute(rw, page); err != nil {
|
if err := t.Execute(rw, page); err != nil {
|
||||||
log.Errorf("Template error: %s", err.Error())
|
log.Errorf("Template error: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user