Added some monitoring widgets and scripts
parent
d4b9dee6d9
commit
8e86943b83
@ -0,0 +1,52 @@
|
|||||||
|
import { byteSizeToString, useRemoteState } from '../../util.jsx'
|
||||||
|
import { PieChart } from '../charts/PieChart.jsx'
|
||||||
|
import { Icon } from '../Icon.jsx'
|
||||||
|
import { ToastMessage, useToasts } from '../Toasts.jsx'
|
||||||
|
|
||||||
|
export const DiskUsage = ({ disk }) => {
|
||||||
|
const [showToast] = useToasts()
|
||||||
|
const [output, err, refreshOutput] = useRemoteState(
|
||||||
|
`/api/monitor/status?script=${encodeURIComponent(`disk-usage ${disk}`)}`,
|
||||||
|
'100\n0'
|
||||||
|
)
|
||||||
|
|
||||||
|
const [total, used] = output.split('\n').map(s => parseInt(s))
|
||||||
|
|
||||||
|
const title = (
|
||||||
|
<>
|
||||||
|
Spazio Utilizzato • <code>{disk}</code>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div class="title">
|
||||||
|
<div class="row">
|
||||||
|
<div class="row-group">
|
||||||
|
<p>{title}</p>
|
||||||
|
</div>
|
||||||
|
<div class="row-group">
|
||||||
|
<button
|
||||||
|
class="icon"
|
||||||
|
onClick={() => {
|
||||||
|
refreshOutput()
|
||||||
|
showToast(
|
||||||
|
<ToastMessage icon="info">Aggiornato "{title}"</ToastMessage>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon name="refresh" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
{err ? (
|
||||||
|
<p>Errore "{err}"</p>
|
||||||
|
) : (
|
||||||
|
<PieChart parts={[used]} labels={[byteSizeToString(used)]} total={total} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
import { PieChart } from '../charts/PieChart.jsx'
|
||||||
|
|
||||||
|
export const PieChartWidget = ({ title, ...chart }) => {
|
||||||
|
title = title || 'Grafico a torta'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div class="title">{title}</div>
|
||||||
|
<div class="content">
|
||||||
|
<PieChart {...chart} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
import { useRemoteState } from '../../util.jsx'
|
||||||
|
import { Icon } from '../Icon.jsx'
|
||||||
|
import { ToastMessage, useToasts } from '../Toasts.jsx'
|
||||||
|
|
||||||
|
export const ScriptStatus = ({ title, script }) => {
|
||||||
|
const [showToast] = useToasts()
|
||||||
|
const [output, err, refreshOutput] = useRemoteState(
|
||||||
|
`/api/monitor/status?script=${encodeURIComponent(script)}`,
|
||||||
|
''
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div class="title">
|
||||||
|
<div class="row">
|
||||||
|
<div class="row-group">{title}</div>
|
||||||
|
<div class="row-group">
|
||||||
|
<button
|
||||||
|
class="icon"
|
||||||
|
onClick={() => {
|
||||||
|
refreshOutput()
|
||||||
|
showToast(
|
||||||
|
<ToastMessage icon="info">Aggiornato "{title}"</ToastMessage>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon name="refresh" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
{err ? (
|
||||||
|
<p>Errore "{err}"</p>
|
||||||
|
) : (
|
||||||
|
<pre>
|
||||||
|
<code>{output}</code>
|
||||||
|
</pre>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -1,39 +0,0 @@
|
|||||||
package jobs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
ScriptsDir string `json:"scriptsDir"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Service struct {
|
|
||||||
Config Config
|
|
||||||
|
|
||||||
scriptPaths []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(config Config) *Service {
|
|
||||||
return &Service{
|
|
||||||
Config: config,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) LoadScripts() error {
|
|
||||||
entries, err := os.ReadDir(s.Config.ScriptsDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.scriptPaths = []string{}
|
|
||||||
|
|
||||||
for _, entry := range entries {
|
|
||||||
s.scriptPaths = append(s.scriptPaths,
|
|
||||||
path.Join(s.Config.ScriptsDir, entry.Name()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -0,0 +1,58 @@
|
|||||||
|
package monitor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
ScriptsDir string `json:"scriptsDir"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
Config *Config
|
||||||
|
|
||||||
|
scriptPaths map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(config *Config) *Service {
|
||||||
|
return &Service{
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) LoadScripts() error {
|
||||||
|
entries, err := os.ReadDir(s.Config.ScriptsDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.scriptPaths = map[string]string{}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
newScriptPath := path.Join(s.Config.ScriptsDir, entry.Name())
|
||||||
|
s.scriptPaths[entry.Name()] = newScriptPath
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add caching
|
||||||
|
func (s *Service) GetLastOutput(command string) (string, error) {
|
||||||
|
args := strings.Fields(command)
|
||||||
|
|
||||||
|
scriptPath := s.scriptPaths[args[0]]
|
||||||
|
|
||||||
|
cmd := exec.Command(scriptPath, args[1:]...)
|
||||||
|
var b bytes.Buffer
|
||||||
|
cmd.Stdout = &b
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.String(), nil
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *Router) ApiMonitor(api fiber.Router) {
|
||||||
|
// Respond to requests like
|
||||||
|
// - "/api/monitor/status?script=SCRIPT_NAME" where SCRIPT_NAME is the name of a file inside "./scripts"
|
||||||
|
api.Get("/status", func(c *fiber.Ctx) error {
|
||||||
|
if qScript := c.Query("script"); qScript != "" {
|
||||||
|
log.Printf("Script %q", qScript)
|
||||||
|
|
||||||
|
output, err := r.Monitor.GetLastOutput(qScript)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("no script, device or entity provided")
|
||||||
|
})
|
||||||
|
}
|
@ -1,7 +1,11 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import "git.phc.dm.unipi.it/phc/storage/database"
|
import (
|
||||||
|
"git.phc.dm.unipi.it/phc/storage/database"
|
||||||
|
"git.phc.dm.unipi.it/phc/storage/monitor"
|
||||||
|
)
|
||||||
|
|
||||||
type Router struct {
|
type Router struct {
|
||||||
Database database.Database
|
Database database.Database
|
||||||
|
Monitor *monitor.Service
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
DEVICE="$1"
|
||||||
|
|
||||||
|
df | grep "$DEVICE" | tr -s ' ' | cut -d' ' -f 2-3 | tr ' ' '\n'
|
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo 'active'
|
||||||
|
echo 'active'
|
||||||
|
echo 'active'
|
||||||
|
echo 'failing'
|
||||||
|
echo 'missing'
|
Loading…
Reference in New Issue