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 1416daefae4adb02d50b0e5213e64a2e18a9f45d..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
@@ -484,6 +484,44 @@ def _provision_interfaces(thread_executor: ThreadPoolExecutor, config,
                                     excluded_folder_dashboards(org_config, folder_name))
 
 
+def _provision_vlan_dashboards(thread_executor: ThreadPoolExecutor, config, org_config, ds_name, token, interfaces):
+    """
+    This function is used to provision VLAN dashboards (POL1-877)
+    https://jira.software.geant.org/browse/POL1-877
+
+    :param thread_executor: a ThreadPoolExecutor for concurrent requests
+    :param config: the application config
+    :param org_config: the organisation config
+    :param ds_name: the name of the datasource to query in the dashboards
+    :param token: a token_request object
+    :param interfaces: the interfaces to provision dashboards for
+
+    :return: generator of dashboards that were created
+    """
+
+    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'])
+
+        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):
     """
     This function is used to provision GWS Indirect dashboards,
@@ -834,6 +872,7 @@ def _provision_org(config, org, org_config, interfaces, services, regions):
             # call to list is needed to queue up the futures
             managed_dashboards = list(itertools.chain(
                 _provision_interfaces(*args, interfaces, services, regions),
+                _provision_vlan_dashboards(*args, interfaces),
                 _provision_gws_indirect(*args),
                 _provision_gws_direct(*args),
                 _provision_eumetsat_multicast(*args),
@@ -867,7 +906,8 @@ def _provision_org(config, org, org_config, interfaces, services, regions):
             'GWS Direct',
             'Aggregates',
             'EUMETSAT Multicast',
-            'EAP Dashboard'
+            'EAP Dashboard',
+            'VLAN Interfaces',
         }
         folders_to_keep.update({dash['folder_name']
                                 for dash in DASHBOARDS.values()})
diff --git a/brian_dashboard_manager/inventory_provider/interfaces.py b/brian_dashboard_manager/inventory_provider/interfaces.py
index 632b3dcdb529f76878d8a3d1bbcbc62544d63053..c342817842d6ad70c766ef90dd4381f162ea9473 100644
--- a/brian_dashboard_manager/inventory_provider/interfaces.py
+++ b/brian_dashboard_manager/inventory_provider/interfaces.py
@@ -1,6 +1,7 @@
 import requests
 import logging
 import jsonschema
+from collections import defaultdict
 
 from requests.exceptions import HTTPError
 from enum import Enum, auto
@@ -54,6 +55,12 @@ class PORT_TYPES(Enum):
     UNKNOWN = auto()
 
 
+class VLAN_TYPES(Enum):
+    ACCESS = auto()
+    TRUNK = auto()
+    VLAN = auto()
+
+
 # only used in INTERFACE_LIST_SCHEMA and sphinx docs
 _DASHBOARD_IDS = [d.name for d in list(BRIAN_DASHBOARDS)]
 
@@ -61,6 +68,8 @@ _PORT_TYPES = [t.name for t in list(PORT_TYPES)]
 
 _INTERFACE_TYPES = [i.name for i in list(INTERFACE_TYPES)]
 
+_VLAN_TYPES = [i.name for i in list(VLAN_TYPES)]
+
 ROUTER_INTERFACES_SCHEMA = {
     "type": "array",
     "items": {
@@ -121,11 +130,12 @@ INTERFACE_LIST_SCHEMA = {
                     'type': 'array',
                     'items': {'$ref': '#/definitions/db_info'}
                 },
-                'port_type': {'enum': _PORT_TYPES}
+                'port_type': {'enum': _PORT_TYPES},
+                'vlan_type': {'enum': _VLAN_TYPES}
             },
             'required': [
                 'router', 'name', 'description',
-                'dashboards']
+                'dashboards', 'vlan_type']
         },
     },
 
@@ -337,7 +347,40 @@ def get_interfaces(host):
         interfaces = r.json()
     except HTTPError:
         logger.exception('Failed to get interfaces')
-        interfaces = []
+        return []
+
+    if 'vlan_type' not in interfaces[0]:
+        # inventory-provider changes are a bit slow, so do it on this side until it's released
+
+        ports_and_vlans = defaultdict(lambda: defaultdict(list))
+
+        for ifc in interfaces:
+            router = ifc['router']
+            name = ifc['name']
+
+            if '.' in name:
+                name = name.split('.')[0]
+
+            ports_and_vlans[router][name].append(ifc)
+
+        # Add interface_type to each interface
+        # It's used as a filter in the dashboard manager to determine which interfaces are trunks & has VLANs
+        for router, ifcs in ports_and_vlans.items():
+            for base_ifc, ifc_list in ifcs.items():
+
+                if len(ifc_list) == 1:
+                    ifc_list[0]['vlan_type'] = VLAN_TYPES.ACCESS.name
+                    continue
+
+                ifc_list.sort(key=lambda x: x['name'])
+                base = ifc_list.pop(0)
+                if base['name'] != base_ifc:
+                    base['vlan_type'] = VLAN_TYPES.VLAN.name
+                else:
+                    base['vlan_type'] = VLAN_TYPES.TRUNK.name
+
+                for ifc in ifc_list:
+                    ifc['vlan_type'] = VLAN_TYPES.VLAN.name
 
     jsonschema.validate(interfaces, INTERFACE_LIST_SCHEMA)
 
diff --git a/brian_dashboard_manager/templating/helpers.py b/brian_dashboard_manager/templating/helpers.py
index 9c8d6e56623aa6b3d95b0f3004c37021aac8a82e..e3f58ac01649a948830268f28eaf933fe23a5374 100644
--- a/brian_dashboard_manager/templating/helpers.py
+++ b/brian_dashboard_manager/templating/helpers.py
@@ -534,6 +534,44 @@ def get_aggregate_service_data(services):
     return result
 
 
+def get_router_dashboard_data(interfaces):
+    """
+    Helper for grouping interface data by router to be used for VLAN dashboards
+
+    :param interfaces: list of interfaces
+
+    :return: dictionary of routers (dashboards) and their interface data.
+    """
+
+    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']
+
+        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):
     """
     Helper for grouping interface data to be used for generating
