Skip to content
Snippets Groups Projects
Commit 02e03f6a authored by geant-release-service's avatar geant-release-service
Browse files

Finished release 0.78.

parents 4ac26963 ba5f88e1
No related branches found
No related tags found
No related merge requests found
......@@ -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,
}
}
]
......
......@@ -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()})
......
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)
......
......@@ -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)
......
......@@ -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(
{
......
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,
}
......@@ -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
......
......@@ -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"
......
......@@ -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='',
......
# 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
......
......@@ -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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment