Fetch secrets from env if set

Change-Id: I29ec0cfb182e64b40d28dcfad3f6e9b78f067c70
diff --git a/network-diag-app.py b/network-diag-app.py
new file mode 100644
index 0000000..d8c9002
--- /dev/null
+++ b/network-diag-app.py
@@ -0,0 +1,195 @@
+"""
+SPDX-FileCopyrightText: 2020-present Open Networking Foundation <info@opennetworking.org>
+SPDX-License-Identifier: LicenseRef-ONF-Member-1.01
+"""
+import sys
+import os
+from datetime import datetime
+
+from flask import Flask, request
+import logging as log
+from argparse import ArgumentParser, SUPPRESS
+import threading
+
+from roc import Roc
+from prom import Prometheus
+from ping import ping
+
+app = Flask(__name__)
+
+devices = {}  # dict imsi:device
+lock = threading.Lock()
+probe_start = threading.Event()
+probe_stop = threading.Event()
+
+
+@app.route("/devices")
+def get_devices():
+    global devices, lock
+    with lock:
+        all = {}
+        for _, device in devices.items():
+            all[device.imsi_id] = {'ip': device.ip, 'imsi': device.imsi, 'last_reachable': '{:%Y-%m-%d %H:%M:%S}'.format(device.last_reachable)}
+    return all
+
+
+@app.route("/devices/reachable")
+def get_devices_reachable():
+    global devices, lock
+    with lock:
+        reachable = {}
+        for _, device in devices.items():
+            if device.reachable is True:
+                reachable[device.imsi_id] = {'ip': device.ip, 'imsi': device.imsi, 'last_reachable': '{:%Y-%m-%d %H:%M:%S}'.format(device.last_reachable)}
+    return reachable
+
+
+@app.route("/devices/unreachable")
+def get_devices_unreachable():
+    global devices, lock
+    with lock:
+        unreachable = {}
+        for _, device in devices.items():
+            if device.reachable is False:
+                unreachable[device.imsi_id] = {'ip': device.ip, 'imsi': device.imsi, 'last_reachable': '{:%Y-%m-%d %H:%M:%S}'.format(device.last_reachable)}
+    return unreachable
+
+
+@app.route("/probe")
+def probe():
+    update_and_probe()
+    return get_devices_reachable()
+
+
+# curl http://localhost:3333/config?period=0
+@app.route("/config")
+def config():
+    global args, probe_stop
+    period = request.args.get('period')
+    if period is not None:
+        period = int(period)
+        if period == 0:
+            log.info("Stopping probes...")
+            args.period = period
+            probe_stop.set()
+        else:
+            log.info("Starting probes...")
+            args.period = period
+            probe_start.set()
+    config = vars(args)
+    config.pop('token', None)
+    config.pop('user', None)
+    config.pop('password', None)
+    return config
+
+
+def build_argparser():
+    parser = ArgumentParser(add_help=False)
+    args = parser.add_argument_group('Options')
+    args.add_argument('-h', '--help',
+                      action='help',
+                      default=SUPPRESS,
+                      help='Show this help message and exit.')
+    args.add_argument("--user",
+                      help="ROC username",
+                      type=str)
+    args.add_argument("--password",
+                      help="ROC password",
+                      type=str)
+    args.add_argument("--token",
+                      help="Rancher bearer token",
+                      type=str)
+    args.add_argument("--port",
+                      help="Service port",
+                      type=str,
+                      default="3333")
+    args.add_argument("--period",
+                      help="Probing period in sec",
+                      type=int,
+                      default=180)
+    args.add_argument("--url",
+                      help="ROC url",
+                      type=str,
+                      default="https://roc.menlo.aetherproject.org/aether-roc-api/aether/v2.0.0/connectivity-service-v2/")
+    args.add_argument("--enterprise",
+                      help="Enterprise Id",
+                      type=str,
+                      default="aether-onf")
+    args.add_argument("--site",
+                      help="Site Id",
+                      type=str,
+                      default="menlo-4g")
+    return parser
+
+
+def update(roc, prom, old):
+    new = roc.update_devices(old)
+    if new is not None:
+        new = prom.update_devices(new)
+    else:
+        new = old
+    return new
+
+
+def do_probe(devices):
+    for imsi_id, device in devices.items():
+        if device.ip is None:
+            continue
+        if ping(device.ip):
+            device.reachable = True
+            device.last_reachable = datetime.now()
+            log.info("{}/{}/{} - reachable".format(device.imsi_id, device.imsi, device.ip))
+        else:
+            device.reachable = False
+            log.info("{}/{}/{} - unreachable".format(device.imsi_id, device.imsi, device.ip))
+
+
+def update_and_probe():
+    global devices, lock
+    new = update(roc, prom, devices)
+    do_probe(new)
+    with lock:
+        devices = new
+
+
+def work_thread(roc, prom):
+    global args
+    while True:
+        probe_start.wait()
+        probe_start.clear()
+        log.info("Probing started")
+        while True:
+            update_and_probe()
+            if probe_stop.wait(timeout=args.period):
+                log.info("Probing stopped")
+                probe_stop.clear()
+                break
+
+
+if __name__ == '__main__':
+
+    log.basicConfig(
+        format='%(asctime)s %(levelname)-8s %(message)s',
+        level=log.DEBUG,
+        datefmt='%Y-%m-%d %H:%M:%S',
+        stream=sys.stdout)
+
+    log.info("Starting network-diag-app...")
+
+    args = build_argparser().parse_args()
+
+    if not args.user:
+        args.user = os.environ.get('ROCUSER')
+    if not args.password:
+        args.password = os.environ.get('ROCPASSWORD')
+    if not args.token:
+        args.token= os.environ.get('KEYCLOAKTOKEN')
+
+    roc = Roc(args.url, args.user, args.password, args.enterprise, args.site)
+    prom = Prometheus(args.token.split(':')[0], args.token.split(':')[1])
+
+    t = threading.Thread(target=work_thread, args=(roc, prom,))
+    t.start()
+    probe_start.set()
+
+    app.run('0.0.0.0', args.port)