191 lines
7.7 KiB
Python
Executable file
191 lines
7.7 KiB
Python
Executable file
#!/usr/bin/python3 -d
|
|
|
|
# Copyright (c) 2020 Johannes Findeisen <you@hanez.org>
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the 'Software'), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is furnished
|
|
# to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice (including the next
|
|
# paragraph) shall be included in all copies or substantial portions of the
|
|
# Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
|
# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
# TODO: CHECK ALL ERROR HANDLING!!!
|
|
# TODO: Refactor all logging and make it clean and consistent.
|
|
# TODO: become more verbose in each log level and log only to error when it really
|
|
# is an error else log to info and even add debug messages in DEBUG level.
|
|
|
|
import argparse
|
|
import calendar
|
|
import json
|
|
import logging
|
|
import logging.handlers
|
|
import os
|
|
import sys
|
|
import time
|
|
|
|
from uplink.configuration import Configuration
|
|
from uplink.uplink import Uplink
|
|
|
|
# MAJOR_RELEASE.MAJOR CHANGES.MINOR_CHANGES.BUG_FIXES
|
|
# Related to: https://wiki.unixpeople.org/linux_kernel_version_numbering
|
|
__version__ = '0.8.2'
|
|
__author__ = 'Johannes Findeisen <you@hanez.org>'
|
|
|
|
logger = logging.getLogger('uplink')
|
|
|
|
|
|
def parse_args():
|
|
parser = argparse.ArgumentParser(
|
|
description='uplink is a tool to monitor the link status of AVM FRITZ!Box Cable and DSL '
|
|
'based routers.',
|
|
epilog='uplink is not some program expecting uplinks to work!',
|
|
prog='uplink')
|
|
|
|
parser.add_argument('configuration_file', metavar='CONFIGFILE',
|
|
help='the configuration file to use')
|
|
|
|
mode = parser.add_mutually_exclusive_group()
|
|
mode.add_argument('-c', '--cron', default=False, dest='cron', action='store_true',
|
|
help='cron mode executes the script only once without loop (default: false)')
|
|
|
|
mode.add_argument('-d', '--daemon', default=False, dest='daemon', action='store_true',
|
|
help='run as native daemon (default: false)')
|
|
|
|
parser.add_argument('-k', '--kill', default=False, dest='kill', action='store_true',
|
|
help='kill the daemon if it is running (default: false)')
|
|
|
|
parser.add_argument('-r', '--restart', default=False, dest='restart', action='store_true',
|
|
help='restart the daemon if it is running(default: false)')
|
|
|
|
mode.add_argument('-f', '--foreground', default=False, dest='foreground', action='store_true',
|
|
help='run looped in foreground (default: false)')
|
|
|
|
parser.add_argument('--httpserver', default=False, dest='httpserver', action='store_true',
|
|
help='enable the http server for status information and statistics')
|
|
|
|
parser.add_argument('-i', '--interval', type=int, help='poll interval in seconds. this '
|
|
'overrides config file settings (default: 60)')
|
|
|
|
parser.add_argument('-l', '--logfile', metavar='LOGFILE', help='logfile to use. this '
|
|
'overrides config file settings')
|
|
|
|
parser.add_argument('-s', '--stdout', default=False, dest='stdout', action='store_true',
|
|
help='log to stdout')
|
|
|
|
parser.add_argument('--version', action='version', version='%(prog)s ' + str(__version__))
|
|
|
|
return parser.parse_args()
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
|
|
try:
|
|
with open(args.configuration_file, 'r', encoding='utf-8') as configuration_file:
|
|
configuration_data = configuration_file.read()
|
|
configuration = Configuration(json.loads(configuration_data))
|
|
except Exception as err:
|
|
logger.error(str('[uplink] configuration error: {0}'.format(err)))
|
|
sys.exit(1)
|
|
|
|
configuration.set_env_var('_internal_start_date', time.strftime('%Y-%m-%d %H:%M:%S',
|
|
time.localtime(calendar.timegm(time.gmtime()))))
|
|
configuration.set_env_var('_internal_start_timestamp', calendar.timegm(time.gmtime()))
|
|
|
|
# interval set in args is overriding configuration and default
|
|
if args.interval:
|
|
configuration.set_interval(args.interval)
|
|
|
|
if args.logfile:
|
|
configuration.set_log_file(args.logfile)
|
|
|
|
if args.logfile or configuration.get_log_file():
|
|
log_file = os.path.expanduser(configuration.get_log_file())
|
|
if not os.path.exists(os.path.dirname(log_file)):
|
|
os.makedirs(os.path.dirname(log_file))
|
|
|
|
formatter1 = logging.Formatter('%(asctime)s:%(levelname)s:%(name)s:%(message)s')
|
|
handler1 = logging.handlers.RotatingFileHandler(log_file,
|
|
maxBytes=configuration.get_log_size(),
|
|
backupCount=configuration.get_log_count())
|
|
handler1.setFormatter(formatter1)
|
|
logger.addHandler(handler1)
|
|
|
|
if args.stdout:
|
|
formatter2 = logging.Formatter('[%(asctime)s] [%(levelname)s] %(message)s')
|
|
handler2 = logging.StreamHandler(sys.stdout)
|
|
handler2.setFormatter(formatter2)
|
|
logger.addHandler(handler2)
|
|
|
|
# errors will always show up even when no log_level is set!
|
|
logger.setLevel(logging.ERROR)
|
|
log_level = configuration.get_log_level()
|
|
if log_level == "warning":
|
|
logger.setLevel(logging.WARNING)
|
|
elif log_level == "verbose":
|
|
logger.setLevel(logging.INFO)
|
|
elif log_level == "debug":
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
if args.httpserver or configuration.get_httpserver():
|
|
configuration.set_httpserver(True)
|
|
|
|
try:
|
|
u = Uplink(configuration)
|
|
except Exception as err:
|
|
logger.error(str('[uplink] core error! {0}'.format(err)))
|
|
sys.exit(1)
|
|
|
|
if args.cron:
|
|
from threading import Thread
|
|
for i in range(len(configuration.get_uplinks())):
|
|
ct = Thread(target=u.fetch_data, daemon=True, args=(i,))
|
|
ct.start()
|
|
elif args.daemon:
|
|
if args.kill:
|
|
u.stop()
|
|
elif args.restart:
|
|
u.restart()
|
|
else:
|
|
u.start()
|
|
elif args.foreground:
|
|
from threading import Thread
|
|
if configuration.get_httpserver():
|
|
from uplink.httpserver import HTTPServer
|
|
s = HTTPServer(configuration)
|
|
st = Thread(target=s.run_server, daemon=True)
|
|
st.start()
|
|
|
|
if configuration.get_speedtest():
|
|
from uplink.speedtest import Speedtest
|
|
se = Speedtest(configuration)
|
|
ste = Thread(target=se.run_speedtest, daemon=True)
|
|
ste.start()
|
|
|
|
while True:
|
|
for i in range(len(configuration.get_uplinks())):
|
|
ft = Thread(target=u.fetch_data, daemon=True, args=(i,))
|
|
ft.start()
|
|
try:
|
|
time.sleep(configuration.get_interval())
|
|
except KeyboardInterrupt:
|
|
logger.info(str('[uplink] program terminated by user!'))
|
|
sys.exit(0)
|
|
else:
|
|
logger.error('[uplink] no run mode selected; use --cron (-c), --daemon (-d) or '
|
|
'--foreground (-f) to run uplink. use --help for more information')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|