new: Major rewrite of the website

pull/12/head
Raphaël Vinot 2018-04-12 18:09:04 +02:00
parent 5cfd5df97e
commit 6b18e9921c
7 changed files with 90 additions and 43 deletions

View File

@ -3,7 +3,6 @@
from typing import TypeVar from typing import TypeVar
import datetime import datetime
from enum import Enum
from dateutil.parser import parse from dateutil.parser import parse
import logging import logging
@ -15,11 +14,6 @@ from .libs.exceptions import InvalidDateFormat
Dates = TypeVar('Dates', datetime.datetime, datetime.date, str) Dates = TypeVar('Dates', datetime.datetime, datetime.date, str)
class IPVersion(Enum):
v4 = 'v4'
v6 = 'v6'
class Querying(): class Querying():
def __init__(self, loglevel: int=logging.DEBUG): def __init__(self, loglevel: int=logging.DEBUG):
@ -43,34 +37,35 @@ class Querying():
except ValueError: except ValueError:
raise InvalidDateFormat('Unable to parse the date. Should be YYYY-MM-DD.') raise InvalidDateFormat('Unable to parse the date. Should be YYYY-MM-DD.')
def asns_global_ranking(self, date: Dates=datetime.date.today(), def asns_global_ranking(self, date: Dates=datetime.date.today(), source: str='', ipversion: str='v4', limit: int=100):
ipversion: IPVersion=IPVersion.v4, limit: int=100):
'''Aggregated ranking of all the ASNs known in the system, weighted by source.''' '''Aggregated ranking of all the ASNs known in the system, weighted by source.'''
d = self.__normalize_date(date) d = self.__normalize_date(date)
key = f'{d}|asns|{ipversion.value}' if source:
key = f'{d}|{source}|asns|{ipversion}'
else:
key = f'{d}|asns|{ipversion}'
return self.ranking.zrevrange(key, start=0, end=limit, withscores=True) return self.ranking.zrevrange(key, start=0, end=limit, withscores=True)
def asn_details(self, asn: int, date: Dates= datetime.date.today(), ipversion: IPVersion=IPVersion.v4): def asn_details(self, asn: int, date: Dates= datetime.date.today(), source: str='', ipversion: str='v4'):
'''Aggregated ranking of all the prefixes anounced by the given ASN, weighted by source.''' '''Aggregated ranking of all the prefixes anounced by the given ASN, weighted by source.'''
d = self.__normalize_date(date) d = self.__normalize_date(date)
key = f'{d}|{asn}|{ipversion.value}' if source:
key = f'{d}|{source}|{asn}|rank{ipversion}|prefixes'
else:
key = f'{d}|{asn}|{ipversion}'
return self.ranking.zrevrange(key, start=0, end=-1, withscores=True) return self.ranking.zrevrange(key, start=0, end=-1, withscores=True)
def asn_rank(self, asn: int, date: Dates= datetime.date.today(), ipversion: IPVersion=IPVersion.v4): def asn_rank(self, asn: int, date: Dates= datetime.date.today(), source: str='', ipversion: str='v4'):
'''Get the rank of a single ASN, weighted by source.''' '''Get the rank of a single ASN, weighted by source.'''
d = self.__normalize_date(date) d = self.__normalize_date(date)
key = f'{d}|asns|{ipversion.value}' if source:
key = f'{d}|{source}|{asn}|rank{ipversion}'
else:
key = f'{d}|asns|{ipversion}'
return self.ranking.zscore(key, asn) return self.ranking.zscore(key, asn)
def asn_rank_by_source(self, asn: int, source: str, date: Dates= datetime.date.today(), ipversion: IPVersion=IPVersion.v4): def get_sources(self, date: Dates= datetime.date.today()):
'''Get the rank of a single ASN, not weighted by source.''' '''Get the sources availables for a specific day (default: today).'''
d = self.__normalize_date(date) d = self.__normalize_date(date)
key = f'{d}|{source}|{asn}|rank{ipversion.value}' key = f'{d}|sources'
return self.ranking.get(key) return self.storage.smembers(key)
def asn_details_by_source(self, source: str, asn: int, date: Dates= datetime.date.today(),
ipversion: IPVersion=IPVersion.v4):
'''Get the rank of all the prefixes announced by an ASN, not weighted by source.'''
d = self.__normalize_date(date)
key = f'{d}|{source}|{asn}|rank{ipversion.value}|prefixes'
return self.ranking.zrevrange(key, start=0, end=-1, withscores=True)

