Basic ping test

Change-Id: Ib61ebd3963db46118a0d8f5c4cea0e002f48aa9a
diff --git a/VERSION b/VERSION
index 4e379d2..b470f6b 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.0.2
+0.0.3-dev
diff --git a/app.py b/app.py
index cb8bedb..c3e3238 100644
--- a/app.py
+++ b/app.py
@@ -3,6 +3,8 @@
 SPDX-License-Identifier: LicenseRef-ONF-Member-1.01
 """
 import sys
+import re
+from datetime import datetime
 
 from flask import Flask, request
 from flask_restful import Resource, Api
@@ -10,24 +12,30 @@
 from argparse import ArgumentParser, SUPPRESS
 
 from roc import Roc
+from prom import Prometheus
+from ping import ping
+import device
 
 app = Flask(__name__)
 api = Api(app)
 
-global roc
+devices = {} # dict imsi:device
 
 
-class Imsi(Resource):
+
+class ReachableDevices(Resource):
     def get(self):
-        return roc.get_imsi()
+        global devices
+        return device.get_reachable(devices)
 
-class Subnet(Resource):
+class UnreachableDevices(Resource):
     def get(self):
-        return roc.get_subnet()
+        global devices
+        return device.get_unreachable(devices)
 
 
-api.add_resource(Imsi, '/imsi')
-api.add_resource(Subnet, '/subnet')
+api.add_resource(ReachableDevices, '/reachable-devices')
+api.add_resource(UnreachableDevices, '/unreachable-devices')
 
 
 def build_argparser():
@@ -43,6 +51,9 @@
     args.add_argument("--password",
                       help="ROC password",
                       type=str)
+    args.add_argument("--token",
+                      help="Rancher bearer token",
+                      type=str)
     return parser
 
 
@@ -58,7 +69,22 @@
     args = build_argparser().parse_args()
 
     roc = Roc(args.user, args.password)
-    #roc.dump()
-    print(roc.get_imsi())
+    prom = Prometheus(args.token.split(':')[0], args.token.split(':')[1])
+
+    roc.update_devices(devices)
+
+    prom.update_devices(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))
+
 
     app.run('0.0.0.0', '3333')
diff --git a/device.py b/device.py
new file mode 100644
index 0000000..767180e
--- /dev/null
+++ b/device.py
@@ -0,0 +1,41 @@
+"""
+SPDX-FileCopyrightText: 2020-present Open Networking Foundation <info@opennetworking.org>
+SPDX-License-Identifier: LicenseRef-ONF-Member-1.01
+"""
+
+import logging as log
+from datetime import datetime
+
+
+class Device(object):
+
+    def __init__(self, imsi_id, imsi, ip=None):
+        # log.debug("creating device, imsi_id={}, imsi={}, ip={}".format(imsi_id, imsi, ip))
+        self.imsi_id = imsi_id
+        self.imsi = imsi
+        self.ip = ip
+        self.reachable = False
+        self.last_reachable = datetime.min
+
+    @classmethod
+    def get_reachable(cls, devices):
+        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)}
+                last_reachable = datetime(1, 1, 1, 0, 0)
+
+        return reachable
+
+    @classmethod
+    def get_unreachable(cls, devices):
+        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
+
+
+
+
diff --git a/ping.py b/ping.py
new file mode 100644
index 0000000..5903fd9
--- /dev/null
+++ b/ping.py
@@ -0,0 +1,18 @@
+"""
+SPDX-FileCopyrightText: 2020-present Open Networking Foundation <info@opennetworking.org>
+SPDX-License-Identifier: LicenseRef-ONF-Member-1.01
+"""
+
+import os
+import subprocess
+import logging as log
+
+
+def ping(host):
+    #log.debug("Pinging {}".format(host))
+    if host != None:
+        return subprocess.call(["ping", "-c", "1", "-W", "1", host],
+                stdout=subprocess.DEVNULL,
+                stderr=subprocess.STDOUT) == 0
+    else:
+        return False
diff --git a/prom.py b/prom.py
new file mode 100644
index 0000000..9a517e1
--- /dev/null
+++ b/prom.py
@@ -0,0 +1,84 @@
+"""
+SPDX-FileCopyrightText: 2020-present Open Networking Foundation <info@opennetworking.org>
+SPDX-License-Identifier: LicenseRef-ONF-Member-1.01
+"""
+
+import sys
+import json
+import logging as log
+
+import pyaml
+import requests
+
+#from device import Devices
+
+PROMETHEUS = "https://rancher.aetherproject.org/k8s/clusters/c-xp25p/api/v1/namespaces/cattle-monitoring-system" \
+             "/services/http:rancher-monitoring-prometheus:9090/proxy/api/v1/query"
+AUTH = ('token-m44n6', 'sgnxmckznp5kl6wqqxxk5pzd5ptc8qx9bqtfkfqxfx29qvqr8gld2f')
+
+
+class Prometheus(object):
+
+    def __init__(self, key, token):
+        self.key = key
+        self.token = token
+
+    def get_ips(self):
+        ips = {}
+        params = (
+            ('query', 'subscribers_info>0'),
+        )
+        response = requests.get(PROMETHEUS, params=params, auth=(self.key, self.token))
+        if response.status_code != 200:
+            log.error("get_subscriberinfo() failed, status_code: {}".format(response.status_code))
+            return None
+        metrics = json.loads(response.text)['data']['result']
+        for metric in metrics:
+            device = metric['metric']
+            if 'imsi' in device and 'mobile_ip' in device:
+                ips[device['imsi']] = device['mobile_ip']
+        return ips
+
+    def update_devices(self, devices):
+        params = (
+            ('query', 'subscribers_info'),
+        )
+
+        response = requests.get(PROMETHEUS, params=params, auth=(self.key, self.token))
+        if response.status_code != 200:
+            log.error("get_subscriberinfo() failed, status_code: {}".format(response.status_code))
+            sys.exit()
+            #return None
+
+        metrics = json.loads(response.text)['data']['result']
+
+        # log.debug(pyaml.dump(metrics))
+
+        for elem in metrics:
+            metric = elem['metric']
+            if 'imsi' in metric and 'mobile_ip' in metric:
+                imsi = metric['imsi']
+                if imsi in devices:
+                    # log.info("{} added to list of monitored devices".format(imsi))
+                    devices[imsi].ip = metric['mobile_ip']
+                else:
+                    log.error("Ignoring device as it was not reported by ROC - {}".format(metric))
+            else:
+                log.error("Ignoring device as imsi or mobile-ip not found - {}".format(metric))
+
+    def dump(self):
+        print(pyaml.dump(self.get_ips()))
+
+
+if __name__ == '__main__':
+    log.basicConfig()
+    log.getLogger().setLevel(log.DEBUG)
+    requests_log = log.getLogger("requests.packages.urllib3")
+    requests_log.setLevel(log.DEBUG)
+    requests_log.propagate = True
+
+    # use Rancher secret-key:bearer-token
+    prom = Prometheus("secret-key", "bearer-token")
+
+    # prom.get_ips()
+    prom.dump()
diff --git a/requirements.txt b/requirements.txt
index 726fd33..c51d4df 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,6 @@
 aniso8601==9.0.1
 certifi==2021.10.8
-charset-normalizer==2.0.10
+charset-normalizer==2.0.11
 click==8.0.3
 dataclasses==0.8
 Flask==2.0.2
diff --git a/roc.py b/roc.py
index 9d9478f..ede71f7 100644
--- a/roc.py
+++ b/roc.py
@@ -5,10 +5,13 @@
 
 import requests
 import json
+# noinspection PyUnresolvedReferences
 from requests.structures import CaseInsensitiveDict
 import logging as log
 import pyaml
 
+from device import Device
+
 URL = "https://roc.menlo.aetherproject.org/aether-roc-api/aether/v4.0.0/connectivity-service-v4/"
 
 
@@ -51,6 +54,7 @@
             if response.status_code != 201:
                 log.error("Failed to set mbr, device_group:{}, mbr:{}, status_code: {}".format(device_group, mbr,
                                                                                                response.status_code))
+                # noinspection PyUnresolvedReferences
                 sys.exit()
 
     def get_key(self):
@@ -92,11 +96,11 @@
             return None
         return json.loads(response.text)
 
-    def get_devicegroup(self):
+    def get_devicegroups(self):
         url = URL + "device-group"
         response = requests.get(url, headers=self.headers())
         if response.status_code != 200:
-            log.error("get_devicegroup() failed, status_code: {}".format(response.status_code))
+            log.error("get_devicegroups() failed, status_code: {}".format(response.status_code))
             return None
         return json.loads(response.text)['device-group']
 
@@ -108,14 +112,20 @@
             return None
         return json.loads(response.text)['ip-domain']
 
-    def get_imsi(self):
-        imsis = {} # imsi-id:imsi
-        devicegroups = self.get_devicegroup()
+    def get_devices(self):
+        imsis = {}
+        devicegroups = self.get_devicegroups()
         for devicegroup in devicegroups:
-            for imsi in devicegroup['imsis']:
-                imsis[imsi['imsi-id']] = imsi['imsi-range-from']
+            for device in devicegroup['imsis']:
+                imsis[device['imsi-id']] = device['imsi-range-from']
         return imsis
 
+    def update_devices(self, devices):
+        dgs = self.get_devicegroups()
+        for dg in dgs:
+            for d in dg['imsis']:
+                devices[str(d['imsi-range-from'])] = Device(str(d['imsi-id']), d['imsi-range-from'])
+
     def get_subnet(self):
         subnets = {}
         ip_domains = self.get_ipdomain()
@@ -128,7 +138,7 @@
         print(pyaml.dump(self.get_enterprise()))
         print(pyaml.dump(self.get_site()))
         print(pyaml.dump(self.get_upf()))
-        print(pyaml.dump(self.get_devicegroup()))
+        print(pyaml.dump(self.get_devicegroups()))
         print(pyaml.dump(self.get_ipdomain()))
 
 
@@ -140,7 +150,7 @@
     print(pyaml.dump(roc.get_enterprise()))
     print(pyaml.dump(roc.get_site()))
     print(pyaml.dump(roc.get_upf()))
-    print(pyaml.dump(roc.get_devicegroup()))
+    print(pyaml.dump(roc.get_devicegroups()))
     print(pyaml.dump(roc.get_ipdomain()))
 
     cameras = "menlo-4g-cameras"