diff --git a/brian_dashboard_manager/templating/helpers.py b/brian_dashboard_manager/templating/helpers.py index 9c8d6e56623aa6b3d95b0f3004c37021aac8a82e..89ca0c847ec4587b57c9eab402b3959c9f13b64c 100644 --- a/brian_dashboard_manager/templating/helpers.py +++ b/brian_dashboard_manager/templating/helpers.py @@ -744,7 +744,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 +760,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 +901,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 +917,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 +933,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 - """ - - peer, dash = data - id_gen = num_generator() + services_dropdown = create_dropdown_panel('Services', panels=[], **next(gridPos), collapsed=False) + service_panels = panel_gen(sorted(dashboard_data.get('SERVICES', []), key=sort_key), datasource) - 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 +986,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 +1023,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 +1045,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 +1091,34 @@ def get_dashboard_data( yield from map(func, data.items()) +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 +1127,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 +1136,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/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, }