Skip to content
Snippets Groups Projects
helpers.py 6.85 KiB
Newer Older
import logging
from multiprocessing.pool import Pool
from itertools import product
from string import ascii_uppercase
from brian_dashboard_manager.templating.render import create_panel

PANEL_HEIGHT = 12
PANEL_WIDTH = 24

logger = logging.getLogger(__file__)


def get_description(interface):
    return interface.get('description', '').strip()


def is_physical_interface(interface):
    return re.match('^PHY', get_description(interface))


def is_aggregate_interface(interface):
    return re.match('^LAG', get_description(interface))


def is_logical_interface(interface):
    return re.match('^SRV_', get_description(interface))


def is_nren(interface):
    regex = '(PHY|LAG|(SRV_(GLOBAL|LHCONE|MDVPN|IAS|CLS|L3VPN))) CUSTOMER'
    return re.match(regex, get_description(interface))


def is_cls(interface):
    return 'SRV_CLS' in get_description(interface)


def is_ias_public(interface):
    return 'SRV_IAS PUBLIC' in get_description(interface)


def is_ias_private(interface):
    return 'SRV_IAS PRIVATE' in get_description(interface)


def is_ias_customer(interface):
    return 'SRV_IAS CUSTOMER' in get_description(interface)


def is_ias_upstream(interface):
    return 'SRV_IAS UPSTREAM' in get_description(interface)


def is_re_peer(interface):
    return 'SRV_GLOBAL RE_INTERCONNECT' in get_description(interface)


def is_re_customer(interface):
    regex = '(PHY|LAG|SRV_GLOBAL) CUSTOMER'
    return re.match(regex, get_description(interface))


def is_gcs(interface):
    return re.match('^SRV_GCS', get_description(interface))


def is_geantopen(interface):
    return 'GEANTOPEN' in get_description(interface)


def is_l2circuit(interface):
    return 'SRV_L2CIRCUIT' in get_description(interface)


def is_lhcone_peer(interface):
    description = get_description(interface)
    return 'LHCONE' in description and 'SRV_L3VPN RE' in description


def is_lhcone_customer(interface):
    description = get_description(interface)
    return 'LHCONE' in description and 'SRV_L3VPN CUSTOMER' in description


def is_mdvpn(interface):
    return re.match('^SRV_MDVPN CUSTOMER', get_description(interface))


def is_infrastructure_backbone(interface):
    regex = '(SRV_GLOBAL|LAG|PHY) INFRASTRUCTURE BACKBONE'
    return re.match(regex, get_description(interface))


def is_lag_backbone(interface):
    is_lag = 'LAG' in get_description(interface)
    return is_infrastructure_backbone(interface) and is_lag


def parse_backbone_name(description, *args, **kwargs):
    link = description.split('|')[1].strip()
    link = link.replace('( ', '(')
    return link


def is_phy_upstream(interface):
    return re.match('^PHY UPSTREAM', get_description(interface))


def parse_phy_upstream_name(description, host):
    name = description.split(' ')[2].strip().upper()
    location = host.split('.')[1].upper()
    return f'{name} - {location}'


def num_generator(start=1):
    num = start
    while True:
        yield num
        num += 1


def gridPos_generator(id_generator, start=0):
    num = start
    while True:
        yield {
            "height": PANEL_HEIGHT,
            "width": PANEL_WIDTH,
            "x": 0,
            "y": num * PANEL_HEIGHT,
            "id": next(id_generator)
        }
        num += 1


def letter_generator():
    i = 0
    j = 0
    num_letters = len(ascii_uppercase)
    while True:
        result = ascii_uppercase[i % num_letters]

        # tack on an extra letter if we are out of them
        if (i >= num_letters):
            result += ascii_uppercase[j % num_letters]
            j += 1
            if (j != 0 and j % num_letters == 0):
                i += 1
        else:
            i += 1

        yield result


# peer_predicate is a function that is used to filter the interfaces
# parse_func receives interface information and returns a peer name.
def get_interface_data(interfaces, name_parse_func=None):
    result = {}
    for interface in interfaces:
        if not name_parse_func:
            # Most (but not all) descriptions use a format
            # which has the peer name as the third element.
            def name_parse_func(desc, *args, **kwargs):
                return desc.split(' ')[2].upper()

        description = interface.get('description', '').strip()
        interface_name = interface.get('name')
        host = interface.get('router', '')

        dashboard_name = name_parse_func(description, host)

        peer = result.get(dashboard_name, [])

        router = host.replace('.geant.net', '')
        panel_title = f"{router} - {{}} - {interface_name} - {description}"

        peer.append({
            'title': panel_title,
            'interface': interface_name,
            'hostname': host
        })
        result[dashboard_name] = peer
    return result


# Helper used for generating all traffic/error panels
# with a single target field (ingress/egress or err/s)
def get_panel_fields(panel, panel_type, datasource):
    letters = letter_generator()

    def get_target_data(alias, field):
        return {
            **panel,  # panel has target hostname and interface
            'alias': alias,
            'refId': next(letters),
            'select_field': field,
            'percentile': 'percentile' in alias.lower(),
        }

    error_fields = [('Ingress Errors', 'errorsIn'),
                    ('Egress Errors', 'errorsOut')]

    ingress = ['Ingress Traffic', 'Ingress 95th Percentile']
    egress = ['Egress Traffic', 'Egress 95th Percentile']

    is_v6 = panel_type == 'IPv6'
    is_error = panel_type == 'errors'
    in_field = 'ingressv6' if is_v6 else 'ingress'
    out_field = 'egressv6' if is_v6 else 'egress'

    fields = [*product(ingress, [in_field]), *product(egress, [out_field])]

    targets = error_fields if is_error else fields

    return create_panel({
        **panel,
        'datasource': datasource,
        'title': panel['title'].format(panel_type),
        'panel_targets': [get_target_data(*target) for target in targets],
        'y_axis_type': 'errors' if is_error else 'bits',
    })


def get_panel_definitions(panel, datasource, errors=False):
    result = []

    result.append(get_panel_fields(
        panel, 'traffic', datasource))
    result.append(get_panel_fields(
        panel, 'IPv6', datasource))
    if errors:
        result.append(get_panel_fields(
            panel, 'errors', datasource))
    return result


def flatten(t): return [item for sublist in t for item in sublist]


def get_dashboard_data(data, datasource, tag, errors=False):
    id_gen = num_generator()
    gridPos = gridPos_generator(id_gen)
    pool = Pool(processes=2)

    for peer, panels in data.items():
        otherpanels = [({**panel, **next(gridPos)}, datasource, errors)
                       for panel in panels]

        all_panels = flatten(pool.starmap(get_panel_definitions, otherpanels))

        yield {
            'title': peer,
            'datasource': datasource,
            'panels': all_panels,