View File

@ -37,6 +37,9 @@ class Ranking():
r_pipeline = self.ranking.pipeline() r_pipeline = self.ranking.pipeline()
for source in self.storage.smembers(f'{today}|sources'): for source in self.storage.smembers(f'{today}|sources'):
self.logger.info(f'{today} - Ranking source: {source}') self.logger.info(f'{today} - Ranking source: {source}')
source_aggregation_key_v4 = f'{today}|{source}|asns|v4'
source_aggregation_key_v6 = f'{today}|{source}|asns|v6'
to_delete.update([source_aggregation_key_v4, source_aggregation_key_v6])
for asn in self.storage.smembers(f'{today}|{source}'): for asn in self.storage.smembers(f'{today}|{source}'):
prefixes_aggregation_key_v4 = f'{today}|{asn}|v4' prefixes_aggregation_key_v4 = f'{today}|{asn}|v4'
prefixes_aggregation_key_v6 = f'{today}|{asn}|v6' prefixes_aggregation_key_v6 = f'{today}|{asn}|v6'
@ -65,11 +68,13 @@ class Ranking():
if asn_rank_v4: if asn_rank_v4:
r_pipeline.set(f'{today}|{source}|{asn}|rankv4', asn_rank_v4) r_pipeline.set(f'{today}|{source}|{asn}|rankv4', asn_rank_v4)
r_pipeline.zincrby(asns_aggregation_key_v4, asn, asn_rank_v4) r_pipeline.zincrby(asns_aggregation_key_v4, asn, asn_rank_v4)
r_pipeline.zadd(source_aggregation_key_v4, asn_rank_v4, asn)
if v6count: if v6count:
asn_rank_v6 /= float(v6count) asn_rank_v6 /= float(v6count)
if asn_rank_v6: if asn_rank_v6:
r_pipeline.set(f'{today}|{source}|{asn}|rankv6', asn_rank_v6) r_pipeline.set(f'{today}|{source}|{asn}|rankv6', asn_rank_v6)
r_pipeline.zincrby(asns_aggregation_key_v6, asn, asn_rank_v6) r_pipeline.zincrby(asns_aggregation_key_v6, asn, asn_rank_v6)
r_pipeline.zadd(source_aggregation_key_v6, asn_rank_v4, asn)
self.ranking.delete(*to_delete) self.ranking.delete(*to_delete)
r_pipeline.execute() r_pipeline.execute()

View File

@ -3,5 +3,7 @@
set -e set -e
set -x set -x
mkdir -p web/static/
wget https://code.jquery.com/ui/1.12.1/jquery-ui.js -O web/static/jquery-ui.js wget https://code.jquery.com/ui/1.12.1/jquery-ui.js -O web/static/jquery-ui.js
wget https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css -O web/static/jquery-ui.css wget https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css -O web/static/jquery-ui.css

View File

@ -1,4 +1,3 @@
# Web thing # Web thing
flask flask
flask-bootstrap flask-bootstrap
Flask-Datepicker

View File

