Public
Edited
Feb 23, 2024
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
/* plot3 = Plot.plot({
marginLeft: 150,
y: {
label: "RIS peers seeing",
grid: true
//domain: d3.groupSort(msgs, (a, b) => a.rrc <= b.rrc, e => e.peer) // #TODO doesn't work yet
},
x: {
domain,
label: "Time (UTC)",
grid: true
},
marks: [
Plot.line( bgp_parsed.visibility, {x: d=> parseTime( d['ts']), y: 'cnt', stroke: "orange"} )
]
}) */
Insert cell
/* {
const zoom = d3.zoom().on("zoom", allZoom);

function allZoom(e) {
handleZoom(e, plot);
//handleZoom(e, plot2);
}

function handleZoom(e, plt) {
const scale = plt.scale("x");
const x = d3.scaleLinear().domain(scale.domain).range(scale.range);
const startDate = new Date(
e.transform.rescaleX(x).domain()[0]
// Math.max(e.transform.rescaleX(x).domain()[0], extent[0].getTime())
);

const endDate = new Date(
e.transform.rescaleX(x).domain()[1]
//Math.min(e.transform.rescaleX(x).domain()[1], extent[1])
);

mutable domain = [startDate, endDate];
}

function initZoom() {
d3.select(plot).call(zoom);
//d3.select(plot2).call(zoom);
} */

initZoom();
}
Insert cell
bgp_raw = {
const url=`https://stat.ripe.net/data/bgplay/data.json?resource=${pfx}&rrcs=0%2C1%2C3%2C5%2C6%2C7%2C10%2C11%2C13%2C14%2C15%2C16%2C18%2C20%2C25&starttime=${timeFmt( domain[0] ) - 1800 }&endtime=${ timeFmt( domain[1] ) + 1800 }&unix_timestamps=TRUE`
console.log( url );
const data = fetch( url ).then((response) => response.json());
return data
}
Insert cell
bgp_parsed = { // need to split fetch and parse (ie. bgp_raw and bgp_parsed cells) because of reactiveness of await/response stuff that I don't really grok
const bgp = {'initial_peers': 0, 'update_msg': [], 'visibility': [], 'po_vis': {}};
const peer_state = new Set(); // TODO remove
const po_state = new Object; // contains prefix/origin state
for (const idx in bgp_raw.data.initial_state) {
const init_state = bgp_raw.data.initial_state[ idx ];
peer_state.add( init_state.source_id );
var po = init_state.target_prefix + " " + init_state.path[ init_state.path.length - 1 ];
if (! po_state.hasOwnProperty( po ) ) {
po_state[ po ] = new Set();
}
po_state[ po ].add( init_state.source_id );
bgp.initial_peers += 1;
}
bgp.visibility.push({'ts': bgp_raw.data.query_starttime, 'cnt': peer_state.size});
for ( var po in po_state ) {
bgp.po_vis[ po ] = [];
bgp.po_vis[ po ].push({'ts': bgp_raw.data.query_starttime, 'cnt': po_state[po].size})
}
for (var idx in bgp_raw.data.events) {
const msg = bgp_raw.data.events[ idx ];
const ts = msg.timestamp;
if( msg.type == 'A' ) {
peer_state.add( msg.attrs.source_id );
var po = msg.attrs.target_prefix + " " + msg.attrs.path[ msg.attrs.path.length - 1 ];
if (! po_state.hasOwnProperty( po ) ) {
po_state[ po ] = new Set();
}
po_state[ po ].add( msg.attrs.source_id )
}
if( msg.type == 'W' ) { // now we need more work, since we don't know the original origin
for ( var po in po_state ) {
po_state[ po ].delete( msg.attrs.source_id )
}
peer_state.delete( msg.attrs.source_id )
}
if ( peer_state.size != bgp.visibility[ bgp.visibility.length - 1 ]['cnt'] ) {
if ( msg.timestamp != bgp.visibility[ bgp.visibility.length - 1]['ts'] ) { // only chance once per ts granularity (seconds) # need to keep as string for this to work
bgp.visibility.push({'ts': ts, 'cnt': peer_state.size});
}
}
bgp.update_msg.push({'ts': parseTimeStat( msg['timestamp'] )
})
// update the per prefix/origin visibility timelines
for ( var po in po_state ) {
if (! bgp.po_vis.hasOwnProperty( po ) ) {
bgp.po_vis[ po ] = [];
bgp.po_vis[ po ].push({'ts': ts, 'cnt': po_state[ po ].size });
} else {
var old_rec = bgp.po_vis[ po ][ bgp.po_vis[ po ].length - 1 ];
if ( old_rec['ts'] != ts && old_rec['cnt'] != po_state[ po ].size ) {
bgp.po_vis[ po ].push({'ts': ts, 'cnt': po_state[ po ].size });
}
}
}

} // end processing each event
// put a stopper at the end
bgp.visibility.push({'ts': bgp_raw.data.query_endtime, 'cnt': peer_state.size});
for ( var po in bgp.po_vis ) {
const cnt = bgp.po_vis[ po ][ bgp.po_vis[po].length - 1 ]['cnt'];
bgp.po_vis[ po ].push({'ts': bgp_raw.data.query_endtime, 'cnt': cnt });
}
return bgp
}
Insert cell
pfx = selected_vrp.split(' ')[1]
Insert cell
extent = d3.extent( data.vrps[ selected_vrp ].timeline, d => parseTime( d[0] ) )
Insert cell
mutable domain = extent;
Insert cell
vrps = Object.keys( data.vrps )
Insert cell
data = fetch("https://rd-www-1.ripe.net/rpki_flutter/daily/rpki-flutter.2024.02.22.json").then((response) => response.json())
//data = fetch("https://sg-pub.ripe.net/emile/rpki-flutter/flutter.2023-11-01.2023-11-08.json").then((response) => response.json())
Insert cell
parseTime = d3.utcParse("%s");
Insert cell
parseTimeStat = d3.utcParse('%Y-%m-%dT%H:%M:%S')
Insert cell
timeFmt = d3.timeFormat("%s")
Insert cell
timeFmtHuman = d3.timeFormat('%Y-%m-%d %H:%M:%S')
Insert cell
Insert cell
timeFmtDots = d3.timeFormat('%Y.%m.%d')
Insert cell
sel_date_dots = date
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more