2018-03-29 22:37:28 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
from pathlib import Path
|
2018-11-14 17:07:30 +01:00
|
|
|
from .exceptions import CreateDirectoryException, MissingEnv, MissingConfigFile, MissingConfigEntry, ThirdPartyUnreachable
|
2018-03-29 22:37:28 +02:00
|
|
|
from redis import StrictRedis
|
|
|
|
from redis.exceptions import ConnectionError
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
import time
|
2018-11-27 11:03:18 +01:00
|
|
|
try:
|
|
|
|
import simplejson as json
|
|
|
|
except ImportError:
|
|
|
|
import json
|
2018-11-14 17:07:30 +01:00
|
|
|
from pyipasnhistory import IPASNHistory
|
2018-04-10 00:20:59 +02:00
|
|
|
|
|
|
|
|
|
|
|
def load_config_files(config_dir: Path=None) -> dict:
|
|
|
|
if not config_dir:
|
|
|
|
config_dir = get_config_path()
|
|
|
|
modules_config = config_dir / 'modules'
|
|
|
|
modules_paths = [modulepath for modulepath in modules_config.glob('*.json')]
|
|
|
|
configs = {}
|
|
|
|
for p in modules_paths:
|
|
|
|
with open(p, 'r') as f:
|
|
|
|
j = json.load(f)
|
|
|
|
configs[f"{j['vendor']}-{j['name']}"] = j
|
|
|
|
return configs
|
2018-03-29 22:37:28 +02:00
|
|
|
|
|
|
|
|
|
|
|
def get_config_path():
|
2018-04-01 18:15:44 +02:00
|
|
|
if Path('bgpranking', 'config').exists():
|
|
|
|
# Running from the repository
|
|
|
|
return Path('bgpranking', 'config')
|
2018-03-29 22:37:28 +02:00
|
|
|
return Path(sys.modules['bgpranking'].__file__).parent / 'config'
|
|
|
|
|
|
|
|
|
|
|
|
def get_list_storage_path():
|
2018-03-29 23:05:07 +02:00
|
|
|
if not os.environ.get('VIRTUAL_ENV'):
|
|
|
|
raise MissingEnv("VIRTUAL_ENV is missing. This project really wants to run from a virtual envoronment.")
|
2018-03-29 22:37:28 +02:00
|
|
|
return Path(os.environ['VIRTUAL_ENV'])
|
|
|
|
|
|
|
|
|
|
|
|
def get_homedir():
|
2018-03-29 23:05:07 +02:00
|
|
|
if not os.environ.get('BGPRANKING_HOME'):
|
2018-04-11 14:55:20 +02:00
|
|
|
guessed_home = Path(__file__).resolve().parent.parent.parent
|
|
|
|
raise MissingEnv(f"BGPRANKING_HOME is missing. \
|
|
|
|
Run the following command (assuming you run the code from the clonned repository):\
|
|
|
|
export BGPRANKING_HOME='{guessed_home}'")
|
2018-03-29 22:37:28 +02:00
|
|
|
return Path(os.environ['BGPRANKING_HOME'])
|
|
|
|
|
|
|
|
|
|
|
|
def safe_create_dir(to_create: Path):
|
|
|
|
if to_create.exists() and not to_create.is_dir():
|
2018-04-10 00:20:59 +02:00
|
|
|
raise CreateDirectoryException(f'The path {to_create} already exists and is not a directory')
|
2018-03-29 22:37:28 +02:00
|
|
|
os.makedirs(to_create, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
|
|
def set_running(name: str):
|
2018-11-14 17:07:30 +01:00
|
|
|
r = StrictRedis(unix_socket_path=get_socket_path('cache'), db=1, decode_responses=True)
|
2018-03-29 22:37:28 +02:00
|
|
|
r.hset('running', name, 1)
|
|
|
|
|
|
|
|
|
|
|
|
def unset_running(name: str):
|
2018-11-14 17:07:30 +01:00
|
|
|
r = StrictRedis(unix_socket_path=get_socket_path('cache'), db=1, decode_responses=True)
|
2018-03-29 22:37:28 +02:00
|
|
|
r.hdel('running', name)
|
|
|
|
|
|
|
|
|
|
|
|
def is_running():
|
2018-11-14 17:07:30 +01:00
|
|
|
r = StrictRedis(unix_socket_path=get_socket_path('cache'), db=1, decode_responses=True)
|
2018-03-29 22:37:28 +02:00
|
|
|
return r.hgetall('running')
|
|
|
|
|
|
|
|
|
2018-04-05 14:36:01 +02:00
|
|
|
def get_socket_path(name: str):
|
|
|
|
mapping = {
|
2018-11-14 17:07:30 +01:00
|
|
|
'cache': Path('cache', 'cache.sock'),
|
2018-04-05 14:36:01 +02:00
|
|
|
'storage': Path('storage', 'storage.sock'),
|
|
|
|
'intake': Path('temp', 'intake.sock'),
|
|
|
|
'prepare': Path('temp', 'prepare.sock'),
|
|
|
|
}
|
|
|
|
return str(get_homedir() / mapping[name])
|
|
|
|
|
|
|
|
|
2019-01-03 18:59:19 +01:00
|
|
|
def load_general_config():
|
2018-11-14 17:07:30 +01:00
|
|
|
general_config_file = get_config_path() / 'bgpranking.json'
|
|
|
|
if not general_config_file.exists():
|
|
|
|
raise MissingConfigFile(f'The general configuration file ({general_config_file}) does not exists.')
|
|
|
|
with open(general_config_file) as f:
|
|
|
|
config = json.load(f)
|
2019-01-03 18:59:19 +01:00
|
|
|
return config, general_config_file
|
|
|
|
|
|
|
|
|
|
|
|
def get_ipasn():
|
|
|
|
config, general_config_file = load_general_config()
|
2018-11-14 17:07:30 +01:00
|
|
|
if 'ipasnhistory_url' not in config:
|
|
|
|
raise MissingConfigEntry(f'"ipasnhistory_url" is missing in {general_config_file}.')
|
|
|
|
ipasn = IPASNHistory(config['ipasnhistory_url'])
|
|
|
|
if not ipasn.is_up:
|
|
|
|
raise ThirdPartyUnreachable(f"Unable to reach IPASNHistory on {config['ipasnhistory_url']}")
|
|
|
|
return ipasn
|
|
|
|
|
|
|
|
|
|
|
|
def sanity_check_ipasn(ipasn):
|
|
|
|
meta = ipasn.meta()
|
|
|
|
if 'error' in meta:
|
|
|
|
raise ThirdPartyUnreachable(f'IP ASN History has a problem: meta["error"]')
|
|
|
|
|
|
|
|
v4_percent = meta['cached_dates']['caida']['v4']['percent']
|
|
|
|
v6_percent = meta['cached_dates']['caida']['v6']['percent']
|
|
|
|
if v4_percent < 90 or v6_percent < 90: # (this way it works if we only load 10 days)
|
|
|
|
# Try again later.
|
|
|
|
return False, f"IP ASN History is not ready: v4 {v4_percent}% / v6 {v6_percent}% loaded"
|
|
|
|
return True, f"IP ASN History is ready: v4 {v4_percent}% / v6 {v6_percent}% loaded"
|
|
|
|
|
|
|
|
|
2018-04-05 14:36:01 +02:00
|
|
|
def check_running(name: str):
|
|
|
|
socket_path = get_socket_path(name)
|
|
|
|
try:
|
|
|
|
r = StrictRedis(unix_socket_path=socket_path)
|
|
|
|
return r.ping()
|
|
|
|
except ConnectionError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2018-03-29 22:37:28 +02:00
|
|
|
def shutdown_requested():
|
|
|
|
try:
|
2018-11-14 17:07:30 +01:00
|
|
|
r = StrictRedis(unix_socket_path=get_socket_path('cache'), db=1, decode_responses=True)
|
2018-03-29 22:37:28 +02:00
|
|
|
return r.exists('shutdown')
|
|
|
|
except ConnectionRefusedError:
|
|
|
|
return True
|
|
|
|
except ConnectionError:
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def long_sleep(sleep_in_sec: int, shutdown_check: int=10):
|
|
|
|
sleep_until = datetime.now() + timedelta(seconds=sleep_in_sec)
|
|
|
|
while sleep_until > datetime.now():
|
|
|
|
time.sleep(shutdown_check)
|
|
|
|
if shutdown_requested():
|
|
|
|
return False
|
|
|
|
return True
|