You've already forked bird-lg-go
mirror of
https://github.com/xddxdd/bird-lg-go
synced 2025-10-21 01:42:15 +02:00
Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e9750a8278 | ||
![]() |
d40dd3a4d3 | ||
![]() |
ffdeeac06e | ||
![]() |
7eb4d75bbf | ||
![]() |
6e5e190d32 | ||
![]() |
1b2573d87c | ||
![]() |
0d5337508b | ||
![]() |
b9094d3d6c | ||
![]() |
ec7f348418 | ||
![]() |
a632739443 | ||
![]() |
a9e278357a | ||
![]() |
e4c00c897f | ||
![]() |
4df3918b35 | ||
![]() |
45dc24470d | ||
![]() |
55ea5c3b28 | ||
![]() |
7eb44c3828 | ||
![]() |
124fdedbda | ||
![]() |
e6a98358b5 | ||
![]() |
761eb2160a | ||
![]() |
cc2a146a88 | ||
![]() |
3db9454350 | ||
![]() |
c30bed112c | ||
![]() |
af5ab3c78f | ||
![]() |
0fdde8afc7 | ||
![]() |
39a129db9d | ||
![]() |
0dd1c07b66 | ||
![]() |
f0f072c4a6 | ||
![]() |
657565857b | ||
![]() |
7ac2158e70 | ||
![]() |
5c433bc27a | ||
![]() |
1b0b923da9 | ||
![]() |
01438edaef | ||
![]() |
90f36610dc | ||
![]() |
6174208d07 |
2
.github/workflows/develop.yaml
vendored
2
.github/workflows/develop.yaml
vendored
@@ -47,7 +47,7 @@ jobs:
|
|||||||
- name: Test whois binary in frontend image
|
- name: Test whois binary in frontend image
|
||||||
run: |
|
run: |
|
||||||
docker build -t local/frontend frontend/
|
docker build -t local/frontend frontend/
|
||||||
docker run --rm --net host --entrypoint whois local/frontend github.com || exit 1
|
docker run --rm --net host --entrypoint whois local/frontend -I github.com || exit 1
|
||||||
docker run --rm --net host --entrypoint whois local/frontend -h whois.ripe.net github.com || exit 1
|
docker run --rm --net host --entrypoint whois local/frontend -h whois.ripe.net github.com || exit 1
|
||||||
docker run --rm --net host --entrypoint whois local/frontend -h whois.ripe.net:43 github.com || exit 1
|
docker run --rm --net host --entrypoint whois local/frontend -h whois.ripe.net:43 github.com || exit 1
|
||||||
|
|
||||||
|
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@@ -67,7 +67,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
context: '{{defaultContext}}:frontend'
|
context: '{{defaultContext}}:frontend'
|
||||||
platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7
|
platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7
|
||||||
push: true{context}/Dockerfile
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
xddxdd/bird-lg-go:latest
|
xddxdd/bird-lg-go:latest
|
||||||
xddxdd/bird-lg-go:${{ github.event.release.tag_name }}
|
xddxdd/bird-lg-go:${{ github.event.release.tag_name }}
|
||||||
|
@@ -20,6 +20,7 @@ type apiGenericResultPair struct {
|
|||||||
type apiSummaryResultPair struct {
|
type apiSummaryResultPair struct {
|
||||||
Server string `json:"server"`
|
Server string `json:"server"`
|
||||||
Data []SummaryRowData `json:"data"`
|
Data []SummaryRowData `json:"data"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type apiResponse struct {
|
type apiResponse struct {
|
||||||
@@ -70,9 +71,12 @@ func apiSummaryHandler(request apiRequest) apiResponse {
|
|||||||
for i, result := range results {
|
for i, result := range results {
|
||||||
parsedSummary, err := summaryParse(result, request.Servers[i])
|
parsedSummary, err := summaryParse(result, request.Servers[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return apiResponse{
|
response.Result = append(response.Result, &apiSummaryResultPair{
|
||||||
Error: err.Error(),
|
Server: request.Servers[i],
|
||||||
}
|
Data: []SummaryRowData{},
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
response.Result = append(response.Result, &apiSummaryResultPair{
|
response.Result = append(response.Result, &apiSummaryResultPair{
|
||||||
|
@@ -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) {
|
||||||
@@ -100,7 +101,10 @@ func TestApiSummaryHandlerError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
response := apiSummaryHandler(request)
|
response := apiSummaryHandler(request)
|
||||||
|
|
||||||
assert.Equal(t, response.Error, "Mock backend error")
|
assert.Equal(t, response.Error, "")
|
||||||
|
|
||||||
|
summary := response.Result[0].(*apiSummaryResultPair)
|
||||||
|
assert.Equal(t, summary.Error, "Mock backend error")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApiWhoisHandler(t *testing.T) {
|
func TestApiWhoisHandler(t *testing.T) {
|
||||||
|
@@ -5,8 +5,19 @@
|
|||||||
<script src="/static/jsdelivr/npm/viz.js@2.1.2/viz.min.js" crossorigin="anonymous"></script>
|
<script src="/static/jsdelivr/npm/viz.js@2.1.2/viz.min.js" crossorigin="anonymous"></script>
|
||||||
<script src="/static/jsdelivr/npm/viz.js@2.1.2/lite.render.js" crossorigin="anonymous"></script>
|
<script src="/static/jsdelivr/npm/viz.js@2.1.2/lite.render.js" crossorigin="anonymous"></script>
|
||||||
<script>
|
<script>
|
||||||
|
function decodeBase64(base64) {
|
||||||
|
const text = atob(base64);
|
||||||
|
const length = text.length;
|
||||||
|
const bytes = new Uint8Array(length);
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
bytes[i] = text.charCodeAt(i);
|
||||||
|
}
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
return decoder.decode(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
var viz = new Viz();
|
var viz = new Viz();
|
||||||
viz.renderSVGElement(atob({{ .Result }}))
|
viz.renderSVGElement(decodeBase64({{ .Result }}))
|
||||||
.then(element => {
|
.then(element => {
|
||||||
document.getElementById("bgpmap").appendChild(element);
|
document.getElementById("bgpmap").appendChild(element);
|
||||||
})
|
})
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||||
<meta name="renderer" content="webkit">
|
<meta name="renderer" content="webkit">
|
||||||
<title>{{ html .Title }}</title>
|
<title>{{ html .Title }}</title>
|
||||||
<link rel="stylesheet" href="/static/jsdelivr/npm/bootstrap@4.5.1/dist/css/bootstrap.min.css" integrity="sha256-VoFZSlmyTXsegReQCNmbXrS4hBBUl/cexZvPmPWoJsY=" crossorigin="anonymous">
|
<link rel="stylesheet" href="/static/jsdelivr/npm/bootstrap@4.5.1/dist/css/bootstrap.min.css" crossorigin="anonymous">
|
||||||
<style>
|
<style>
|
||||||
.navbar-nav {
|
.navbar-nav {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -87,8 +87,8 @@
|
|||||||
{{ .Content }}
|
{{ .Content }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/jsdelivr/npm/jquery@3.5.1/dist/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
|
<script src="/static/jsdelivr/npm/jquery@3.5.1/dist/jquery.min.js" crossorigin="anonymous"></script>
|
||||||
<script src="/static/jsdelivr/npm/bootstrap@4.5.1/dist/js/bootstrap.min.js" integrity="sha256-0IiaoZCI++9oAAvmCb5Y0r93XkuhvJpRalZLffQXLok=" crossorigin="anonymous"></script>
|
<script src="/static/jsdelivr/npm/bootstrap@4.5.1/dist/js/bootstrap.min.js" crossorigin="anonymous"></script>
|
||||||
<script src="/static/sortTable.js"></script>
|
<script src="/static/sortTable.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -69,11 +70,15 @@ func (graph *RouteGraph) attrsToString(attrs RouteAttrs) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (graph *RouteGraph) escape(s string) string {
|
func (graph *RouteGraph) escape(s string) string {
|
||||||
result, err := json.Marshal(s)
|
buffer := &bytes.Buffer{}
|
||||||
|
encoder := json.NewEncoder(buffer)
|
||||||
|
encoder.SetEscapeHTML(false)
|
||||||
|
err := encoder.Encode(s)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err.Error()
|
return err.Error()
|
||||||
} else {
|
} else {
|
||||||
return string(result)
|
return string(buffer.Bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -33,7 +33,7 @@ func TestBirdRouteToGraphvizXSS(t *testing.T) {
|
|||||||
fakeResult,
|
fakeResult,
|
||||||
}, fakeResult)
|
}, fakeResult)
|
||||||
|
|
||||||
if strings.Contains(result, "<script>") {
|
if strings.Contains(result, fakeResult) {
|
||||||
t.Errorf("XSS injection succeeded: %s", result)
|
t.Errorf("XSS injection succeeded: %s", result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,18 +5,18 @@ go 1.17
|
|||||||
require (
|
require (
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||||
github.com/gorilla/handlers v1.5.2
|
github.com/gorilla/handlers v1.5.2
|
||||||
github.com/jarcoal/httpmock v1.3.1
|
github.com/jarcoal/httpmock v1.4.0
|
||||||
github.com/magiconair/properties v1.8.7
|
github.com/magiconair/properties v1.8.10
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.6
|
||||||
github.com/spf13/viper v1.18.2
|
github.com/spf13/viper v1.19.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
@@ -26,7 +26,7 @@ require (
|
|||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||||
golang.org/x/sys v0.15.0 // indirect
|
golang.org/x/sys v0.18.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
420
frontend/go.sum
420
frontend/go.sum
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@ type settingType struct {
|
|||||||
nameFilter string
|
nameFilter string
|
||||||
timeOut int
|
timeOut int
|
||||||
connectionTimeOut int
|
connectionTimeOut int
|
||||||
|
trustProxyHeaders bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var setting settingType
|
var setting settingType
|
||||||
|
@@ -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
|
||||||
|
@@ -27,6 +27,7 @@ type viperSettingType struct {
|
|||||||
NameFilter string `mapstructure:"name_filter"`
|
NameFilter string `mapstructure:"name_filter"`
|
||||||
TimeOut int `mapstructure:"timeout"`
|
TimeOut int `mapstructure:"timeout"`
|
||||||
ConnectionTimeOut int `mapstructure:"connection_timeout"`
|
ConnectionTimeOut int `mapstructure:"connection_timeout"`
|
||||||
|
TrustProxyHeaders bool `mapstructure:"trust_proxy_headers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse settings with viper, and convert to legacy setting format
|
// Parse settings with viper, and convert to legacy setting format
|
||||||
@@ -94,6 +95,9 @@ func parseSettings() {
|
|||||||
pflag.Int("connection-time-out", 5, "time before backend TCP connection times out, in seconds; defaults to 5 if not set")
|
pflag.Int("connection-time-out", 5, "time before backend TCP connection times out, in seconds; defaults to 5 if not set")
|
||||||
viper.BindPFlag("connection_timeout", pflag.Lookup("connection-time-out"))
|
viper.BindPFlag("connection_timeout", pflag.Lookup("connection-time-out"))
|
||||||
|
|
||||||
|
pflag.Bool("trust-proxy-headers", false, "Trust X-Forwared-For, X-Real-IP, X-Forwarded-Proto, X-Forwarded-Scheme and X-Forwarded-Host sent by the client")
|
||||||
|
viper.BindPFlag("trust_proxy_headers", pflag.Lookup("trust-proxy-headers"))
|
||||||
|
|
||||||
pflag.Parse()
|
pflag.Parse()
|
||||||
|
|
||||||
if err := viper.ReadInConfig(); err != nil {
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
@@ -144,6 +148,7 @@ func parseSettings() {
|
|||||||
setting.nameFilter = viperSettings.NameFilter
|
setting.nameFilter = viperSettings.NameFilter
|
||||||
setting.timeOut = viperSettings.TimeOut
|
setting.timeOut = viperSettings.TimeOut
|
||||||
setting.connectionTimeOut = viperSettings.ConnectionTimeOut
|
setting.connectionTimeOut = viperSettings.ConnectionTimeOut
|
||||||
|
setting.trustProxyHeaders = viperSettings.TrustProxyHeaders
|
||||||
|
|
||||||
fmt.Printf("%#v\n", setting)
|
fmt.Printf("%#v\n", setting)
|
||||||
}
|
}
|
||||||
|
@@ -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,67 @@ 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSummaryRowDataFromLineWithDash(t *testing.T) {
|
||||||
|
data := SummaryRowDataFromLine("ibgp_test-01 BGP --- up 07:16:51.656 Established")
|
||||||
|
|
||||||
|
assert.Equal(t, data.Name, "ibgp_test-01")
|
||||||
|
assert.Equal(t, data.Proto, "BGP")
|
||||||
|
assert.Equal(t, data.Table, "---")
|
||||||
|
assert.Equal(t, data.State, "up")
|
||||||
|
assert.Equal(t, data.Since, "07:16:51.656")
|
||||||
|
assert.Equal(t, data.Info, "Established")
|
||||||
|
}
|
||||||
|
@@ -18,13 +18,13 @@ import (
|
|||||||
|
|
||||||
var primitiveMap = map[string]string{
|
var primitiveMap = map[string]string{
|
||||||
"summary": "show protocols",
|
"summary": "show protocols",
|
||||||
"detail": "show protocols all %s",
|
"detail": "show protocols all \"%s\"",
|
||||||
"route_from_protocol": "show route protocol %s",
|
"route_from_protocol": "show route protocol \"%s\"",
|
||||||
"route_from_protocol_all": "show route protocol %s all",
|
"route_from_protocol_all": "show route protocol \"%s\" all",
|
||||||
"route_from_protocol_primary": "show route protocol %s primary",
|
"route_from_protocol_primary": "show route protocol \"%s\" primary",
|
||||||
"route_from_protocol_all_primary": "show route protocol %s all primary",
|
"route_from_protocol_all_primary": "show route protocol \"%s\" all primary",
|
||||||
"route_filtered_from_protocol": "show route filtered protocol %s",
|
"route_filtered_from_protocol": "show route filtered protocol \"%s\"",
|
||||||
"route_filtered_from_protocol_all": "show route filtered protocol %s all",
|
"route_filtered_from_protocol_all": "show route filtered protocol \"%s\" all",
|
||||||
"route_from_origin": "show route where bgp_path.last = %s",
|
"route_from_origin": "show route where bgp_path.last = %s",
|
||||||
"route_from_origin_all": "show route where bgp_path.last = %s all",
|
"route_from_origin_all": "show route where bgp_path.last = %s all",
|
||||||
"route_from_origin_primary": "show route where bgp_path.last = %s primary",
|
"route_from_origin_primary": "show route where bgp_path.last = %s primary",
|
||||||
@@ -75,7 +75,6 @@ func webHandlerWhois(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// serve up results from bird
|
// serve up results from bird
|
||||||
func webBackendCommunicator(endpoint string, command string) func(w http.ResponseWriter, r *http.Request) {
|
func webBackendCommunicator(endpoint string, command string) func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
backendCommandPrimitive, commandPresent := primitiveMap[command]
|
backendCommandPrimitive, commandPresent := primitiveMap[command]
|
||||||
if !commandPresent {
|
if !commandPresent {
|
||||||
panic("invalid command: " + command)
|
panic("invalid command: " + command)
|
||||||
@@ -195,7 +194,6 @@ func webHandlerBGPMap(endpoint string, command string) func(w http.ResponseWrite
|
|||||||
|
|
||||||
// set up routing paths and start webserver
|
// set up routing paths and start webserver
|
||||||
func webServerStart(l net.Listener) {
|
func webServerStart(l net.Listener) {
|
||||||
|
|
||||||
// redirect main page to all server summary
|
// redirect main page to all server summary
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, "/summary/"+url.PathEscape(strings.Join(setting.servers, "+")), 302)
|
http.Redirect(w, r, "/summary/"+url.PathEscape(strings.Join(setting.servers, "+")), 302)
|
||||||
@@ -239,5 +237,11 @@ func webServerStart(l net.Listener) {
|
|||||||
http.HandleFunc("/telegram/", webHandlerTelegramBot)
|
http.HandleFunc("/telegram/", webHandlerTelegramBot)
|
||||||
|
|
||||||
// Start HTTP server
|
// Start HTTP server
|
||||||
http.Serve(l, handlers.LoggingHandler(os.Stdout, http.DefaultServeMux))
|
var handler http.Handler
|
||||||
|
handler = http.DefaultServeMux
|
||||||
|
if setting.trustProxyHeaders {
|
||||||
|
handler = handlers.ProxyHeaders(handler)
|
||||||
|
}
|
||||||
|
handler = handlers.LoggingHandler(os.Stdout, handler)
|
||||||
|
http.Serve(l, handler)
|
||||||
}
|
}
|
||||||
|
@@ -88,7 +88,7 @@ func TestWhoisWithoutServer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestWhoisConnectionError(t *testing.T) {
|
func TestWhoisConnectionError(t *testing.T) {
|
||||||
setting.whoisServer = "127.0.0.1:0"
|
setting.whoisServer = "127.0.0.1:1"
|
||||||
result := whois("AS6939")
|
result := whois("AS6939")
|
||||||
if !strings.Contains(result, "connect: connection refused") {
|
if !strings.Contains(result, "connect: connection refused") {
|
||||||
t.Errorf("Whois AS6939 without server produced output, got %s", result)
|
t.Errorf("Whois AS6939 without server produced output, got %s", result)
|
||||||
|
12
proxy/go.mod
12
proxy/go.mod
@@ -5,17 +5,17 @@ go 1.17
|
|||||||
require (
|
require (
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||||
github.com/gorilla/handlers v1.5.2
|
github.com/gorilla/handlers v1.5.2
|
||||||
github.com/magiconair/properties v1.8.7
|
github.com/magiconair/properties v1.8.10
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.6
|
||||||
github.com/spf13/viper v1.18.2
|
github.com/spf13/viper v1.19.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
@@ -25,7 +25,7 @@ require (
|
|||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||||
golang.org/x/sys v0.15.0 // indirect
|
golang.org/x/sys v0.18.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
412
proxy/go.sum
412
proxy/go.sum
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user