diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..c7d916a6d03e5ca7f2af6d2d66d0ef51a6d9928f --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,28 @@ +variables: + IMAGE_URL: 'marula.c3sl.ufpr.br:5000/c3sl/agent-gesac' + IMAGE_VERSION: '0.1' +stages: + - build + - deploy + +build: + stage: build + script: + - docker build -t ${IMAGE_URL}:${IMAGE_VERSION} -t ${IMAGE_URL}:latest src + tags: + - docker + - build + +deploy: + stage: deploy + variables: + IMAGE_VERSION: '' + script: + - docker push ${IMAGE_URL}:${IMAGE_VERSION} + - docker push ${IMAGE_URL}:latest + tags: + - docker + - build + only: + - master + diff --git a/src/Dockerfile b/src/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..3c68e1486d451a81ed2351aba6af04ad4be211f5 --- /dev/null +++ b/src/Dockerfile @@ -0,0 +1,33 @@ +FROM python:3 + +LABEL author="C3SL - Centro de Computação CientÃfica e Software Livre" + +ENV LOG_LEVEL="warn" + +# Database configuration +ENV DB_ENABLED="true" +ENV DB_NAME="gesac" +ENV DB_USER="gesac" +ENV DB_HOST="localhost" +ENV DB_PASS="changeme" +ENV DB_PORT="5432" + +# Not implemented yet +ENV API_ENABLED="false" + +RUN groupadd -r gesac && useradd --no-log-init -m -r -g gesac gesac + +WORKDIR /home/gesac + +COPY --chown=gesac:gesac gesacmonit.py . +COPY --chown=gesac:gesac requirements.txt . + +RUN pip install --no-cache-dir -r requirements.txt + +RUN chmod +x gesacmonit.py + +USER gesac + +ENTRYPOINT ["./gesacmonit.py"] + +CMD ["-p gesacmonit.pid", "-v"] diff --git a/src/gesacmonit.py b/src/gesacmonit.py index 21898170976320d30afbb67321cc8b7cee475c90..37bd55fd58a5f0111508ff20eb859e7a0bbdfcf9 100755 --- a/src/gesacmonit.py +++ b/src/gesacmonit.py @@ -27,10 +27,9 @@ # minutes, comupting the network traffic (download and upload) as a # difference between two measures. -import threading, sys, os, psycopg2, math, signal -import dateutil.parser, dateutil.relativedelta -import multiprocessing, logging, pickle, re -import time, requests, json +import threading, sys, os, psycopg2, math, signal, argparse, multiprocessing +import logging, pickle, re, time, requests, json, dateutil.parser +import dateutil.relativedelta from psycopg2.pool import PersistentConnectionPool from queue import Queue from pysnmp.entity.rfc3413.oneliner import cmdgen @@ -53,12 +52,6 @@ SNMP_PORT = 161 SNMP_COM_SATELLITE = "public" # for private IPs SNMP_COM_TERRESTRIAL = "gesac_mng" # for public IPs -### Logging settings -LOG_LEVEL = logging.WARNING -LOG_FILE = os.getcwd() + "/gesacmonit.log" -LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' -logging.basicConfig(filename=LOG_FILE, level=LOG_LEVEL, format=LOG_FORMAT) - ### Define new signals SIGSLEEP = signal.SIGUSR1 SIGWAKE = signal.SIGUSR2 @@ -818,10 +811,44 @@ def chunks(l, chunk): for i in range(0, len(l), n): yield l[i:i+n] -def get_conf(conf_file): - """Return a configuration dictionary used to create queries for the API and to Database.""" - with open(conf_file) as f: - data = json.load(f) +def parser_config(conf_file): + """ + Return a configuration dictionary used to create queries for the API and Database. + Priotiry order: env > conf file > default values + """ + + default_data = { + "db_backup_file" : "db.user" + , "api_backup_file" : "host.route" + , "api_enabled" : False + , "db_enabled" : True + , "db" : { + "dbname" : "gesac" + , "user" : "gesac" + , "password" : "changeme" + , "host" : "localhost" + , "port" : "5432" + } + , "api" : { + "host" : "localhost:3000" + , "collect_route" : "api/v1/collect" + , "points_route" : "api/v1/points?params" + } + } + + if conf_file: + with open(conf_file) as f: + data = json.load(f) + else: + data = default_data + + data['db_enabled'] = bool(os.environ.get('DB_ENABLED', data['db_enabled'])) + data['db']['dbname'] = os.environ.get('DB_NAME', data['db']['dbname']) + data['db']['user'] = os.environ.get('DB_USER', data['db']['user']) + data['db']['password'] = os.environ.get('DB_PASS', data['db']['password']) + data['db']['host'] = os.environ.get('DB_HOST', data['db']['host']) + data['db']['port'] = os.environ.get('DB_PORT', data['db']['port']) + data['collect_url'] = 'http://' + data['api']['host'] + '/' + data['api']['collect_route'] data['points_url'] = 'http://' + data['api']['host'] + '/' + data['api']['points_route'] dsn = '' @@ -959,35 +986,50 @@ def ensure_proccesses_are_running(config, logger, children, points): ######################################################################## if __name__ == '__main__': - if len(sys.argv) < 3: - print("Usage: %s <database config> <pid file>" % sys.argv[0]) - sys.exit(1) + pid_default = '/run/user/{}/gesacmonit.pid'.format(os.getuid()) - logger = logging.getLogger('__main__') + parser = argparse.ArgumentParser() + parser.add_argument("-c", "--config", help="Location of the configuration file") + parser.add_argument("-p", "--pid", default=pid_default, help="Location of the pid file") + parser.add_argument("-l", "--log", help="Location of the log file (default: stdout)") + parser.add_argument('-v', '--verbose', action='count', default=0, help="Increase the verbose level") - pid_file = sys.argv[2] + args = parser.parse_args() - ### Save PID of process - with open(pid_file, 'w') as pid_file: - pid_file.write(str(os.getpid())+ '\n') + # Set up logging + log_level = ['ERROR', 'WARNING', 'INFO', 'DEBUG'] + log_level = log_level[min(args.verbose, 3)] + log_level = getattr(logging, log_level) + log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' + log_file = args.log - children = [] + if args.log: + try: + logging.basicConfig(filename=log_file, level=log_level, format=log_format) + except PermissionError: + print("ERROR: Permission denied for log file {}".format(log_file)) + sys.exit(1) + else: + logging.basicConfig(level=log_level, format=log_format) + + logger = logging.getLogger('__main__') def exit_handler(signal, frame): logger.warning('Stopping main program...') sys.exit(0) - conf_file = sys.argv[1] + # Save PID + with open(args.pid, 'w') as pid_file: + pid_file.write(str(os.getpid())+ '\n') # Create db connection and get cursor - config = get_conf(conf_file) + config = parser_config(args.config) points = get_gesac_points(config, logger) if not points: logger.error('Error fetching points. Check DB or API connection and try again') sys.exit(1) - # Get the number of available cores cores = multiprocessing.cpu_count() @@ -995,6 +1037,7 @@ if __name__ == '__main__': check_and_restore_data(config, logger, []) # init all processes + children = [] for pts in chunks(points, cores): proc = GesacMonit(config, Points(pts)) proc.start() diff --git a/src/requirements.txt b/src/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..072b35ef86572f64d3a0c67a5d0333725a91eaaa --- /dev/null +++ b/src/requirements.txt @@ -0,0 +1,13 @@ +certifi==2018.1.18 +chardet==3.0.4 +idna==2.6 +ply==3.11 +psycopg2-binary==2.7.4 +pyasn1==0.4.2 +pycryptodomex==3.5.1 +pysmi==0.2.2 +pysnmp==4.4.4 +python-dateutil==2.7.0 +requests==2.18.4 +six==1.11.0 +urllib3==1.22