Skip to content
Snippets Groups Projects
Commit 66f1972c authored by Erik Reid's avatar Erik Reid
Browse files

small improvement in complexity

parent 48e74750
No related branches found
No related tags found
No related merge requests found
...@@ -88,11 +88,21 @@ PHYSICAL_INTERFACE_COUNTER_SCHEMA = { ...@@ -88,11 +88,21 @@ PHYSICAL_INTERFACE_COUNTER_SCHEMA = {
'required': ['broadcast', 'multicast'], 'required': ['broadcast', 'multicast'],
'additionalProperties': False 'additionalProperties': False
}, },
'l2-pcs-counters': {
'type': 'object',
'properties': {
'bit-error-seconds': {'type': 'integer'},
'errored-blocks-seconds': {'type': 'integer'}
},
'required': ['bit-error-seconds', 'errored-blocks-seconds'],
'additionalProperties': False
},
'l2-counters': { 'l2-counters': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'ingress': {'$ref': '#/definitions/l2-dir-counters'}, 'ingress': {'$ref': '#/definitions/l2-dir-counters'},
'egress': {'$ref': '#/definitions/l2-dir-counters'}, 'egress': {'$ref': '#/definitions/l2-dir-counters'},
'pcs': {'$ref': '#/definitions/l2-pcs-counters'},
}, },
'required': ['ingress', 'egress'], 'required': ['ingress', 'egress'],
'additionalProperties': False 'additionalProperties': False
......
ERROR_POINT_SCHEMA = {
'$schema': 'https://json-schema.org/draft/2020-12/schema',
'type': 'object',
'properties': {
'hostname': {'type': 'string'},
'interface_name': {'type': 'string'},
'framing-errors': {'type': 'integer'},
'bit-error-seconds': {'type': 'integer'},
'input-crc-errors': {'type': 'integer'},
'input-total-errors': {'type': 'integer'},
'input-discards': {'type': 'integer'},
'input-drops': {'type': 'integer'},
'output-drops': {'type': 'integer'},
},
'required': ['hostname', 'interface_name', 'egressOctets', 'ingressOctets'],
'additionalProperties': False
}
def points(router_fqdn, interface_counters):
"""
:param router_fqdn: hostname
:param interface_counters: either PHYSICAL_INTERFACE_COUNTER_SCHEMA
or LOGICAL_INTERFACE_COUNTER_SCHEMA
:return: iterable of BRIAN_POINT_SCHEMA objects
"""
def _counters2point(_ifc):
_brct = _ifc['brian']
_p = {
'hostname': router_fqdn,
'interface_name': _ifc['name'],
'egressOctets': _brct['egress']['bytes'],
'ingressOctets': _brct['ingress']['bytes']
}
if 'v6' in _brct['egress']:
_p['egressOctetsv6'] = _brct['egress']['v6']['bytes']
if 'v6' in _brct['ingress']:
_p['ingressOctetsv6'] = _brct['ingress']['v6']['bytes']
if 'errors' in _brct['egress']:
_p['egressErrors'] = _brct['egress']['errors']['errors']
if 'errors' in _brct['ingress']:
_p['ingressDiscards'] = _brct['ingress']['errors']['discards']
_p['ingressErrors'] = _brct['ingress']['errors']['errors']
return _p
return map(_counters2point, interface_counters)
...@@ -65,6 +65,25 @@ def _elem_int(node, name): ...@@ -65,6 +65,25 @@ def _elem_int(node, name):
return int(_subelems[0]) return int(_subelems[0])
def _counter_dict(node, paths_and_keys):
"""
:param node: a node element
:param paths_and_keys: list of dicts {'path': str, 'key': str, 'required': boolean (optional)}
:return:
"""
result = {}
for pk in paths_and_keys:
_elems = node.xpath(pk['path'] + '/text()')
if _elems:
if len(_elems) > 1:
logger.warning(f'found more than one {pk["path"]} in {node}')
result[pk['key']] = int(_elems[0])
else:
if pk.get('required', False):
assert False, f'path {pk["path"]} not found in {node}'
return result
def _ifc_name(ifc_node): def _ifc_name(ifc_node):
return ifc_node.xpath('./name/text()')[0].strip() return ifc_node.xpath('./name/text()')[0].strip()
...@@ -77,88 +96,109 @@ def physical_interface_counters(ifc_doc): ...@@ -77,88 +96,109 @@ def physical_interface_counters(ifc_doc):
return any(map(name.startswith, log_prefixes)) return any(map(name.startswith, log_prefixes))
def _brian_counters(ifc_node): def _brian_counters(ifc_node):
tr_stats = ifc_node.xpath('./traffic-statistics')[0] ingress_fields = [
v6_stats = ifc_node.xpath('./traffic-statistics/ipv6-transit-statistics')[0] {'path': './traffic-statistics/input-bytes', 'key': 'bytes', 'required': True},
{'path': './traffic-statistics/input-packets', 'key': 'packets', 'required': True},
]
egress_fields = [
{'path': './traffic-statistics/output-bytes', 'key': 'bytes', 'required': True},
{'path': './traffic-statistics/output-packets', 'key': 'packets', 'required': True},
]
baseline_stats = { baseline_stats = {
'ingress': { 'ingress': _counter_dict(ifc_node, ingress_fields),
'bytes': _elem_int(tr_stats, 'input-bytes'), 'egress': _counter_dict(ifc_node, egress_fields)
'packets': _elem_int(tr_stats, 'input-packets'),
'v6': {
'bytes': _elem_int(v6_stats, 'input-bytes'),
'packets': _elem_int(v6_stats, 'input-packets'),
}
},
'egress': {
'bytes': _elem_int(tr_stats, 'output-bytes'),
'packets': _elem_int(tr_stats, 'output-packets'),
'v6': {
'bytes': _elem_int(v6_stats, 'output-bytes'),
'packets': _elem_int(v6_stats, 'output-packets'),
}
}
} }
in_errors = ifc_node.xpath('./input-error-list') v6_ingress_fields = [
out_errors = ifc_node.xpath('./output-error-list') {'path': './traffic-statistics/ipv6-transit-statistics/input-bytes', 'key': 'bytes'},
{'path': './traffic-statistics/ipv6-transit-statistics/input-packets', 'key': 'packets'},
if in_errors and out_errors: ]
in_errors = in_errors[0] v6_egress_fields = [
out_errors = out_errors[0] {'path': './traffic-statistics/ipv6-transit-statistics/output-bytes', 'key': 'bytes'},
{'path': './traffic-statistics/ipv6-transit-statistics/output-packets', 'key': 'packets'},
baseline_stats['ingress']['errors'] = { ]
'errors': _elem_int(in_errors, 'input-errors'),
'drops': _elem_int(in_errors, 'input-drops'), v6 = _counter_dict(ifc_node, v6_ingress_fields)
'resource': _elem_int(in_errors, 'input-resource-errors'), if v6:
'discards': _elem_int(in_errors, 'input-discards'), baseline_stats['ingress']['v6'] = v6
} v6 = _counter_dict(ifc_node, v6_egress_fields)
baseline_stats['egress']['errors'] = { if v6:
'errors': _elem_int(out_errors, 'output-errors'), baseline_stats['egress']['v6'] = v6
'drops': _elem_int(out_errors, 'output-drops'),
'resource': _elem_int(out_errors, 'output-resource-errors') ingress_error_fields = [
} {'path': './input-error-list/input-errors', 'key': 'errors'},
{'path': './input-error-list/input-drops', 'key': 'drops'},
if not _is_logical(ifc_node): {'path': './input-error-list/input-resource-errors', 'key': 'resource'},
baseline_stats['ingress']['errors']['fifo'] = _elem_int(in_errors, 'input-fifo-errors') {'path': './input-error-list/input-discards', 'key': 'discards'},
baseline_stats['egress']['errors']['fifo'] = _elem_int(out_errors, 'output-fifo-errors') {'path': './input-error-list/input-fifo-errors', 'key': 'fifo'},
baseline_stats['egress']['errors']['collisions'] = _elem_int(out_errors, 'output-collisions') ]
egress_error_fields = [
{'path': './output-error-list/output-errors', 'key': 'errors'},
{'path': './output-error-list/output-drops', 'key': 'drops'},
{'path': './output-error-list/output-resource-errors', 'key': 'resource'},
{'path': './output-error-list/output-fifo-errors', 'key': 'fifo'},
{'path': './output-error-list/output-collisions', 'key': 'collisions'},
]
errors = _counter_dict(ifc_node, ingress_error_fields)
if errors:
baseline_stats['ingress']['errors'] = errors
errors = _counter_dict(ifc_node, egress_error_fields)
if errors:
baseline_stats['egress']['errors'] = errors
return baseline_stats return baseline_stats
def _l2_counters(ifc_node): def _l2_counters(ifc_node):
mac_stats = ifc_node.xpath('./ethernet-mac-statistics')[0]
ingress_fields = [
{'path': '././ethernet-mac-statistics/input-broadcasts', 'key': 'broadcast', 'required': True},
{'path': '././ethernet-mac-statistics/input-multicasts', 'key': 'multicast', 'required': True},
{'path': '././ethernet-mac-statistics/input-bytes', 'key': 'bytes'},
{'path': '././ethernet-mac-statistics/input-packets', 'key': 'packets'},
{'path': '././ethernet-mac-statistics/input-unicasts', 'key': 'unicast'},
]
egress_fields = [
{'path': '././ethernet-mac-statistics/output-broadcasts', 'key': 'broadcast', 'required': True},
{'path': '././ethernet-mac-statistics/output-multicasts', 'key': 'multicast', 'required': True},
{'path': '././ethernet-mac-statistics/output-bytes', 'key': 'bytes'},
{'path': '././ethernet-mac-statistics/output-packets', 'key': 'packets'},
{'path': '././ethernet-mac-statistics/output-unicasts', 'key': 'unicast'},
]
baseline_stats = { baseline_stats = {
'ingress': { 'ingress': _counter_dict(ifc_node, ingress_fields),
'broadcast': _elem_int(mac_stats, 'input-broadcasts'), 'egress': _counter_dict(ifc_node, egress_fields)
'multicast': _elem_int(mac_stats, 'input-multicasts')
},
'egress': {
'broadcast': _elem_int(mac_stats, 'output-broadcasts'),
'multicast': _elem_int(mac_stats, 'output-multicasts')
}
} }
if not _is_logical(ifc_node): pcs_stats_fields = [
baseline_stats['ingress']['bytes'] = _elem_int(mac_stats, 'input-bytes') {'path': '././ethernet-pcs-statistics/bit-error-seconds', 'key': 'bit-error-seconds'},
baseline_stats['ingress']['packets'] = _elem_int(mac_stats, 'input-packets') {'path': '././ethernet-pcs-statistics/errored-blocks-seconds', 'key': 'errored-blocks-seconds'},
baseline_stats['ingress']['unicast'] = _elem_int(mac_stats, 'input-unicasts') ]
baseline_stats['ingress']['errors'] = { pcs = _counter_dict(ifc_node, pcs_stats_fields)
# this one isn't available on qfx's if pcs:
# 'total': _elem_int(mac_stats, 'input-total-errors'), baseline_stats['pcs'] = pcs
'crc': _elem_int(mac_stats, 'input-crc-errors'),
'fifo': _elem_int(mac_stats, 'input-fifo-errors'), ingress_error_fields = [
} # this one isn't available on qfx's
# 'total': _elem_int(mac_stats, 'input-total-errors'),
baseline_stats['egress']['bytes'] = _elem_int(mac_stats, 'output-bytes') {'path': '././ethernet-mac-statistics/input-crc-errors', 'key': 'crc'},
baseline_stats['egress']['packets'] = _elem_int(mac_stats, 'output-packets') {'path': '././ethernet-mac-statistics/input-fifo-errors', 'key': 'fifo'},
baseline_stats['egress']['unicast'] = _elem_int(mac_stats, 'output-unicasts') ]
baseline_stats['egress']['errors'] = { egress_error_fields = [
# this one isn't available on qfx's # 'total': _elem_int(mac_stats, 'input-total-errors'),
# 'total': _elem_int(mac_stats, 'output-total-errors'), {'path': '././ethernet-mac-statistics/output-crc-errors', 'key': 'crc'},
'crc': _elem_int(mac_stats, 'output-crc-errors'), {'path': '././ethernet-mac-statistics/output-fifo-errors', 'key': 'fifo'},
'fifo': _elem_int(mac_stats, 'output-fifo-errors'), ]
}
errors = _counter_dict(ifc_node, ingress_error_fields)
if errors:
baseline_stats['ingress']['errors'] = errors
errors = _counter_dict(ifc_node, egress_error_fields)
if errors:
baseline_stats['egress']['errors'] = errors
return baseline_stats return baseline_stats
...@@ -187,32 +227,37 @@ def physical_interface_counters(ifc_doc): ...@@ -187,32 +227,37 @@ def physical_interface_counters(ifc_doc):
def logical_interface_counters(ifc_doc): def logical_interface_counters(ifc_doc):
def _brian_counters(ifc_node): def _brian_counters(ifc_node):
tr_stats = ifc_node.xpath('./lag-traffic-statistics/lag-bundle') or ifc_node.xpath('./traffic-statistics') traffic = 'traffic-statistics' if ifc_node.xpath('./traffic-statistics') else 'lag-traffic-statistics/lag-bundle'
if not tr_stats:
assert False, f'no traffic-statistics for {ifc_node}' ingress_fields = [
tr_stats = tr_stats[0] {'path': f'./{traffic}/input-bytes', 'key': 'bytes', 'required': True},
{'path': f'./{traffic}/input-packets', 'key': 'packets', 'required': True},
]
egress_fields = [
{'path': f'./{traffic}/output-bytes', 'key': 'bytes', 'required': True},
{'path': f'./{traffic}/output-packets', 'key': 'packets', 'required': True},
]
baseline_stats = { baseline_stats = {
'ingress': { 'ingress': _counter_dict(ifc_node, ingress_fields),
'bytes': _elem_int(tr_stats, 'input-bytes'), 'egress': _counter_dict(ifc_node, egress_fields)
'packets': _elem_int(tr_stats, 'input-packets'),
},
'egress': {
'bytes': _elem_int(tr_stats, 'output-bytes'),
'packets': _elem_int(tr_stats, 'output-packets')
}
} }
v6_stats = ifc_node.xpath('./traffic-statistics/ipv6-transit-statistics') v6_ingress_fields = [
if v6_stats: {'path': './traffic-statistics/ipv6-transit-statistics/input-bytes', 'key': 'bytes'},
baseline_stats['ingress']['v6'] = { {'path': './traffic-statistics/ipv6-transit-statistics/input-packets', 'key': 'packets'},
'bytes': _elem_int(v6_stats[0], 'input-bytes'), ]
'packets': _elem_int(v6_stats[0], 'input-packets'), v6_egress_fields = [
} {'path': './traffic-statistics/ipv6-transit-statistics/output-bytes', 'key': 'bytes'},
baseline_stats['egress']['v6'] = { {'path': './traffic-statistics/ipv6-transit-statistics/output-packets', 'key': 'packets'},
'bytes': _elem_int(v6_stats[0], 'output-bytes'), ]
'packets': _elem_int(v6_stats[0], 'output-packets'),
} v6 = _counter_dict(ifc_node, v6_ingress_fields)
if v6:
baseline_stats['ingress']['v6'] = v6
v6 = _counter_dict(ifc_node, v6_egress_fields)
if v6:
baseline_stats['egress']['v6'] = v6
return baseline_stats return baseline_stats
......
...@@ -48,7 +48,7 @@ def test_validate_physical_counter_schema(router_fqdn, ifc_netconf_rpc): ...@@ -48,7 +48,7 @@ def test_validate_physical_counter_schema(router_fqdn, ifc_netconf_rpc):
@pytest.mark.parametrize('router_fqdn', ROUTERS) @pytest.mark.parametrize('router_fqdn', ROUTERS)
def test_logical_counters(router_fqdn, ifc_netconf_rpc): def test_validate_logical_counters_schema(router_fqdn, ifc_netconf_rpc):
doc = juniper.get_interface_info_ncrpc(router_fqdn) doc = juniper.get_interface_info_ncrpc(router_fqdn)
for ifc in juniper.logical_interface_counters(doc): for ifc in juniper.logical_interface_counters(doc):
jsonschema.validate(ifc, LOGICAL_INTERFACE_COUNTER_SCHEMA) jsonschema.validate(ifc, LOGICAL_INTERFACE_COUNTER_SCHEMA)
...@@ -123,6 +123,7 @@ def test_physical_brian_points(router_fqdn, ifc_netconf_rpc): ...@@ -123,6 +123,7 @@ def test_physical_brian_points(router_fqdn, ifc_netconf_rpc):
doc = juniper.get_interface_info_ncrpc(router_fqdn) doc = juniper.get_interface_info_ncrpc(router_fqdn)
interfaces = juniper.physical_interface_counters(doc) interfaces = juniper.physical_interface_counters(doc)
for point in brian.points(router_fqdn=router_fqdn, interface_counters=interfaces): for point in brian.points(router_fqdn=router_fqdn, interface_counters=interfaces):
print(point)
jsonschema.validate(point, brian.BRIAN_POINT_SCHEMA) jsonschema.validate(point, brian.BRIAN_POINT_SCHEMA)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment