console: ability to display L2 bps in addition to L3 bps

This commit is contained in:
Vincent Bernat
2022-06-03 11:43:20 +02:00
parent beea3ff2be
commit a1ff21eb09
8 changed files with 76 additions and 25 deletions

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="inline-flex items-center gap-2"> <div class="inline-flex items-center gap-1">
<span class="text-sm">{{ label }}</span> <span class="text-sm">{{ label }}</span>
<div <div
class="inline-flex rounded-md shadow-sm dark:shadow-white/10" class="inline-flex rounded-md shadow-sm dark:shadow-white/10"
@@ -9,7 +9,7 @@
v-for="({ name, label: blabel }, idx) in choices" v-for="({ name, label: blabel }, idx) in choices"
:key="name" :key="name"
:for="id(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 <input
:id="id(name)" :id="id(name)"
@@ -23,7 +23,7 @@
'rounded-l-md border-l': idx === 0, 'rounded-l-md border-l': idx === 0,
'rounded-r-md border-r': idx === choices.length - 1, '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 }} {{ blabel }}
</div> </div>

View File

@@ -107,7 +107,7 @@ const table = computed(() => {
data.average[idx], data.average[idx],
data["95th"][idx], data["95th"][idx],
].map((d) => ({ ].map((d) => ({
value: formatXps(d) + data.units, value: formatXps(d) + data.units.slice(-3),
classNames: "text-right tabular-nums", classNames: "text-right tabular-nums",
})), })),
], ],
@@ -132,7 +132,7 @@ const table = computed(() => {
...rows.map((r) => ({ value: r })), ...rows.map((r) => ({ value: r })),
// Average // Average
{ {
value: formatXps(data.xps[idx]) + data.units, value: formatXps(data.xps[idx]) + data.units.slice(-3),
classNames: "text-right tabular-nums", classNames: "text-right tabular-nums",
}, },
], ],

View File