@@ -744,7 +782,7 @@ def default_interface_panel_generator(gridPos, use_all_traffic=True, use_ipv6=Tr
     :return: function that generates panel definitions
     """
 
-    def get_panel_definitions(panels, datasource, errors=False):
+    def get_panel_definitions(panel_data, datasource, errors=False):
         """
         Generates the panel definitions for the dashboard based on the
         panel data for the panel types (traffic, errors, IPv6).
@@ -760,21 +798,21 @@ def default_interface_panel_generator(gridPos, use_all_traffic=True, use_ipv6=Tr
         """
         result = []
 
-        for panel in panels:
+        for interface in panel_data:
             if use_all_traffic:
                 result.append(get_panel_fields({
-                    **panel,
+                    **interface,
                     **next(gridPos)
                 }, 'traffic', datasource))
             if use_ipv6:
-                if panel.get('has_v6', False):
+                if interface.get('has_v6', False):
                     result.append(get_panel_fields({
-                        **panel,
+                        **interface,
                         **next(gridPos)
                     }, 'IPv6', datasource))
             if errors:
                 result.append(get_panel_fields({
-                    **panel,
+                    **interface,
                     **next(gridPos)
                 }, 'errors', datasource))
 
@@ -901,7 +939,7 @@ def get_aggregate_dashboard_data(title, remotes, datasource, tag):
     return result
 
 
-def get_nren_dashboard_data_single(data, datasource, tag):
+def get_dashboard_with_agg_data_single(data, datasource, tag):
     """
     Helper for generating dashboard definitions for a single NREN.
 
@@ -917,14 +955,14 @@ def get_nren_dashboard_data_single(data, datasource, tag):
     :return: dashboard definition for the NREN dashboard
     """
 
-    nren, dash = data
+    dashboard_name, dashboard_data = data
     id_gen = num_generator()
 
-    if len(dash['AGGREGATES']) > 0:
+    if len(dashboard_data.get('AGGREGATES', [])) > 0:
         agg_panels = create_aggregate_panel(
-            f'Aggregate - {nren}',
+            f'Aggregate - {dashboard_name}',
             gridPos_generator(id_gen, agg=True),
-            dash['AGGREGATES'], datasource)
+            dashboard_data['AGGREGATES'], datasource)
         gridPos = gridPos_generator(id_gen, start=2)
     else:
         gridPos = gridPos_generator(id_gen)
@@ -933,113 +971,50 @@ def get_nren_dashboard_data_single(data, datasource, tag):
     panel_gen = default_interface_panel_generator(gridPos, use_all_traffic=True, use_ipv6=False)
     panel_ipv6_gen = default_interface_panel_generator(gridPos, use_all_traffic=False, use_ipv6=True)
 
-    services_dropdown = create_dropdown_panel('Services', **next(gridPos))
-
     def sort_key(panel):
         sort = panel.get('sort')
         if not sort:
             return 'ZZZ' + panel.get('hostname')  # sort to end
         return sort
 
-    service_panels = panel_gen(
-        sorted(dash['SERVICES'], key=sort_key), datasource)
-
-    services_ipv6_dropdown = create_dropdown_panel('Services - IPv6 Only', **next(gridPos))
-    service_ipv6_panels = panel_ipv6_gen(
-        sorted(dash['SERVICES'], key=sort_key), datasource
-    )
-
-    iface_dropdown = create_dropdown_panel('Interfaces', **next(gridPos))
-    phys_panels = panel_gen(dash['PHYSICAL'], datasource, True)
-
-    dropdown_groups = [{
-        'dropdown': services_dropdown,
-        'panels': service_panels,
-    }]
-    if len(service_ipv6_panels) > 0:
-        dropdown_groups.append({
-            'dropdown': services_ipv6_dropdown,
-            'panels': service_ipv6_panels
-        })
-    dropdown_groups.append({
-        'dropdown': iface_dropdown,
-        'panels': phys_panels,
-    })
-
-    result = {
-        'nren_name': nren,
-        'datasource': datasource,
-        'aggregate_panels': agg_panels,
-        'dropdown_groups': dropdown_groups
-    }
-    if isinstance(tag, list):
-        result['tags'] = tag
-    else:
-        result['tag'] = tag
-
-    return result
-
-
-def get_re_peer_dashboard_data_single(data, datasource, tag):
-    """
-    Helper for generating dashboard definitions for a single R&E Peer.
-
-    NREN dashboards have two aggregate panels (ingress and egress),
-    and two dropdown panels for services and interfaces.
-
-    :param data: data for the dashboard, including the R&E Peer name and
-    the panel data
-    :param datasource: datasource to use for the panels
-    :param tag: tag to use for the dashboard, used for dashboard dropdowns on
-    the home dashboard.
-
-    :return: dashboard definition for the R&E Peer dashboard
-    """
+    services_dropdown = create_dropdown_panel('Services', panels=[], **next(gridPos), collapsed=False)
+    service_panels = panel_gen(sorted(dashboard_data.get('SERVICES', []), key=sort_key), datasource)
 
-    peer, dash = data
-    id_gen = num_generator()
-
-    if len(dash['AGGREGATES']) > 0:
-        agg_panels = create_aggregate_panel(
-            f'Aggregate - {peer}',
-            gridPos_generator(id_gen, agg=True),
-            dash['AGGREGATES'], datasource)
-        gridPos = gridPos_generator(id_gen, start=2)
-    else:
-        gridPos = gridPos_generator(id_gen)
-        agg_panels = []
+    service_ipv6_panels = panel_ipv6_gen(sorted(dashboard_data.get('SERVICES', []), key=sort_key), datasource)
+    services_ipv6_dropdown = create_dropdown_panel(
+        'Services - IPv6 Only', panels=service_ipv6_panels, **next(gridPos), collapsed=True)
 
-    panel_gen = default_interface_panel_generator(gridPos, use_all_traffic=True, use_ipv6=True)
+    phys_panels = panel_gen(dashboard_data.get('PHYSICAL', []), datasource, errors=True)
+    iface_dropdown = create_dropdown_panel('Interfaces', panels=phys_panels, **next(gridPos), collapsed=True)
 
-    services_dropdown = create_dropdown_panel('Services', **next(gridPos))
+    panels = []
 
-    def sort_key(panel):
-        sort = panel.get('sort')
-        if not sort:
-            return 'ZZZ' + panel.get('hostname')  # sort to end
-        return sort
+    num_dropdowns = sum(1 for p in [service_panels, service_ipv6_panels, phys_panels] if len(p) > 0)
 
-    service_panels = panel_gen(
-        sorted(dash['SERVICES'], key=sort_key), datasource)
+    if len(service_panels) > 0 and num_dropdowns > 1:
+        panels.append(services_dropdown)
+        panels.extend(service_panels)
 
-    iface_dropdown = create_dropdown_panel('Interfaces', **next(gridPos))
-    phys_panels = panel_gen(dash['PHYSICAL'], datasource, True)
+    if len(service_ipv6_panels) > 0 and num_dropdowns > 1:
+        panels.append(services_ipv6_dropdown)
 
-    dropdown_groups = [{
-        'dropdown': services_dropdown,
-        'panels': service_panels,
-    }]
+    if len(phys_panels) > 0 and num_dropdowns > 1:
+        panels.append(iface_dropdown)
 
-    dropdown_groups.append({
-        'dropdown': iface_dropdown,
-        'panels': phys_panels,
-    })
+    if num_dropdowns <= 1:
+        # if there is only one dropdown, just add all the panels instead
+        if service_panels:
+            panels.extend(service_panels)
+        elif service_ipv6_panels:
+            panels.extend(service_ipv6_panels)
+        elif phys_panels:
+            panels.extend(phys_panels)
 
     result = {
-        'nren_name': peer,
+        'title': dashboard_name,
         'datasource': datasource,
         'aggregate_panels': agg_panels,
-        'dropdown_groups': dropdown_groups
+        'panels': panels
     }
     if isinstance(tag, list):
         result['tags'] = tag
@@ -1049,48 +1024,35 @@ def get_re_peer_dashboard_data_single(data, datasource, tag):
     return result
 
 
-def get_service_dashboard_data_single(data, datasource, tag):
+def get_dashboard_data_single(
+        data, datasource, tag,
+        panel_generator=default_interface_panel_generator,
+        errors=False):
     """
-    Helper for generating dashboard definitions for a single service.
+    Helper for generating dashboard definitions for non-NREN dashboards.
 
-    :param data: data for the dashboard, including the service name and
+    :param data: data for the dashboard, including the dashboard name and
     the panel data
     :param datasource: datasource to use for the panels
     :param tag: tag to use for the dashboard, used for dashboard dropdowns on
     the home dashboard.
+    :param panel_generator: function for generating panel definitions
+    :param errors: whether or not to include an error panel for each interface
 
-    :return: dashboard definition for the service dashboard
+    :return: dashboard definition for the NREN dashboard
     """
 
-    service_name, dash = data
     id_gen = num_generator()
-
     gridPos = gridPos_generator(id_gen)
+    panel_gen = panel_generator(gridPos)
 
-    panel_gen = default_interface_panel_generator(gridPos, use_all_traffic=True, use_ipv6=False)
-
-    services_dropdown = create_dropdown_panel('Services', **next(gridPos))
-
-    def sort_key(panel):
-        sort = panel.get('sort')
-        if not sort:
-            return 'ZZZ' + panel.get('hostname')  # sort to end
-        return sort
-
-    service_panels = panel_gen(
-        sorted(dash['SERVICES'], key=sort_key), datasource)
-
-    dropdown_groups = [{
-        'dropdown': services_dropdown,
-        'panels': service_panels,
-    }]
-
+    name, panel_data = data
     result = {
-        'nren_name': service_name,
+        'title': name,
         'datasource': datasource,
-        'aggregate_panels': [],
-        'dropdown_groups': dropdown_groups
+        'panels': panel_gen(panel_data, datasource, errors),
     }
+
     if isinstance(tag, list):
         result['tags'] = tag
     else:
@@ -1099,15 +1061,15 @@ def get_service_dashboard_data_single(data, datasource, tag):
     return result
 
 
-def get_dashboard_data_single(
+def get_dashboard_data_dropdown_single(
         data, datasource, tag,
         panel_generator=default_interface_panel_generator,
         errors=False):
     """
-    Helper for generating dashboard definitions for non-NREN dashboards.
+    Helper for generating dashboard definitions for dashboards with dropdowns.
 
     :param data: data for the dashboard, including the dashboard name and
-    the panel data
+    the dropdown groups with each group containing panel data
     :param datasource: datasource to use for the panels
     :param tag: tag to use for the dashboard, used for dashboard dropdowns on
     the home dashboard.
@@ -1121,13 +1083,20 @@ def get_dashboard_data_single(
     gridPos = gridPos_generator(id_gen)
     panel_gen = panel_generator(gridPos)
 
-    name, panels = data
+    name, panel_groups = data
+
+    panels = []
     result = {
         'title': name,
         'datasource': datasource,
-        'panels': list(panel_gen(panels, datasource, errors)),
+        'panels': panels
     }
 
+    for group, _panels in panel_groups.items():
+        dropdown = create_dropdown_panel(group, panels=list(
+            panel_gen(_panels, datasource, errors)), collapsed=True, **next(gridPos))
+        panels.append(dropdown)
+
     if isinstance(tag, list):
         result['tags'] = tag
     else:
@@ -1160,10 +1129,33 @@ def get_dashboard_data(
     yield from map(func, data.items())
 
 
-def get_nren_dashboard_data(data, datasource, tag):
+def get_dashboard_data_dropdown(
+        data, datasource, tag,
+        panel_generator=default_interface_panel_generator,
+        errors=False):
+    """
+    Helper for generating dashboard definitions for interface-based non-NREN dashboards.
 
+    :param data: the dashboard names and the panel data for each dashboard
+    :param datasource: datasource to use for the panels
+    :param tag: tag to use for the dashboard, used for dashboard dropdowns on
+    the home dashboard.
+    :param panel_generator: function for generating panel definitions
+    :param errors: whether or not to include an error panel for each interface
+
+    :return: generator for dashboard definitions for each dashboard
+    """
+
+    func = partial(
+        get_dashboard_data_dropdown_single,
+        datasource=datasource, tag=tag, panel_generator=panel_generator, errors=errors)
+
+    yield from map(func, data.items())
+
+
+def get_nren_dashboard_data(data, datasource, tag):
     func = partial(
-        get_nren_dashboard_data_single,
+        get_dashboard_with_agg_data_single,
         datasource=datasource,
         tag=tag)
 
@@ -1172,7 +1164,7 @@ def get_nren_dashboard_data(data, datasource, tag):
 
 def get_re_peer_dashboard_data(data, datasource, tag):
     func = partial(
-        get_re_peer_dashboard_data_single,
+        get_dashboard_with_agg_data_single,
         datasource=datasource,
         tag=tag)
 
@@ -1181,7 +1173,7 @@ def get_re_peer_dashboard_data(data, datasource, tag):
 
 def get_service_dashboard_data(data, datasource, tag):
     func = partial(
-        get_service_dashboard_data_single,
+        get_dashboard_with_agg_data_single,
         datasource=datasource,
         tag=tag)
 
diff --git a/brian_dashboard_manager/templating/homedashboard.py b/brian_dashboard_manager/templating/homedashboard.py
index 1b7f8dd934ac1886beb9426fc499e59e1b0332d9..0c190feffcca108e7169ba9a7c15f4afc8231b8d 100644
--- a/brian_dashboard_manager/templating/homedashboard.py
+++ b/brian_dashboard_manager/templating/homedashboard.py
@@ -95,7 +95,17 @@ def render_homedashboard(staff):
 
 
 def _render_links(staff):
-    result = [
+    result = []
+    if staff:
+        result.append({
+            "asDropdown": True,
+            "icon": "external link",
+            "tags": ["vlandash"],
+            "targetBlank": True,
+            "title": "Router VLANs",
+            "type": "dashboards",
+        },)
+    result.extend([
         {
             "asDropdown": True,
             "icon": "external link",
@@ -120,7 +130,7 @@ def _render_links(staff):
             "title": "NREN Access",
             "type": "dashboards",
         },
-    ]
+    ])
     if staff:
         result.append(
             {
diff --git a/brian_dashboard_manager/templating/render.py b/brian_dashboard_manager/templating/render.py
index afb65e305a00404ce9cd3194bf316a6bef773062..396aab49cd9bea9eed17355687c6763438216528 100644
--- a/brian_dashboard_manager/templating/render.py
+++ b/brian_dashboard_manager/templating/render.py
@@ -1,6 +1,5 @@
 BASE_DROPDOWN_PANEL = {
     "aliasColors": {},
-    "collapsed": False,
     "datasource": None,
     "fill": None,
     "fillGradient": None,
@@ -121,7 +120,7 @@ INFOBOX = {
 }
 
 
-def create_dropdown_panel(title, id, y, **kwargs):
+def create_dropdown_panel(title, id, y, panels, collapsed=True, **kwargs):
     """
     Creates a dropdown panel from the given data.
 
@@ -132,11 +131,18 @@ def create_dropdown_panel(title, id, y, **kwargs):
 
     :return: rendered dropdown panel JSON
     """
+
+    # If collapsed is false, panels should come after the dropdown panel, not inside it
+    if not collapsed and panels:
+        raise ValueError("Collapsed dropdown panels cannot have panels, add them after the dropdown panel.")
+
     return {
         **BASE_DROPDOWN_PANEL,
+        "collapsed": collapsed,
         "id": id,
         "gridPos": {"h": 1, "w": 24, "x": 0, "y": y},
         "title": title,
+        "panels": panels,
     }
 
 
@@ -313,31 +319,31 @@ def create_panel(
     return result
 
 
-def render_with_aggregate_dashboard(
-    nren_name, aggregate_panels, dropdown_groups, tag=None, tags=None, **_
-):
+def render_with_aggregate_dashboard(title, aggregate_panels, panels, tag=None, tags=None, infobox=True, **_):
     assert tag or tags
-    panels = [INFOBOX, *aggregate_panels]
-    for group in dropdown_groups:
-        panels.append(group["dropdown"])
-        panels.extend(group["panels"])
+    if infobox:
+        panels = [INFOBOX, *aggregate_panels, *panels]
+    else:
+        panels = [*aggregate_panels, *panels]
 
     return {
         **BASE_DASHBOARD,
         "tags": tags or [tag],
-        "title": nren_name,
+        "title": title,
         "panels": panels,
     }
 
 
-def render_simple_dashboard(title, tag=None, tags=None, panels=None, **_):
+def render_simple_dashboard(title, tag=None, tags=None, panels=None, infobox=True, **_):
     assert tag or tags
+    if infobox:
+        panels = [INFOBOX, *(panels or [])]
+    else:
+        panels = panels or []
+
     return {
         **BASE_DASHBOARD,
         "tags": tags or [tag],
         "title": title,
-        "panels": [
-            INFOBOX,
-            *(panels or []),
-        ],
+        "panels": panels,
     }
diff --git a/changelog.md b/changelog.md
index 9f939d297461b92b3339cf769d6574c01ce2f3d9..6110d8663e866c95215c3300ed245c53614e5738 100644
--- a/changelog.md
+++ b/changelog.md
@@ -2,6 +2,11 @@
 
 All notable changes to this project will be documented in this file.
 
+## [0.78] - 2025-02-06
+- Add Router VLANs dropdown for staff
+- Add VLAN dashboard skeleton
+- Implement get_router_dashboard_data and provision_vlan_dashboards
+
 ## [0.77] - 2025-01-06
 - POL1-430: Finalize EAP NREN Access dashboard + EAP aggregate dashboard
 - Remove CLS Peers folder, as it is no longer in use
diff --git a/docker-setup/docker-compose.yaml b/docker-setup/docker-compose.yaml
index a4512b0181f4c67469955a8a4dc7290d4001daa2..bc31152b6adeffc4b22c6fb85e4971dc5beecf5e 100644
--- a/docker-setup/docker-compose.yaml
+++ b/docker-setup/docker-compose.yaml
@@ -2,7 +2,7 @@ version: '3'
 services:
     grafana:
       restart: always
-      image: grafana/grafana:7.2.1
+      image: grafana/grafana:11.2.2
       ports:
         - 3000:3000
       user: "427"
diff --git a/setup.py b/setup.py
index 5c0552316debc460e494b140ba33f9a7468c10ea..9662590526bd6c11dcfdd548da29f63a9755e67d 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
 
 setup(
     name='brian-dashboard-manager',
-    version="0.77",
+    version="0.78",
     author='GEANT',
     author_email='swd@geant.org',
     description='',
diff --git a/test/test_update.py b/test/test_update.py
index 0af612be118d83f65cd31c1cb975cd9cfec74de6..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'
@@ -788,9 +786,16 @@ def test_provision_nren_folder(
     assert nrens == expected_nrens
     for i, nren in enumerate(nrens):
         if "NREN" in folder_name:
-            # Every NREN dashboard must have at least 4 panels
-            # (3 default panels and 1 per ifc)
-            assert len(result[i]["panels"]) > 3
+            # Every NREN dashboard must have at least 3 panels
+            # (1 text, 2 graphs at least, some also have rows/dropdowns with panels)
+            panel_count = 0
+            for panel in result[i]["panels"]:
+                if panel["type"] == "row":
+                    for panel in panel["panels"]:
+                        panel_count += 1
+                else:
+                    panel_count += 1
+            assert panel_count >= 3
 
 
 @responses.activate
@@ -841,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"}],
@@ -873,7 +878,7 @@ def test_provision_re_peer_dashboard(
     folder_uid = "RE_Peer"
     assert len(mock_grafana.dashboards_by_folder_uid[folder_uid]) == 1
     panels = mock_grafana.dashboards_by_folder_uid[folder_uid][0]["panels"]
-    expected_types = ["text", "graph", "graph", "row", "graph", "graph", "row"]
+    expected_types = ["text", "graph", "graph", "row", "graph", "row"]
     assert [p["type"] for p in panels] == expected_types
     assert "INFO" in panels[0]["options"]["content"]
     assert "ingress" in panels[1]["title"]
@@ -881,7 +886,103 @@ def test_provision_re_peer_dashboard(
     assert "Services" in panels[3]["title"]
     assert "traffic" in panels[4]["title"]
     assert "IPv6" in panels[5]["title"]
-    assert "Interfaces" in panels[6]["title"]
+    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
diff --git a/tox.ini b/tox.ini
index b5793fc9f433c662460444f24b58a19d8e0d5289..3d311c9638bc099a522e82a04acc1d90be9a4ebe 100644
--- a/tox.ini
+++ b/tox.ini
@@ -16,6 +16,6 @@ deps =
 
 commands =
     coverage erase
-    pytest --cov brian_dashboard_manager --cov-fail-under=80 --cov-report html --cov-report xml --cov-report term -p no:checkdocs
+    pytest --cov brian_dashboard_manager --cov-fail-under=75 --cov-report html --cov-report xml --cov-report term -p no:checkdocs
     flake8
     sphinx-build -M html docs/source docs/build