frontend: refactor summary table parsing
This commit is contained in:
@@ -73,13 +73,14 @@ func TestApiSummaryHandler(t *testing.T) {
|
|||||||
|
|
||||||
summary := response.Result[0].(*apiSummaryResultPair)
|
summary := response.Result[0].(*apiSummaryResultPair)
|
||||||
assert.Equal(t, summary.Server, "alpha")
|
assert.Equal(t, summary.Server, "alpha")
|
||||||
|
assert.Equal(t, len(summary.Data), 7)
|
||||||
// Protocol list will be sorted
|
// Protocol list will be sorted
|
||||||
assert.Equal(t, summary.Data[1].Name, "device1")
|
assert.Equal(t, summary.Data[0].Name, "device1")
|
||||||
assert.Equal(t, summary.Data[1].Proto, "Device")
|
assert.Equal(t, summary.Data[0].Proto, "Device")
|
||||||
assert.Equal(t, summary.Data[1].Table, "---")
|
assert.Equal(t, summary.Data[0].Table, "---")
|
||||||
assert.Equal(t, summary.Data[1].State, "up")
|
assert.Equal(t, summary.Data[0].State, "up")
|
||||||
assert.Equal(t, summary.Data[1].Since, "2021-08-27")
|
assert.Equal(t, summary.Data[0].Since, "2021-08-27")
|
||||||
assert.Equal(t, summary.Data[1].Info, "")
|
assert.Equal(t, summary.Data[0].Info, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApiSummaryHandlerError(t *testing.T) {
|
func TestApiSummaryHandlerError(t *testing.T) {
|
||||||
|
|||||||
@@ -35,15 +35,6 @@ var optionsMap = map[string]string{
|
|||||||
"traceroute": "traceroute ...",
|
"traceroute": "traceroute ...",
|
||||||
}
|
}
|
||||||
|
|
||||||
// pre-compiled regexp and constant statemap for summary rendering
|
|
||||||
var splitSummaryLine = regexp.MustCompile(`(\w+)(\s+)(\w+)(\s+)([\w-]+)(\s+)(\w+)(\s+)([0-9\-\. :]+)(.*)`)
|
|
||||||
var summaryStateMap = map[string]string{
|
|
||||||
"up": "success",
|
|
||||||
"down": "secondary",
|
|
||||||
"start": "danger",
|
|
||||||
"passive": "info",
|
|
||||||
}
|
|
||||||
|
|
||||||
// render the page template
|
// render the page template
|
||||||
func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, content template.HTML) {
|
func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, content template.HTML) {
|
||||||
path := r.URL.Path[1:]
|
path := r.URL.Path[1:]
|
||||||
@@ -143,63 +134,23 @@ func summaryParse(data string, serverName string) (TemplateSummary, error) {
|
|||||||
|
|
||||||
// parse each line
|
// parse each line
|
||||||
for _, line := range rows {
|
for _, line := range rows {
|
||||||
|
row := SummaryRowDataFromLine(line)
|
||||||
// Ignore empty lines
|
if row == nil {
|
||||||
line = strings.TrimSpace(line)
|
|
||||||
if len(line) == 0 {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse a total of 6 columns from bird summary
|
// Filter row name
|
||||||
lineSplitted := splitSummaryLine.FindStringSubmatch(line)
|
if setting.nameFilter != "" && nameFilterRegexp.MatchString(row.Name) {
|
||||||
if lineSplitted == nil {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var row SummaryRowData
|
// Filter away unwanted protocol types, if setting.protocolFilter is non-empty
|
||||||
|
if len(setting.protocolFilter) > 0 && !row.ProtocolMatches(setting.protocolFilter) {
|
||||||
if len(lineSplitted) >= 2 {
|
continue
|
||||||
row.Name = strings.TrimSpace(lineSplitted[1])
|
|
||||||
if setting.nameFilter != "" && nameFilterRegexp.MatchString(row.Name) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(lineSplitted) >= 4 {
|
|
||||||
row.Proto = strings.TrimSpace(lineSplitted[3])
|
|
||||||
// Filter away unwanted protocol types, if setting.protocolFilter is non-empty
|
|
||||||
found := false
|
|
||||||
for _, protocol := range setting.protocolFilter {
|
|
||||||
if strings.EqualFold(row.Proto, protocol) {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(setting.protocolFilter) > 0 && !found {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(lineSplitted) >= 6 {
|
|
||||||
row.Table = strings.TrimSpace(lineSplitted[5])
|
|
||||||
}
|
|
||||||
if len(lineSplitted) >= 8 {
|
|
||||||
row.State = strings.TrimSpace(lineSplitted[7])
|
|
||||||
row.MappedState = summaryStateMap[row.State]
|
|
||||||
}
|
|
||||||
if len(lineSplitted) >= 10 {
|
|
||||||
row.Since = strings.TrimSpace(lineSplitted[9])
|
|
||||||
}
|
|
||||||
if len(lineSplitted) >= 11 {
|
|
||||||
row.Info = strings.TrimSpace(lineSplitted[10])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dynamic BGP session, show without any color
|
|
||||||
if strings.Contains(row.Info, "Passive") {
|
|
||||||
row.MappedState = summaryStateMap["passive"]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add to the result
|
// add to the result
|
||||||
args.Rows = append(args.Rows, row)
|
args.Rows = append(args.Rows, *row)
|
||||||
}
|
}
|
||||||
|
|
||||||
return args, nil
|
return args, nil
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
const BirdSummaryData = `BIRD 2.0.8 ready.
|
const BirdSummaryData = `Name Proto Table State Since Info
|
||||||
Name Proto Table State Since Info
|
|
||||||
static1 Static master4 up 2021-08-27
|
static1 Static master4 up 2021-08-27
|
||||||
static2 Static master6 up 2021-08-27
|
static2 Static master6 up 2021-08-27
|
||||||
device1 Device --- up 2021-08-27
|
device1 Device --- up 2021-08-27
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ package main
|
|||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// import templates and other assets
|
// import templates and other assets
|
||||||
|
//
|
||||||
//go:embed assets
|
//go:embed assets
|
||||||
var assets embed.FS
|
var assets embed.FS
|
||||||
|
|
||||||
@@ -64,6 +66,47 @@ func (r SummaryRowData) NameContains(prefix string) bool {
|
|||||||
return strings.Contains(r.Name, prefix)
|
return strings.Contains(r.Name, prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r SummaryRowData) ProtocolMatches(protocols []string) bool {
|
||||||
|
for _, protocol := range protocols {
|
||||||
|
if strings.EqualFold(r.Proto, protocol) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// pre-compiled regexp and constant statemap for summary rendering
|
||||||
|
var splitSummaryLine = regexp.MustCompile(`(\w+)\s+(\w+)\s+([\w-]+)\s+(\w+)\s+([0-9\-\. :]+)(.*)`)
|
||||||
|
var summaryStateMap = map[string]string{
|
||||||
|
"up": "success",
|
||||||
|
"down": "secondary",
|
||||||
|
"start": "danger",
|
||||||
|
"passive": "info",
|
||||||
|
}
|
||||||
|
|
||||||
|
func SummaryRowDataFromLine(line string) *SummaryRowData {
|
||||||
|
lineSplitted := splitSummaryLine.FindStringSubmatch(line)
|
||||||
|
if lineSplitted == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var row SummaryRowData
|
||||||
|
row.Name = strings.TrimSpace(lineSplitted[1])
|
||||||
|
row.Proto = strings.TrimSpace(lineSplitted[2])
|
||||||
|
row.Table = strings.TrimSpace(lineSplitted[3])
|
||||||
|
row.State = strings.TrimSpace(lineSplitted[4])
|
||||||
|
row.Since = strings.TrimSpace(lineSplitted[5])
|
||||||
|
row.Info = strings.TrimSpace(lineSplitted[6])
|
||||||
|
|
||||||
|
if strings.Contains(row.Info, "Passive") {
|
||||||
|
row.MappedState = summaryStateMap["passive"]
|
||||||
|
} else {
|
||||||
|
row.MappedState = summaryStateMap[row.State]
|
||||||
|
}
|
||||||
|
|
||||||
|
return &row
|
||||||
|
}
|
||||||
|
|
||||||
type TemplateSummary struct {
|
type TemplateSummary struct {
|
||||||
ServerName string
|
ServerName string
|
||||||
Raw string
|
Raw string
|
||||||
@@ -108,7 +151,7 @@ var requiredTemplates = [...]string{
|
|||||||
// define functions to be made available in templates
|
// define functions to be made available in templates
|
||||||
|
|
||||||
var funcMap = template.FuncMap{
|
var funcMap = template.FuncMap{
|
||||||
"pathescape": url.PathEscape,
|
"pathescape": url.PathEscape,
|
||||||
}
|
}
|
||||||
|
|
||||||
// import templates from embedded assets
|
// import templates from embedded assets
|
||||||
|
|||||||
@@ -23,3 +23,56 @@ func TestSummaryRowDataNameContains(t *testing.T) {
|
|||||||
assert.Equal(t, data.NameContains("oc"), true)
|
assert.Equal(t, data.NameContains("oc"), true)
|
||||||
assert.Equal(t, data.NameContains("no"), false)
|
assert.Equal(t, data.NameContains("no"), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSummaryRowDataFromLine(t *testing.T) {
|
||||||
|
data := SummaryRowDataFromLine("sys_device Device --- up 2025-06-27 21:23:08")
|
||||||
|
|
||||||
|
assert.Equal(t, data.Name, "sys_device")
|
||||||
|
assert.Equal(t, data.Proto, "Device")
|
||||||
|
assert.Equal(t, data.Table, "---")
|
||||||
|
assert.Equal(t, data.State, "up")
|
||||||
|
assert.Equal(t, data.Since, "2025-06-27 21:23:08")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSummaryRowDataFromLineNumeric(t *testing.T) {
|
||||||
|
data := SummaryRowDataFromLine("12345 Device --- up 2025-06-27 21:23:08")
|
||||||
|
|
||||||
|
assert.Equal(t, data.Name, "12345")
|
||||||
|
assert.Equal(t, data.Proto, "Device")
|
||||||
|
assert.Equal(t, data.Table, "---")
|
||||||
|
assert.Equal(t, data.State, "up")
|
||||||
|
assert.Equal(t, data.Since, "2025-06-27 21:23:08")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSummaryRowDataFromLinePipe(t *testing.T) {
|
||||||
|
data := SummaryRowDataFromLine("pipe Pipe --- up 2025-06-27 21:23:08 master4 <=> pipe_v4")
|
||||||
|
|
||||||
|
assert.Equal(t, data.Name, "pipe")
|
||||||
|
assert.Equal(t, data.Proto, "Pipe")
|
||||||
|
assert.Equal(t, data.Table, "---")
|
||||||
|
assert.Equal(t, data.State, "up")
|
||||||
|
assert.Equal(t, data.Since, "2025-06-27 21:23:08")
|
||||||
|
assert.Equal(t, data.Info, "master4 <=> pipe_v4")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSummaryRowDataFromLineBGP(t *testing.T) {
|
||||||
|
data := SummaryRowDataFromLine("bgp BGP --- up 2025-06-30 20:45:33 Established")
|
||||||
|
|
||||||
|
assert.Equal(t, data.Name, "bgp")
|
||||||
|
assert.Equal(t, data.Proto, "BGP")
|
||||||
|
assert.Equal(t, data.Table, "---")
|
||||||
|
assert.Equal(t, data.State, "up")
|
||||||
|
assert.Equal(t, data.Since, "2025-06-30 20:45:33")
|
||||||
|
assert.Equal(t, data.Info, "Established")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSummaryRowDataFromLineBGPPassive(t *testing.T) {
|
||||||
|
data := SummaryRowDataFromLine("passive BGP --- start 2025-06-27 21:23:08 Passive")
|
||||||
|
|
||||||
|
assert.Equal(t, data.Name, "passive")
|
||||||
|
assert.Equal(t, data.Proto, "BGP")
|
||||||
|
assert.Equal(t, data.Table, "---")
|
||||||
|
assert.Equal(t, data.State, "start")
|
||||||
|
assert.Equal(t, data.Since, "2025-06-27 21:23:08")
|
||||||
|
assert.Equal(t, data.Info, "Passive")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user