175 lines
7.8 KiB
Python
175 lines
7.8 KiB
Python
import constants as C
|
|
import dns.resolver
|
|
import time
|
|
from collections import Counter
|
|
from flask import jsonify
|
|
from itertools import islice
|
|
from pymongo import MongoClient
|
|
from functions import asn_name_query
|
|
|
|
|
|
class Stats(object):
|
|
def __init__(self):
|
|
self.db = self.db_connect()
|
|
self.peer_counter = 0
|
|
self.ipv4_table_size = 0
|
|
self.ipv6_table_size = 0
|
|
self.nexthop_ip_counter = 0
|
|
self.avg_as_path_length = 0
|
|
self.top_n_peers = []
|
|
self.cidr_breakdown = []
|
|
self.communities = []
|
|
self.peers = []
|
|
self.customers = []
|
|
self.customer_count = 0
|
|
self.customer_ipv4_prefixes = 0
|
|
self.customer_ipv6_prefixes = 0
|
|
self.timestamp = self.epoch_to_date(time.time())
|
|
|
|
# @property
|
|
# def peer_counter(self):
|
|
# return self._peer_counter
|
|
|
|
# @peer_counter.setter
|
|
# def peer_counter(self):
|
|
# self._peer_counter = len(self.db['bgp'].distinct('nexthop_asn', {'active': True}))
|
|
|
|
def db_connect(self):
|
|
"""Return a connection to the Mongo Database."""
|
|
client = MongoClient(host='mongodb')
|
|
return client.bgp
|
|
|
|
def take(self, n, iterable):
|
|
"""Return first n items of the iterable as a list."""
|
|
return list(islice(iterable, n))
|
|
|
|
def peer_count(self):
|
|
"""Return the number of directly connected ASNs."""
|
|
return len(self.db['bgp'].distinct('nexthop_asn', {'active': True}))
|
|
|
|
def prefix_count(self, version):
|
|
"""Given the IP version, return the number of prefixes in the database."""
|
|
return self.db['bgp'].count_documents({'ip_version': version, 'active': True})
|
|
|
|
def nexthop_ip_count(self):
|
|
"""Return the number of unique next hop IPv4 and IPv6 addresses."""
|
|
return len(self.db['bgp'].distinct('nexthop', {'active': True}))
|
|
|
|
def epoch_to_date(self, epoch):
|
|
"""Given an *epoch* time stamp, return a human readable equivalent."""
|
|
return time.strftime('%Y-%m-%d %H:%M:%S %Z', time.gmtime(epoch))
|
|
|
|
def get_list_of(self, customers=False, peers=False, community=C.CUSTOMER_BGP_COMMUNITY):
|
|
"""Return a list of prefix dictionaries. Specify which type of prefix to
|
|
return by setting *customers* or *peers* to True."""
|
|
if peers:
|
|
query_results = {prefix['nexthop_asn'] for prefix in self.db['bgp'].find({'active': True})}
|
|
else: # customers
|
|
query_results = {prefix['nexthop_asn'] for prefix in
|
|
self.db['bgp'].find({'communities': community, 'active': True})}
|
|
|
|
return [{'asn': asn if asn is not None else C.DEFAULT_ASN, # Set "None" ASNs to default
|
|
'name': asn_name_query(asn),
|
|
'ipv4_origin_count': self.db['bgp'].count_documents(
|
|
{'origin_asn': asn, 'ip_version': 4, 'active': True}),
|
|
'ipv6_origin_count': self.db['bgp'].count_documents(
|
|
{'origin_asn': asn, 'ip_version': 6, 'active': True}),
|
|
'ipv4_nexthop_count': self.db['bgp'].count_documents(
|
|
{'nexthop_asn': asn, 'ip_version': 4, 'active': True}),
|
|
'ipv6_nexthop_count': self.db['bgp'].count_documents(
|
|
{'nexthop_asn': asn, 'ip_version': 6, 'active': True}),
|
|
'asn_count': len(self.db['bgp'].distinct('as_path.1', {'nexthop_asn': asn, 'active': True}))}
|
|
for asn in query_results]
|
|
|
|
def avg_as_path_len(self, decimal_point_accuracy=2):
|
|
"""Return the computed average *as_path* length of all prefixes in the
|
|
database. Using a python *set* to remove any AS prepending."""
|
|
as_path_counter = 0
|
|
all_prefixes = list(self.db['bgp'].find({'active': True}))
|
|
for prefix in all_prefixes:
|
|
try:
|
|
as_path_counter += len(set(prefix['as_path'])) # sets remove duplicate ASN prepending
|
|
except Exception:
|
|
pass
|
|
return round(as_path_counter / (len(all_prefixes) * 1.0), decimal_point_accuracy)
|
|
|
|
def communities_count(self):
|
|
"""Return a list of BGP communities and their count"""
|
|
return [{'community': community,
|
|
# 'count': self.db['bgp'].count_documents({'communities': {'$regex': str(community)}, 'active': True}),
|
|
'count': self.db['bgp'].count_documents({'communities': str(community), 'active': True}),
|
|
'name': None if C.BGP_COMMUNITY_MAP.get(community) is None else C.BGP_COMMUNITY_MAP.get(community)}
|
|
for community in self.db['bgp'].distinct('communities') if community is not None]
|
|
|
|
def cidrs(self):
|
|
""" Return a list of IPv4 and IPv6 network mask counters."""
|
|
ipv4_masks = [int(prefix['_id'].split('/', 1)[1])
|
|
for prefix in self.db['bgp'].find({'ip_version': 4, 'active': True})]
|
|
ipv6_masks = [int(prefix['_id'].split('/', 1)[1])
|
|
for prefix in self.db['bgp'].find({'ip_version': 6, 'active': True})]
|
|
# Use a *Counter* to count masks in the lists, then combine, sort on mask, and return results
|
|
return sorted(
|
|
[{'mask': mask,
|
|
'count': count,
|
|
'ip_version': 4}
|
|
for mask, count in list(Counter(ipv4_masks).items())]
|
|
+
|
|
[{'mask': mask,
|
|
'count': count,
|
|
'ip_version': 6}
|
|
for mask, count in list(Counter(ipv6_masks).items())], key=lambda x: x['mask'])
|
|
|
|
def top_peers(self, count):
|
|
"""Return a sorted list of top peer dictionaries ordered by prefix count.
|
|
Limit to *count*."""
|
|
peers = {peer: self.db['bgp'].count_documents({'nexthop_asn': peer, 'active': True})
|
|
for peer in self.db['bgp'].distinct('nexthop_asn')}
|
|
return [{'asn': asn[0],
|
|
'count': asn[1],
|
|
'name': asn_name_query(asn[0])}
|
|
for asn in self.take(count, sorted(peers.items(), key=lambda x: x[1], reverse=True))]
|
|
|
|
def get_data(self, json=False):
|
|
data_dict = {
|
|
'peer_count': self.peer_counter,
|
|
'ipv6_table_size': self.ipv6_table_size,
|
|
'ipv4_table_size': self.ipv4_table_size,
|
|
'nexthop_ip_count': self.nexthop_ip_counter,
|
|
'avg_as_path_length': self.avg_as_path_length,
|
|
'top_n_peers': self.top_n_peers,
|
|
'cidr_breakdown': self.cidr_breakdown,
|
|
'communities': self.communities,
|
|
'peers': self.peers,
|
|
'customers': self.customers,
|
|
'customer_count': self.customer_count,
|
|
'customer_ipv4_prefixes': self.customer_ipv4_prefixes,
|
|
'customer_ipv6_prefixes': self.customer_ipv6_prefixes,
|
|
'timestamp': self.timestamp}
|
|
if json:
|
|
return jsonify(data_dict)
|
|
else:
|
|
return data_dict
|
|
|
|
def update_stats(self):
|
|
self.peer_counter = self.peer_count()
|
|
self.ipv4_table_size = self.prefix_count(4)
|
|
self.ipv6_table_size = self.prefix_count(6)
|
|
self.nexthop_ip_counter = self.nexthop_ip_count()
|
|
self.timestamp = self.epoch_to_date(time.time())
|
|
|
|
def update_advanced_stats(self):
|
|
self.avg_as_path_length = self.avg_as_path_len()
|
|
self.top_n_peers = self.top_peers(5)
|
|
self.cidr_breakdown = self.cidrs()
|
|
# self.customers = self.get_list_of(customers=True)
|
|
self.communities = self.communities_count()
|
|
self.customers = self.get_list_of(customers=True)
|
|
self.peers = self.get_list_of(peers=True)
|
|
self.customer_count = len(self.customers)
|
|
self.customer_ipv4_prefixes = 0
|
|
self.customer_ipv6_prefixes = 0
|
|
for customer in self.customers:
|
|
self.customer_ipv4_prefixes += customer['ipv4_origin_count']
|
|
self.customer_ipv6_prefixes += customer['ipv6_origin_count']
|
|
self.timestamp = self.epoch_to_date(time.time())
|