@ -1,34 +1,60 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from flask import Flask, render_template, request from flask import Flask, render_template, request, session
from flask_bootstrap import Bootstrap from flask_bootstrap import Bootstrap
from flask_datepicker import datepicker
from bgpranking.querying import Querying from bgpranking.querying import Querying
from pathlib import Path
from datetime import date, timedelta
app = Flask(__name__) app = Flask(__name__)
app.secret_key = '\xeb\xfd\x1b\xee\xed<\xa5~\xd5H\x85\x00\xa5r\xae\x80t5@\xa2&>\x03S'
Bootstrap(app) Bootstrap(app)
app.config['BOOTSTRAP_SERVE_LOCAL'] = True app.config['BOOTSTRAP_SERVE_LOCAL'] = True
datepicker(app=app, local=['static/jquery-ui.js', 'static/jquery-ui.css'])
app.debug = True jquery_js = Path('static', 'jquery-ui.js')
jquery_css = Path('static', 'jquery-ui.css')
@app.route('/', methods=['GET']) def load_session():
if request.method == 'POST':
d = request.form
elif request.method == 'GET':
d = request.args
if 'date' in d:
session['date'] = d['date']
if 'ipversion' in d:
session['ipversion'] = d['ipversion']
if 'source' in d:
session['source'] = d['source']
if 'asn' in d:
session['asn'] = d['asn']
set_default_date_session()
def set_default_date_session():
if 'date' not in session:
session['date'] = (date.today() - timedelta(days=1)).isoformat()
@app.route('/', methods=['GET', 'POST'])
def index(): def index():
load_session()
q = Querying() q = Querying()
ranks = q.asns_global_ranking(limit=-1) sources = q.get_sources(date=session['date'])
return render_template('index.html', ranks=ranks) session.pop('asn', None)
ranks = q.asns_global_ranking(limit=-1, **session)
return render_template('index.html', ranks=ranks, sources=sources, **session)
@app.route('/asn', methods=['GET', 'POST']) @app.route('/asn', methods=['GET', 'POST'])
def asn_details(): def asn_details():
load_session()
q = Querying() q = Querying()
if request.method == 'POST': ranks = q.asn_details(**session)
asn = request.form['asn'] return render_template('asn.html', ranks=ranks, **session)
if request.method == 'GET':
asn = request.args['asn']
ranks = q.asn_details(asn)
return render_template('asn.html', asn=asn, ranks=ranks)

View File

@ -6,13 +6,35 @@
<center> <center>
<h1>BGP Ranking</h1></br></br> <h1>BGP Ranking</h1></br></br>
</center> </center>
<p>
<form style="width:300px; display:inline-block;" action="" method=post>
<input name="date" type="date" value="{{ date }}">
<input type="submit" value="Set date">
</form>
<form style="width:300px; display:inline-block;" action="" method=post>
<select name="ipversion">
<option value="v4" {% if ipversion == 'v4' %} selected="selected"{% endif %}>v4</option>
<option value="v6" {% if ipversion == 'v6' %} selected="selected"{% endif %}>v6</option>
</select>
<input type="submit" value="Set IP version">
</form>
<form style="width:300px; display:inline-block;" action="" method=post>
<select name="source">
<option value="" {% if not source %} selected="selected"{% endif %}>all</option>
{% for s in sources %}
<option value="{{ s }}" {% if source == s %} selected="selected"{% endif %}>{{ s }}</option>
{% endfor %}
</select>
<input type="submit" value="Set source">
</form>
<p>
<br/>
<form action="{{ url_for('asn_details') }}" method=post> <form action="{{ url_for('asn_details') }}" method=post>
ASN to search: <input type=number name=asn> ASN to search: <input type=number name=asn>
<input type="submit" value="Search"> <input type="submit" value="Search">
</form> </form>
<form class="verticalform"> <br/>
<input type="text" class="form-control dp" />
</form>
<table class="table"> <table class="table">
<tr> <tr>
<th>ASN</th> <th>ASN</th>

View File

@ -2,8 +2,6 @@
{% block scripts %} {% block scripts %}
{{ super() }} {{ super() }}
{{ datepicker.loader() }}
{{ datepicker.picker(id=".dp") }}
{% endblock %} {% endblock %}
{% block head %} {% block head %}