AETHER-1717 Validate JSON in request
Change-Id: Ia3891f6c1ff85a05f98031185ebd02576b968328
diff --git a/edge-monitoring/VERSION b/edge-monitoring/VERSION
index a918a2a..ee6cdce 100644
--- a/edge-monitoring/VERSION
+++ b/edge-monitoring/VERSION
@@ -1 +1 @@
-0.6.0
+0.6.1
diff --git a/edge-monitoring/edge_monitoring_server.py b/edge-monitoring/edge_monitoring_server.py
index f8f8c91..aa4d7e5 100755
--- a/edge-monitoring/edge_monitoring_server.py
+++ b/edge-monitoring/edge_monitoring_server.py
@@ -12,6 +12,7 @@
from icalevents.icalevents import events
from flask import Flask, jsonify, abort, request, Response
import prometheus_client as prom
+import jsonschema
# URL of maintenance calendar
SECRET_ICAL_URL = os.environ.get("SECRET_ICAL_URL")
@@ -24,6 +25,64 @@
NO_RESULT_THRESHOLD = 720
app = Flask(__name__)
+
+edgeSchema = {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "status": {
+ "type": "object",
+ "properties": {
+ "control_plane": {"type": "string"},
+ "user_plane": {"type": "string"}
+ },
+ "required": ["control_plane", "user_plane"]
+ },
+ "speedtest": {
+ "type": "object",
+ "properties": {
+ "ping": {
+ "type": "object",
+ "properties": {
+ "dns": {
+ "type": "object",
+ "properties": {
+ "min": {"type": "number"},
+ "avg": {"type": "number"},
+ "max": {"type": "number"},
+ "stddev": {"type": "number"}
+ },
+ "required": ["min", "avg", "max", "stddev"]
+ }
+ }
+ },
+ "iperf": {
+ "type": "object",
+ "properties": {
+ "cluster": {
+ "type": "object",
+ "properties": {
+ "downlink": {"type": "number"},
+ "uplink": {"type": "number"}
+ },
+ "required": ["downlink", "uplink"]
+ }
+ }
+ }
+ }
+ },
+ "signal_quality": {
+ "type": "object",
+ "properties": {
+ "rsrq": {"type": "number"},
+ "rsrp": {"type": "number"}
+ },
+ "required": ["rsrq", "rsrp"]
+ }
+ },
+ "required": ["name", "status"]
+}
+
edges = [
{
'name': 'ace-example',
@@ -305,11 +364,10 @@
@app.route('/edges', methods=['POST'])
@app.route('/testresults', methods=['POST'])
def create_or_update_edge():
- if not request.json:
- abort(400)
- if 'name' not in request.json:
- abort(400)
- if 'status' not in request.json:
+ try:
+ jsonschema.validate(instance=request.json, schema=edgeSchema)
+ except jsonschema.exceptions.ValidationError as err:
+ print(err)
abort(400)
req_edge = {
diff --git a/edge-monitoring/requirements.txt b/edge-monitoring/requirements.txt
index 7b0752e..bc3f77b 100644
--- a/edge-monitoring/requirements.txt
+++ b/edge-monitoring/requirements.txt
@@ -2,3 +2,4 @@
prometheus-client
pytz
icalevents
+jsonschema
diff --git a/edge-monitoring/test_edge_monitoring_server.py b/edge-monitoring/test_edge_monitoring_server.py
index 33e26f7..e326a06 100755
--- a/edge-monitoring/test_edge_monitoring_server.py
+++ b/edge-monitoring/test_edge_monitoring_server.py
@@ -10,6 +10,7 @@
import pytz
import json
import time
+from copy import deepcopy
test_edge = {
@@ -243,7 +244,7 @@
response = self.app.get('/edges/metrics')
data = response.get_data(as_text=True)
- print(data)
+ # print(data)
self._assert_status_metrics_exist(data)
self._assert_speedtest_metrics_exist(data)
@@ -255,7 +256,7 @@
response = self.app.get('/edges/metrics')
data = response.get_data(as_text=True)
- print(data)
+ # print(data)
self.assertFalse('ace-menlo-pixel' in data)
response = self.app.get('/edges')
@@ -273,7 +274,7 @@
response = self.app.get('/edges/metrics')
data = response.get_data(as_text=True)
- print(data)
+ # print(data)
self._assert_status_metrics_exist(data)
self._assert_speedtest_metrics_exist(data)
@@ -285,7 +286,7 @@
response = self.app.get('/edges/metrics')
data = response.get_data(as_text=True)
- print(data)
+ # print(data)
self.assertFalse('ace-menlo-pixel' in data)
response = self.app.get('/edges')
@@ -299,7 +300,7 @@
response = self.app.get('/edges/metrics')
data = response.get_data(as_text=True)
- print(data)
+ # print(data)
self._assert_speedtest_metrics_exist(data)
@@ -314,7 +315,7 @@
response = self.app.get('/edges/metrics')
data = response.get_data(as_text=True)
- print(data)
+ # print(data)
self.assertFalse('aetheredge_signal_quality_rsrq{name="ace-menlo-pixel"}' in data)
self.assertFalse('aetheredge_signal_quality_rsrp{name="ace-menlo-pixel"}' in data)
@@ -332,7 +333,7 @@
response = self.app.get('/edges/metrics')
data = response.get_data(as_text=True)
- print(data)
+ # print(data)
self.assertFalse('aetheredge_ping_dns_test_min{name="ace-menlo-pixel"}' in data)
self.assertFalse('aetheredge_ping_dns_test_avg{name="ace-menlo-pixel"}' in data)
@@ -355,7 +356,7 @@
response = self.app.get('/edges/metrics')
data = response.get_data(as_text=True)
- print(data)
+ # print(data)
self.assertFalse('aetheredge_signal_quality_rsrq{name="ace-menlo-pixel"}' in data)
self.assertFalse('aetheredge_signal_quality_rsrp{name="ace-menlo-pixel"}' in data)
@@ -370,7 +371,7 @@
def test_timeout_stale_result(self):
response = self.app.post('/testresults', json=test_edge)
data = json.loads(response.get_data(as_text=True))
- print(json.dumps(data, indent=2))
+ # print(json.dumps(data, indent=2))
self.assertEqual(data['edge']['status']['control_plane'], 'connected')
self.assertEqual(data['edge']['status']['user_plane'], 'connected')
@@ -381,7 +382,7 @@
response = self.app.get('/edges/ace-menlo-pixel')
data = json.loads(response.get_data(as_text=True))
- print(json.dumps(data, indent=2))
+ # print(json.dumps(data, indent=2))
self.assertEqual(data['edge']['status']['control_plane'], 'no result')
self.assertEqual(data['edge']['status']['user_plane'], 'no result')
@@ -395,7 +396,7 @@
response = self.app.get('/edges/metrics')
data = response.get_data(as_text=True)
- print(data)
+ # print(data)
self.assertFalse('iperf_cluster_downlink{name="ace-menlo-pixel"}' in data)
self.assertFalse('iperf_cluster_uplink{name="ace-menlo-pixel"}' in data)
@@ -404,6 +405,57 @@
data = json.loads(response.get_data(as_text=True))
self.assertEqual(data['result'], True)
+ def test_handle_invalid_schema(self):
+ response = self.app.post('/testresults', json="")
+ self.assertEqual(response.status_code, 400)
+
+ no_name = deepcopy(test_edge)
+ del no_name['name']
+ response = self.app.post('/testresults', json=no_name)
+ self.assertEqual(response.status_code, 400)
+ response = self.app.get('/edges/metrics')
+ data = response.get_data(as_text=True)
+ self.assertFalse('ace-menlo-pixel' in data)
+
+ no_status = deepcopy(test_edge)
+ del no_status['status']
+ response = self.app.post('/testresults', json=no_status)
+ self.assertEqual(response.status_code, 400)
+ response = self.app.get('/edges/metrics')
+ data = response.get_data(as_text=True)
+ self.assertFalse('ace-menlo-pixel' in data)
+
+ bad_status = deepcopy(test_edge)
+ bad_status['status']['control_plane'] = 1
+ response = self.app.post('/testresults', json=bad_status)
+ self.assertEqual(response.status_code, 400)
+ response = self.app.get('/edges/metrics')
+ data = response.get_data(as_text=True)
+ self.assertFalse('ace-menlo-pixel' in data)
+
+ bad_ping_result = deepcopy(test_edge)
+ bad_ping_result['speedtest']['ping']['dns']['min'] = "foo"
+ response = self.app.post('/testresults', json=bad_ping_result)
+ self.assertEqual(response.status_code, 400)
+ response = self.app.get('/edges/metrics')
+ data = response.get_data(as_text=True)
+ self.assertFalse('ace-menlo-pixel' in data)
+
+ bad_iperf_result = deepcopy(test_edge)
+ bad_iperf_result['speedtest']['iperf']['cluster']['uplink'] = "foo"
+ response = self.app.post('/testresults', json=bad_iperf_result)
+ self.assertEqual(response.status_code, 400)
+ response = self.app.get('/edges/metrics')
+ data = response.get_data(as_text=True)
+ self.assertFalse('ace-menlo-pixel' in data)
+
+ bad_signal_quality = deepcopy(test_edge)
+ del bad_signal_quality['signal_quality']['rsrq']
+ response = self.app.post('/testresults', json=bad_signal_quality)
+ self.assertEqual(response.status_code, 400)
+ response = self.app.get('/edges/metrics')
+ data = response.get_data(as_text=True)
+ self.assertFalse('ace-menlo-pixel' in data)
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(TestEdgeMonitoringServer)