diff --git a/brian_dashboard_manager/config.py b/brian_dashboard_manager/config.py index eab88718e4cb01f4ad3dd9f7bde09bd55c6a428b..00546a696837a07ce5c3bedf0404cf1e8627c238 100644 --- a/brian_dashboard_manager/config.py +++ b/brian_dashboard_manager/config.py @@ -58,7 +58,8 @@ DEFAULT_ORGANIZATIONS = [ "excluded_folders": { "Aggregates": ["CAE1"], "EUMETSAT Multicast": True, - "NREN Access LEGACY": True + "NREN Access LEGACY": True, + "VLAN Interfaces": True, } }, { @@ -90,7 +91,8 @@ DEFAULT_ORGANIZATIONS = [ "GWS Direct": True, "GWS Indirect": True, "EUMETSAT Multicast": True, - "NREN Access LEGACY": True + "NREN Access LEGACY": True, + "VLAN Interfaces": True, } }, { @@ -111,7 +113,8 @@ DEFAULT_ORGANIZATIONS = [ ], "excluded_folders": { "EUMETSAT Multicast": True, - "NREN Access LEGACY": True + "NREN Access LEGACY": True, + "VLAN Interfaces": True, } }, { @@ -141,7 +144,8 @@ DEFAULT_ORGANIZATIONS = [ "IAS UPSTREAM": True, "GWS PHY Upstream": True, "EUMETSAT Multicast": True, - "NREN Access LEGACY": True + "NREN Access LEGACY": True, + "VLAN Interfaces": True, } } ] diff --git a/brian_dashboard_manager/grafana/provision.py b/brian_dashboard_manager/grafana/provision.py index cacf06e449c44f08a2883c61fd2e35a26f7d4a86..6cfafec124540725db385d03e084b2caea418c42 100644 --- a/brian_dashboard_manager/grafana/provision.py +++ b/brian_dashboard_manager/grafana/provision.py @@ -33,7 +33,7 @@ from brian_dashboard_manager.templating.helpers import \ get_nren_interface_data, get_dashboard_data, \ get_nren_dashboard_data, get_aggregate_interface_data, \ get_nren_interface_data_old, get_re_peer_dashboard_data, get_re_peer_interface_data, get_service_data, \ - get_service_dashboard_data, get_aggregate_service_data + get_service_dashboard_data, get_aggregate_service_data, get_router_dashboard_data, get_dashboard_data_dropdown from brian_dashboard_manager.templating.gws import generate_gws, generate_indirect from brian_dashboard_manager.templating.eumetsat import generate_eumetsat_multicast @@ -501,8 +501,25 @@ def _provision_vlan_dashboards(thread_executor: ThreadPoolExecutor, config, org_ folder_name = "VLAN Interfaces" # hardcoded, keep this in sync with the folder name specified in folders_to_keep logger.info(f'Provisioning {org_config["name"]}/{folder_name} dashboards') + excluded_folders = org_config.get('excluded_folders', {}) + if is_excluded_folder(excluded_folders, folder_name): + delete_folder(token, title=folder_name) + else: + folder = find_folder(token, title=folder_name) + if not folder: + raise Exception(f'Folder {folder_name} not found') + + folder_dashboards_by_name = list_folder_dashboards(token, folder['uid']) - yield + vlan_data = get_router_dashboard_data(interfaces) + provisioned = [] + for dashboard in get_dashboard_data_dropdown(vlan_data, ds_name, 'vlandash'): + rendered = render_simple_dashboard(**dashboard) + provisioned.append( + thread_executor.submit(create_dashboard, token, rendered, folder['id'], folder_dashboards_by_name) + ) + + yield from provisioned def _provision_gws_indirect(thread_executor: ThreadPoolExecutor, config, org_config, ds_name, token): diff --git a/brian_dashboard_manager/templating/helpers.py b/brian_dashboard_manager/templating/helpers.py index 6b2300cac6d6d988c39530467d7608dd6e5ad333..e3f58ac01649a948830268f28eaf933fe23a5374 100644 --- a/brian_dashboard_manager/templating/helpers.py +++ b/brian_dashboard_manager/templating/helpers.py @@ -543,9 +543,33 @@ def get_router_dashboard_data(interfaces): :return: dictionary of routers (dashboards) and their interface data. """ - # TODO: implement + result = {} + + filtered_interfaces = [interface for interface in interfaces if interface.get('vlan_type') in {'TRUNK', 'VLAN'}] + + sorted_interfaces = sorted(filtered_interfaces, key=lambda x: x['name']) + + for interface in sorted_interfaces: + description = interface['description'].strip() + interface_name = interface['name'] + host = interface['router'] - return {} + router = host.replace('.geant.net', '') + panel_title = f'{router} - {{}} - {interface_name} - {description}' + + dashboard_name = interface['router'] + dashboard = result.setdefault(dashboard_name, {}) + + base_interface = interface_name.split('.')[0] + dropdown = dashboard.setdefault(base_interface, []) + + dropdown.append({ + 'title': panel_title, + 'interface': interface_name, + 'hostname': host + }) + + return result def get_interface_data(interfaces): @@ -1130,7 +1154,6 @@ def get_dashboard_data_dropdown( def get_nren_dashboard_data(data, datasource, tag): - func = partial( get_dashboard_with_agg_data_single, datasource=datasource, diff --git a/test/test_update.py b/test/test_update.py index ba02ff73108cec438e20d086dd098a580dd2ca5c..688afe2b74c94276a76e817ec6b870a3c9a8b49c 100644 --- a/test/test_update.py +++ b/test/test_update.py @@ -1,3 +1,4 @@ +# flake8: noqa import pytest import responses from concurrent.futures import ThreadPoolExecutor @@ -11,7 +12,7 @@ TEST_INTERFACES = [ "name": "ge-0/0/8", "bundle": [], "bundle-parents": [], - "description": "PHY CUSTOMER GEANT CORPORATE SRF000001 | GEANT Corporate to MX1.LON - Via Vodafone", # noqa: E501 + "description": "PHY CUSTOMER GEANT CORPORATE SRF000001 | GEANT Corporate to MX1.LON - Via Vodafone", "circuits": [ { "id": 679232, @@ -41,7 +42,7 @@ TEST_INTERFACES = [ "name": "ge-0/0/8.10", "bundle": [], "bundle-parents": [], - "description": "SRV_GLOBAL CUSTOMER GEANT #GEANT_CORPORATE-ViaVodafone | GEANT Corporate to mx1.lon - Via Vodafone ", # noqa: E501 + "description": "SRV_GLOBAL CUSTOMER GEANT #GEANT_CORPORATE-ViaVodafone | GEANT Corporate to mx1.lon - Via Vodafone ", "circuits": [ { "id": 679360, @@ -75,7 +76,7 @@ TEST_INTERFACES = [ "name": "ge-0/0/8.11", "bundle": [], "bundle-parents": [], - "description": "SRV_GLOBAL CUSTOMER GEANT #GEANT_CORPORATE_ViaVodafone-VRF | GEANT Corporate to mx1.lon - Via Vodafone - for VRF", # noqa: E501 + "description": "SRV_GLOBAL CUSTOMER GEANT #GEANT_CORPORATE_ViaVodafone-VRF | GEANT Corporate to mx1.lon - Via Vodafone - for VRF", "circuits": [ { "id": 712144, @@ -107,11 +108,11 @@ TEST_INTERFACES = [ "name": "ge-0/0/8.12", "bundle": [], "bundle-parents": [], - "description": "SRV_GLOBAL CUSTOMER GEANT #GEANT_CORPORATE_ViaVodafone-VRF-TEST | GEANT Corporate to mx1.lon - Via Vodafone - DASHBOARD BGP TEST VLAN", # noqa: E501 + "description": "SRV_GLOBAL CUSTOMER GEANT #GEANT_CORPORATE_ViaVodafone-VRF-TEST | GEANT Corporate to mx1.lon - Via Vodafone - DASHBOARD BGP TEST VLAN", "circuits": [ { "id": 678920, - "name": "GEANT_CORPORATE_VIAVODAFONE-VRF-TEST (DO NOT OPEN A TICKET)", # noqa: E501 + "name": "GEANT_CORPORATE_VIAVODAFONE-VRF-TEST (DO NOT OPEN A TICKET)", "type": "GEANT IP", "status": "non-monitored" } @@ -139,7 +140,7 @@ TEST_INTERFACES = [ "name": "ge-0/0/8.996", "bundle": [], "bundle-parents": [], - "description": "SRV_GLOBAL CUSTOMER GEANT #GEANT_OPERATIONS_LabConnectivity | GEANT MX1.LON Infinera VRF to Operations Lab", # noqa: E501 + "description": "SRV_GLOBAL CUSTOMER GEANT #GEANT_OPERATIONS_LabConnectivity | GEANT MX1.LON Infinera VRF to Operations Lab", "circuits": [ { "id": 678999, @@ -221,7 +222,7 @@ TEST_INTERFACES = [ "name": "ge-0/2/1.0", "bundle": [], "bundle-parents": [], - "description": "SRV_L2CIRCUIT INFRASTRUCTURE GEANT GEANT #AMS-ZAG OPENFLOW |", # noqa: E501 + "description": "SRV_L2CIRCUIT INFRASTRUCTURE GEANT GEANT #AMS-ZAG OPENFLOW |", "circuits": [], "snmp-index": 687, "dashboards": [ @@ -243,7 +244,7 @@ TEST_INTERFACES = [ "name": "ge-0/2/2.0", "bundle": [], "bundle-parents": [], - "description": "SRV_L2CIRCUIT INFRASTRUCTURE GEANT GEANT #AMS-VIE OPENFLOW |", # noqa: E501 + "description": "SRV_L2CIRCUIT INFRASTRUCTURE GEANT GEANT #AMS-VIE OPENFLOW |", "circuits": [], "snmp-index": 711, "dashboards": [ @@ -265,7 +266,7 @@ TEST_INTERFACES = [ "name": "ge-0/2/4.0", "bundle": [], "bundle-parents": [], - "description": "SRV_L2CIRCUIT INFRASTRUCTURE GEANT GEANT #AMS-FRA OPENFLOW |", # noqa: E501 + "description": "SRV_L2CIRCUIT INFRASTRUCTURE GEANT GEANT #AMS-FRA OPENFLOW |", "circuits": [], "snmp-index": 718, "dashboards": [ @@ -283,7 +284,6 @@ TEST_INTERFACES = [ "ipv6": [] }, { - "router": "rt1.fra.de.geant.net", "name": "xe-11/2/5.300", "bundle": [], @@ -326,7 +326,7 @@ NREN_INTERFACES = [ "ae10" ], "bundle-parents": [], - "description": "PHY CUSTOMER HEANET P_AE10 SRF9948758 | HEANET-AP2-LL3", # noqa: E501 + "description": "PHY CUSTOMER HEANET P_AE10 SRF9948758 | HEANET-AP2-LL3", "circuits": [], "snmp-index": 554, "dashboards": [ @@ -351,7 +351,7 @@ NREN_INTERFACES = [ "ae10" ], "bundle-parents": [], - "description": "PHY CUSTOMER HEANET P_AE10 SRF0000001 | HEANET-AP2-LL2", # noqa: E501 + "description": "PHY CUSTOMER HEANET P_AE10 SRF0000001 | HEANET-AP2-LL2", "circuits": [], "snmp-index": 527, "dashboards": [ @@ -376,7 +376,7 @@ NREN_INTERFACES = [ "ae10" ], "bundle-parents": [], - "description": "PHY CUSTOMER HEANET P_AE10 SRF9925903 | HEANET-AP2-LL1", # noqa: E501 + "description": "PHY CUSTOMER HEANET P_AE10 SRF9925903 | HEANET-AP2-LL1", "circuits": [], "snmp-index": 528, "dashboards": [ @@ -468,7 +468,7 @@ NREN_INTERFACES = [ "xe-1/0/1", "xe-1/1/0" ], - "description": "SRV_MDVPN CUSTOMER HEANET AP2 #HEANET-BGP-LU-CoC-1 |", # noqa: E501 + "description": "SRV_MDVPN CUSTOMER HEANET AP2 #HEANET-BGP-LU-CoC-1 |", "circuits": [ { "id": 663160, @@ -504,7 +504,7 @@ NREN_INTERFACES = [ "xe-1/0/1", "xe-1/1/0" ], - "description": "SRV_IAS CUSTOMER HEANET #HEANET-AP2-IAS IASPS | ASN1213 ", # noqa: E501 + "description": "SRV_IAS CUSTOMER HEANET #HEANET-AP2-IAS IASPS | ASN1213 ", "circuits": [ { "id": 663214, @@ -542,7 +542,7 @@ NREN_INTERFACES = [ "xe-1/0/1", "xe-1/1/0" ], - "description": "SRV_L2CIRCUIT CUSTOMER HEANET GEANT #ams-dub2-HEANET-RARE-21061 |", # noqa: E501 + "description": "SRV_L2CIRCUIT CUSTOMER HEANET GEANT #ams-dub2-HEANET-RARE-21061 |", "circuits": [ { "id": 713335, @@ -569,9 +569,7 @@ NREN_INTERFACES = [ "ipv4": [], "ipv6": [] }, - { - - "router": "rt1.fra.de.geant.net", + {"router": "rt1.fra.de.geant.net", "name": "xe-11/2/5.300", "bundle": [], "bundle-parents": [], @@ -602,48 +600,48 @@ NREN_INTERFACES = [ "port_type": "SERVICE", "ipv4": [], "ipv6": [] - } + } ] EUMETSAT_MULTICAST = [ { 'router': 'mx1.ams.nl.geant.net', - 'oid': '1.3.6.1.2.1.83.1.1.2.1.16.232.223.222.1.193.17.9.3.255.255.255.255', # noqa: E501 + 'oid': '1.3.6.1.2.1.83.1.1.2.1.16.232.223.222.1.193.17.9.3.255.255.255.255', 'community': '0pBiFbD', 'subscription': '232.223.222.1', 'endpoint': '193.17.9.3' }, { 'router': 'mx1.ams.nl.geant.net', - 'oid': '1.3.6.1.2.1.83.1.1.2.1.16.232.223.222.2.193.17.9.3.255.255.255.255', # noqa: E501 + 'oid': '1.3.6.1.2.1.83.1.1.2.1.16.232.223.222.2.193.17.9.3.255.255.255.255', 'community': '0pBiFbD', 'subscription': '232.223.222.2', 'endpoint': '193.17.9.3' }, { 'router': 'mx1.lon.uk.geant.net', - 'oid': '1.3.6.1.2.1.83.1.1.2.1.16.232.223.222.1.193.17.9.3.255.255.255.255', # noqa: E501 + 'oid': '1.3.6.1.2.1.83.1.1.2.1.16.232.223.222.1.193.17.9.3.255.255.255.255', 'community': '0pBiFbD', 'subscription': '232.223.222.1', 'endpoint': '193.17.9.3' }, { 'router': 'mx1.lon.uk.geant.net', - 'oid': '1.3.6.1.2.1.83.1.1.2.1.16.232.223.222.2.193.17.9.3.255.255.255.255', # noqa: E501 + 'oid': '1.3.6.1.2.1.83.1.1.2.1.16.232.223.222.2.193.17.9.3.255.255.255.255', 'community': '0pBiFbD', 'subscription': '232.223.222.2', 'endpoint': '193.17.9.3' }, { 'router': 'mx1.fra.de.geant.net', - 'oid': '1.3.6.1.2.1.83.1.1.2.1.16.232.223.222.1.193.17.9.3.255.255.255.255', # noqa: E501 + 'oid': '1.3.6.1.2.1.83.1.1.2.1.16.232.223.222.1.193.17.9.3.255.255.255.255', 'community': '0pBiFbD', 'subscription': '232.223.222.1', 'endpoint': '193.17.9.3' }, { 'router': 'mx1.fra.de.geant.net', - 'oid': '1.3.6.1.2.1.83.1.1.2.1.16.232.223.222.2.193.17.9.3.255.255.255.255', # noqa: E501 + 'oid': '1.3.6.1.2.1.83.1.1.2.1.16.232.223.222.2.193.17.9.3.255.255.255.255', 'community': '0pBiFbD', 'subscription': '232.223.222.2', 'endpoint': '193.17.9.3' @@ -848,7 +846,7 @@ def test_provision_re_peer_dashboard( { "router": "mx1.dub2.ie.geant.net", "name": "xe-0/0/0.1", - "description": "PHY SVC P_AE10 SRF9948758 | HEANET-AP2-LL3", # noqa: E501 + "description": "PHY SVC P_AE10 SRF9948758 | HEANET-AP2-LL3", "dashboards": ["RE_PEER"], "dashboard_info": {"name": "ESNET", "interface_type": "LOGICAL"}, "dashboards_info": [{"name": "ESNET", "interface_type": "LOGICAL"}], @@ -891,6 +889,102 @@ def test_provision_re_peer_dashboard( assert len(panels[-1]['panels'][0]) > 0 +@responses.activate +def test_vlan_interfaces(mocker, data_config, mock_grafana, reporting_provider, populate_inventory + ): + interfaces = [ + {'bundle': ['et-4/0/0', 'et-5/0/5', 'et-8/1/2', 'et-8/1/5'], + 'bundle-parents': ['et-4/0/0', 'et-5/0/5', 'et-8/1/2', 'et-8/1/5'], 'circuits': [], + 'dashboard_info': {'interface_type': 'AGGREGATE', 'name': 'REDIRIS'}, 'dashboards': ['NREN', 'RE_CUST'], + 'dashboards_info': [{'interface_type': 'AGGREGATE', 'name': 'REDIRIS'}], + 'description': 'LAG CUSTOMER REDIRIS SRF21114 $GA-01800 |', 'ipv4': [], 'ipv6': [], 'name': 'ae16', + 'port_type': 'ACCESS', 'router': 'mx1.mad.es.geant.net', 'snmp-index': 654, 'vlan_type': 'TRUNK'}, {'bundle': [], 'bundle-parents': ['et-4/0/0', 'et-5/0/5', 'et-8/1/2', 'et-8/1/5'], 'circuits': [ + {'id': 739804, 'name': 'UC3M-REDIRIS-BELNET-SLICES-IMEC', 'status': 'non-monitored', 'type': 'GEANT PLUS'}], + 'dashboard_info': {'interface_type': 'LOGICAL', 'name': 'REDIRIS'}, + 'dashboards': ["VLAN Interfaces"], + 'dashboards_info': [{'interface_type': 'LOGICAL', 'name': 'REDIRIS'}, + {'interface_type': 'LOGICAL', 'name': 'BELNET'}], + 'description': 'SRV_L2CIRCUIT CUSTOMER REDIRIS BELNET #UC3M-RedIRIS-BELNET-SLICES-IMEC $GS-02514 |', + 'ipv4': [], 'ipv6': [], 'name': 'ae16.975', 'port_type': 'SERVICE', 'router': 'mx1.mad.es.geant.net', + 'snmp-index': 818, 'vlan_type': 'VLAN'}, {'bundle': [], 'bundle-parents': ['et-4/0/0', 'et-5/0/5', 'et-8/1/2', 'et-8/1/5'], 'circuits': [ + {'id': 732759, 'name': 'FRA-MAD-RARE-REDIRIS-23017-VL201', 'status': 'non-monitored', + 'type': 'GEANT PLUS'}], 'dashboard_info': {'interface_type': 'LOGICAL', 'name': 'RARE'}, + 'dashboards': ['L2_CIRCUIT'], 'dashboards_info': [{'interface_type': 'LOGICAL', 'name': 'RARE'}, + {'interface_type': 'LOGICAL', 'name': 'REDIRIS'}], + 'description': 'SRV_L2CIRCUIT CUSTOMER RARE REDIRIS #fra-mad-RARE-REDIRIS-23017-VL201 $GS-02274', 'ipv4': [], + 'ipv6': [], 'name': 'ae16.201', 'port_type': 'SERVICE', 'router': 'mx1.mad.es.geant.net', 'snmp-index': 642, + 'vlan_type': 'VLAN'}, {'bundle': [], 'bundle-parents': [], 'circuits': [ + {'id': 729417, 'name': 'PAR-LON2-SUPERPOP-QFX-2-GEANT', 'status': 'operational', 'type': 'GEANT - GBS'}], + 'dashboard_info': {'interface_type': 'LOGICAL', 'name': 'GEANT-IT'}, 'dashboards': ['GBS_10G'], + 'dashboards_info': [{'interface_type': 'LOGICAL', 'name': 'GEANT-IT'}], + 'description': 'SRV_10GGBS CUSTOMER GEANT-IT #par-lon2-SUPERPOP-QFX-2-GEANT $GS-00081 |', 'ipv4': [], + 'ipv6': [], 'name': 'xe-2/2/7.0', 'port_type': 'SERVICE', 'router': 'mx1.lon2.uk.geant.net', 'snmp-index': 613, + 'vlan_type': 'VLAN'}, {'bundle': ['xe-0/1/0', 'xe-0/1/1'], 'bundle-parents': ['xe-0/1/0', 'xe-0/1/1'], 'circuits': [], + 'dashboard_info': {'interface_type': 'AGGREGATE', 'name': 'LITNET'}, 'dashboards': ['NREN', 'RE_CUST'], + 'dashboards_info': [{'interface_type': 'AGGREGATE', 'name': 'LITNET'}], + 'description': 'LAG CUSTOMER LITNET AP2 #LITNET-AP2-LAG $GA-02071 |', 'ipv4': [], 'ipv6': [], 'name': 'ae10', + 'port_type': 'ACCESS', 'router': 'art1.kau.lt.geant.net', 'snmp-index': 588, 'vlan_type': 'TRUNK'}, {'bundle': [], 'bundle-parents': ['et-0/0/2'], 'circuits': [ + {'id': 679356, 'name': 'LAT-AP1-IPV6', 'status': 'operational', 'type': 'GEANT IP'}], + 'dashboard_info': {'interface_type': 'LOGICAL', 'name': 'LAT'}, + 'dashboards': ['NREN', 'RE_CUST'], + 'dashboards_info': [{'interface_type': 'LOGICAL', 'name': 'LAT'}], + 'description': 'SRV_GLOBAL CUSTOMER LAT #LAT-AP1 $GS-00484 | ASN5538', + 'ipv4': ['62.40.124.237/30'], 'ipv6': ['2001:798:99:1::51/126'], 'name': 'ae10.83', + 'port_type': 'SERVICE', 'router': 'art1.kau.lt.geant.net', 'snmp-index': 604, + 'vlan_type': 'VLAN'}, {'bundle': [], 'bundle-parents': ['et-2/1/2', 'et-2/1/5', 'et-11/1/0'], 'circuits': [ + {'id': 707643, 'name': 'GARR-UDMILANO_EXPRESSROUTE_VLAN4086', 'status': 'operational', + 'type': 'EXPRESS ROUTE'}], 'dashboard_info': {'interface_type': 'LOGICAL', 'name': 'GARR'}, + 'dashboards': ['GCS'], 'dashboards_info': [{'interface_type': 'LOGICAL', 'name': 'GARR'}], + 'description': 'SRV_GCS CUSTOMER GARR MICROSOFT #GARR-UDMilano_ExpressRoute_Vlan4086 $GS-01148 | UNIT CONFIGURATION HAS BEEN SYSTEM GENERATED', + 'ipv4': [], 'ipv6': [], 'name': 'ae10.4086', 'port_type': 'SERVICE', 'router': 'art1.kau.lt.geant.net', + 'snmp-index': 795, 'vlan_type': 'VLAN'}, {'bundle': [], 'bundle-parents': [], 'circuits': [], + 'dashboard_info': {'interface_type': 'PHYSICAL', 'name': 'BUD-ZAG'}, 'dashboards': ['INFRASTRUCTURE_BACKBONE'], + 'dashboards_info': [{'interface_type': 'PHYSICAL', 'name': 'BUD-ZAG'}], + 'description': 'PHY INFRASTRUCTURE BACKBONE P_ae5 | BUD-ZAG', 'ipv4': [], 'ipv6': [], 'name': 'et-7/0/2', + 'port_type': 'UNKNOWN', 'router': 'mx1.bud.hu.geant.net', 'snmp-index': 1133, 'vlan_type': 'ACCESS'}, + + ] + populate_inventory( + { + "/poller/interfaces": interfaces, + "/data/interfaces": interfaces, + "/poller/eumetsat-multicast": EUMETSAT_MULTICAST, + "/poller/regions": NREN_REGIONS, + } + ) + _mocked_gws = mocker.patch( + "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 = [] + + data_config["organizations"] = [ + {"name": "Testorg1", "excluded_nrens": ["GEANT"], "excluded_dashboards": []}, + ] + provision(data_config) + folder_uid = "VLAN_Interfaces" + + result = mock_grafana.dashboards_by_folder_uid[folder_uid] + assert len(result) == 3 + + assert result[0]["tags"][0] == "vlandash" + dashboards = mock_grafana.dashboards_by_folder_uid[folder_uid] + panels = [] + for dashboard in dashboards: + panels.extend(dashboard["panels"]) + expected_types = ["text", "row", "text", "row", "text", "row"] + assert [p["type"] for p in panels] == expected_types + assert "INFO" in panels[0]["options"]["content"] + assert "ae10" in set(p['title'] for p in panels) + + panel_ae10 = [p for p in panels if p['title'] == 'ae10'][0] + assert len(panel_ae10["panels"]) == 3 + + @responses.activate @pytest.mark.parametrize( "folder_name, dashboard_id, expected_dashboard_count",