mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2026-04-04 06:57:30 +02:00
Add job-archive-demo.tar to .gitignore and ignore .idea directory in migrations/mysql***
***Update config-demo.json with new database connection details*** ***Add console.log statement in Systems.root.svelte*** ***Add "Partitions" menu item in Header.svelte*** ***Add partitions related files and routes*** ***Add partitions.entrypoint.js*** ***Update Makefile to use pnpm instead of npm*** ***Comment out wget commands in startDemo.sh*** ***Update rollup.config.mjs to include partitions.entrypoint.js*** ***Add PartitionSetting.svelte and Dashboard.svelte
This commit is contained in:
@@ -96,6 +96,14 @@
|
||||
perCluster: true,
|
||||
menu: "Stats",
|
||||
},
|
||||
{
|
||||
title: "Partitions",
|
||||
requiredRole: roles.admin,
|
||||
href: "partitions/systems/",
|
||||
icon: "device-hdd",
|
||||
perCluster: true,
|
||||
menu: "Stats",
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
|
||||
223
web/frontend/src/Partitions.root.svelte
Normal file
223
web/frontend/src/Partitions.root.svelte
Normal file
@@ -0,0 +1,223 @@
|
||||
<script>
|
||||
import { init, checkMetricDisabled } from "./utils.js";
|
||||
import Refresher from "./joblist/Refresher.svelte";
|
||||
import {
|
||||
Row,
|
||||
Col,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputGroupText,
|
||||
Icon,
|
||||
Spinner,
|
||||
Card,
|
||||
CardBody,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardSubtitle,
|
||||
CardText,
|
||||
CardTitle,
|
||||
Accordion,
|
||||
AccordionItem,
|
||||
} from "sveltestrap";
|
||||
import { queryStore, gql, getContextClient } from "@urql/svelte";
|
||||
import TimeSelection from "./filters/TimeSelection.svelte";
|
||||
import PlotTable from "./PlotTable.svelte";
|
||||
import MetricPlot from "./plots/MetricPlot.svelte";
|
||||
import { getContext } from "svelte";
|
||||
import PartitionSetting from "./partition/PartitionSetting.svelte";
|
||||
|
||||
export let cluster;
|
||||
export let from = null;
|
||||
export let to = null;
|
||||
|
||||
const { query: initq } = init();
|
||||
|
||||
if (from == null || to == null) {
|
||||
to = new Date(Date.now());
|
||||
from = new Date(to.getTime());
|
||||
from.setMinutes(from.getMinutes() - 30);
|
||||
}
|
||||
|
||||
const clusters = getContext("clusters");
|
||||
console.log(clusters);
|
||||
const ccconfig = getContext("cc-config");
|
||||
const metricConfig = getContext("metrics");
|
||||
|
||||
let plotHeight = 300;
|
||||
let hostnameFilter = "";
|
||||
let selectedMetric = ccconfig.system_view_selectedMetric;
|
||||
|
||||
const client = getContextClient();
|
||||
$: nodesQuery = queryStore({
|
||||
client: client,
|
||||
query: gql`
|
||||
query (
|
||||
$cluster: String!
|
||||
$metrics: [String!]
|
||||
$from: Time!
|
||||
$to: Time!
|
||||
) {
|
||||
nodeMetrics(
|
||||
cluster: $cluster
|
||||
metrics: $metrics
|
||||
from: $from
|
||||
to: $to
|
||||
) {
|
||||
host
|
||||
subCluster
|
||||
metrics {
|
||||
name
|
||||
scope
|
||||
metric {
|
||||
timestep
|
||||
unit {
|
||||
base
|
||||
prefix
|
||||
}
|
||||
series {
|
||||
statistics {
|
||||
min
|
||||
avg
|
||||
max
|
||||
}
|
||||
data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
cluster: cluster,
|
||||
metrics: [selectedMetric],
|
||||
from: from.toISOString(),
|
||||
to: to.toISOString(),
|
||||
},
|
||||
});
|
||||
|
||||
let metricUnits = {};
|
||||
$: if ($nodesQuery.data) {
|
||||
let thisCluster = clusters.find((c) => c.name == cluster);
|
||||
if (thisCluster) {
|
||||
for (let metric of thisCluster.metricConfig) {
|
||||
if (metric.unit.prefix || metric.unit.base) {
|
||||
metricUnits[metric.name] =
|
||||
"(" +
|
||||
(metric.unit.prefix ? metric.unit.prefix : "") +
|
||||
(metric.unit.base ? metric.unit.base : "") +
|
||||
")";
|
||||
} else {
|
||||
// If no unit defined: Omit Unit Display
|
||||
metricUnits[metric.name] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let notifications = [
|
||||
{
|
||||
type: "success",
|
||||
message: "This is a success notification!",
|
||||
},
|
||||
{
|
||||
type: "error",
|
||||
message: "An error occurred.",
|
||||
},
|
||||
{
|
||||
type: "info",
|
||||
message: "Just a friendly reminder.",
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<Row>
|
||||
{#if $initq.error}
|
||||
<Card body color="danger">{$initq.error.message}</Card>
|
||||
{:else if $initq.fetching}
|
||||
<Spinner />
|
||||
{:else}
|
||||
<Col>
|
||||
<Refresher
|
||||
on:reload={() => {
|
||||
const diff = Date.now() - to;
|
||||
from = new Date(from.getTime() + diff);
|
||||
to = new Date(to.getTime() + diff);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col>
|
||||
<TimeSelection bind:from bind:to />
|
||||
</Col>
|
||||
<Col>
|
||||
<InputGroup>
|
||||
<InputGroupText><Icon name="graph-up" /></InputGroupText>
|
||||
<InputGroupText>Metric</InputGroupText>
|
||||
<select class="form-select" bind:value={selectedMetric}>
|
||||
{#each clusters.find((c) => c.name == cluster).metricConfig as metric}
|
||||
<option value={metric.name}
|
||||
>{metric.name} {metricUnits[metric.name]}</option
|
||||
>
|
||||
{/each}
|
||||
</select>
|
||||
</InputGroup>
|
||||
</Col>
|
||||
<Col>
|
||||
<InputGroup>
|
||||
<InputGroupText><Icon name="hdd" /></InputGroupText>
|
||||
<InputGroupText>Find Node</InputGroupText>
|
||||
<Input
|
||||
placeholder="hostname..."
|
||||
type="text"
|
||||
bind:value={hostnameFilter}
|
||||
/>
|
||||
</InputGroup>
|
||||
</Col>
|
||||
{/if}
|
||||
</Row>
|
||||
<br />
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Storage Partition Graph</CardTitle>
|
||||
</CardHeader>
|
||||
<CardBody class="h5">
|
||||
<CardSubtitle>Realtime storage partition comes right here</CardSubtitle>
|
||||
<CardText></CardText>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<br />
|
||||
<!-- notification -->
|
||||
<!-- <Accordion open={0}>
|
||||
{#each notifications as notification, i}
|
||||
<AccordionItem key={i}>
|
||||
<div
|
||||
class="d-flex justify-content-between align-items-center"
|
||||
role="button"
|
||||
tabindex={i}
|
||||
>
|
||||
<span class="me-2">
|
||||
<i class={`bi bi-circle-fill text-${notification.type}`}
|
||||
></i>
|
||||
{notification.type.toUpperCase()}
|
||||
</span>
|
||||
<i class="bi bi-chevron-down" />
|
||||
</div>
|
||||
<div class="collapse show">
|
||||
{notification.message}
|
||||
</div>
|
||||
</AccordionItem>
|
||||
{/each}
|
||||
</Accordion> -->
|
||||
<!-- <br /> -->
|
||||
|
||||
<Card class="mb-1">
|
||||
<CardHeader>
|
||||
<CardTitle>Partition Configuration</CardTitle>
|
||||
</CardHeader>
|
||||
<CardBody class="h5">
|
||||
<CardSubtitle>Create and manage LVM partitions</CardSubtitle>
|
||||
<CardText></CardText>
|
||||
<PartitionSetting />
|
||||
</CardBody>
|
||||
</Card>
|
||||
<br />
|
||||
@@ -21,6 +21,7 @@
|
||||
}
|
||||
|
||||
const clusters = getContext('clusters')
|
||||
console.log(clusters)
|
||||
const ccconfig = getContext('cc-config')
|
||||
const metricConfig = getContext('metrics')
|
||||
|
||||
|
||||
45
web/frontend/src/partition/Content/Dashboard.svelte
Normal file
45
web/frontend/src/partition/Content/Dashboard.svelte
Normal file
@@ -0,0 +1,45 @@
|
||||
<!-- Dashboard.svelte -->
|
||||
<script>
|
||||
let data = {
|
||||
users: 100,
|
||||
revenue: "$10,000",
|
||||
newUsers: 20,
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<h2>Dashboard</h2>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Total Users</h5>
|
||||
<p class="card-text">{data.users}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Total Revenue</h5>
|
||||
<p class="card-text">{data.revenue}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">New Users Today</h5>
|
||||
<p class="card-text">{data.newUsers}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
</style>
|
||||
32
web/frontend/src/partition/PartitionSetting.svelte
Normal file
32
web/frontend/src/partition/PartitionSetting.svelte
Normal file
@@ -0,0 +1,32 @@
|
||||
<script>
|
||||
import SideNav from "./SideNav.svelte";
|
||||
import Dashboard from "./Content/Dashboard.svelte";
|
||||
let activeContent = "Dashboard";
|
||||
|
||||
function handleContentChange(event) {
|
||||
activeContent = event.detail;
|
||||
}
|
||||
</script>
|
||||
|
||||
<SideNav on:change={handleContentChange} />
|
||||
|
||||
<div>
|
||||
{#if activeContent === "Dashboard"}
|
||||
<Dashboard />
|
||||
{:else if activeContent === "Create LV"}
|
||||
<!-- <CreateLV /> -->
|
||||
<Dashboard />
|
||||
|
||||
{:else if activeContent === "Monitor"}
|
||||
<!-- <Monitor /> -->
|
||||
<Dashboard />
|
||||
|
||||
{:else if activeContent === "Auto Storage"}
|
||||
<!-- <AutoStorage /> -->
|
||||
<Dashboard />
|
||||
|
||||
{:else if activeContent === "Settings"}
|
||||
<!-- <Settings /> -->
|
||||
<Dashboard />
|
||||
{/if}
|
||||
</div>
|
||||
49
web/frontend/src/partition/SideNav.svelte
Normal file
49
web/frontend/src/partition/SideNav.svelte
Normal file
@@ -0,0 +1,49 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { Nav, NavItem } from "sveltestrap";
|
||||
|
||||
let activeItem = "Dashboard";
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function handleItemClick(item) {
|
||||
activeItem = item;
|
||||
dispatch("change", activeItem);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
dispatch("change", activeItem);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="d-flex flex-column p-3">
|
||||
<Nav vertical>
|
||||
<NavItem on:click={() => handleItemClick("Dashboard")}>
|
||||
<a href="google.com">Dashboard</a>
|
||||
</NavItem>
|
||||
<NavItem on:click={() => handleItemClick("Create LV")}>
|
||||
<a href="google.com">Create LV</a>
|
||||
</NavItem>
|
||||
<NavItem on:click={() => handleItemClick("Monitor")}>
|
||||
<a href="google.com">Monitor</a>
|
||||
</NavItem>
|
||||
<NavItem on:click={() => handleItemClick("Auto Storage")}>
|
||||
<a href="google.com">Auto Storage</a>
|
||||
</NavItem>
|
||||
<NavItem on:click={() => handleItemClick("Settings")}>
|
||||
<a href="google.com">Settings</a>
|
||||
</NavItem>
|
||||
</Nav>
|
||||
</div>
|
||||
|
||||
{#if activeItem === "Dashboard"}
|
||||
<div>Dashboard Content</div>
|
||||
{:else if activeItem === "Create LV"}
|
||||
<div>Create LV Content</div>
|
||||
{:else if activeItem === "Monitor"}
|
||||
<div>Monitor Content</div>
|
||||
{:else if activeItem === "Auto Storage"}
|
||||
<div>Auto Storage Content</div>
|
||||
{:else if activeItem === "Settings"}
|
||||
<div>Settings Content</div>
|
||||
{/if}
|
||||
14
web/frontend/src/partitions.entrypoint.js
Normal file
14
web/frontend/src/partitions.entrypoint.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import {} from './header.entrypoint.js'
|
||||
import Partitions from './Partitions.root.svelte'
|
||||
|
||||
new Partitions({
|
||||
target: document.getElementById('svelte-app'),
|
||||
props: {
|
||||
cluster: infos.cluster,
|
||||
from: infos.from,
|
||||
to: infos.to
|
||||
},
|
||||
context: new Map([
|
||||
['cc-config', clusterCockpitConfig]
|
||||
])
|
||||
})
|
||||
Reference in New Issue
Block a user