diff --git a/brian_dashboard_manager/grafana/provision.py b/brian_dashboard_manager/grafana/provision.py index d0a21f35dc694acbbe6c207c0aeee37cc3212932..dca966d756ace3a2808984a56c6800fedb0b5ec6 100644 --- a/brian_dashboard_manager/grafana/provision.py +++ b/brian_dashboard_manager/grafana/provision.py @@ -8,6 +8,7 @@ import time import json import datetime from functools import reduce +from concurrent.futures import Future from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor from brian_dashboard_manager.config import DEFAULT_ORGANIZATIONS, STATE_PATH from brian_dashboard_manager.grafana.utils.request import \ @@ -23,7 +24,7 @@ from brian_dashboard_manager.grafana.datasource import \ from brian_dashboard_manager.grafana.folder import find_folder, \ delete_folder from brian_dashboard_manager.inventory_provider.interfaces import \ - get_gws_direct, get_interfaces + get_gws_direct, get_gws_indirect, get_interfaces from brian_dashboard_manager.templating.nren_access import generate_nrens from brian_dashboard_manager.templating.helpers import is_re_customer, \ @@ -34,7 +35,8 @@ from brian_dashboard_manager.templating.helpers import is_re_customer, \ get_interface_data, parse_backbone_name, parse_phy_upstream_name, \ get_dashboard_data, get_aggregate_interface_data -from brian_dashboard_manager.templating.gws import generate_gws +from brian_dashboard_manager.templating.gws import generate_gws, \ + generate_indirect from brian_dashboard_manager.templating.render import render_dashboard @@ -119,6 +121,9 @@ def provision_maybe(config): now = datetime.datetime.now() write_timestamp(now.timestamp(), val) provision(config) + except Exception as e: + logger.exception('Uncaught Exception:') + raise e finally: now = datetime.datetime.now() write_timestamp(now.timestamp(), False) @@ -182,7 +187,10 @@ def provision(config): def excluded(interface): desc = interface.get('description', '').lower() - return not any(nren.lower() in desc for nren in excluded_nrens) + lab = 'lab.office' in interface.get('router', '').lower() + excluded_desc = any( + nren.lower() in desc for nren in excluded_nrens) + return not (excluded_desc or lab) excluded_interfaces = list(filter(excluded, interfaces)) @@ -265,6 +273,14 @@ def provision(config): dash_list = find_dashboard(token_request) or [] dash_list = reduce(get_uid, dash_list, {}) + def update_dash_list(dashboards): + for dashboard in dashboards: + if isinstance(dashboard, Future): + dashboard = dashboard.result() + if dashboard is None: + continue + dash_list[dashboard.get('uid')] = True + with ProcessPoolExecutor(max_workers=4) as executor: provisioned = [] for folder_name, dash in dashboards.items(): @@ -289,10 +305,35 @@ def provision(config): folder = result.result() if folder is None: continue - for dashboard in folder: - if dashboard is None: + update_dash_list(folder) + + # fetch GWS direct data and provision related dashboards + logger.info('Provisioning GWS Indirect dashboards') + folder_name = 'GWS Indirect' + exclude_indirect = excluded_folders.get(folder_name, []) + exclude_indirect = list(map(lambda f: f.lower(), exclude_indirect)) + if isinstance(exclude_indirect, bool) and exclude_indirect: + # don't provision GWS Direct folder + delete_folder(token_request, folder_name) + else: + folder = find_folder(token_request, folder_name) + with ProcessPoolExecutor(max_workers=4) as executor: + gws_indirect_data = get_gws_indirect( + config['inventory_provider']) + provisioned = [] + + dashes = generate_indirect(gws_indirect_data, datasource_name) + for dashboard in dashes: + rendered = render_dashboard(dashboard) + if rendered.get('title').lower() in exclude_indirect: + executor.submit(delete_dashboard, token_request, + rendered, folder['id']) continue - dash_list[dashboard.get('uid')] = True + provisioned.append(executor.submit(create_dashboard, + token_request, + rendered, folder['id'])) + + update_dash_list(provisioned) # fetch GWS direct data and provision related dashboards logger.info('Provisioning GWS Direct dashboards') @@ -318,11 +359,7 @@ def provision(config): token_request, rendered, folder['id'])) - for result in provisioned: - dashboard = result.result() - if dashboard is None: - continue - dash_list[dashboard.get('uid')] = True + update_dash_list(provisioned) aggregate_dashboards = { 'CLS PEERS': { @@ -370,11 +407,7 @@ def provision(config): excluded_interfaces, datasource_name) provisioned.append(res) - for result in provisioned: - dashboard = result.result() - if dashboard is None: - continue - dash_list[dashboard.get('uid')] = True + update_dash_list(provisioned) # NREN Access dashboards # uses a different template than the above. diff --git a/brian_dashboard_manager/templating/gws.py b/brian_dashboard_manager/templating/gws.py index e9f4b1353244bb31b67dc399769882afe20daa35..6a321a6156acccec5ce5afbc172c4258e6f06875 100644 --- a/brian_dashboard_manager/templating/gws.py +++ b/brian_dashboard_manager/templating/gws.py @@ -2,7 +2,7 @@ from typing import DefaultDict from brian_dashboard_manager.templating.helpers import get_dashboard_data -def get_interface_data(interfaces): +def get_panel_data(interfaces): result = DefaultDict(list) for interface in interfaces: @@ -39,8 +39,36 @@ def get_interface_data(interfaces): return result +def get_gws_indirect_panel_data(interfaces): + result = DefaultDict(list) + + for interface in interfaces: + + hostname = interface.get('hostname').replace('.geant.net', '') + interface_name = interface.get('interface') + service_name = interface.get('name') + customer = interface.get('customer') + + measurement = 'dscp32_rates' + panel_title = f'{hostname} - {{}} - {interface_name} - #{service_name}' + result[f'GWS Indirect - {customer}'].append({ + 'measurement': measurement, + 'title': panel_title, + 'interface': interface_name, + 'hostname': interface.get('hostname'), + 'has_v6': False + }) + return result + + def generate_gws(gws_data, datasource): - panel_data = get_interface_data(gws_data) - for dashboard in get_dashboard_data(panel_data, datasource, 'GWS_DIRECT'): - yield dashboard + panel_data = get_panel_data(gws_data) + for dash in get_dashboard_data(panel_data, datasource, 'GWS_DIRECT'): + yield dash + + +def generate_indirect(gws_data, datasource): + panel_data = get_gws_indirect_panel_data(gws_data) + for dash in get_dashboard_data(panel_data, datasource, 'GWS_INDIRECT'): + yield dash diff --git a/test/test_gws_indirect.py b/test/test_gws_indirect.py new file mode 100644 index 0000000000000000000000000000000000000000..9edd611152bbd469f5ba3deec6a38e4069ed86e4 --- /dev/null +++ b/test/test_gws_indirect.py @@ -0,0 +1,102 @@ +import responses +import json +from brian_dashboard_manager.templating.gws import generate_indirect +from brian_dashboard_manager.inventory_provider.interfaces import \ + get_gws_indirect + + +TEST_DATA = [ + { + "id": 712361, + "name": "FCCN-AP3-IAS", + "customer": "FCCN", + "speed": 107374182400, + "pop": "PORTO", + "hostname": "rt1.por.pt.geant.net", + "interface": "ae10.333", + "type": "GWS - INDIRECT", + "status": "operational" + }, + { + "id": 661222, + "name": "FCCN-AP2-IAS", + "customer": "FCCN", + "speed": 42949672960, + "pop": "LISBON 2", + "hostname": "mx1.lis.pt.geant.net", + "interface": "ae10.333", + "type": "GWS - INDIRECT", + "status": "operational" + }, + { + "id": 661500, + "name": "IUCC-AP1-IAS", + "customer": "IUCC", + "speed": 32212254720, + "pop": "LONDON", + "hostname": "mx1.lon.uk.geant.net", + "interface": "ae21.333", + "type": "GWS - INDIRECT", + "status": "operational" + }, + { + "id": 663112, + "name": "ROEDUNET_AP1_IAS", + "customer": "ROEDUNET", + "speed": 42949672960, + "pop": "BUCHAREST", + "hostname": "mx1.buc.ro.geant.net", + "interface": "ae11.333", + "type": "GWS - INDIRECT", + "status": "operational" + }, + { + "id": 663228, + "name": "IUCC-AP2-IAS", + "customer": "IUCC", + "speed": 32212254720, + "pop": "FRANKFURT", + "hostname": "mx1.fra.de.geant.net", + "interface": "ae21.333", + "type": "GWS - INDIRECT", + "status": "operational" + }, + { + "id": 661641, + "name": "FCCN-AP1-IAS", + "customer": "FCCN", + "speed": 42949672960, + "pop": "LISBON", + "hostname": "mx2.lis.pt.geant.net", + "interface": "ae10.333", + "type": "GWS - INDIRECT", + "status": "operational" + } +] + + +@responses.activate +def test_gws(data_config, mocker, client): + + def get_callback(request): + return 200, {}, json.dumps(TEST_DATA) + + responses.add_callback( + method=responses.GET, + url=f"{data_config['inventory_provider']}/poller/gws/indirect", + callback=get_callback) + + gws_data = get_gws_indirect(data_config['inventory_provider']) + + dashboards = list(generate_indirect(gws_data, 'testdatasource')) + + assert len(dashboards) == 3 + + assert dashboards[0]['title'] == 'GWS Indirect - FCCN' + assert len(dashboards[0]['panels']) == 3 + + assert dashboards[1]['title'] == 'GWS Indirect - IUCC' + assert len(dashboards[1]['panels']) == 2 + + assert dashboards[2]['title'] == 'GWS Indirect - ROEDUNET' + assert len(dashboards[2]['panels']) == 1 diff --git a/test/test_update.py b/test/test_update.py index 29b955c71b2b95aa5118a13d78bec06d4087c379..55050de6be23493d7b201cdecb1dfaa0f36ad556 100644 --- a/test/test_update.py +++ b/test/test_update.py @@ -464,6 +464,10 @@ def test_provision(data_config, mocker, client): 'brian_dashboard_manager.grafana.provision.get_gws_direct') _mocked_gws.return_value = [] + _mocked_gws_indirect = mocker.patch( + 'brian_dashboard_manager.grafana.provision.get_gws_indirect') + _mocked_gws_indirect.return_value = [] + UID = 1 ID = 1 VERSION = 1