Skip to content
Snippets Groups Projects
test_nokia.py 8.64 KiB
import ipaddress
import os
import pathlib
from copy import deepcopy
from functools import lru_cache
import pytest

from lxml import etree
import jsonschema

from inventory_provider import nokia


PORT_SCHEMA = {
    '$schema': 'https://json-schema.org/draft/2020-12/schema',
    'type': 'object',
    'properties': {
        'port-id': {'type': 'string'},
        'admin-state': {'type': 'string'},
        'description': {'type': 'string'},
        'speed': {'type': 'integer'},
        'speed-unit': {'type': 'string', 'enum': ['Gbps', 'Unknown']}
    },
    'required': ['port-id', 'description', 'admin-state'],
    'additionalProperties': True
}

PEERS_SCHEMA = {
    '$schema': 'https://json-schema.org/draft/2020-12/schema',
    'type': 'object',
    'properties': {
        'address': {'type': 'string'},
        'remote-asn': {'type': 'integer'},
        'local-asn': {'type': 'integer'},
        'description': {'type': 'string'},
        'group': {'type': 'string'},
        'instance': {'type': 'string'}
    },
    'required': ['address', 'group'],
    'additionalProperties': False
}


@lru_cache
def _load_xml_doc(filename):
    full_filename = os.path.join(
        os.path.dirname(__file__),
        'data',
        'nokia',
        filename)
    return nokia.remove_xml_namespaces(etree.parse(full_filename))


def _load_netconf_config(hostname):
    return _load_xml_doc(f'{hostname}-netconf.xml')


def _load_state_doc(hostname):
    return _load_xml_doc(f'{hostname}-state.xml')


state_doc = nokia.remove_xml_namespaces(etree.parse(pathlib.Path(__file__).parent.joinpath(
    'data/nokia/rt0.lon.uk.lab.office.geant.net-netconf-nokia-state.xml')))


@pytest.mark.parametrize('hostname,expected_bundles', [
    ('rt0.ams.nl.lab.office.geant.net', {
        'lag-1': ['1/1/c2/1'],
        'lag-2': ['1/1/c5/1'],
        'lag-4': ['1/1/c8/1'],
        'lag-5': ['1/1/c11/1', '2/1/c11/1'],
        'lag-6': ['2/1/c8/1'],
        'lag-11': ['1/1/c2/3']}),
    ('rt0.ams.nl.geant.net', {
        'lag-5': ['1/1/c19/1', '2/1/c19/1'],
        'lag-8': ['1/1/c1/1', '1/1/c5/1'],
        'lag-RL': ['9/1/c1/1']}),
    ('rt0.fra.de.geant.net', {'lag-1': ['1/1/c2/1', '1/1/c2/2']}),
    ('rt0.gen.ch.geant.net', {'lag-1': ['1/1/c2/1', '1/1/c2/2']}),
    ('rt0.lon.uk.geant.net', {'lag-1': ['1/1/c2/1', '1/1/c2/2']}),
    ('rt0.lon2.uk.geant.net', {'lag-1': ['2/1/c2/1', '2/1/c2/2']}),
])
def test_get_lags(hostname, expected_bundles):
    netconf_doc = _load_netconf_config(hostname)
    lags = {lag['name']: lag for lag in nokia.get_lags_config(netconf_doc)}
    assert set(lags.keys()) == set(expected_bundles.keys())
    for ex_name, ex_ports in expected_bundles.items():
        assert lags[ex_name]['ports'] == ex_ports


@pytest.mark.parametrize(
    'hostname,all_expected_data', [
        ('rt0.ams.nl.lab.office.geant.net', (
                ('lag-1.0', {'62.40.119.88/31'}, {'2001:799:1ab:2::31/126'}),
                ('lag-2.0', {'62.40.119.90/31'}, {'2001:799:1ab:2::41/126'}),
                ('lag-4.0', {'62.40.119.98/31'}, {'2001:799:1ab:2::55/126'}),
                ('lag-5.0', {'62.40.119.100/31'}, {'2001:799:1ab:2::59/126'}),
                ('lag-6.0', {'62.40.119.102/31'}, {'2001:799:1ab:2::5d/126'}),
                ('system', {'62.40.119.6/32'}, {'2001:799:1ab::6/128'}),
                ('lag-11:100', {'62.40.124.217/30'}, {'2001:798:14:10aa::9/126'}),
                ('LAG-11.333', {'83.97.88.245/30'}, {'2001:7f8:3c::35/126'}),
                ('LAG-11.111', {'62.40.126.184/31'}, {'2001:798:111:1::31/126'}),
        )),
        ('rt0.ams.nl.geant.net', (
                ('lag-5.0', {'62.40.98.39/31'}, {'2001:798:cc::62/126'}),
                ('lag-8.0', {'62.40.98.20/31'}, {'2001:798:cc::21/126'}),
                ('system', {'62.40.96.16/32'}, {'2001:798:aa:1::8/128'}),
        )),
        ('rt0.fra.de.geant.net', (
                ('lag-1.0', {'62.40.98.73/31'}, {'2001:798:cc::72/126'}),
                ('system', {'62.40.96.26/32'}, {'2001:798:aa:1::a/128'}),
        )),
        ('rt0.gen.ch.geant.net', (
                ('lag-1.0', {'62.40.98.77/31'}, {'2001:798:cc::7a/126'}),
                ('system', {'62.40.96.29/32'}, {'2001:798:aa:1::20/128'}),
        )),
        ('rt0.lon.uk.geant.net', (
                ('lag-1.0', {'62.40.98.60/31'}, {'2001:798:cc::65/126'}),
                ('system', {'62.40.96.21/32'}, {'2001:798:aa:1::9/128'}),
        )),
        ('rt0.lon2.uk.geant.net', (
                ('lag-1.0', {'62.40.98.62/31'}, {'2001:798:cc::69/126'}),
                ('system', {'62.40.96.25/32'}, {'2001:798:aa:1::b/128'}),
        )),
    ])
