1
0
Fork 0

feat: refactor di alcune cose e grafici

main
Antonio De Lucreziis 2 years ago
parent b9073cebbb
commit 6d3b35ee66

@ -1,11 +1,11 @@
package database package database
import ( import (
"math/rand"
"time" "time"
"git.phc.dm.unipi.it/phc/cluster-dashboard/backend/executor" "git.phc.dm.unipi.it/phc/cluster-dashboard/backend/executor"
"git.phc.dm.unipi.it/phc/cluster-dashboard/backend/model" "git.phc.dm.unipi.it/phc/cluster-dashboard/backend/model"
"golang.org/x/exp/maps"
) )
// simpleDB è una implementazione di [database.Database] che tiene giusto una cache in memoria e // simpleDB è una implementazione di [database.Database] che tiene giusto una cache in memoria e
@ -37,37 +37,121 @@ func NewSimpleDatabase(ex executor.Service) Database {
} }
func (s *simpleDB) GetNode(hostname string) (*model.Node, error) { func (s *simpleDB) GetNode(hostname string) (*model.Node, error) {
panic("todo") return &model.Node{
Hostname: hostname,
StartTime: time.Now().Add(-1 * time.Hour),
}, nil
} }
func (s *simpleDB) GetJob(id string) (*model.Job, error) { func (s *simpleDB) GetJob(id string) (*model.Job, error) {
panic("todo") return &model.Job{
Id: id,
Name: "example-job",
Status: "active",
Nodes: []string{"node-1", "node-2"},
Resources: []string{},
}, nil
} }
func (s *simpleDB) AllNodes() ([]*model.Node, error) { func (s *simpleDB) AllNodes() ([]*model.Node, error) {
return maps.Values(s.nodes), nil return []*model.Node{
{
Hostname: "node-1",
StartTime: time.Now().Add(-1 * time.Hour),
},
{
Hostname: "node-2",
StartTime: time.Now().Add(-1 * time.Hour),
},
{
Hostname: "node-3",
StartTime: time.Now().Add(-1 * time.Hour),
},
}, nil
} }
func (s *simpleDB) AllJobs() ([]*model.Job, error) { func (s *simpleDB) AllJobs() ([]*model.Job, error) {
return maps.Values(s.jobs), nil return []*model.Job{
{
Id: "job-1",
Name: "example-job",
Status: "active",
Nodes: []string{"node-1", "node-2"},
Resources: []string{},
},
{
Id: "job-2",
Name: "example-job",
Status: "active",
Nodes: []string{"node-1", "node-2"},
Resources: []string{},
},
{
Id: "job-3",
Name: "example-job",
Status: "active",
Nodes: []string{"node-1", "node-2"},
Resources: []string{},
},
{
Id: "job-4",
Name: "example-job",
Status: "active",
Nodes: []string{"node-1", "node-2"},
Resources: []string{},
},
{
Id: "job-5",
Name: "example-job",
Status: "active",
Nodes: []string{"node-1", "node-2"},
Resources: []string{},
},
}, nil
} }
func (s *simpleDB) QueryTemperatureSamples(from, to time.Time) ([]model.Sample[float64], error) { func (s *simpleDB) QueryTemperatureSamples(from, to time.Time) ([]model.Sample[float64], error) {
panic("todo") return generateFakeFloat64Samples(100), nil
} }
func (s *simpleDB) QueryMemorySamples(from, to time.Time) ([]model.Sample[int64], error) { func (s *simpleDB) QueryMemorySamples(from, to time.Time) ([]model.Sample[int64], error) {
panic("todo") return generateFakeInt64Samples(100), nil
} }
func (s *simpleDB) QueryStorageSamples(from, to time.Time) ([]model.Sample[int64], error) { func (s *simpleDB) QueryStorageSamples(from, to time.Time) ([]model.Sample[int64], error) {
panic("todo") return generateFakeInt64Samples(100), nil
} }
func (s *simpleDB) QueryNetworkUploadSamples(from, to time.Time) ([]model.Sample[int64], error) { func (s *simpleDB) QueryNetworkUploadSamples(from, to time.Time) ([]model.Sample[int64], error) {
panic("todo") return generateFakeInt64Samples(100), nil
} }
func (s *simpleDB) QueryNetworkDownloadSamples(from, to time.Time) ([]model.Sample[int64], error) { func (s *simpleDB) QueryNetworkDownloadSamples(from, to time.Time) ([]model.Sample[int64], error) {
panic("todo") return generateFakeInt64Samples(100), nil
}
func generateFakeFloat64Samples(amount int) []model.Sample[float64] {
fakeSamples := make([]model.Sample[float64], amount)
for i := range fakeSamples {
fakeSamples[i] = model.Sample[float64]{
Timestamp: time.Now().Add(time.Duration(i-amount) * time.Second),
Value: rand.Float64(),
}
}
return fakeSamples
}
func generateFakeInt64Samples(amount int) []model.Sample[int64] {
fakeSamples := make([]model.Sample[int64], amount)
for i := range fakeSamples {
fakeSamples[i] = model.Sample[int64]{
Timestamp: time.Now().Add(time.Duration(i-amount) * time.Second),
Value: rand.Int63n(1000),
}
}
return fakeSamples
} }

