diff --git a/deploy.sh b/deploy.sh index c5dee68a06e000bf7df90ef437e77c14232f2cec..e3c55c9da36d1a8c2b39d5fffa190ac5e42d7580 100755 --- a/deploy.sh +++ b/deploy.sh @@ -247,19 +247,22 @@ if [[ "$TFS_COMPONENTS" == *"webui"* ]] && [[ "$TFS_COMPONENTS" == *"monitoring" "url" : "monitoringservice:8812", "database" : "monitoring", "user" : "admin", - "password" : "quest", - "basicAuth" : false, + "basicAuth": false, "isDefault": true, "jsonData" : { - "sslmode" : "disable", - "postgresVersion" : 1100, - "tlsAuth" : false, - "tlsAuthWithCACert": false, + "sslmode" : "disable", + "postgresVersion" : 1100, + "maxOpenConns" : 0, + "maxIdleConns" : 2, + "connMaxLifetime" : 14400, + "tlsAuth" : false, + "tlsAuthWithCACert" : false, + "timescaledb" : false, "tlsConfigurationMethod": "file-path", - "tlsSkipVerify": true + "tlsSkipVerify" : true }, - "secureJsonFields" : { - "password" : true + "secureJsonData": { + "password": "quest" } }' ${GRAFANA_URL_UPDATED}/api/datasources echo @@ -267,7 +270,7 @@ if [[ "$TFS_COMPONENTS" == *"webui"* ]] && [[ "$TFS_COMPONENTS" == *"monitoring" # Create Monitoring Dashboard # Ref: https://grafana.com/docs/grafana/latest/http_api/dashboard/ curl -X POST -H "Content-Type: application/json" \ - -d '@src/webui/grafana_dashboard.json' \ + -d '@src/webui/grafana_dashboard_psql.json' \ ${GRAFANA_URL_UPDATED}/api/dashboards/db echo diff --git a/src/webui/grafana_dashboard_psql.json b/src/webui/grafana_dashboard_psql.json new file mode 100644 index 0000000000000000000000000000000000000000..aa2676e26a0336c8279a658dbbdabaafa9c6b4d0 --- /dev/null +++ b/src/webui/grafana_dashboard_psql.json @@ -0,0 +1,313 @@ +{"overwrite": true, "folderId": 0, "dashboard": + { + "id": null, + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "iteration": 1664814762635, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "postgres", + "uid": "monitoringdb" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": ".*PACKETS_.*" + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "left" + }, + { + "id": "unit", + "value": "pps" + }, + { + "id": "custom.axisLabel", + "value": "Packets / sec" + }, + { + "id": "custom.axisSoftMin", + "value": 0 + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": ".*BYTES_.*" + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "unit", + "value": "Bps" + }, + { + "id": "custom.axisLabel", + "value": "Bytes / sec" + }, + { + "id": "custom.axisSoftMin", + "value": 0 + } + ] + } + ] + }, + "gridPos": { + "h": 19, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [ + "first", + "min", + "mean", + "max", + "lastNotNull" + ], + "displayMode": "table", + "placement": "right" + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "postgres", + "uid": "monitoringdb" + }, + "format": "time_series", + "group": [], + "hide": false, + "metricColumn": "kpi_value", + "rawQuery": true, + "rawSql": "SELECT\r\n $__time(timestamp), kpi_value AS metric, device_id, endpoint_id, kpi_sample_type\r\nFROM\r\n monitoring\r\nWHERE\r\n $__timeFilter(timestamp) AND device_id IN ($device_id) AND endpoint_id IN ($endpoint_id) AND kpi_sample_type IN ($kpi_sample_type)\r\nGROUP BY\r\n device_id, endpoint_id, kpi_sample_type\r\nORDER BY\r\n timestamp\r\n", + "refId": "A", + "select": [ + [ + { + "params": [ + "kpi_value" + ], + "type": "column" + } + ] + ], + "table": "monitoring", + "timeColumn": "timestamp", + "where": [ + { + "name": "", + "params": [ + "device_id", + "IN", + "$device_id" + ], + "type": "expression" + } + ] + } + ], + "title": "L3 Monitoring Packets/Bytes Received/Sent", + "transformations": [ + { + "id": "renameByRegex", + "options": { + "regex": "metric {device_id=\\\"([^\\\"]+)\\\", endpoint_id=\\\"([^\\\"]+)\\\", kpi_sample_type=\\\"([^\\\"]+)\\\"}", + "renamePattern": "$3 ($1 $2)" + } + } + ], + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 36, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "type": "postgres", + "uid": "monitoringdb" + }, + "definition": "SELECT DISTINCT device_id FROM monitoring;", + "hide": 0, + "includeAll": true, + "label": "Device", + "multi": true, + "name": "device_id", + "options": [], + "query": "SELECT DISTINCT device_id FROM monitoring;", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "postgres", + "uid": "monitoringdb" + }, + "definition": "SELECT DISTINCT endpoint_id FROM monitoring WHERE device_id IN (${device_id})", + "hide": 0, + "includeAll": true, + "label": "EndPoint", + "multi": true, + "name": "endpoint_id", + "options": [], + "query": "SELECT DISTINCT endpoint_id FROM monitoring WHERE device_id IN (${device_id})", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": true, + "text": [ + "PACKETS_RECEIVED", + "PACKETS_TRANSMITTED" + ], + "value": [ + "PACKETS_RECEIVED", + "PACKETS_TRANSMITTED" + ] + }, + "datasource": { + "type": "postgres", + "uid": "monitoringdb" + }, + "definition": "SELECT DISTINCT kpi_sample_type FROM monitoring;", + "hide": 0, + "includeAll": true, + "label": "Kpi Sample Type", + "multi": true, + "name": "kpi_sample_type", + "options": [], + "query": "SELECT DISTINCT kpi_sample_type FROM monitoring;", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "L3 Monitoring", + "uid": "tf-l3-monit", + "version": 1, + "weekStart": "" + } +} diff --git a/src/webui/service/main/routes.py b/src/webui/service/main/routes.py index e9545ade40949a1ad772b35b669e02a1fa39d64d..d115444487c7356a541f3c567e5dea183da73ade 100644 --- a/src/webui/service/main/routes.py +++ b/src/webui/service/main/routes.py @@ -13,6 +13,7 @@ # limitations under the License. import copy, json, logging +from typing import Optional from flask import jsonify, redirect, render_template, Blueprint, flash, session, url_for, request from common.proto.context_pb2 import Connection, Context, Device, Empty, Link, Service, Slice, Topology, ContextIdList from common.tools.grpc.Tools import grpc_message_to_json_string @@ -43,9 +44,10 @@ ENTITY_TO_TEXT = { } ACTION_TO_TEXT = { - # action => infinitive, past - 'add' : ('Add', 'Added'), - 'update' : ('Update', 'Updated'), + # action => infinitive, past + 'add' : ('Add', 'Added'), + 'update' : ('Update', 'Updated'), + 'config' : ('Configure', 'Configured'), } def process_descriptor(entity_name, action_name, grpc_method, grpc_class, entities): @@ -94,14 +96,14 @@ def process_descriptors(descriptors): topology['device_ids'] = [] topology['link_ids'] = [] - process_descriptor('context', 'add', context_client.SetContext, Context, contexts_add ) - process_descriptor('topology', 'add', context_client.SetTopology, Topology, topologies_add) - process_descriptor('device', 'add', context_client.SetDevice, Device, devices ) - process_descriptor('link', 'add', context_client.SetLink, Link, links ) - process_descriptor('service', 'add', context_client.SetService, Service, services ) - process_descriptor('context', 'update', context_client.SetContext, Context, contexts ) - process_descriptor('topology', 'update', context_client.SetTopology, Topology, topologies ) - process_descriptor('slice', 'add', context_client.SetSlice, Slice, slices ) + process_descriptor('context', 'add', context_client.SetContext, Context, contexts_add ) + process_descriptor('topology', 'add', context_client.SetTopology, Topology, topologies_add) + process_descriptor('device', 'add', context_client.SetDevice, Device, devices ) + process_descriptor('link', 'add', context_client.SetLink, Link, links ) + process_descriptor('service', 'add', context_client.SetService, Service, services ) + process_descriptor('context', 'update', context_client.SetContext, Context, contexts ) + process_descriptor('topology', 'update', context_client.SetTopology, Topology, topologies ) + process_descriptor('slice', 'add', context_client.SetSlice, Slice, slices ) process_descriptor('connection', 'add', context_client.SetConnection, Connection, connections ) context_client.close() return @@ -111,6 +113,28 @@ def process_descriptors(descriptors): # in normal mode, connections should not be set assert len(connections) == 0 + devices_add = [] + devices_config = [] + for device in devices: + connect_rules = [] + config_rules = [] + for config_rule in device.get('device_config', {}).get('config_rules', []): + custom_resource_key : Optional[str] = config_rule.get('custom', {}).get('resource_key') + if custom_resource_key is not None and custom_resource_key.startswith('_connect/'): + connect_rules.append(config_rule) + else: + config_rules.append(config_rule) + + if len(connect_rules) > 0: + device_add = copy.deepcopy(device) + device_add['device_endpoints'] = [] + device_add['device_config'] = {'config_rules': connect_rules} + devices_add.append(device_add) + + if len(config_rules) > 0: + device['device_config'] = {'config_rules': config_rules} + devices_config.append(device) + services_add = [] for service in services: service_copy = copy.deepcopy(service) @@ -132,14 +156,15 @@ def process_descriptors(descriptors): service_client.connect() slice_client.connect() - process_descriptor('context', 'add', context_client.SetContext, Context, contexts ) - process_descriptor('topology', 'add', context_client.SetTopology, Topology, topologies ) - process_descriptor('device', 'add', device_client .AddDevice, Device, devices ) - process_descriptor('link', 'add', context_client.SetLink, Link, links ) - process_descriptor('service', 'add', service_client.CreateService, Service, services_add) - process_descriptor('service', 'update', service_client.UpdateService, Service, services ) - process_descriptor('slice', 'add', slice_client.CreateSlice, Slice, slices_add ) - process_descriptor('slice', 'update', slice_client.UpdateSlice, Slice, slices ) + process_descriptor('context', 'add', context_client.SetContext, Context, contexts ) + process_descriptor('topology', 'add', context_client.SetTopology, Topology, topologies ) + process_descriptor('device', 'add', device_client .AddDevice, Device, devices_add ) + process_descriptor('device', 'config', device_client .ConfigureDevice, Device, devices_config) + process_descriptor('link', 'add', context_client.SetLink, Link, links ) + process_descriptor('service', 'add', service_client.CreateService, Service, services_add ) + process_descriptor('service', 'update', service_client.UpdateService, Service, services ) + process_descriptor('slice', 'add', slice_client .CreateSlice, Slice, slices_add ) + process_descriptor('slice', 'update', slice_client .UpdateSlice, Slice, slices ) slice_client.close() service_client.close() diff --git a/src/webui/service/templates/base.html b/src/webui/service/templates/base.html index 9804e4afd1c1b7c889c2f3e0d627471ee13b5c68..5d7801d11880e89869120985307c6b43416f5a05 100644 --- a/src/webui/service/templates/base.html +++ b/src/webui/service/templates/base.html @@ -83,9 +83,9 @@ <a class="nav-link" href="{{ url_for('slice.home') }}">Slice</a> {% endif %} </li> - <!--<li class="nav-item"> + <li class="nav-item"> <a class="nav-link" href="/grafana" id="grafana_link" target="grafana">Grafana</a> - </li>--> + </li> <li class="nav-item"> <a class="nav-link" href="{{ url_for('main.debug') }}">Debug</a> diff --git a/src/webui/service/templates/device/detail.html b/src/webui/service/templates/device/detail.html index f2cdc581553bbd8d45f237fd99d2b746ab0ad61b..69ca93727310db7f89034f56510ceb5df504083f 100644 --- a/src/webui/service/templates/device/detail.html +++ b/src/webui/service/templates/device/detail.html @@ -45,6 +45,7 @@ <div class="col-sm-4"> <b>UUID: </b>{{ device.device_id.device_uuid.uuid }}<br><br> <b>Type: </b>{{ device.device_type }}<br><br> + <b>Status: </b> {{ dose.Name(device.device_operational_status).replace('DEVICEOPERATIONALSTATUS_', '') }}<br> <b>Drivers: </b> <ul> {% for driver in device.device_drivers %}