mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-04-19 18:21:42 +02:00
feat: add nodename matcher select to filter, defaults to equal match
- see PR !353
This commit is contained in:
parent
b3a1037ade
commit
d770292be8
@ -194,10 +194,12 @@ func (r *JobRepository) FindConcurrentJobs(
|
||||
|
||||
queryRunning := query.Where("job.job_state = ?").Where("(job.start_time BETWEEN ? AND ? OR job.start_time < ?)",
|
||||
"running", startTimeTail, stopTimeTail, startTime)
|
||||
// Get At Least One Exact Hostname Match from JSON Resources Array in Database
|
||||
queryRunning = queryRunning.Where("EXISTS (SELECT 1 FROM json_each(job.resources) WHERE json_extract(value, '$.hostname') = ?)", hostname)
|
||||
|
||||
query = query.Where("job.job_state != ?").Where("((job.start_time BETWEEN ? AND ?) OR (job.start_time + job.duration) BETWEEN ? AND ? OR (job.start_time < ?) AND (job.start_time + job.duration) > ?)",
|
||||
"running", startTimeTail, stopTimeTail, startTimeFront, stopTimeTail, startTime, stopTime)
|
||||
// Get At Least One Exact Hostname Match from JSON Resources Array in Database
|
||||
query = query.Where("EXISTS (SELECT 1 FROM json_each(job.resources) WHERE json_extract(value, '$.hostname') = ?)", hostname)
|
||||
|
||||
rows, err := query.RunWith(r.stmtCache).Query()
|
||||
|
@ -67,7 +67,8 @@ func (r *JobRepository) QueryJobs(
|
||||
|
||||
rows, err := query.RunWith(r.stmtCache).Query()
|
||||
if err != nil {
|
||||
log.Errorf("Error while running query: %v", err)
|
||||
queryString, queryVars, _ := query.ToSql()
|
||||
log.Errorf("Error while running query '%s' %v: %v", queryString, queryVars, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -197,14 +198,7 @@ func BuildWhereClause(filter *model.JobFilter, query sq.SelectBuilder) sq.Select
|
||||
query = buildIntCondition("job.num_hwthreads", filter.NumHWThreads, query)
|
||||
}
|
||||
if filter.Node != nil {
|
||||
log.Infof("Applying node filter: %v", filter.Node)
|
||||
if filter.Node.Eq != nil {
|
||||
query = query.Where("EXISTS (SELECT 1 FROM json_each(job.resources) WHERE json_extract(value, '$.hostname') = ?)", *filter.Node.Eq)
|
||||
} else if filter.Node.Contains != nil {
|
||||
query = query.Where("EXISTS (SELECT 1 FROM json_each(job.resources) WHERE json_extract(value, '$.hostname') LIKE ?)", "%"+*filter.Node.Contains+"%")
|
||||
} else {
|
||||
query = buildStringCondition("job.resources", filter.Node, query)
|
||||
}
|
||||
query = buildResourceJsonCondition("hostname", filter.Node, query)
|
||||
}
|
||||
if filter.Energy != nil {
|
||||
query = buildFloatCondition("job.energy", filter.Energy, query)
|
||||
@ -306,6 +300,28 @@ func buildMetaJsonCondition(jsonField string, cond *model.StringInput, query sq.
|
||||
return query
|
||||
}
|
||||
|
||||
func buildResourceJsonCondition(jsonField string, cond *model.StringInput, query sq.SelectBuilder) sq.SelectBuilder {
|
||||
// Verify and Search Only in Valid Jsons
|
||||
query = query.Where("JSON_VALID(resources)")
|
||||
// add "AND" Sql query Block for field match
|
||||
if cond.Eq != nil {
|
||||
return query.Where("EXISTS (SELECT 1 FROM json_each(job.resources) WHERE json_extract(value, \"$."+jsonField+"\") = ?)", *cond.Eq)
|
||||
}
|
||||
if cond.Neq != nil { // Currently Unused
|
||||
return query.Where("EXISTS (SELECT 1 FROM json_each(job.resources) WHERE json_extract(value, \"$."+jsonField+"\") != ?)", *cond.Neq)
|
||||
}
|
||||
if cond.StartsWith != nil { // Currently Unused
|
||||
return query.Where("EXISTS (SELECT 1 FROM json_each(job.resources) WHERE json_extract(value, \"$."+jsonField+"\")) LIKE ?)", fmt.Sprint(*cond.StartsWith, "%"))
|
||||
}
|
||||
if cond.EndsWith != nil { // Currently Unused
|
||||
return query.Where("EXISTS (SELECT 1 FROM json_each(job.resources) WHERE json_extract(value, \"$."+jsonField+"\") LIKE ?)", fmt.Sprint("%", *cond.EndsWith))
|
||||
}
|
||||
if cond.Contains != nil {
|
||||
return query.Where("EXISTS (SELECT 1 FROM json_each(job.resources) WHERE json_extract(value, \"$."+jsonField+"\") LIKE ?)", fmt.Sprint("%", *cond.Contains, "%"))
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
var (
|
||||
matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
|
||||
matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
|
||||
|
@ -53,10 +53,16 @@
|
||||
{ range: "last30d", rangeLabel: "Last 30 days"}
|
||||
];
|
||||
|
||||
const nodeMatchLabels = {
|
||||
eq: "",
|
||||
contains: " Contains",
|
||||
}
|
||||
|
||||
let filters = {
|
||||
projectMatch: filterPresets.projectMatch || "contains",
|
||||
userMatch: filterPresets.userMatch || "contains",
|
||||
jobIdMatch: filterPresets.jobIdMatch || "eq",
|
||||
nodeMatch: filterPresets.nodeMatch || "eq",
|
||||
|
||||
cluster: filterPresets.cluster || null,
|
||||
partition: filterPresets.partition || null,
|
||||
@ -106,7 +112,7 @@
|
||||
|
||||
let items = [];
|
||||
if (filters.cluster) items.push({ cluster: { eq: filters.cluster } });
|
||||
if (filters.node) items.push({ node: { contains: filters.node } });
|
||||
if (filters.node) items.push({ node: { [filters.nodeMatch]: filters.node } });
|
||||
if (filters.partition) items.push({ partition: { eq: filters.partition } });
|
||||
if (filters.states.length != allJobStates.length)
|
||||
items.push({ state: filters.states });
|
||||
@ -178,6 +184,8 @@
|
||||
let opts = [];
|
||||
if (filters.cluster) opts.push(`cluster=${filters.cluster}`);
|
||||
if (filters.node) opts.push(`node=${filters.node}`);
|
||||
if (filters.node && filters.nodeMatch != "eq") // "eq" is default-case
|
||||
opts.push(`nodeMatch=${filters.nodeMatch}`);
|
||||
if (filters.partition) opts.push(`partition=${filters.partition}`);
|
||||
if (filters.states.length != allJobStates.length)
|
||||
for (let state of filters.states) opts.push(`state=${state}`);
|
||||
@ -196,7 +204,7 @@
|
||||
opts.push(`jobId=${singleJobId}`);
|
||||
}
|
||||
if (filters.jobIdMatch != "eq")
|
||||
opts.push(`jobIdMatch=${filters.jobIdMatch}`);
|
||||
opts.push(`jobIdMatch=${filters.jobIdMatch}`); // "eq" is default-case
|
||||
for (let tag of filters.tags) opts.push(`tag=${tag}`);
|
||||
if (filters.duration.from && filters.duration.to)
|
||||
opts.push(`duration=${filters.duration.from}-${filters.duration.to}`);
|
||||
@ -218,13 +226,13 @@
|
||||
} else {
|
||||
for (let singleUser of filters.user) opts.push(`user=${singleUser}`);
|
||||
}
|
||||
if (filters.userMatch != "contains")
|
||||
if (filters.userMatch != "contains") // "contains" is default-case
|
||||
opts.push(`userMatch=${filters.userMatch}`);
|
||||
if (filters.project) opts.push(`project=${filters.project}`);
|
||||
if (filters.project && filters.projectMatch != "contains") // "contains" is default-case
|
||||
opts.push(`projectMatch=${filters.projectMatch}`);
|
||||
if (filters.jobName) opts.push(`jobName=${filters.jobName}`);
|
||||
if (filters.arrayJobId) opts.push(`arrayJobId=${filters.arrayJobId}`);
|
||||
if (filters.project && filters.projectMatch != "contains")
|
||||
opts.push(`projectMatch=${filters.projectMatch}`);
|
||||
if (filters.stats.length != 0)
|
||||
for (let stat of filters.stats) {
|
||||
opts.push(`stat=${stat.field}-${stat.from}-${stat.to}`);
|
||||
@ -386,7 +394,7 @@
|
||||
|
||||
{#if filters.node != null}
|
||||
<Info icon="hdd-stack" on:click={() => (isResourcesOpen = true)}>
|
||||
Node: {filters.node}
|
||||
Node{nodeMatchLabels[filters.nodeMatch]}: {filters.node}
|
||||
</Info>
|
||||
{/if}
|
||||
|
||||
@ -449,6 +457,7 @@
|
||||
bind:numHWThreads={filters.numHWThreads}
|
||||
bind:numAccelerators={filters.numAccelerators}
|
||||
bind:namedNode={filters.node}
|
||||
bind:nodeMatch={filters.nodeMatch}
|
||||
bind:isNodesModified
|
||||
bind:isHwthreadsModified
|
||||
bind:isAccsModified
|
||||
|
@ -24,6 +24,7 @@
|
||||
ModalBody,
|
||||
ModalHeader,
|
||||
ModalFooter,
|
||||
Input
|
||||
} from "@sveltestrap/sveltestrap";
|
||||
import DoubleRangeSlider from "../select/DoubleRangeSlider.svelte";
|
||||
|
||||
@ -40,11 +41,18 @@
|
||||
export let isHwthreadsModified = false;
|
||||
export let isAccsModified = false;
|
||||
export let namedNode = null;
|
||||
export let nodeMatch = "eq"
|
||||
|
||||
let pendingNumNodes = numNodes,
|
||||
pendingNumHWThreads = numHWThreads,
|
||||
pendingNumAccelerators = numAccelerators,
|
||||
pendingNamedNode = namedNode;
|
||||
pendingNamedNode = namedNode,
|
||||
pendingNodeMatch = nodeMatch;
|
||||
|
||||
const nodeMatchLabels = {
|
||||
eq: "Equal To",
|
||||
contains: "Contains",
|
||||
}
|
||||
|
||||
const findMaxNumAccels = (clusters) =>
|
||||
clusters.reduce(
|
||||
@ -145,7 +153,17 @@
|
||||
<ModalHeader>Select number of utilized Resources</ModalHeader>
|
||||
<ModalBody>
|
||||
<h6>Named Node</h6>
|
||||
<input type="text" class="form-control" bind:value={pendingNamedNode} />
|
||||
<div class="d-flex">
|
||||
<Input type="text" class="w-75" bind:value={pendingNamedNode} />
|
||||
<div class="mx-1"></div>
|
||||
<Input type="select" class="w-25" bind:value={pendingNodeMatch}>
|
||||
{#each Object.entries(nodeMatchLabels) as [nodeMatchKey, nodeMatchLabel]}
|
||||
<option value={nodeMatchKey}>
|
||||
{nodeMatchLabel}
|
||||
</option>
|
||||
{/each}
|
||||
</Input>
|
||||
</div>
|
||||
<h6 style="margin-top: 1rem;">Number of Nodes</h6>
|
||||
<DoubleRangeSlider
|
||||
on:change={({ detail }) => {
|
||||
@ -215,11 +233,13 @@
|
||||
to: pendingNumAccelerators.to,
|
||||
};
|
||||
namedNode = pendingNamedNode;
|
||||
nodeMatch = pendingNodeMatch;
|
||||
dispatch("set-filter", {
|
||||
numNodes,
|
||||
numHWThreads,
|
||||
numAccelerators,
|
||||
namedNode,
|
||||
nodeMatch
|
||||
});
|
||||
}}
|
||||
>
|
||||
@ -233,6 +253,7 @@
|
||||
pendingNumHWThreads = { from: null, to: null };
|
||||
pendingNumAccelerators = { from: null, to: null };
|
||||
pendingNamedNode = null;
|
||||
pendingNodeMatch = null;
|
||||
numNodes = { from: pendingNumNodes.from, to: pendingNumNodes.to };
|
||||
numHWThreads = {
|
||||
from: pendingNumHWThreads.from,
|
||||
@ -246,11 +267,13 @@
|
||||
isHwthreadsModified = false;
|
||||
isAccsModified = false;
|
||||
namedNode = pendingNamedNode;
|
||||
nodeMatch = pendingNodeMatch;
|
||||
dispatch("set-filter", {
|
||||
numNodes,
|
||||
numHWThreads,
|
||||
numAccelerators,
|
||||
namedNode,
|
||||
nodeMatch
|
||||
});
|
||||
}}>Reset</Button
|
||||
>
|
||||
|
Loading…
x
Reference in New Issue
Block a user