From 8cde2ae854c08851db36531e7b7a1485abf3b08e Mon Sep 17 00:00:00 2001 From: Simon Marsh Date: Sun, 18 Oct 2020 13:55:13 +0100 Subject: [PATCH] re-work loadsa stuff --- StaticRoot/grc.js | 327 +++++++++++++++++++++++++++++++++--------- StaticRoot/index.html | 146 ++++++++++--------- data.go | 47 ++++-- ingest.go | 14 +- 4 files changed, 384 insertions(+), 150 deletions(-) diff --git a/StaticRoot/grc.js b/StaticRoot/grc.js index 347502d..157f21b 100644 --- a/StaticRoot/grc.js +++ b/StaticRoot/grc.js @@ -7,6 +7,7 @@ const ExplorerURL='https://explorer.burble.com/#' ////////////////////////////////////////////////////////////////////////// // smaller display components +// display an ASN Vue.component('reg-asn', { template: '#reg-asn', props: [ 'asn' ], @@ -27,6 +28,7 @@ Vue.component('reg-asn', { } }) +// display a path of ASNs Vue.component('reg-path', { template: '#reg-path', props: [ 'path' ], @@ -40,6 +42,7 @@ Vue.component('reg-path', { } }) +// display a inet{6,}num Vue.component('reg-prefix', { template: '#reg-prefix', props: [ 'prefix' ], @@ -63,7 +66,88 @@ Vue.component('reg-prefix', { } }) +// display mntner(s) +Vue.component('reg-mnts', { + template: '#reg-mnts', + props: [ 'mnts' ], + data() { + return { } + } +}) +// display mntner +Vue.component('reg-mnt', { + template: '#reg-mnt', + props: [ 'mnt' ], + data() { + return { } + }, + computed: { + url: function() { + return ExplorerURL + "/mntner/" + this.mnt + } + } +}) + + +// display a set of AS Paths +Vue.component('rt-paths', { + template: '#rt-paths', + props: [ 'paths' ], + data() { + return { } + }, + methods: { + showPaths: function() { + const e = this.$createElement + + // create a new array of reversed paths + var paths = [ ] + this.paths.forEach(path => { + paths.push(path.split(" ").reverse()) + }) + // and sort for easier comparing + paths.sort( + (a,b) => a.join(" ").localeCompare(b.join(" ")) + ) + + // now build the modal dialog + + // for each path in the list + var pathList = [ e('p', { }, 'Paths are reverse sorted') ] + paths.forEach(path => { + + // for each ASN in the path + var asnList = [ ] + path.forEach(asn => { + asnList.push(e('b-link', { + class: 'px-1 my-0 py-0', + props: { + href: ExplorerURL + "/aut-num/" + asn + } + }, asn)) + }) + + pathList.push(e('li', { + class: 'my-0 py-0' + }, asnList)) + }) + + var vnode = e('ul', { }, pathList) + + this.$bvModal.msgBoxOk([vnode], { + title: 'AS Paths', + size: 'xl', + centered: true + }) + } + }, + computed: { + count: function() { + return this.paths.length + } + } +}) ////////////////////////////////////////////////////////////////////////// // flap list component @@ -73,52 +157,47 @@ Vue.component('app-flaps', { data() { return { filter: '', - currentPage: 1, - total: 0, - fields: [ - { - key: 'prefix', - label: 'Prefix', - sortable: true - }, - { - key: 'path', - label: 'Path', - sortable: true - }, - { - key: 'count', - label: 'Updates', - sortable: true, - sortDirection: 'desc' - } - ], - flaps: [ ] } }, computed: { - rows: function() { - return this.flaps.length; - } - }, - methods: { - update: function(data) { - this.flaps.splice(0); + + total: function() { + return this.$root.flapsTotal + }, + + // converts the map to an array for iterating, + // applying any filtering that is required + list: function() { - this.total = data.total; - this.flaps = data.list.map((update) => { - var tmp = update.path.split(" "); - update.path = tmp.map((asn) => { - return "AS" + asn; - }).join(" ") - return update; + var list = [ ] + var flaps = this.$root.flaps + var total = this.total + + Object.keys(flaps).forEach((prefix) => { + + if (this.filter == '' || prefix.includes(this.filter)) { + list.push({ + prefix: prefix, + count: flaps[prefix].count, + percent: Math.round(flaps[prefix].count*100 / total), + paths: flaps[prefix].paths, + mntner: flaps[prefix].mntner + }) + } }) + + list.sort( + (a,b) => b.count - a.count + ) + + return list.slice(0, 10) } }, mounted() { this.$root.$on('flaps-update', data => { this.update(data) }) + } }) @@ -131,37 +210,53 @@ Vue.component('app-roa', { data() { return { filter4: '', - filter6: '', - roaFields: [ - { - key: 'prefix', - label: 'Prefix', - sortable: true - }, - { - key: 'origin', - label: 'Origin', - sortable: true - } - ], - roa4: [ ], - roa6: [ ] + filter6: '' } }, - methods: { - update: function(data) { - this.roa4.splice(0); - this.roa6.splice(0); - - data.list.forEach((roa) => { - var nroa = { - origin: "AS" + roa.origin, - prefix: roa.prefix - }; - if (roa.prefix.includes(":")) { this.roa6.push(nroa) } - else { this.roa4.push(nroa) } + computed: { + list4: function() { + var list = [ ] + + this.$root.roas.forEach((roa) => { + if (!roa.prefix.includes(':') && + (this.filter4 == '' || + roa.prefix.includes(this.filter4))) { + list.push(roa) + } }) - } + + // sort by origin + list.sort((a,b) => { + var asna = a.origin.substr(2) + var asnb = b.origin.substr(2) + return asna - asnb + }) + + return list + }, + + list6: function() { + var list = [ ] + + this.$root.roas.forEach((roa) => { + if (roa.prefix.includes(':') && + (this.filter6 == '' || + roa.prefix.includes(this.filter6))) { + list.push(roa) + } + }) + + // sort by origin + list.sort((a,b) => { + var asna = a.origin.substr(2) + var asnb = b.origin.substr(2) + return asna - asnb + }) + + return list + } + + }, mounted() { this.$root.$on('roa-update', data => { @@ -178,7 +273,8 @@ Vue.component('app-timer', { data() { return { seconds: 0, - timer: 0 + timer: 0, + roaCounter: 0 } }, methods: { @@ -186,15 +282,65 @@ Vue.component('app-timer', { if (this.seconds <= 0) { this.seconds = 61; + // refresh flaps data axios.get('/api/flaps').then(response => { - this.$root.$emit('flaps-update', response.data) + + // build a new map of the prefixes and total updates + var flaps = { } + var total = 0 + + response.data.list.forEach((update) => { + + // initialise if no existing entry for this prefix + if (!(update.prefix in flaps)) { + flaps[update.prefix] = { + count: 0, + paths: [ ], + mntner: this.$root.mntmap[update.prefix] || [ ] + } + } + + // canonicalise the ASN path + var tmp = update.path.split(" ") + var path = tmp.map((asn) => { + return "AS" + asn + }).join(" ") + + // finally update the prefix entry + flaps[update.prefix].count += update.count + flaps[update.prefix].paths.push(path) + + total += update.count + }) + + this.$root.flaps = flaps + this.$root.flapsTotal = total + }); - - axios.get('/api/roa').then(response => { - this.$root.$emit('roa-update', response.data) - }); - + + if (this.roaCounter == 0) { + this.roaCounter = 59 + + // refresh roa data + axios.get('/api/roa').then(response => { + var roas = [ ] + response.data.list.forEach((roa) => { + var asn = "AS" + roa.origin + roas.push({ + origin: asn, + prefix: roa.prefix, + mntner: this.$root.mntmap[asn] || [ ] + }) + }) + this.$root.roas = roas + }); + } + else { + this.roaCounter -= 1 + } + } + // countdown every second this.seconds -= 1; this.timer = setTimeout(() => this.trigger(), 1000); } @@ -220,7 +366,46 @@ const router = new VueRouter({ // and the main app instance const vm = new Vue({ el: '#grc_realtime', - data: { }, + data: { + flaps: { }, + flapsTotal: 0, + roas: [ ], + mntmap: { } + }, + mounted() { + axios.get('https://explorer.burble.com/api/registry/*inet/*/mnt-by?raw').then(response => { + + // put the response in the mntmap hash + Object.keys(response.data).forEach(index => { + var slash = index.indexOf('/') + var prefix = index.substr(slash + 1) + var prefix = prefix.replace('_', '/') + this.mntmap[prefix] = response.data[index]['mnt-by'] + }) + + // update the flaps list if required + Object.keys(this.flaps).forEach(prefix => { + this.flaps[prefix].mntner = this.mntmap[prefix] + }) + + }) + + axios.get('https://explorer.burble.com/api/registry/aut-num/*/mnt-by?raw').then(response => { + + // put the response in the mntmap hash + Object.keys(response.data).forEach(index => { + var slash = index.indexOf('/') + var autnum = index.substr(slash + 1) + this.mntmap[autnum] = response.data[index]['mnt-by'] + }) + + // and roas + this.roas.forEach(roa => { + roa.mntner = this.mntmap[roa.origin] + }) + + }) + }, router }) diff --git a/StaticRoot/index.html b/StaticRoot/index.html index b79174b..0e5d35d 100644 --- a/StaticRoot/index.html +++ b/StaticRoot/index.html @@ -76,47 +76,43 @@ @@ -131,26 +127,28 @@ - - - - + + + Prefix + Origin + MNTNER + + + + + + + + + - +