def test_interface_info(hostname, all_expected_data):
    netconf_doc = _load_netconf_config(hostname=hostname)
    interfaces_by_id = {ifc['interface-name']: ifc for ifc in
                        nokia.get_interfaces_config(netconf_doc)}
    assert len(interfaces_by_id) == len(all_expected_data)
    for expected_data in all_expected_data:
        ifc = interfaces_by_id[expected_data[0]]
        assert ifc['interface-name'] == expected_data[0]
        assert set(ifc['ipv4']) == expected_data[1]
        assert set(ifc['ipv6']) == expected_data[2]


@pytest.mark.parametrize('hostname,port_count', [
    ('rt0.ams.nl.lab.office.geant.net', 65),
    ('rt0.ams.nl.geant.net', 66),
    ('rt0.fra.de.geant.net', 58),
    ('rt0.gen.ch.geant.net', 141),
    ('rt0.lon.uk.geant.net', 58),
    ('rt0.lon2.uk.geant.net', 139),
])
def test_get_ports(hostname, port_count):
    netconf_doc = _load_netconf_config(hostname=hostname)
    ports = list(nokia.get_ports_config(netconf_doc))

    assert len(ports) == port_count
    for port in ports:
        jsonschema.validate(port, PORT_SCHEMA)


def test_get_port_state():
    ports = {p['port-id']: p for p in nokia.get_ports_state(state_doc)}
    assert len(ports) == 197
    found_port_ids = {p_id for p_id in ports}
    ports_sample = {
        '1/1/c2', '1/1/c2/1', '1/1/c2/2', '1/1/c2/3', '1/1/c7', '1/1/c7/1',
        '1/1/c8', '1/1/c8/1', '1/1/c9', '1/1/c9/1', '1/1/c13', '1/1/c13/1',
        '2/1/c7', '2/1/c7/1', '2/1/c8', '2/1/c8/1', '2/1/c13', '2/1/c13/1',
    }
    assert found_port_ids > ports_sample
    expected_up_ports = {
        '1/1/c2', '1/1/c5', '1/1/c5/1', '1/1/c7', '1/1/c8', '1/1/c9', '1/1/c13',
        '1/1/c13/1', '1/1/c19', '1/1/c26', '1/1/c26/1', '1/1/c27', '1/1/c27/1',
        '1/1/c30', '2/1/c7', '2/1/c8', '2/1/c13', '2/1/c13/1', 'A/1', 'B/1'
    }
    assert {k for k, v in ports.items() if v['oper-state'] == 'up'} == expected_up_ports


def test_get_lag_state():
    lags = {x['name']: x for x in nokia.get_lags_state(state_doc)}
    found_names = {lag_name for lag_name in lags}
    expected_names = {'lag-1', 'lag-2', 'lag-3'}
    assert found_names == expected_names
    expected_up_lags = {'lag-2'}
    assert {k for k, v in lags.items() if v['oper-state'] == 'up'} == expected_up_lags


def test_get_interface_state():
    interfaces = {x['interface-name']: x for x in nokia.get_interfaces_state(state_doc)}
    found_names = {interface_name for interface_name in interfaces}
    expected_names = {'system', 'lag-1.0', 'lag-2.0', 'lag-3.0', 'exfo400', 'guy',
                      'exfo400-100', 'management'}
    assert found_names == expected_names
    expected_up_interfaces = {'system', 'lag-2.0', 'guy', 'exfo400-100', 'management'}
    assert {k for k, v in interfaces.items() if
            v['oper-state'] == 'up'} == expected_up_interfaces


def test_snmp_index():
    interfaces = nokia.get_interfaces_state(state_doc)
    expected = {
        "system": 1,
        "lag-1.0": 2,
        "lag-2.0": 3,
        "lag-3.0": 4,
        "exfo400": 5,
        "guy": 6,
        "exfo400-100": 7,
        "management": 1280,
    }
    assert {ifc["interface-name"]: ifc["if-index"] for ifc in interfaces} == expected


@pytest.mark.parametrize('hostname', [
    'rt0.ams.nl.lab.office.geant.net',
    'rt0.ams.nl.geant.net',
    'rt0.fra.de.geant.net',
    'rt0.gen.ch.geant.net',
    'rt0.lon.uk.geant.net',
    'rt0.lon2.uk.geant.net'
])
def test_get_peers(hostname):
    netconf_doc = _load_netconf_config(hostname)
    all_peers_from_doc = set()
    all_peers_from_call = set()
    for neighbor_element in netconf_doc.xpath('//bgp/neighbor'):
        address = neighbor_element.find('ip-address').text
        all_peers_from_doc.add(ipaddress.ip_address(address).exploded)
    for peer in nokia.get_all_vprn_peers(netconf_doc):
        jsonschema.validate(peer, PEERS_SCHEMA)
        all_peers_from_call.add(peer['address'])
    router_peers_schema = deepcopy(PEERS_SCHEMA)
    del router_peers_schema['properties']['instance']
    for peer in nokia.get_router_peers(netconf_doc):
        jsonschema.validate(peer, PEERS_SCHEMA)
        all_peers_from_call.add(peer['address'])
    assert all_peers_from_doc == all_peers_from_call