Merge pull request #129 from ClusterCockpit/hotfix

Hotfix
This commit is contained in:
Jan Eitzinger 2023-06-12 17:18:05 +02:00 committed by GitHub
commit 88fe367ecb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 114 additions and 60 deletions

View File

@ -23,6 +23,8 @@ import (
const Version = 1 const Version = 1
var ar FsArchive var ar FsArchive
var srcPath string
var dstPath string
func loadJobData(filename string) (*JobData, error) { func loadJobData(filename string) (*JobData, error) {
@ -243,17 +245,65 @@ func deepCopyClusterConfig(co *Cluster) schema.Cluster {
return cn return cn
} }
func convertJob(job *JobMeta) {
// check if source data is available, otherwise skip job
src_data_path := getPath(job, srcPath, "data.json")
info, err := os.Stat(src_data_path)
if err != nil {
log.Fatal(err)
}
if info.Size() == 0 {
fmt.Printf("Skip path %s, filesize is 0 Bytes.", src_data_path)
return
}
path := getPath(job, dstPath, "meta.json")
err = os.MkdirAll(filepath.Dir(path), 0750)
if err != nil {
log.Fatal(err)
}
f, err := os.Create(path)
if err != nil {
log.Fatal(err)
}
jmn := deepCopyJobMeta(job)
if err = EncodeJobMeta(f, &jmn); err != nil {
log.Fatal(err)
}
if err = f.Close(); err != nil {
log.Fatal(err)
}
f, err = os.Create(getPath(job, dstPath, "data.json"))
if err != nil {
log.Fatal(err)
}
var jd *JobData
jd, err = loadJobData(src_data_path)
if err != nil {
log.Fatal(err)
}
jdn := deepCopyJobData(jd, job.Cluster, job.SubCluster)
if err := EncodeJobData(f, jdn); err != nil {
log.Fatal(err)
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
}
func main() { func main() {
var flagLogLevel, flagConfigFile string var flagLogLevel, flagConfigFile string
var flagLogDateTime bool var flagLogDateTime, debug bool
var srcPath string
var dstPath string
flag.BoolVar(&flagLogDateTime, "logdate", false, "Set this flag to add date and time to log messages") flag.BoolVar(&flagLogDateTime, "logdate", false, "Set this flag to add date and time to log messages")
flag.BoolVar(&debug, "debug", false, "Set this flag to force sequential execution for debugging")
flag.StringVar(&flagLogLevel, "loglevel", "warn", "Sets the logging level: `[debug,info,warn (default),err,fatal,crit]`") flag.StringVar(&flagLogLevel, "loglevel", "warn", "Sets the logging level: `[debug,info,warn (default),err,fatal,crit]`")
flag.StringVar(&flagConfigFile, "config", "./config.json", "Specify alternative path to `config.json`") flag.StringVar(&flagConfigFile, "config", "./config.json", "Specify alternative path to `config.json`")
flag.StringVar(&srcPath, "s", "./var/job-archive", "Specify the source job archive path") flag.StringVar(&srcPath, "src", "./var/job-archive", "Specify the source job archive path")
flag.StringVar(&dstPath, "d", "./var/job-archive-new", "Specify the destination job archive path") flag.StringVar(&dstPath, "dst", "./var/job-archive-new", "Specify the destination job archive path")
flag.Parse() flag.Parse()
if _, err := os.Stat(filepath.Join(srcPath, "version.txt")); !errors.Is(err, os.ErrNotExist) { if _, err := os.Stat(filepath.Join(srcPath, "version.txt")); !errors.Is(err, os.ErrNotExist) {
@ -302,60 +352,19 @@ func main() {
var wg sync.WaitGroup var wg sync.WaitGroup
for job := range ar.Iter() { for job := range ar.Iter() {
// fmt.Printf("Job %d\n", job.JobID) if debug {
fmt.Printf("Job %d\n", job.JobID)
convertJob(job)
} else {
job := job job := job
wg.Add(1) wg.Add(1)
go func() { go func() {
defer wg.Done() defer wg.Done()
// check if source data is available, otherwise skip job convertJob(job)
src_data_path := getPath(job, srcPath, "data.json")
info, err := os.Stat(src_data_path)
if err != nil {
log.Fatal(err)
}
if info.Size() == 0 {
fmt.Printf("Skip path %s, filesize is 0 Bytes.", src_data_path)
return
}
path := getPath(job, dstPath, "meta.json")
err = os.MkdirAll(filepath.Dir(path), 0750)
if err != nil {
log.Fatal(err)
}
f, err := os.Create(path)
if err != nil {
log.Fatal(err)
}
jmn := deepCopyJobMeta(job)
if err = EncodeJobMeta(f, &jmn); err != nil {
log.Fatal(err)
}
if err = f.Close(); err != nil {
log.Fatal(err)
}
f, err = os.Create(getPath(job, dstPath, "data.json"))
if err != nil {
log.Fatal(err)
}
var jd *JobData
jd, err = loadJobData(src_data_path)
if err != nil {
log.Fatal(err)
}
jdn := deepCopyJobData(jd, job.Cluster, job.SubCluster)
if err := EncodeJobData(f, jdn); err != nil {
log.Fatal(err)
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
}() }()
} }
}
wg.Wait() wg.Wait()
os.WriteFile(filepath.Join(dstPath, "version.txt"), []byte(fmt.Sprintf("%d", Version)), 0644) os.WriteFile(filepath.Join(dstPath, "version.txt"), []byte(fmt.Sprintf("%d", Version)), 0644)

View File

@ -1,7 +1,7 @@
<script> <script>
import { onMount, getContext } from 'svelte' import { onMount, getContext } from 'svelte'
import { init } from './utils.js' import { init } from './utils.js'
import { Table, Row, Col, Button, Icon, Card, Spinner } from 'sveltestrap' import { Table, Row, Col, Button, Icon, Card, Spinner, Input } from 'sveltestrap'
import { queryStore, gql, getContextClient } from '@urql/svelte' import { queryStore, gql, getContextClient } from '@urql/svelte'
import Filters from './filters/Filters.svelte' import Filters from './filters/Filters.svelte'
import JobList from './joblist/JobList.svelte' import JobList from './joblist/JobList.svelte'
@ -25,6 +25,13 @@
let metrics = ccconfig.plot_list_selectedMetrics, isMetricsSelectionOpen = false let metrics = ccconfig.plot_list_selectedMetrics, isMetricsSelectionOpen = false
let w1, w2, histogramHeight = 250 let w1, w2, histogramHeight = 250
let selectedCluster = filterPresets?.cluster ? filterPresets.cluster : null let selectedCluster = filterPresets?.cluster ? filterPresets.cluster : null
let resize = false
/* Resize Context
* A) Each viewport change triggers histogram rerender due to variable dimensions clearing canvas if not rerendered
* B) Opening filters (and some other things) triggers small change in viewport dimensions (Fix here?)
* A+B) Histogram rerenders if filters opened, high performance impact if dataload heavy
* Solution: Default to fixed histogram dimensions, allow user to enable automatic resizing
*/
const client = getContextClient(); const client = getContextClient();
$: stats = queryStore({ $: stats = queryStore({
@ -130,27 +137,47 @@
<th scope="row">Total Core Hours</th> <th scope="row">Total Core Hours</th>
<td>{$stats.data.jobsStatistics[0].totalCoreHours}</td> <td>{$stats.data.jobsStatistics[0].totalCoreHours}</td>
</tr> </tr>
<tr>
<th scope="row">Toggle Histogram Resizing</th>
<td><Input id="c3" value={resize} type="switch" on:change={() => (resize = !resize)}/></td>
</tr>
</tbody> </tbody>
</Table> </Table>
</Col> </Col>
<div class="col-4" style="text-align: center;" bind:clientWidth={w1}> <div class="col-4" style="text-align: center;" bind:clientWidth={w1}>
<b>Duration Distribution</b> <b>Duration Distribution</b>
{#key $stats.data.jobsStatistics[0].histDuration} {#key $stats.data.jobsStatistics[0].histDuration}
{#if resize == true}
<Histogram <Histogram
data={$stats.data.jobsStatistics[0].histDuration} data={$stats.data.jobsStatistics[0].histDuration}
width={w1 - 25} height={histogramHeight} width={w1 - 25} height={histogramHeight}
xlabel="Current Runtimes [h]" xlabel="Current Runtimes [h]"
ylabel="Number of Jobs"/> ylabel="Number of Jobs"/>
{:else}
<Histogram
data={$stats.data.jobsStatistics[0].histDuration}
width={400} height={250}
xlabel="Current Runtimes [h]"
ylabel="Number of Jobs"/>
{/if}
{/key} {/key}
</div> </div>
<div class="col-4" style="text-align: center;" bind:clientWidth={w2}> <div class="col-4" style="text-align: center;" bind:clientWidth={w2}>
<b>Number of Nodes Distribution</b> <b>Number of Nodes Distribution</b>
{#key $stats.data.jobsStatistics[0].histNumNodes} {#key $stats.data.jobsStatistics[0].histNumNodes}
{#if resize == true}
<Histogram <Histogram
data={$stats.data.jobsStatistics[0].histNumNodes} data={$stats.data.jobsStatistics[0].histNumNodes}
width={w2 - 25} height={histogramHeight} width={w2 - 25} height={histogramHeight}
xlabel="Allocated Nodes [#]" xlabel="Allocated Nodes [#]"
ylabel="Number of Jobs" /> ylabel="Number of Jobs" />
{:else}
<Histogram
data={$stats.data.jobsStatistics[0].histNumNodes}
width={400} height={250}
xlabel="Allocated Nodes [#]"
ylabel="Number of Jobs" />
{/if}
{/key} {/key}
</div> </div>
{/if} {/if}

View File

@ -9,8 +9,8 @@
<th>Running Jobs</th> <th>Running Jobs</th>
<th>Total Jobs</th> <th>Total Jobs</th>
{{if .User.HasRole .Roles.admin}} {{if .User.HasRole .Roles.admin}}
<th>Status View</th>
<th>System View</th> <th>System View</th>
<th>Analysis View</th>
{{end}} {{end}}
</tr> </tr>
</thead> </thead>
@ -21,8 +21,8 @@
<td>{{.ID}}</td> <td>{{.ID}}</td>
<td><a href="/monitoring/jobs/?cluster={{.ID}}&state=running">{{.RunningJobs}} jobs</a></td> <td><a href="/monitoring/jobs/?cluster={{.ID}}&state=running">{{.RunningJobs}} jobs</a></td>
<td><a href="/monitoring/jobs/?cluster={{.ID}}">{{.TotalJobs}} jobs</a></td> <td><a href="/monitoring/jobs/?cluster={{.ID}}">{{.TotalJobs}} jobs</a></td>
<td><a href="/monitoring/status/{{.ID}}">Status View</a></td>
<td><a href="/monitoring/systems/{{.ID}}">System View</a></td> <td><a href="/monitoring/systems/{{.ID}}">System View</a></td>
<td><a href="/monitoring/analysis/{{.ID}}">Analysis View</a></td>
</tr> </tr>
{{end}} {{end}}
{{else}} {{else}}

View File

@ -9,6 +9,7 @@ import (
"html/template" "html/template"
"io/fs" "io/fs"
"net/http" "net/http"
"os"
"strings" "strings"
"github.com/ClusterCockpit/cc-backend/internal/auth" "github.com/ClusterCockpit/cc-backend/internal/auth"
@ -46,6 +47,23 @@ func init() {
return nil return nil
} }
if path == "templates/imprint.tmpl" {
if _, err := os.Stat("./var/imprint.tmpl"); err == nil {
log.Info("overwrite imprint.tmpl with local file")
templates[strings.TrimPrefix(path, "templates/")] =
template.Must(template.Must(base.Clone()).ParseFiles("./var/imprint.tmpl"))
return nil
}
}
if path == "templates/privacy.tmpl" {
if _, err := os.Stat("./var/privacy.tmpl"); err == nil {
log.Info("overwrite privacy.tmpl with local file")
templates[strings.TrimPrefix(path, "templates/")] =
template.Must(template.Must(base.Clone()).ParseFiles("./var/privacy.tmpl"))
return nil
}
}
templates[strings.TrimPrefix(path, "templates/")] = template.Must(template.Must(base.Clone()).ParseFS(templateFiles, path)) templates[strings.TrimPrefix(path, "templates/")] = template.Must(template.Must(base.Clone()).ParseFS(templateFiles, path))
return nil return nil
}); err != nil { }); err != nil {