Type to filter prefixes

@@ -164,26 +162,28 @@
- - - - - + + + Prefix + Origin + MNTNER + + + + + + + + + + - +

Type to filter prefixes

@@ -203,6 +203,18 @@ + + + + + + diff --git a/data.go b/data.go index cd01901..85ff74e 100644 --- a/data.go +++ b/data.go @@ -70,7 +70,8 @@ type SnapshotData struct { } type DataStruct struct { - shutdown context.CancelFunc + shutdown context.CancelFunc + roaCounter uint // sets for currently cellecting and previous data active *ActiveData @@ -119,25 +120,49 @@ func (data *DataStruct) swap(ctx context.Context) { case <-ticker.C: // tiemr expired - log.Debug("Data Update") + log.Debug("Route Data Update") var upp *unsafe.Pointer var up unsafe.Pointer var oldp unsafe.Pointer - // swap in a new empty data set - upp = (*unsafe.Pointer)(unsafe.Pointer(&data.active)) - up = unsafe.Pointer(data.newActiveData()) + // swap in new route data + upp = (*unsafe.Pointer)(unsafe.Pointer(&data.active.route)) + up = unsafe.Pointer(data.newRouteData()) oldp = atomic.SwapPointer(upp, up) - // generate the reporting snapshot - active := (*ActiveData)(oldp) - snapshot := active.snapshot() + // create a snapshot of the old data + routed := (*RouteData)(oldp) + snapshot := routed.snapshot() // then swap in the new snapshot - upp = (*unsafe.Pointer)(unsafe.Pointer(&data.snapshot)) + upp = (*unsafe.Pointer)(unsafe.Pointer(&data.snapshot.route)) up = unsafe.Pointer(snapshot) atomic.SwapPointer(upp, up) + + if data.roaCounter == 0 { + data.roaCounter = 59 + + log.Debug("ROA Data Update") + + // swap in new roa data + upp = (*unsafe.Pointer)(unsafe.Pointer(&data.active.roa)) + up = unsafe.Pointer(data.newROAData()) + oldp = atomic.SwapPointer(upp, up) + + // create a snapshot of the old data + road := (*ROAData)(oldp) + snapshot := road.snapshot() + + // then swap in the new snapshot + upp = (*unsafe.Pointer)(unsafe.Pointer(&data.snapshot.roa)) + up = unsafe.Pointer(snapshot) + atomic.SwapPointer(upp, up) + + } else { + data.roaCounter -= 1 + } + } } } @@ -171,8 +196,8 @@ func (route *RouteData) add(path string) { route.paths[path] += 1 } -func (roa *ROAData) add(path string) { - roa.roas[path] += 1 +func (roa *ROAData) add(prefix string) { + roa.roas[prefix] += 1 } ////////////////////////////////////////////////////////////////////////// diff --git a/ingest.go b/ingest.go index 22f9568..b048940 100644 --- a/ingest.go +++ b/ingest.go @@ -98,8 +98,9 @@ func (ingest *Ingest) tail(ctx context.Context, sockPath string) { // trim the trailing newline line = strings.TrimSuffix(line, "\n") - - if len(line) > 3 { + ix := strings.Index(line, " ") + if (ix != -1) && (len(line) > (ix + 10)) { + line = line[ix+7:] log.WithFields(log.Fields{ "line": line, @@ -137,7 +138,14 @@ func (ingest *Ingest) route(data string) { "data": data, }).Debug("route update") - ingest.data.active.route.add(data) + space := strings.IndexByte(data, ' ') + if space != -1 { + + prefix := data[:space] + path := data[space+7 : len(data)-1] + + ingest.data.active.route.add(prefix + " " + path) + } } //////////////////////////////////////////////////////////////////////////