@ -25,111 +25,6 @@
<link rel="stylesheet" href="/styles/main.scss" /> <link rel="stylesheet" href="/styles/main.scss" />
</head> </head>
<body> <body>
<div class="sidebar"> <script type="module" src="/src/main.jsx"></script>
<div class="top">
<div class="logo">
<pre><code>_______________________ _______ _______ _______ _
( ____ \__ __( ____ ( ____ ( ____ ( ____ ( )
| ( \/ ) ( | ( \/ ( \/ ( \/ ( \//
| (_____ | | | (__ | (__ | (__ | (__
(_____ ) | | | __) | __) | __) | __)
) | | | | ( | ( | ( | (
/\____) | | | | (____/\ ) | ) | (____/\
\_______) )_( (_______// |/ (_______/</code></pre>
</div>
<div class="item">
<div class="icon">
<span class="material-symbols-outlined">
dashboard
</span>
</div>
<div class="label">Overview</div>
</div>
<div class="item">
<div class="icon">
<span class="material-symbols-outlined">
device_thermostat
</span>
</div>
<div class="label">Temperature</div>
</div>
<div class="item">
<div class="icon">
<span class="material-symbols-outlined"> speed </span>
</div>
<div class="label">CPU</div>
</div>
<div class="item">
<div class="icon">
<span class="material-symbols-outlined">
swap_vert
</span>
</div>
<div class="label">Network</div>
</div>
<div class="item">
<div class="icon">
<span class="material-symbols-outlined"> memory </span>
</div>
<div class="label">Memory</div>
</div>
<div class="item">
<div class="icon">
<span class="material-symbols-outlined">
database
</span>
</div>
<div class="label">Storage</div>
</div>
<div class="item">
<div class="icon">
<span class="material-symbols-outlined">
view_list
</span>
</div>
<div class="label">Jobs</div>
</div>
</div>
<div class="bottom">
<div class="item">
<div class="icon">
<img src="/public/logo-gitea.svg" alt="logo gitea" />
</div>
<div class="label"><a href="#">Dashboard</a></div>
</div>
<div class="item">
<div class="icon">
<img src="/public/logo-gitea.svg" alt="logo gitea" />
</div>
<div class="label"><a href="#">cpar2023</a></div>
</div>
</div>
</div>
<div class="main">
<div class="card">
<div class="label">Uptime</div>
<div class="graph"></div>
</div>
<div class="card">
<div class="label">CPU</div>
<div class="graph"></div>
</div>
<div class="card">
<div class="label">Storage</div>
<div class="graph"></div>
</div>
<div class="card">
<div class="label">Memory</div>
<div class="graph"></div>
</div>
<div class="card">
<div class="label">Network</div>
<div class="graph"></div>
</div>
<div class="card">
<div class="label">Temperature</div>
<div class="graph"></div>
</div>
</div>
</body> </body>
</html> </html>

@ -0,0 +1,20 @@
import { useEffect, useState } from 'preact/hooks'
import { Plot } from './Plot.jsx'
import { server } from '../utils.js'
export const GraphCard = ({ label, url }) => {
const [samples, setSamples] = useState([])
useEffect(() => {
if (url) {
server.get(url).then(data => setSamples(data))
}
}, [])
return (
<div class="card">
<div class="label">{label}</div>
<div class="graph">{url && <Plot samples={samples} />}</div>
</div>
)
}

@ -1,13 +1,83 @@
import d3 from 'd3' import * as d3 from 'd3'
import { useEffect, useRef } from 'preact/hooks'
// export const Plot = ({ samples }) => { // set the dimensions and margins of the graph
// const mountElement = el => { const margin = { top: 10, right: 10, bottom: 30, left: 30 }
// d3.select(el).append('svg')
// }
// return <div class="d3-plot" ref={mountElement}></div>
// }
export const Plot = ({ samples }) => { export const Plot = ({ samples }) => {
return <div class="d3-plot">Work in progress...</div> if (samples.length === 0) {
return <>Loading...</>
}
const data = samples.map(({ timestamp, value }) => ({
timestamp: d3.isoParse(timestamp),
value: value,
}))
const plotRef = useRef(null)
useEffect(() => {
if (plotRef.current) {
const el = plotRef.current
const width = el.offsetWidth - margin.left - margin.right
const height = el.offsetHeight - margin.top - margin.bottom
const svg = d3
.select(el)
.append('svg')
.attr('width', el.offsetWidth)
.attr('height', el.offsetHeight)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
const x = d3
.scaleTime()
.domain(
d3.extent(data, function (d) {
return d.timestamp
})
)
.range([0, width])
const y = d3
.scaleLinear()
.domain([
0,
d3.max(data, function (d) {
return +d.value
}),
])
.range([height, 0])
svg.append('g')
.attr('transform', 'translate(0,' + height + ')')
.call(d3.axisBottom(x))
svg.append('g').call(d3.axisLeft(y))
const line = svg.append('g')
line.append('path')
.datum(data)
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('stroke-width', 2)
.attr(
'd',
d3
.line()
.x(d => x(d.timestamp))
.y(d => y(d.value))
)
}
return () => {
if (plotRef.current) {
plotRef.current.firstChild.remove()
}
}
}, [plotRef])
return <div class="d3-plot" ref={plotRef}></div>
} }

@ -7,6 +7,7 @@ import { DashboardPage, JobsPage } from './pages.jsx'
const Redirect = ({ to }) => { const Redirect = ({ to }) => {
useEffect(() => { useEffect(() => {
console.log('Redirecting...')
route(to, true) route(to, true)
}, []) }, [])

@ -1,6 +1,100 @@
import { GraphCard } from './components/Graph.jsx'
const LOGO = String.raw`
_______________________ _______ _______ _______ _
( ____ \__ __( ____ ( ____ ( ____ ( ____ ( )
| ( \/ ) ( | ( \/ ( \/ ( \/ ( \//
| (_____ | | | (__ | (__ | (__ | (__
(_____ ) | | | __) | __) | __) | __)
) | | | | ( | ( | ( | (
/\____) | | | | (____/\ ) | ) | (____/\
\_______) )_( (_______// |/ (_______/
`.trim()
export const DashboardPage = ({}) => { export const DashboardPage = ({}) => {
return <>Dashboard Page</> return (
<>
<div class="sidebar">
<div class="top">
<div class="logo">
<pre>
<code>{LOGO}</code>
</pre>
</div>
<div class="item">
<div class="icon">
<span class="material-symbols-outlined">dashboard</span>
</div>
<div class="label">Overview</div>
</div>
<div class="item">
<div class="icon">
<span class="material-symbols-outlined">device_thermostat</span>
</div>
<div class="label">Temperature</div>
</div>
<div class="item">
<div class="icon">
<span class="material-symbols-outlined"> speed </span>
</div>
<div class="label">CPU</div>
</div>
<div class="item">
<div class="icon">
<span class="material-symbols-outlined">swap_vert</span>
</div>
<div class="label">Network</div>
</div>
<div class="item">
<div class="icon">
<span class="material-symbols-outlined"> memory </span>
</div>
<div class="label">Memory</div>
</div>
<div class="item">
<div class="icon">
<span class="material-symbols-outlined">database</span>
</div>
<div class="label">Storage</div>
</div>
<div class="item">
<div class="icon">
<span class="material-symbols-outlined">view_list</span>
</div>
<div class="label">Jobs</div>
</div>
</div>
<div class="bottom">
<div class="item link">
<div class="icon">
<img src="/public/logo-gitea.svg" alt="logo gitea" />
</div>
<div class="label">
<a href="https://git.phc.dm.unipi.it/phc/cluster-dashboard">Dashboard</a>
</div>
</div>
<div class="item link">
<div class="icon">
<img src="/public/logo-gitea.svg" alt="logo gitea" />
</div>
<div class="label">
<a href="https://git.phc.dm.unipi.it/fdurastante/cpar2023">cpar2023</a>
</div>
</div>
</div>
</div>
<div class="main">
<GraphCard label="Uptime" />
<GraphCard label="CPU" />
<GraphCard label="Storage" />
<GraphCard label="Memory" />
<GraphCard label="Network" />
<GraphCard label="Temperature" url="/api/stats/temperature" />
</div>
</>
)
} }
export const JobsPage = ({}) => { export const JobsPage = ({}) => {
return <>Jobs Page</> return <>Jobs Page</>
} }

@ -0,0 +1,19 @@
export const server = {
async get(url) {
const res = await fetch(url)
return await res.json()
},
async post(url, data) {
const res = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
return await res.json()
},
}
window.server = server

@ -10,7 +10,7 @@ body {
width: 100%; width: 100%;
min-height: 100vh; min-height: 100vh;
font-family: "Open Sans", "Segoe UI", "Helvetica", "Arial", sans-serif; font-family: 'Open Sans', 'Segoe UI', 'Helvetica', 'Arial', sans-serif;
font-size: 16px; font-size: 16px;
color: #284a36; color: #284a36;
@ -57,7 +57,7 @@ body {
padding: 16px; padding: 16px;
font-family: "Ubuntu Mono", monospace; font-family: 'Ubuntu Mono', monospace;
font-weight: 700; font-weight: 700;
display: grid; display: grid;
@ -99,6 +99,22 @@ body {
&:hover { &:hover {
background-color: #e8ede9; background-color: #e8ede9;
} }
&.link {
padding: 0 16px;
.label {
display: flex;
flex-grow: 1;
a {
display: flex;
flex-grow: 1;
padding: 8px 0;
}
}
}
} }
} }
} }
@ -128,9 +144,13 @@ body {
} }
.graph { .graph {
background-color: #f4f5f9;
min-height: 300px; min-height: 300px;
.d3-plot {
display: flex;
width: 100%;
height: 100%;
}
} }
} }

Loading…
Cancel
Save