Varken/varken/iniparser.py
2019-01-01 23:41:23 -05:00

282 lines
13 KiB
Python

from shutil import copyfile
from logging import getLogger
from os.path import join, exists
from re import match, compile, IGNORECASE
from configparser import ConfigParser, NoOptionError, NoSectionError
from varken.helpers import clean_sid_check, rfc1918_ip_check
from varken.structures import SickChillServer
from varken.varkenlogger import BlacklistFilter
from varken.structures import SonarrServer, RadarrServer, OmbiServer, TautulliServer, InfluxServer, CiscoASAFirewall
class INIParser(object):
def __init__(self, data_folder):
self.config = None
self.data_folder = data_folder
self.filtered_strings = None
self.services = ['sonarr', 'radarr', 'ombi', 'tautulli', 'sickchill', 'ciscoasa']
self.logger = getLogger()
self.influx_server = InfluxServer()
try:
self.parse_opts(read_file=True)
except NoSectionError as e:
self.logger.error('Missing section in (varken.ini): %s', e)
self.rectify_ini()
def config_blacklist(self):
filtered_strings = [section.get(k) for key, section in self.config.items()
for k in section if k in BlacklistFilter.blacklisted_strings]
self.filtered_strings = list(filter(None, filtered_strings))
# Added matching for domains that use /locations. ConnectionPool ignores the location in logs
domains_only = [string.split('/')[0] for string in filtered_strings if '/' in string]
self.filtered_strings.extend(domains_only)
for handler in self.logger.handlers:
handler.addFilter(BlacklistFilter(set(self.filtered_strings)))
def enable_check(self, server_type=None):
t = server_type
global_server_ids = self.config.get('global', t)
if global_server_ids.lower() in ['false', 'no', '0']:
self.logger.info('%s disabled.', t.upper())
else:
sids = clean_sid_check(global_server_ids, t)
return sids
def read_file(self, inifile):
config = ConfigParser(interpolation=None)
ini = inifile
file_path = join(self.data_folder, ini)
if not exists(file_path):
self.logger.error('File missing (%s) in %s', ini, self.data_folder)
if inifile == 'varken.ini':
try:
self.logger.debug('Creating varken.ini from varken.example.ini')
copyfile(join(self.data_folder, 'varken.example.ini'), file_path)
except IOError as e:
self.logger.error("Varken does not have permission to write to %s. Error: %s - Exiting.", e,
self.data_folder)
exit(1)
self.logger.debug('Reading from %s', inifile)
with open(file_path) as config_ini:
config.read_file(config_ini)
return config
def write_file(self, inifile):
ini = inifile
file_path = join(self.data_folder, ini)
if exists(file_path):
self.logger.debug('Writing to %s', inifile)
with open(file_path, 'w') as config_ini:
self.config.write(config_ini)
else:
self.logger.error('File missing (%s) in %s', ini, self.data_folder)
exit(1)
def url_check(self, url=None, include_port=True, section=None):
url_check = url
module = section
inc_port = include_port
search = (r'(?:([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}|' # domain...
r'localhost|' # localhost...
r'^[a-zA-Z0-9_-]*|' # hostname only. My soul dies a little every time this is used...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
)
# Include search for port if it is needed.
if inc_port:
search = (search + r'(?::\d+)?' + r'(?:/?|[/?]\S+)$')
else:
search = (search + r'(?:/?|[/?]\S+)$')
regex = compile('{}'.format(search), IGNORECASE)
valid = match(regex, url_check) is not None
if not valid:
if inc_port:
self.logger.error('%s is invalid in module [%s]! URL must host/IP and '
'port if not 80 or 443. ie. localhost:8080',
url_check, module)
exit(1)
else:
self.logger.error('%s is invalid in module [%s]! URL must host/IP. ie. localhost', url_check, module)
exit(1)
else:
self.logger.debug('%s is a valid URL in module [%s].', url_check, module)
return url_check
def rectify_ini(self):
self.logger.debug('Rectifying varken.ini with varken.example.ini')
current_ini = self.config
example_ini = self.read_file('varken.example.ini')
for name, section in example_ini.items():
if name not in current_ini:
self.logger.debug('Section %s missing. Adding...', name)
current_ini[name] = {}
for key, value in section.items():
if not current_ini[name].get(key):
self.logger.debug('%s is missing in %s. Adding defaults...', key, name)
current_ini[name][key] = value
self.config = current_ini
self.write_file('varken.ini')
self.parse_opts()
def parse_opts(self, read_file=False):
for service in self.services:
setattr(self, f'{service}_servers', [])
if read_file:
self.config = self.read_file('varken.ini')
self.config_blacklist()
# Parse InfluxDB options
url = self.url_check(self.config.get('influxdb', 'url'), include_port=False, section='influxdb')
port = self.config.getint('influxdb', 'port')
username = self.config.get('influxdb', 'username')
password = self.config.get('influxdb', 'password')
self.influx_server = InfluxServer(url=url, port=port, username=username, password=password)
# Check for all enabled services
for service in self.services:
try:
setattr(self, f'{service}_enabled', self.enable_check(f'{service}_server_ids'))
except NoOptionError as e:
self.logger.error('Missing global %s. Error: %s', f'{service}_server_ids', e)
self.rectify_ini()
return
service_enabled = getattr(self, f'{service}_enabled')
if service_enabled:
for server_id in service_enabled:
server = None
section = f"{service}-{server_id}"
try:
url = self.url_check(self.config.get(section, 'url'), section=section)
apikey = None
if service != 'ciscoasa':
apikey = self.config.get(section, 'apikey')
scheme = 'https://' if self.config.getboolean(section, 'ssl') else 'http://'
verify_ssl = self.config.getboolean(section, 'verify_ssl')
if scheme != 'https://':
verify_ssl = False
if service == 'sonarr':
queue = self.config.getboolean(section, 'queue')
missing_days = self.config.getint(section, 'missing_days')
future_days = self.config.getint(section, 'future_days')
missing_days_run_seconds = self.config.getint(section, 'missing_days_run_seconds')
future_days_run_seconds = self.config.getint(section, 'future_days_run_seconds')
queue_run_seconds = self.config.getint(section, 'queue_run_seconds')
server = SonarrServer(id=server_id, url=scheme + url, api_key=apikey, verify_ssl=verify_ssl,
missing_days=missing_days, future_days=future_days,
missing_days_run_seconds=missing_days_run_seconds,
future_days_run_seconds=future_days_run_seconds,
queue=queue, queue_run_seconds=queue_run_seconds)
if service == 'radarr':
queue = self.config.getboolean(section, 'queue')
queue_run_seconds = self.config.getint(section, 'queue_run_seconds')
get_missing = self.config.getboolean(section, 'get_missing')
get_missing_run_seconds = self.config.getint(section, 'get_missing_run_seconds')
server = RadarrServer(id=server_id, url=scheme + url, api_key=apikey, verify_ssl=verify_ssl,
queue_run_seconds=queue_run_seconds, get_missing=get_missing,
queue=queue, get_missing_run_seconds=get_missing_run_seconds)
if service == 'tautulli':
fallback_ip = self.config.get(section, 'fallback_ip')
get_activity = self.config.getboolean(section, 'get_activity')
get_activity_run_seconds = self.config.getint(section, 'get_activity_run_seconds')
get_stats = self.config.getboolean(section, 'get_stats')
get_stats_run_seconds = self.config.getint(section, 'get_stats_run_seconds')
invalid_wan_ip = rfc1918_ip_check(fallback_ip)
if invalid_wan_ip:
self.logger.error('Invalid failback_ip [%s] set for %s-%s!', fallback_ip, service, server_id)
exit(1)
server = TautulliServer(id=server_id, url=scheme + url, api_key=apikey,
verify_ssl=verify_ssl, get_activity=get_activity,
fallback_ip=fallback_ip, get_stats=get_stats,
get_activity_run_seconds=get_activity_run_seconds,
get_stats_run_seconds=get_stats_run_seconds)
if service == 'ombi':
request_type_counts = self.config.getboolean(section, 'get_request_type_counts')
request_type_run_seconds = self.config.getint(section, 'request_type_run_seconds')
request_total_counts = self.config.getboolean(section, 'get_request_total_counts')
request_total_run_seconds = self.config.getint(section, 'request_total_run_seconds')
issue_status_counts = self.config.getboolean(section, 'get_issue_status_counts')
issue_status_run_seconds = self.config.getint(section, 'issue_status_run_seconds')
server = OmbiServer(id=server_id, url=scheme + url, api_key=apikey, verify_ssl=verify_ssl,
request_type_counts=request_type_counts,
request_type_run_seconds=request_type_run_seconds,
request_total_counts=request_total_counts,
request_total_run_seconds=request_total_run_seconds,
issue_status_counts=issue_status_counts,
issue_status_run_seconds=issue_status_run_seconds)
if service == 'sickchill':
get_missing = self.config.getboolean(section, 'get_missing')
get_missing_run_seconds = self.config.getint(section, 'get_missing_run_seconds')
server = SickChillServer(id=server_id, url=scheme + url, api_key=apikey,
verify_ssl=verify_ssl, get_missing=get_missing,
get_missing_run_seconds=get_missing_run_seconds)
if service == 'ciscoasa':
username = self.config.get(section, 'username')
password = self.config.get(section, 'password')
outside_interface = self.config.get(section, 'outside_interface')
get_bandwidth_run_seconds = self.config.getint(section, 'get_bandwidth_run_seconds')
server = CiscoASAFirewall(id=server_id, url=scheme + url, verify_ssl=verify_ssl,
username=username, password=password,
outside_interface=outside_interface,
get_bandwidth_run_seconds=get_bandwidth_run_seconds)
getattr(self, f'{service}_servers').append(server)
except NoOptionError as e:
self.logger.error('Missing key in %s. Error: %s', section, e)
self.rectify_ini()
return