mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-11 22:14:02 +01:00
console/filter: add completion endpoint
This commit is contained in:
@@ -85,7 +85,7 @@ func TestHTTPEndpoints(t *testing.T, serverAddr net.Addr, cases HTTPEndpointCase
|
||||
tc.StatusCode = 200
|
||||
}
|
||||
if resp.StatusCode != tc.StatusCode {
|
||||
t.Fatalf("GET %s: got status code %d, not %d", tc.URL,
|
||||
t.Errorf("GET %s: got status code %d, not %d", tc.URL,
|
||||
resp.StatusCode, tc.StatusCode)
|
||||
}
|
||||
if tc.JSONOutput != nil {
|
||||
|
||||
@@ -132,8 +132,8 @@ guaranteed, so an URL may stop working after a few upgrades.
|
||||
|
||||
The filter language looks like SQL with a few variations. Fields
|
||||
listed as dimensions can usually be used. Accepted operators are `=`,
|
||||
`!=`, `<`, `<=`, `>`, `>=`, `IN`, `LIKE`, `ILIKE`, when they make
|
||||
sense. Here are a few examples:
|
||||
`!=`, `<`, `<=`, `>`, `>=`, `IN`, `NOTIN`, `LIKE`, `UNLIKE`, `ILIKE`,
|
||||
`IUNLIKE`, when they make sense. Here are a few examples:
|
||||
|
||||
- `InIfBoundary = external` only selects flows whose incoming
|
||||
interface was classified as external. The value should not be
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package console
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
@@ -11,7 +13,7 @@ import (
|
||||
|
||||
// filterValidateHandlerInput describes the input for the /filter/validate endpoint.
|
||||
type filterValidateHandlerInput struct {
|
||||
Filter string `json:"filter"`
|
||||
Filter string `json:"filter" binding:"required"`
|
||||
}
|
||||
|
||||
// filterValidateHandlerOutput describes the output for the /filter/validate endpoint.
|
||||
@@ -41,3 +43,185 @@ func (c *Component) filterValidateHandlerFunc(gc *gin.Context) {
|
||||
Errors: filter.AllErrors(err),
|
||||
})
|
||||
}
|
||||
|
||||
// filterCompleteHandlerInput describes the input of the /filter/complete endpoint.
|
||||
type filterCompleteHandlerInput struct {
|
||||
What string `json:"what" binding:"required,oneof=column operator value"`
|
||||
Column string `json:"column" binding:"required_unless=What column"`
|
||||
Prefix string `json:"prefix"`
|
||||
}
|
||||
|
||||
// filterCompleteHandlerOutput describes the output of the /filter/complete endpoint.
|
||||
type filterCompleteHandlerOutput struct {
|
||||
Completions []filterCompletion `json:"completions"`
|
||||
}
|
||||
type filterCompletion struct {
|
||||
Label string `json:"label"`
|
||||
Detail string `json:"detail,omitempty"`
|
||||
Quoted bool `json:"quoted"` // should the return value be quoted?
|
||||
}
|
||||
|
||||
func (c *Component) filterCompleteHandlerFunc(gc *gin.Context) {
|
||||
ctx := c.t.Context(gc.Request.Context())
|
||||
var input filterCompleteHandlerInput
|
||||
if err := gc.ShouldBindJSON(&input); err != nil {
|
||||
gc.JSON(http.StatusBadRequest, gin.H{"message": helpers.Capitalize(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
completions := []filterCompletion{}
|
||||
switch input.What {
|
||||
case "column":
|
||||
_, err := filter.Parse("", []byte{}, filter.Entrypoint("ConditionExpr"))
|
||||
if err != nil {
|
||||
for _, candidate := range filter.Expected(err) {
|
||||
if !strings.HasSuffix(candidate, `"i`) {
|
||||
continue
|
||||
}
|
||||
candidate = candidate[1 : len(candidate)-2]
|
||||
completions = append(completions, filterCompletion{
|
||||
Label: candidate,
|
||||
Detail: "column name",
|
||||
})
|
||||
}
|
||||
}
|
||||
case "operator":
|
||||
_, err := filter.Parse("",
|
||||
[]byte(fmt.Sprintf("%s ", input.Column)),
|
||||
filter.Entrypoint("ConditionExpr"))
|
||||
if err != nil {
|
||||
for _, candidate := range filter.Expected(err) {
|
||||
if !strings.HasPrefix(candidate, `"`) {
|
||||
continue
|
||||
}
|
||||
candidate = strings.TrimSuffix(
|
||||
strings.TrimSuffix(candidate[1:len(candidate)-1], `"i`),
|
||||
`"`)
|
||||
if candidate != "--" && candidate != "/*" {
|
||||
completions = append(completions, filterCompletion{
|
||||
Label: candidate,
|
||||
Detail: "condition operator",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
case "value":
|
||||
var column, detail string
|
||||
switch strings.ToLower(input.Column) {
|
||||
case "inifboundary", "outifboundary":
|
||||
completions = append(completions, filterCompletion{
|
||||
Label: "internal",
|
||||
Detail: "network boundary",
|
||||
}, filterCompletion{
|
||||
Label: "external",
|
||||
Detail: "network boundary",
|
||||
}, filterCompletion{
|
||||
Label: "undefined",
|
||||
Detail: "network boundary",
|
||||
})
|
||||
case "etype":
|
||||
completions = append(completions, filterCompletion{
|
||||
Label: "IPv4",
|
||||
Detail: "ethernet type",
|
||||
}, filterCompletion{
|
||||
Label: "IPv6",
|
||||
Detail: "ethernet type",
|
||||
})
|
||||
case "proto":
|
||||
// Do not complete from Clickhouse, we want a subset of options
|
||||
completions = append(completions,
|
||||
filterCompletion{"TCP", "protocol", true},
|
||||
filterCompletion{"UDP", "protocol", true},
|
||||
filterCompletion{"SCTP", "protocol", true},
|
||||
filterCompletion{"ICMP", "protocol", true},
|
||||
filterCompletion{"IPv6-ICMP", "protocol", true},
|
||||
filterCompletion{"GRE", "protocol", true},
|
||||
filterCompletion{"ESP", "protocol", true},
|
||||
filterCompletion{"AH", "protocol", true},
|
||||
filterCompletion{"IPIP", "protocol", true},
|
||||
filterCompletion{"VRRP", "protocol", true},
|
||||
filterCompletion{"L2TP", "protocol", true},
|
||||
filterCompletion{"IGMP", "protocol", true},
|
||||
filterCompletion{"PIM", "protocol", true},
|
||||
filterCompletion{"IPv4", "protocol", true},
|
||||
filterCompletion{"IPv6", "protocol", true})
|
||||
case "srcas", "dstas":
|
||||
// Query "asns" dictionary if we have at last 3 letters as a prefix
|
||||
if len(input.Prefix) >= 3 {
|
||||
sqlQuery := `
|
||||
SELECT concat('AS', toString(asn)) AS label, detail
|
||||
FROM asns
|
||||
WHERE positionCaseInsensitive(name, $1) >= 1
|
||||
ORDER BY positionCaseInsensitive(name, $1) ASC, asn ASC
|
||||
LIMIT 20`
|
||||
results := []struct {
|
||||
Label string `ch:"label"`
|
||||
Detail string `ch:"detail"`
|
||||
}{}
|
||||
if err := c.d.ClickHouseDB.Conn.Select(ctx, &results, sqlQuery, input.Prefix); err != nil {
|
||||
c.r.Err(err).Msg("unable to query database")
|
||||
break
|
||||
}
|
||||
for _, result := range results {
|
||||
completions = append(completions, filterCompletion{
|
||||
Label: result.Label,
|
||||
Detail: result.Detail,
|
||||
Quoted: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
input.Prefix = "" // We have handled this internally
|
||||
case "exportername":
|
||||
column = "ExporterName"
|
||||
detail = "exporter name"
|
||||
case "exportergroup":
|
||||
column = "ExporterGroup"
|
||||
detail = "exporter group"
|
||||
case "inifname", "outifname":
|
||||
column = "IfName"
|
||||
detail = "interface name"
|
||||
case "inifdescription", "outifdescription":
|
||||
column = "IfDescription"
|
||||
detail = "interface description"
|
||||
case "inifconnectivity", "outifconnectivity":
|
||||
column = "IfConnectivity"
|
||||
detail = "connectivity type"
|
||||
case "inifprovider", "outifprovider":
|
||||
column = "IfProvider"
|
||||
detail = "provider name"
|
||||
}
|
||||
if column != "" {
|
||||
// Query "exporter" table
|
||||
sqlQuery := fmt.Sprintf(`
|
||||
SELECT %s AS label
|
||||
FROM exporters
|
||||
WHERE positionCaseInsensitive(%s, $1) >= 1
|
||||
GROUP BY %s
|
||||
ORDER BY positionCaseInsensitive(%s, $1) ASC, %s ASC
|
||||
LIMIT 20`, column, column, column, column, column)
|
||||
results := []struct {
|
||||
Label string `ch:"label"`
|
||||
}{}
|
||||
if err := c.d.ClickHouseDB.Conn.Select(ctx, &results, sqlQuery, input.Prefix); err != nil {
|
||||
c.r.Err(err).Msg("unable to query database")
|
||||
break
|
||||
}
|
||||
for _, result := range results {
|
||||
completions = append(completions, filterCompletion{
|
||||
Label: result.Label,
|
||||
Detail: detail,
|
||||
Quoted: true,
|
||||
})
|
||||
}
|
||||
input.Prefix = ""
|
||||
}
|
||||
}
|
||||
filteredCompletions := []filterCompletion{}
|
||||
for _, completion := range completions {
|
||||
if strings.HasPrefix(strings.ToLower(completion.Label), strings.ToLower(input.Prefix)) {
|
||||
filteredCompletions = append(filteredCompletions, completion)
|
||||
}
|
||||
}
|
||||
gc.JSON(http.StatusOK, filterCompleteHandlerOutput{filteredCompletions})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -48,3 +48,20 @@ func AllErrors(err error) Errors {
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// Expected returns a list of expected strings from the first error.
|
||||
func Expected(err error) []string {
|
||||
el, ok := err.(errList)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if len(el) == 0 {
|
||||
return nil
|
||||
}
|
||||
switch e := el[0].(type) {
|
||||
case *parserError:
|
||||
return e.expected
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,3 +35,11 @@ OR`))
|
||||
t.Errorf("AllErrors() (-got, +want):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpected(t *testing.T) {
|
||||
_, err := Parse("", []byte{}, Entrypoint("ConditionBoundaryExpr"))
|
||||
expected := []string{`"InIfBoundary"i`, `"OutIfBoundary"i`}
|
||||
if diff := helpers.Diff(Expected(err), expected); diff != "" {
|
||||
t.Errorf("AllErrors() (-got, +want):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ ConditionBoundaryExpr "condition on boundary" ←
|
||||
column:("InIfBoundary"i { return "InIfBoundary", nil }
|
||||
/ "OutIfBoundary"i { return "OutIfBoundary", nil }) _
|
||||
operator:("=" / "!=") _
|
||||
boundary:("external"i / "internal"i) {
|
||||
boundary:("external"i / "internal"i / "undefined"i) {
|
||||
return fmt.Sprintf("%s %s %s", toString(column), toString(operator),
|
||||
quote(strings.ToLower(toString(boundary)))), nil
|
||||
}
|
||||
@@ -208,17 +208,20 @@ Unsigned64 "unsigned 64-bit integer" ← [0-9]+ !IdentStart {
|
||||
LikeOperator "LIKE operators" ←
|
||||
KW_LIKE
|
||||
/ KW_ILIKE
|
||||
/ (KW_NOT _ KW_LIKE) { return "NOT LIKE", nil }
|
||||
/ (KW_NOT _ KW_ILIKE) { return "NOT ILIKE", nil }
|
||||
/ KW_UNLIKE
|
||||
/ KW_IUNLIKE
|
||||
InOperator "IN operators" ←
|
||||
KW_IN
|
||||
/ (KW_NOT _ KW_IN) { return "NOT IN", nil }
|
||||
/ KW_NOTIN
|
||||
KW_AND "AND operator" ← "AND"i !IdentStart { return "AND", nil }
|
||||
KW_OR "OR operator" ← "OR"i !IdentStart { return "OR", nil }
|
||||
KW_NOT "NOT operator" ← "NOT"i !IdentStart { return "NOT", nil }
|
||||
KW_LIKE "LIKE operator" ← "LIKE"i !IdentStart { return "LIKE", nil }
|
||||
KW_ILIKE "ILIKE operator" ← "ILIKE"i !IdentStart { return "ILIKE", nil }
|
||||
KW_IN "IN operator" ← "IN"i !IdentStart { return "IN", nil }
|
||||
KW_UNLIKE "UNLIKE operator" ← "UNLIKE"i !IdentStart { return "NOT LIKE", nil }
|
||||
KW_IUNLIKE "IUNLIKE operator" ← "IUNLIKE"i !IdentStart { return "NOT ILIKE", nil }
|
||||
KW_NOTIN "NOTIN operator" ← "NOTIN"i !IdentStart { return "NOT IN", nil }
|
||||
|
||||
SingleLineComment "comment" ← "--" ( !EOL SourceChar )*
|
||||
MultiLineComment ← "/*" ( !"*/" SourceChar )* ("*/" / EOF {
|
||||
|
||||
@@ -19,9 +19,8 @@ func TestValidFilter(t *testing.T) {
|
||||
{`ExporterName="something\"`, `ExporterName = 'something\\'`},
|
||||
{`ExporterName!="something"`, `ExporterName != 'something'`},
|
||||
{`ExporterName LIKE "something%"`, `ExporterName LIKE 'something%'`},
|
||||
{`ExporterName NOT LIKE "something%"`, `ExporterName NOT LIKE 'something%'`},
|
||||
{`ExporterName ILIKE "something%"`, `ExporterName ILIKE 'something%'`},
|
||||
{`ExporterName not ILIKE "something%"`, `ExporterName NOT ILIKE 'something%'`},
|
||||
{`ExporterName UNLIKE "something%"`, `ExporterName NOT LIKE 'something%'`},
|
||||
{`ExporterName IUNLIKE "something%"`, `ExporterName NOT ILIKE 'something%'`},
|
||||
{`ExporterName="something with spaces"`, `ExporterName = 'something with spaces'`},
|
||||
{`ExporterName="something with 'quotes'"`, `ExporterName = 'something with \'quotes\''`},
|
||||
{`ExporterAddress=203.0.113.1`, `ExporterAddress = IPv6StringToNum('203.0.113.1')`},
|
||||
@@ -34,8 +33,8 @@ func TestValidFilter(t *testing.T) {
|
||||
{`SrcAS=AS12322`, `SrcAS = 12322`},
|
||||
{`SrcAS=as12322`, `SrcAS = 12322`},
|
||||
{`SrcAS IN(12322, 29447)`, `SrcAS IN (12322, 29447)`},
|
||||
{`SrcAS NOT IN(12322, 29447)`, `SrcAS NOT IN (12322, 29447)`},
|
||||
{`SrcAS NOT IN (AS12322, 29447)`, `SrcAS NOT IN (12322, 29447)`},
|
||||
{`SrcAS NOTIN(12322, 29447)`, `SrcAS NOT IN (12322, 29447)`},
|
||||
{`SrcAS NOTIN (AS12322, 29447)`, `SrcAS NOT IN (12322, 29447)`},
|
||||
{`DstAS=12322`, `DstAS = 12322`},
|
||||
{`SrcCountry='FR'`, `SrcCountry = 'FR'`},
|
||||
{`DstCountry='FR'`, `DstCountry = 'FR'`},
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
"akvorado/common/clickhousedb"
|
||||
"akvorado/common/daemon"
|
||||
@@ -14,7 +15,7 @@ import (
|
||||
|
||||
func TestFilterHandlers(t *testing.T) {
|
||||
r := reporter.NewMock(t)
|
||||
ch, _ := clickhousedb.NewMock(t, r)
|
||||
ch, mockConn := clickhousedb.NewMock(t, r)
|
||||
h := http.NewMock(t, r)
|
||||
c, err := New(r, Configuration{}, Dependencies{
|
||||
Daemon: daemon.NewMock(t),
|
||||
@@ -26,6 +27,48 @@ func TestFilterHandlers(t *testing.T) {
|
||||
}
|
||||
helpers.StartStop(t, c)
|
||||
|
||||
mockConn.EXPECT().
|
||||
Select(gomock.Any(), gomock.Any(), `
|
||||
SELECT ExporterName AS label
|
||||
FROM exporters
|
||||
WHERE positionCaseInsensitive(ExporterName, $1) >= 1
|
||||
GROUP BY ExporterName
|
||||
ORDER BY positionCaseInsensitive(ExporterName, $1) ASC, ExporterName ASC
|
||||
LIMIT 20`,
|
||||
"th2-").
|
||||
SetArg(1, []struct {
|
||||
Label string `ch:"label"`
|
||||
}{
|
||||
{"th2-router1"},
|
||||
{"th2-router2"},
|
||||
{"th2-router3"}}).
|
||||
Return(nil)
|
||||
mockConn.EXPECT().
|
||||
Select(gomock.Any(), gomock.Any(), `
|
||||
SELECT concat('AS', toString(asn)) AS label, detail
|
||||
FROM asns
|
||||
WHERE positionCaseInsensitive(name, $1) >= 1
|
||||
ORDER BY positionCaseInsensitive(name, $1) ASC, asn ASC
|
||||
LIMIT 20`,
|
||||
"goog").
|
||||
SetArg(1, []struct {
|
||||
Label string `ch:"label"`
|
||||
Detail string `ch:"detail"`
|
||||
}{
|
||||
{"AS15169", "Google"},
|
||||
{"AS16550", "Google Private Cloud"},
|
||||
{"AS16591", "Google Fiber"},
|
||||
{"AS19527", "Google"},
|
||||
{"AS26910", "GOOGLE-CLOUD-2"},
|
||||
{"AS36040", "Google"},
|
||||
{"AS36384", "Google"},
|
||||
{"AS36385", "Google IT"},
|
||||
{"AS36492", "Google"},
|
||||
{"AS36987", "Google Kenya"},
|
||||
{"AS41264", "Google Switzerland"},
|
||||
}).
|
||||
Return(nil)
|
||||
|
||||
helpers.TestHTTPEndpoints(t, h.Address, helpers.HTTPEndpointCases{
|
||||
{
|
||||
URL: "/api/v0/console/filter/validate",
|
||||
@@ -47,6 +90,83 @@ func TestFilterHandlers(t *testing.T) {
|
||||
"message": "string literal not terminated",
|
||||
}},
|
||||
},
|
||||
}, {
|
||||
URL: "/api/v0/console/filter/complete",
|
||||
StatusCode: 200,
|
||||
JSONInput: gin.H{"what": "column", "prefix": "dSt"},
|
||||
JSONOutput: gin.H{"completions": []gin.H{
|
||||
{"label": "DstAS", "detail": "column name", "quoted": false},
|
||||
{"label": "DstAddr", "detail": "column name", "quoted": false},
|
||||
{"label": "DstCountry", "detail": "column name", "quoted": false},
|
||||
{"label": "DstPort", "detail": "column name", "quoted": false},
|
||||
}},
|
||||
}, {
|
||||
URL: "/api/v0/console/filter/complete",
|
||||
StatusCode: 200,
|
||||
JSONInput: gin.H{"what": "operator", "column": "ExporterName"},
|
||||
JSONOutput: gin.H{"completions": []gin.H{
|
||||
{"label": "!=", "detail": "condition operator", "quoted": false},
|
||||
{"label": "=", "detail": "condition operator", "quoted": false},
|
||||
{"label": "ILIKE", "detail": "condition operator", "quoted": false},
|
||||
{"label": "IUNLIKE", "detail": "condition operator", "quoted": false},
|
||||
{"label": "LIKE", "detail": "condition operator", "quoted": false},
|
||||
{"label": "UNLIKE", "detail": "condition operator", "quoted": false},
|
||||
}},
|
||||
}, {
|
||||
URL: "/api/v0/console/filter/complete",
|
||||
StatusCode: 200,
|
||||
JSONInput: gin.H{"what": "value", "column": "outifboundary"},
|
||||
JSONOutput: gin.H{"completions": []gin.H{
|
||||
{"label": "internal", "detail": "network boundary", "quoted": false},
|
||||
{"label": "external", "detail": "network boundary", "quoted": false},
|
||||
{"label": "undefined", "detail": "network boundary", "quoted": false},
|
||||
}},
|
||||
}, {
|
||||
URL: "/api/v0/console/filter/complete",
|
||||
StatusCode: 200,
|
||||
JSONInput: gin.H{"what": "value", "column": "etype"},
|
||||
JSONOutput: gin.H{"completions": []gin.H{
|
||||
{"label": "IPv4", "detail": "ethernet type", "quoted": false},
|
||||
{"label": "IPv6", "detail": "ethernet type", "quoted": false},
|
||||
}},
|
||||
}, {
|
||||
URL: "/api/v0/console/filter/complete",
|
||||
StatusCode: 200,
|
||||
JSONInput: gin.H{"what": "value", "column": "proto", "prefix": "I"},
|
||||
JSONOutput: gin.H{"completions": []gin.H{
|
||||
{"label": "ICMP", "detail": "protocol", "quoted": true},
|
||||
{"label": "IPv6-ICMP", "detail": "protocol", "quoted": true},
|
||||
{"label": "IPIP", "detail": "protocol", "quoted": true},
|
||||
{"label": "IGMP", "detail": "protocol", "quoted": true},
|
||||
{"label": "IPv4", "detail": "protocol", "quoted": true},
|
||||
{"label": "IPv6", "detail": "protocol", "quoted": true},
|
||||
}},
|
||||
}, {
|
||||
URL: "/api/v0/console/filter/complete",
|
||||
StatusCode: 200,
|
||||
JSONInput: gin.H{"what": "value", "column": "exportername", "prefix": "th2-"},
|
||||
JSONOutput: gin.H{"completions": []gin.H{
|
||||
{"label": "th2-router1", "detail": "exporter name", "quoted": true},
|
||||
{"label": "th2-router2", "detail": "exporter name", "quoted": true},
|
||||
{"label": "th2-router3", "detail": "exporter name", "quoted": true},
|
||||
}},
|
||||
}, {
|
||||
URL: "/api/v0/console/filter/complete",
|
||||
StatusCode: 200,
|
||||
JSONInput: gin.H{"what": "value", "column": "srcAS", "prefix": "goog"},
|
||||
JSONOutput: gin.H{"completions": []gin.H{
|
||||
{"label": "AS15169", "detail": "Google", "quoted": false},
|
||||
{"label": "AS16550", "detail": "Google Private Cloud", "quoted": false},
|
||||
{"label": "AS16591", "detail": "Google Fiber", "quoted": false},
|
||||
{"label": "AS19527", "detail": "Google", "quoted": false},
|
||||
{"label": "AS26910", "detail": "GOOGLE-CLOUD-2", "quoted": false},
|
||||
{"label": "AS36040", "detail": "Google", "quoted": false},
|
||||
{"label": "AS36384", "detail": "Google", "quoted": false},
|
||||
{"label": "AS36385", "detail": "Google IT", "quoted": false},
|
||||
{"label": "AS36492", "detail": "Google", "quoted": false},
|
||||
{"label": "AS36987", "detail": "Google Kenya", "quoted": false},
|
||||
{"label": "AS41264", "detail": "Google Switzerland", "quoted": false},
|
||||
}},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ func (c *Component) Start() error {
|
||||
c.d.HTTP.GinRouter.POST("/api/v0/console/graph", c.graphHandlerFunc)
|
||||
c.d.HTTP.GinRouter.POST("/api/v0/console/sankey", c.sankeyHandlerFunc)
|
||||
c.d.HTTP.GinRouter.POST("/api/v0/console/filter/validate", c.filterValidateHandlerFunc)
|
||||
c.d.HTTP.GinRouter.POST("/api/v0/console/filter/complete", c.filterCompleteHandlerFunc)
|
||||
|
||||
c.t.Go(func() error {
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
|
||||
9
go.mod
9
go.mod
@@ -45,9 +45,9 @@ require (
|
||||
github.com/eapache/queue v1.1.0 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.5 // indirect
|
||||
github.com/go-playground/locales v0.13.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.17.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.4.1 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.11.0 // indirect
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
@@ -62,7 +62,7 @@ require (
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.15.1 // indirect
|
||||
github.com/leodido/go-urn v1.2.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
@@ -84,6 +84,7 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.4.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 // indirect
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
)
|
||||
|
||||
replace github.com/slayercat/gosnmp => github.com/slayercat/gosnmp v1.24.1-0.20220124233957-4b805977d286
|
||||
|
||||
18
go.sum
18
go.sum
@@ -171,12 +171,15 @@ github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
|
||||
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
@@ -355,8 +358,9 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
@@ -417,6 +421,7 @@ github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9F
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -458,8 +463,9 @@ github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498/go.mod h1:6lkG1x+13OShE
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4=
|
||||
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc=
|
||||
@@ -558,6 +564,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s=
|
||||
@@ -761,6 +768,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
@@ -5,7 +5,7 @@ proto,name,description
|
||||
3,GGP,Gateway-to-Gateway
|
||||
4,IPv4,IPv4 encapsulation
|
||||
5,ST,Stream
|
||||
6,TCP,Transmission Control
|
||||
6,TCP,Transmission Control Protocol
|
||||
7,CBT,CBT
|
||||
8,EGP,Exterior Gateway Protocol
|
||||
10,BBN-RCC-MON,BBN RCC Monitoring
|
||||
@@ -14,7 +14,7 @@ proto,name,description
|
||||
14,EMCON,EMCON
|
||||
15,XNET,Cross Net Debugger
|
||||
16,CHAOS,Chaos
|
||||
17,UDP,User Datagram
|
||||
17,UDP,User Datagram Protocol
|
||||
18,MUX,Multiplexing
|
||||
19,DCN-MEAS,DCN Measurement Subsystems
|
||||
20,HMP,Host Monitoring
|
||||
|
||||
|
Reference in New Issue
Block a user