new: Allow to select multiple sources, caching.
parent
9cab8d1196
commit
0398d2f2b9
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from typing import TypeVar
|
from typing import TypeVar, Union
|
||||||
import datetime
|
import datetime
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from dateutil.parser import parse
|
from dateutil.parser import parse
|
||||||
|
@ -24,6 +24,7 @@ class Querying():
|
||||||
self.storage = StrictRedis(unix_socket_path=get_socket_path('storage'), decode_responses=True)
|
self.storage = StrictRedis(unix_socket_path=get_socket_path('storage'), decode_responses=True)
|
||||||
self.ranking = StrictRedis(unix_socket_path=get_socket_path('storage'), db=1, decode_responses=True)
|
self.ranking = StrictRedis(unix_socket_path=get_socket_path('storage'), db=1, decode_responses=True)
|
||||||
self.asn_meta = StrictRedis(unix_socket_path=get_socket_path('storage'), db=2, decode_responses=True)
|
self.asn_meta = StrictRedis(unix_socket_path=get_socket_path('storage'), db=2, decode_responses=True)
|
||||||
|
self.cache = StrictRedis(unix_socket_path=get_socket_path('ris'), db=1, decode_responses=True)
|
||||||
|
|
||||||
def __init_logger(self, loglevel: int):
|
def __init_logger(self, loglevel: int):
|
||||||
self.logger = logging.getLogger(f'{self.__class__.__name__}')
|
self.logger = logging.getLogger(f'{self.__class__.__name__}')
|
||||||
|
@ -40,33 +41,77 @@ 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(), source: str='', ipversion: str='v4', limit: int=100):
|
def ranking_cache_wrapper(self, key):
|
||||||
|
if not self.cache.exists(key):
|
||||||
|
if self.ranking.exists(key):
|
||||||
|
key_dump = self.ranking.dump(key)
|
||||||
|
# Cache for 10 hours
|
||||||
|
self.cache.restore(key, 36000, key_dump, True)
|
||||||
|
|
||||||
|
def asns_global_ranking(self, date: Dates=datetime.date.today(), source: Union[list, str]='',
|
||||||
|
ipversion: str='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)
|
||||||
if source:
|
if source:
|
||||||
key = f'{d}|{source}|asns|{ipversion}'
|
if isinstance(source, list):
|
||||||
|
keys = []
|
||||||
|
for s in source:
|
||||||
|
key = f'{d}|{s}|asns|{ipversion}'
|
||||||
|
self.ranking_cache_wrapper(key)
|
||||||
|
keys.append(key)
|
||||||
|
# union the ranked sets
|
||||||
|
key = '|'.join(sorted(source)) + f'|{d}|asns|{ipversion}'
|
||||||
|
if not self.cache.exists(key):
|
||||||
|
self.cache.zunionstore(key, keys)
|
||||||
|
else:
|
||||||
|
key = f'{d}|{source}|asns|{ipversion}'
|
||||||
else:
|
else:
|
||||||
key = f'{d}|asns|{ipversion}'
|
key = f'{d}|asns|{ipversion}'
|
||||||
return self.ranking.zrevrange(key, start=0, end=limit, withscores=True)
|
self.ranking_cache_wrapper(key)
|
||||||
|
return self.cache.zrevrange(key, start=0, end=limit, withscores=True)
|
||||||
|
|
||||||
def asn_details(self, asn: int, date: Dates= datetime.date.today(), source: str='', ipversion: str='v4'):
|
def asn_details(self, asn: int, date: Dates= datetime.date.today(), source: Union[list, 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)
|
||||||
if source:
|
if source:
|
||||||
key = f'{d}|{source}|{asn}|{ipversion}|prefixes'
|
if isinstance(source, list):
|
||||||
|
keys = []
|
||||||
|
for s in source:
|
||||||
|
key = f'{d}|{s}|{asn}|{ipversion}|prefixes'
|
||||||
|
self.ranking_cache_wrapper(key)
|
||||||
|
keys.append(key)
|
||||||
|
# union the ranked sets
|
||||||
|
key = '|'.join(sorted(source)) + f'|{d}|{asn}|{ipversion}'
|
||||||
|
if not self.cache.exists(key):
|
||||||
|
self.cache.zunionstore(key, keys)
|
||||||
|
else:
|
||||||
|
key = f'{d}|{source}|{asn}|{ipversion}|prefixes'
|
||||||
else:
|
else:
|
||||||
key = f'{d}|{asn}|{ipversion}'
|
key = f'{d}|{asn}|{ipversion}'
|
||||||
return self.ranking.zrevrange(key, start=0, end=-1, withscores=True)
|
self.ranking_cache_wrapper(key)
|
||||||
|
return self.cache.zrevrange(key, start=0, end=-1, withscores=True)
|
||||||
|
|
||||||
def asn_rank(self, asn: int, date: Dates=datetime.date.today(), source: str='', ipversion: str='v4'):
|
def asn_rank(self, asn: int, date: Dates=datetime.date.today(), source: Union[list, 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)
|
||||||
if source:
|
if source:
|
||||||
key = f'{d}|{source}|{asn}|{ipversion}'
|
if isinstance(source, list):
|
||||||
r = self.ranking.get(key)
|
keys = []
|
||||||
|
for s in source:
|
||||||
|
key = f'{d}|{s}|{asn}|{ipversion}'
|
||||||
|
self.ranking_cache_wrapper(key)
|
||||||
|
keys.append(key)
|
||||||
|
r = sum(float(self.cache.get(key)) for key in keys if self.cache.exists(key))
|
||||||
|
else:
|
||||||
|
key = f'{d}|{source}|{asn}|{ipversion}'
|
||||||
|
self.ranking_cache_wrapper(key)
|
||||||
|
r = self.cache.get(key)
|
||||||
else:
|
else:
|
||||||
key = f'{d}|asns|{ipversion}'
|
key = f'{d}|asns|{ipversion}'
|
||||||
r = self.ranking.zscore(key, asn)
|
self.ranking_cache_wrapper(key)
|
||||||
|
r = self.cache.zscore(key, asn)
|
||||||
if r:
|
if r:
|
||||||
return float(r)
|
return float(r)
|
||||||
return 0
|
return 0
|
||||||
|
@ -83,9 +128,13 @@ class Querying():
|
||||||
return descriptions
|
return descriptions
|
||||||
return descriptions[sorted(descriptions.keys(), reverse=True)[0]]
|
return descriptions[sorted(descriptions.keys(), reverse=True)[0]]
|
||||||
|
|
||||||
def get_prefix_ips(self, asn: int, prefix: str, date: Dates=datetime.date.today(), source: str='', ipversion: str='v4'):
|
def get_prefix_ips(self, asn: int, prefix: str, date: Dates=datetime.date.today(),
|
||||||
|
source: Union[list, str]='', ipversion: str='v4'):
|
||||||
if source:
|
if source:
|
||||||
sources = [source]
|
if isinstance(source, list):
|
||||||
|
sources = source
|
||||||
|
else:
|
||||||
|
sources = [source]
|
||||||
else:
|
else:
|
||||||
sources = self.get_sources(date)
|
sources = self.get_sources(date)
|
||||||
prefix_ips = defaultdict(list)
|
prefix_ips = defaultdict(list)
|
||||||
|
@ -96,7 +145,8 @@ class Querying():
|
||||||
[prefix_ips[ip].append(source) for ip in ips]
|
[prefix_ips[ip].append(source) for ip in ips]
|
||||||
return prefix_ips
|
return prefix_ips
|
||||||
|
|
||||||
def get_asn_history(self, asn: int, period: int=100, source: str='', ipversion: str='v4', date: Dates=datetime.date.today()):
|
def get_asn_history(self, asn: int, period: int=100, source: Union[list, str]='',
|
||||||
|
ipversion: str='v4', date: Dates=datetime.date.today()):
|
||||||
to_return = []
|
to_return = []
|
||||||
|
|
||||||
if isinstance(date, str):
|
if isinstance(date, str):
|
||||||
|
@ -113,7 +163,8 @@ class Querying():
|
||||||
to_return.insert(0, (d.isoformat(), rank))
|
to_return.insert(0, (d.isoformat(), rank))
|
||||||
return to_return
|
return to_return
|
||||||
|
|
||||||
def country_rank(self, country: str, date: Dates=datetime.date.today(), source: str='', ipversion: str='v4'):
|
def country_rank(self, country: str, date: Dates=datetime.date.today(), source: Union[list, str]='',
|
||||||
|
ipversion: str='v4'):
|
||||||
ripe = StatsRIPE()
|
ripe = StatsRIPE()
|
||||||
d = self.__normalize_date(date)
|
d = self.__normalize_date(date)
|
||||||
response = ripe.country_asns(country, query_time=d, details=1)
|
response = ripe.country_asns(country, query_time=d, details=1)
|
||||||
|
@ -128,7 +179,8 @@ class Querying():
|
||||||
daily_sum = sum(ranks)
|
daily_sum = sum(ranks)
|
||||||
return daily_sum, to_return
|
return daily_sum, to_return
|
||||||
|
|
||||||
def country_history(self, country: str, period: int=30, source: str='', ipversion: str='v4', date: Dates=datetime.date.today()):
|
def country_history(self, country: str, period: int=30, source: Union[list, str]='',
|
||||||
|
ipversion: str='v4', date: Dates=datetime.date.today()):
|
||||||
to_return = []
|
to_return = []
|
||||||
|
|
||||||
if isinstance(date, str):
|
if isinstance(date, str):
|
||||||
|
|
|
@ -6,3 +6,8 @@ set -x
|
||||||
mkdir -p web/static/
|
mkdir -p web/static/
|
||||||
|
|
||||||
wget https://d3js.org/d3.v5.js -O web/static/d3.v5.js
|
wget https://d3js.org/d3.v5.js -O web/static/d3.v5.js
|
||||||
|
|
||||||
|
BOOTSTRAP_SELECT="1.12.4"
|
||||||
|
|
||||||
|
wget https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/${BOOTSTRAP_SELECT}/css/bootstrap-select.min.css -O web/static/bootstrap-select.min.css
|
||||||
|
wget https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/${BOOTSTRAP_SELECT}/js/bootstrap-select.min.js -O web/static/bootstrap-select.min.js
|
||||||
|
|
|
@ -39,12 +39,15 @@ def load_session():
|
||||||
if 'ipversion' in d:
|
if 'ipversion' in d:
|
||||||
session['ipversion'] = d['ipversion']
|
session['ipversion'] = d['ipversion']
|
||||||
if 'source' in d:
|
if 'source' in d:
|
||||||
session['source'] = d['source']
|
if '_all' in d.getlist('source'):
|
||||||
|
session.pop('source', None)
|
||||||
|
else:
|
||||||
|
session['source'] = d.getlist('source')
|
||||||
if 'asn' in d:
|
if 'asn' in d:
|
||||||
session['asn'] = d['asn']
|
session['asn'] = d['asn']
|
||||||
session.pop('country', None)
|
session.pop('country', None)
|
||||||
elif 'country' in d:
|
elif 'country' in d:
|
||||||
session['country'] = d['country']
|
session['country'] = d.getlist('country')
|
||||||
session.pop('asn', None)
|
session.pop('asn', None)
|
||||||
set_default_date_session()
|
set_default_date_session()
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<script src='{{ url_for('static', filename='d3.v5.js') }}'></script>
|
<script src='{{ url_for('static', filename='d3.v5.js') }}'></script>
|
||||||
|
<script src='{{ url_for('static', filename='bootstrap-select.min.js') }}'></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='bootstrap-select.min.css') }}">
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,10 @@
|
||||||
<button type="submit" class="btn btn-primary my-1">IP version</button>
|
<button type="submit" class="btn btn-primary my-1">IP version</button>
|
||||||
</form>
|
</form>
|
||||||
<form class="form-inline" style="width:400px; display:inline-block;" action="" method=post>
|
<form class="form-inline" style="width:400px; display:inline-block;" action="" method=post>
|
||||||
<select name="source" class="form-control">
|
<select name="source" class="selectpicker" multiple>
|
||||||
<option value="" {% if not source %} selected="selected"{% endif %}>all</option>
|
<option value="_all" {% if not source %} selected="selected"{% endif %}>all</option>
|
||||||
{% for s in sources %}
|
{% for s in sources %}
|
||||||
<option value="{{ s }}" {% if source == s %} selected="selected"{% endif %}>{{ s }}</option>
|
<option value="{{ s }}" {% if s in source %} selected="selected"{% endif %}>{{ s }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<input type="submit" class="btn btn-primary my-1"value="Set source">
|
<input type="submit" class="btn btn-primary my-1"value="Set source">
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
<button type="submit" class="btn btn-primary my-1">Search ASN</button>
|
<button type="submit" class="btn btn-primary my-1">Search ASN</button>
|
||||||
</form>
|
</form>
|
||||||
<form class="form-inline"style="width:500px; display:inline-block;" action="{{ url_for('country') }}" method=post>
|
<form class="form-inline"style="width:500px; display:inline-block;" action="{{ url_for('country') }}" method=post>
|
||||||
<select name="country" class="form-control">
|
<select name="country" class="selectpicker" multiple>
|
||||||
<option value="" {% if not country %} selected="selected"{% endif %}>all</option>
|
<option value="" {% if not country %} selected="selected"{% endif %}>all</option>
|
||||||
{% for cc, name in countries %}
|
{% for cc, name in countries %}
|
||||||
<option value="{{ cc }}" {% if country == cc %} selected="selected"{% endif %}>{{ name }}</option>
|
<option value="{{ cc }}" {% if country == cc %} selected="selected"{% endif %}>{{ name }}</option>
|
||||||
|
|
Loading…
Reference in New Issue