mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-12 06:24:10 +01:00
console: ability to display L2 bps in addition to L3 bps
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="inline-flex items-center gap-2">
|
||||
<div class="inline-flex items-center gap-1">
|
||||
<span class="text-sm">{{ label }}</span>
|
||||
<div
|
||||
class="inline-flex rounded-md shadow-sm dark:shadow-white/10"
|
||||
@@ -9,7 +9,7 @@
|
||||
v-for="({ name, label: blabel }, idx) in choices"
|
||||
:key="name"
|
||||
:for="id(name)"
|
||||
class="cursor-pointer first:rounded-l-lg last:rounded-r-lg focus-within:z-10 focus-within:ring-2 focus-within:ring-blue-300 dark:focus-within:ring-blue-800"
|
||||
class="cursor-pointer first:rounded-l-md last:rounded-r-md focus-within:z-10 focus-within:ring-2 focus-within:ring-blue-300 dark:focus-within:ring-blue-800"
|
||||
>
|
||||
<input
|
||||
:id="id(name)"
|
||||
@@ -23,7 +23,7 @@
|
||||
'rounded-l-md border-l': idx === 0,
|
||||
'rounded-r-md border-r': idx === choices.length - 1,
|
||||
}"
|
||||
class="border-t border-b border-gray-200 bg-white py-0.5 px-2 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 peer-checked:bg-blue-700 peer-checked:bg-blue-700 peer-checked:font-bold peer-checked:text-white peer-checked:hover:bg-blue-800 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600 dark:hover:text-white peer-checked:dark:bg-blue-600 peer-checked:dark:hover:bg-blue-700"
|
||||
class="border-t border-b border-gray-200 bg-white py-0.5 px-1 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 peer-checked:bg-blue-700 peer-checked:bg-blue-700 peer-checked:text-white peer-checked:hover:bg-blue-800 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600 dark:hover:text-white peer-checked:dark:bg-blue-600 peer-checked:dark:hover:bg-blue-700"
|
||||
>
|
||||
{{ blabel }}
|
||||
</div>
|
||||
|
||||
@@ -107,7 +107,7 @@ const table = computed(() => {
|
||||
data.average[idx],
|
||||
data["95th"][idx],
|
||||
].map((d) => ({
|
||||
value: formatXps(d) + data.units,
|
||||
value: formatXps(d) + data.units.slice(-3),
|
||||
classNames: "text-right tabular-nums",
|
||||
})),
|
||||
],
|
||||
@@ -132,7 +132,7 @@ const table = computed(() => {
|
||||
...rows.map((r) => ({ value: r })),
|
||||
// Average
|
||||
{
|
||||
value: formatXps(data.xps[idx]) + data.units,
|
||||
value: formatXps(data.xps[idx]) + data.units.slice(-3),
|
||||
classNames: "text-right tabular-nums",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<aside
|
||||
class="transition-height transition-width w-full shrink-0 duration-100 lg:h-auto"
|
||||
:class="open ? 'h-80 lg:w-64' : 'h-4 lg:w-4'"
|
||||
:class="open ? 'h-80 lg:w-72' : 'h-4 lg:w-4'"
|
||||
>
|
||||
<span
|
||||
class="absolute z-30 translate-x-4 transition-transform lg:translate-y-4"
|
||||
:class="
|
||||
open
|
||||
? 'translate-y-80 rotate-180 lg:translate-x-64'
|
||||
? 'translate-y-80 rotate-180 lg:translate-x-72'
|
||||
: 'translate-y-4 lg:translate-x-0'
|
||||
"
|
||||
>
|
||||
@@ -42,7 +42,8 @@
|
||||
<InputChoice
|
||||
v-model="units"
|
||||
:choices="[
|
||||
{ label: 'ᵇ⁄ₛ', name: 'bps' },
|
||||
{ label: 'L3ᵇ⁄ₛ', name: 'l3bps' },
|
||||
{ label: 'L2ᵇ⁄ₛ', name: 'l2bps' },
|
||||
{ label: 'ᵖ⁄ₛ', name: 'pps' },
|
||||
]"
|
||||
label="Unit"
|
||||
@@ -134,7 +135,7 @@ const graphType = ref(graphTypeList[0]);
|
||||
const timeRange = ref({});
|
||||
const dimensions = ref([]);
|
||||
const filter = ref({});
|
||||
const units = ref("bps");
|
||||
const units = ref("l3bps");
|
||||
|
||||
const options = computed(() => ({
|
||||
// Common to all graph types
|
||||
@@ -168,7 +169,7 @@ watch(
|
||||
limit = 10,
|
||||
points /* eslint-disable-line no-unused-vars */,
|
||||
filter: _filter = "InIfBoundary = external",
|
||||
units: _units = "bps",
|
||||
units: _units = "l3bps",
|
||||
} = modelValue;
|
||||
|
||||
// Dispatch values in refs
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
<span v-if="request.units" class="min-w-[4 shrink-0 py-0.5">
|
||||
<HashtagIcon class="inline h-4 px-1 align-middle" />
|
||||
<span class="align-middle">{{
|
||||
{ bps: "ᵇ⁄ₛ", pps: "ᵖ⁄ₛ" }[request.units] || requests.units
|
||||
{ l3bps: "L3ᵇ⁄ₛ", l2bps: "L2ᵇ⁄ₛ", pps: "ᵖ⁄ₛ" }[request.units] ||
|
||||
requests.units
|
||||
}}</span>
|
||||
</span>
|
||||
<span
|
||||
|
||||
@@ -20,7 +20,7 @@ type graphHandlerInput struct {
|
||||
Dimensions []queryColumn `json:"dimensions"` // group by ...
|
||||
Limit int `json:"limit" binding:"min=1,max=50"` // limit product of dimensions
|
||||
Filter queryFilter `json:"filter"` // where ...
|
||||
Units string `json:"units" binding:"required,oneof=pps bps"`
|
||||
Units string `json:"units" binding:"required,oneof=pps l2bps l3bps"`
|
||||
}
|
||||
|
||||
// graphHandlerOutput describes the output for the /graph endpoint.
|
||||
@@ -50,10 +50,13 @@ func (input graphHandlerInput) toSQL() (string, error) {
|
||||
fields := []string{
|
||||
`toStartOfInterval(TimeReceived, INTERVAL slot second) AS time`,
|
||||
}
|
||||
if input.Units == "pps" {
|
||||
switch input.Units {
|
||||
case "pps":
|
||||
fields = append(fields, `SUM(Packets*SamplingRate/slot) AS xps`)
|
||||
} else {
|
||||
case "l3bps":
|
||||
fields = append(fields, `SUM(Bytes*SamplingRate*8/slot) AS xps`)
|
||||
case "l2bps":
|
||||
fields = append(fields, `SUM((Bytes+18*Packets)*SamplingRate*8/slot) AS xps`)
|
||||
}
|
||||
selectFields := []string{}
|
||||
dimensions := []string{}
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestGraphQuerySQL(t *testing.T) {
|
||||
Points: 100,
|
||||
Dimensions: []queryColumn{},
|
||||
Filter: queryFilter{},
|
||||
Units: "bps",
|
||||
Units: "l3bps",
|
||||
},
|
||||
Expected: `
|
||||
WITH
|
||||
@@ -41,6 +41,27 @@ SELECT
|
||||
FROM {table}
|
||||
WHERE {timefilter}
|
||||
GROUP BY time, dimensions
|
||||
ORDER BY time`,
|
||||
}, {
|
||||
Description: "no dimensions, no filters, l2 bps",
|
||||
Input: graphHandlerInput{
|
||||
Start: time.Date(2022, 04, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 04, 11, 15, 45, 10, 0, time.UTC),
|
||||
Points: 100,
|
||||
Dimensions: []queryColumn{},
|
||||
Filter: queryFilter{},
|
||||
Units: "l2bps",
|
||||
},
|
||||
Expected: `
|
||||
WITH
|
||||
intDiv(864, {resolution})*{resolution} AS slot
|
||||
SELECT
|
||||
toStartOfInterval(TimeReceived, INTERVAL slot second) AS time,
|
||||
SUM((Bytes+18*Packets)*SamplingRate*8/slot) AS xps,
|
||||
emptyArrayString() AS dimensions
|
||||
FROM {table}
|
||||
WHERE {timefilter}
|
||||
GROUP BY time, dimensions
|
||||
ORDER BY time`,
|
||||
}, {
|
||||
Description: "no dimensions, no filters, pps",
|
||||
@@ -71,7 +92,7 @@ ORDER BY time`,
|
||||
Points: 100,
|
||||
Dimensions: []queryColumn{},
|
||||
Filter: queryFilter{"DstCountry = 'FR' AND SrcCountry = 'US'"},
|
||||
Units: "bps",
|
||||
Units: "l3bps",
|
||||
},
|
||||
Expected: `
|
||||
WITH
|
||||
@@ -96,7 +117,7 @@ ORDER BY time`,
|
||||
queryColumnInIfProvider,
|
||||
},
|
||||
Filter: queryFilter{},
|
||||
Units: "bps",
|
||||
Units: "l3bps",
|
||||
},
|
||||
Expected: `
|
||||
WITH
|
||||
@@ -171,7 +192,7 @@ func TestGraphHandler(t *testing.T) {
|
||||
"limit": 20,
|
||||
"dimensions": []string{"ExporterName", "InIfProvider"},
|
||||
"filter": "DstCountry = 'FR' AND SrcCountry = 'US'",
|
||||
"units": "bps",
|
||||
"units": "l3bps",
|
||||
},
|
||||
JSONOutput: gin.H{
|
||||
// Sorted by sum of bps
|
||||
|
||||
@@ -19,7 +19,7 @@ type sankeyHandlerInput struct {
|
||||
Dimensions []queryColumn `json:"dimensions" binding:"required,min=2"` // group by ...
|
||||
Limit int `json:"limit" binding:"min=1,max=50"` // limit product of dimensions
|
||||
Filter queryFilter `json:"filter"` // where ...
|
||||
Units string `json:"units" binding:"required,oneof=pps bps"`
|
||||
Units string `json:"units" binding:"required,oneof=pps l3bps l2bps"`
|
||||
}
|
||||
|
||||
// sankeyHandlerOutput describes the output for the /sankey endpoint.
|
||||
@@ -58,10 +58,13 @@ func (input sankeyHandlerInput) toSQL() (string, error) {
|
||||
dimensions = append(dimensions, column.String())
|
||||
}
|
||||
fields := []string{}
|
||||
if input.Units == "pps" {
|
||||
switch input.Units {
|
||||
case "pps":
|
||||
fields = append(fields, `SUM(Packets*SamplingRate/range) AS xps`)
|
||||
} else {
|
||||
case "l3bps":
|
||||
fields = append(fields, `SUM(Bytes*SamplingRate*8/range) AS xps`)
|
||||
case "l2bps":
|
||||
fields = append(fields, `SUM((Bytes+18*Packets)*SamplingRate*8/range) AS xps`)
|
||||
}
|
||||
fields = append(fields, fmt.Sprintf("[%s] AS dimensions", strings.Join(arrayFields, ",\n ")))
|
||||
|
||||
|
||||
@@ -22,14 +22,14 @@ func TestSankeyQuerySQL(t *testing.T) {
|
||||
Expected string
|
||||
}{
|
||||
{
|
||||
Description: "two dimensions, no filters, bps",
|
||||
Description: "two dimensions, no filters, l3 bps",
|
||||
Input: sankeyHandlerInput{
|
||||
Start: time.Date(2022, 04, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 04, 11, 15, 45, 10, 0, time.UTC),
|
||||
Dimensions: []queryColumn{queryColumnSrcAS, queryColumnExporterName},
|
||||
Limit: 5,
|
||||
Filter: queryFilter{},
|
||||
Units: "bps",
|
||||
Units: "l3bps",
|
||||
},
|
||||
Expected: `
|
||||
WITH
|
||||
@@ -42,6 +42,28 @@ SELECT
|
||||
FROM {table}
|
||||
WHERE {timefilter}
|
||||
GROUP BY dimensions
|
||||
ORDER BY xps DESC`,
|
||||
}, {
|
||||
Description: "two dimensions, no filters, l2 bps",
|
||||
Input: sankeyHandlerInput{
|
||||
Start: time.Date(2022, 04, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 04, 11, 15, 45, 10, 0, time.UTC),
|
||||
Dimensions: []queryColumn{queryColumnSrcAS, queryColumnExporterName},
|
||||
Limit: 5,
|
||||
Filter: queryFilter{},
|
||||
Units: "l2bps",
|
||||
},
|
||||
Expected: `
|
||||
WITH
|
||||
(SELECT MAX(TimeReceived) - MIN(TimeReceived) FROM {table} WHERE {timefilter}) AS range,
|
||||
rows AS (SELECT SrcAS, ExporterName FROM {table} WHERE {timefilter} GROUP BY SrcAS, ExporterName ORDER BY SUM(Bytes) DESC LIMIT 5)
|
||||
SELECT
|
||||
SUM((Bytes+18*Packets)*SamplingRate*8/range) AS xps,
|
||||
[if(SrcAS IN (SELECT SrcAS FROM rows), concat(toString(SrcAS), ': ', dictGetOrDefault('asns', 'name', SrcAS, '???')), 'Other'),
|
||||
if(ExporterName IN (SELECT ExporterName FROM rows), ExporterName, 'Other')] AS dimensions
|
||||
FROM {table}
|
||||
WHERE {timefilter}
|
||||
GROUP BY dimensions
|
||||
ORDER BY xps DESC`,
|
||||
}, {
|
||||
Description: "two dimensions, no filters, pps",
|
||||
@@ -73,7 +95,7 @@ ORDER BY xps DESC`,
|
||||
Dimensions: []queryColumn{queryColumnSrcAS, queryColumnExporterName},
|
||||
Limit: 10,
|
||||
Filter: queryFilter{"DstCountry = 'FR'"},
|
||||
Units: "bps",
|
||||
Units: "l3bps",
|
||||
},
|
||||
Expected: `
|
||||
WITH
|
||||
@@ -157,7 +179,7 @@ func TestSankeyHandler(t *testing.T) {
|
||||
"dimensions": []string{"SrcAS", "InIfProvider", "ExporterName"},
|
||||
"limit": 10,
|
||||
"filter": "DstCountry = 'FR'",
|
||||
"units": "bps",
|
||||
"units": "l3bps",
|
||||
},
|
||||
JSONOutput: gin.H{
|
||||
// Raw data
|
||||
|
||||
Reference in New Issue
Block a user