@@ -1,13 +1,13 @@
<template> <template>
<aside <aside
class="transition-height transition-width w-full shrink-0 duration-100 lg:h-auto" 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 <span
class="absolute z-30 translate-x-4 transition-transform lg:translate-y-4" class="absolute z-30 translate-x-4 transition-transform lg:translate-y-4"
:class=" :class="
open 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' : 'translate-y-4 lg:translate-x-0'
" "
> >
@@ -42,7 +42,8 @@
<InputChoice <InputChoice
v-model="units" v-model="units"
:choices="[ :choices="[
{ label: 'ᵇ⁄ₛ', name: 'bps' }, { label: 'L3ᵇ⁄ₛ', name: 'l3bps' },
{ label: 'L2ᵇₛ', name: 'l2bps' },
{ label: 'ᵖ⁄ₛ', name: 'pps' }, { label: 'ᵖ⁄ₛ', name: 'pps' },
]" ]"
label="Unit" label="Unit"
@@ -134,7 +135,7 @@ const graphType = ref(graphTypeList[0]);
const timeRange = ref({}); const timeRange = ref({});
const dimensions = ref([]); const dimensions = ref([]);
const filter = ref({}); const filter = ref({});
const units = ref("bps"); const units = ref("l3bps");
const options = computed(() => ({ const options = computed(() => ({
// Common to all graph types // Common to all graph types
@@ -168,7 +169,7 @@ watch(
limit = 10, limit = 10,
points /* eslint-disable-line no-unused-vars */, points /* eslint-disable-line no-unused-vars */,
filter: _filter = "InIfBoundary = external", filter: _filter = "InIfBoundary = external",
units: _units = "bps", units: _units = "l3bps",
} = modelValue; } = modelValue;
// Dispatch values in refs // Dispatch values in refs

View File

@@ -17,7 +17,8 @@
<span v-if="request.units" class="min-w-[4 shrink-0 py-0.5"> <span v-if="request.units" class="min-w-[4 shrink-0 py-0.5">
<HashtagIcon class="inline h-4 px-1 align-middle" /> <HashtagIcon class="inline h-4 px-1 align-middle" />
<span class="align-middle">{{ <span class="align-middle">{{
{ bps: "ᵇ⁄ₛ", pps: "ᵖ⁄ₛ" }[request.units] || requests.units { l3bps: "L3ᵇₛ", l2bps: "L2ᵇ⁄ₛ", pps: "ᵖ⁄ₛ" }[request.units] ||
requests.units
}}</span> }}</span>
</span> </span>
<span <span

View File

@@ -20,7 +20,7 @@ type graphHandlerInput struct {
Dimensions []queryColumn `json:"dimensions"` // group by ... Dimensions []queryColumn `json:"dimensions"` // group by ...
Limit int `json:"limit" binding:"min=1,max=50"` // limit product of dimensions Limit int `json:"limit" binding:"min=1,max=50"` // limit product of dimensions
Filter queryFilter `json:"filter"` // where ... 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. // graphHandlerOutput describes the output for the /graph endpoint.
@@ -50,10 +50,13 @@ func (input graphHandlerInput) toSQL() (string, error) {
fields := []string{ fields := []string{
`toStartOfInterval(TimeReceived, INTERVAL slot second) AS time`, `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`) fields = append(fields, `SUM(Packets*SamplingRate/slot) AS xps`)
} else { case "l3bps":
fields = append(fields, `SUM(Bytes*SamplingRate*8/slot) AS xps`) 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{} selectFields := []string{}
dimensions := []string{} dimensions := []string{}

View File

@@ -29,7 +29,7 @@ func TestGraphQuerySQL(t *testing.T) {
Points: 100, Points: 100,
Dimensions: []queryColumn{}, Dimensions: []queryColumn{},
Filter: queryFilter{}, Filter: queryFilter{},
Units: "bps", Units: "l3bps",
}, },
Expected: ` Expected: `
WITH WITH
@@ -41,6 +41,27 @@ SELECT
FROM {table} FROM {table}
WHERE {timefilter} WHERE {timefilter}
GROUP BY time, dimensions 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`, ORDER BY time`,
}, { }, {
Description: "no dimensions, no filters, pps", Description: "no dimensions, no filters, pps",
@@ -71,7 +92,7 @@ ORDER BY time`,
Points: 100, Points: 100,
Dimensions: []queryColumn{}, Dimensions: []queryColumn{},
Filter: queryFilter{"DstCountry = 'FR' AND SrcCountry = 'US'"}, Filter: queryFilter{"DstCountry = 'FR' AND SrcCountry = 'US'"},
Units: "bps", Units: "l3bps",
}, },
Expected: ` Expected: `
WITH WITH
@@ -96,7 +117,7 @@ ORDER BY time`,
queryColumnInIfProvider, queryColumnInIfProvider,
}, },
Filter: queryFilter{}, Filter: queryFilter{},
Units: "bps", Units: "l3bps",
}, },
Expected: ` Expected: `
WITH WITH
@@ -171,7 +192,7 @@ func TestGraphHandler(t *testing.T) {
"limit": 20, "limit": 20,
"dimensions": []string{"ExporterName", "InIfProvider"}, "dimensions": []string{"ExporterName", "InIfProvider"},
"filter": "DstCountry = 'FR' AND SrcCountry = 'US'", "filter": "DstCountry = 'FR' AND SrcCountry = 'US'",
"units": "bps", "units": "l3bps",
}, },
JSONOutput: gin.H{ JSONOutput: gin.H{
// Sorted by sum of bps // Sorted by sum of bps

View File

@@ -19,7 +19,7 @@ type sankeyHandlerInput struct {
Dimensions []queryColumn `json:"dimensions" binding:"required,min=2"` // group by ... Dimensions []queryColumn `json:"dimensions" binding:"required,min=2"` // group by ...
Limit int `json:"limit" binding:"min=1,max=50"` // limit product of dimensions Limit int `json:"limit" binding:"min=1,max=50"` // limit product of dimensions
Filter queryFilter `json:"filter"` // where ... 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. // sankeyHandlerOutput describes the output for the /sankey endpoint.
@@ -58,10 +58,13 @@ func (input sankeyHandlerInput) toSQL() (string, error) {
dimensions = append(dimensions, column.String()) dimensions = append(dimensions, column.String())
} }
fields := []string{} fields := []string{}
if input.Units == "pps" { switch input.Units {
case "pps":
fields = append(fields, `SUM(Packets*SamplingRate/range) AS xps`) fields = append(fields, `SUM(Packets*SamplingRate/range) AS xps`)
} else { case "l3bps":
fields = append(fields, `SUM(Bytes*SamplingRate*8/range) AS xps`) 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 "))) fields = append(fields, fmt.Sprintf("[%s] AS dimensions", strings.Join(arrayFields, ",\n ")))

View File

@@ -22,14 +22,14 @@ func TestSankeyQuerySQL(t *testing.T) {
Expected string Expected string
}{ }{
{ {
Description: "two dimensions, no filters, bps", Description: "two dimensions, no filters, l3 bps",
Input: sankeyHandlerInput{ Input: sankeyHandlerInput{
Start: time.Date(2022, 04, 10, 15, 45, 10, 0, time.UTC), Start: time.Date(2022, 04, 10, 15, 45, 10, 0, time.UTC),
End: time.Date(2022, 04, 11, 15, 45, 10, 0, time.UTC), End: time.Date(2022, 04, 11, 15, 45, 10, 0, time.UTC),
Dimensions: []queryColumn{queryColumnSrcAS, queryColumnExporterName}, Dimensions: []queryColumn{queryColumnSrcAS, queryColumnExporterName},
Limit: 5, Limit: 5,
Filter: queryFilter{}, Filter: queryFilter{},
Units: "bps", Units: "l3bps",
}, },
Expected: ` Expected: `
WITH WITH
@@ -42,6 +42,28 @@ SELECT
FROM {table} FROM {table}
WHERE {timefilter} WHERE {timefilter}
GROUP BY dimensions 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`, ORDER BY xps DESC`,
}, { }, {
Description: "two dimensions, no filters, pps", Description: "two dimensions, no filters, pps",
@@ -73,7 +95,7 @@ ORDER BY xps DESC`,
Dimensions: []queryColumn{queryColumnSrcAS, queryColumnExporterName}, Dimensions: []queryColumn{queryColumnSrcAS, queryColumnExporterName},
Limit: 10, Limit: 10,
Filter: queryFilter{"DstCountry = 'FR'"}, Filter: queryFilter{"DstCountry = 'FR'"},
Units: "bps", Units: "l3bps",
}, },
Expected: ` Expected: `
WITH WITH
@@ -157,7 +179,7 @@ func TestSankeyHandler(t *testing.T) {
"dimensions": []string{"SrcAS", "InIfProvider", "ExporterName"}, "dimensions": []string{"SrcAS", "InIfProvider", "ExporterName"},
"limit": 10, "limit": 10,
"filter": "DstCountry = 'FR'", "filter": "DstCountry = 'FR'",
"units": "bps", "units": "l3bps",
}, },
JSONOutput: gin.H{ JSONOutput: gin.H{
// Raw data // Raw data