Skip to content
Snippets Groups Projects
Select Git revision
  • 40ba6d19826e9d3b60b7a51ab041f5ce90472846
  • develop default protected
  • master protected
  • feature/POL1-813-error-report-sensu-check
  • 0.23
  • 0.22
  • 0.21
  • 0.20
  • 0.19
  • 0.18
  • 0.17
  • 0.16
  • 0.15
  • 0.14
  • 0.13
  • 0.12
  • 0.11
  • 0.10
  • 0.9
  • 0.8
  • 0.7
  • 0.6
  • 0.5
  • 0.4
24 results

juniper.py

Blame
  • juniper.py 10.55 KiB
    """
        # rpc: 'get-interface-information' (child elem: <extensive />)
        # cli: 'show interfaces extensive'
    """
    import contextlib
    import logging
    import time
    from lxml import etree
    import ncclient.manager
    from ncclient.devices.junos import JunosDeviceHandler
    from ncclient.xml_ import NCElement
    
    logger = logging.getLogger(__name__)
    
    
    def _remove_ns(rpc_response):
        junos_dev_handler = JunosDeviceHandler(
            device_params={'name': 'junos', 'local': False})
        return NCElement(rpc_response, junos_dev_handler.transform_reply()) \
            .remove_namespaces(rpc_response.xml)
    
    
    def nc_connection(hostname, host_verify=True):
        conn = ncclient.manager.connect(
            host=hostname,
            port=830,
            ssh_config='~/.ssh/config.d/routers-jump',
            hostkey_verify=host_verify
            # device_params={'name': 'junos'}
        )
    
        conn.async_mode = True
        return conn
    
    
    def rpc(router, command):
        obj = router.rpc(command)
    
        logger.info(f'sent rpc command: {etree.tostring(command)}')
    
        # just wait for the result
        while not obj.event.is_set():
            logging.info('waiting for rpc reply ...')
            time.sleep(.3)
    
        return obj.reply
    
    
    def netconf_rpc(router_name, command, host_verify=True):
        with contextlib.closing(nc_connection(hostname=router_name, host_verify=host_verify)) as router:
            reply = rpc(router, command)
            return _remove_ns(reply)
    
    
    def get_interface_info_ncrpc(router_name, host_verify=True):
        request = etree.Element('get-interface-information')
        request.append(etree.Element('extensive'))
        return netconf_rpc(router_name=router_name, command=request, host_verify=host_verify)
    
    
    def _elem_int(node, name):
        _subelems = node.xpath(f'./{name}/text()')
        if not _subelems:
            assert False, f"can't find {name} in {node} [{_subelems}]"
        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):
        return ifc_node.xpath('./name/text()')[0].strip()
    
    
    def physical_interface_counters(ifc_doc):
    
        def _is_logical(ifc_node):
            name = _ifc_name(ifc_node)
            log_prefixes = ['ae', 'lt', 'irb']
            return any(map(name.startswith, log_prefixes))
    
        def _brian_counters(ifc_node):
            ingress_fields = [
                {'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 = {
                'ingress': _counter_dict(ifc_node, ingress_fields),
                'egress': _counter_dict(ifc_node, egress_fields)
            }
    
            v6_ingress_fields = [
                {'path': './traffic-statistics/ipv6-transit-statistics/input-bytes', 'key': 'bytes'},
                {'path': './traffic-statistics/ipv6-transit-statistics/input-packets', 'key': 'packets'},
            ]
            v6_egress_fields = [
                {'path': './traffic-statistics/ipv6-transit-statistics/output-bytes', 'key': 'bytes'},
                {'path': './traffic-statistics/ipv6-transit-statistics/output-packets', 'key': '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
    
            ingress_error_fields = [
                {'path': './input-error-list/input-errors', 'key': 'errors'},
                {'path': './input-error-list/input-drops', 'key': 'drops'},
                {'path': './input-error-list/input-resource-errors', 'key': 'resource'},
                {'path': './input-error-list/input-discards', 'key': 'discards'},
                {'path': './input-error-list/input-fifo-errors', 'key': 'fifo'},
            ]
            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
    
        def _l2_counters(ifc_node):
    
            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 = {
                'ingress': _counter_dict(ifc_node, ingress_fields),
                'egress': _counter_dict(ifc_node, egress_fields)
            }
    
            pcs_stats_fields = [
                {'path': '././ethernet-pcs-statistics/bit-error-seconds', 'key': 'bit-error-seconds'},
                {'path': '././ethernet-pcs-statistics/errored-blocks-seconds', 'key': 'errored-blocks-seconds'},
            ]
            pcs = _counter_dict(ifc_node, pcs_stats_fields)
            if pcs:
                baseline_stats['pcs'] = pcs
    
            ingress_error_fields = [
                # this one isn't available on qfx's
                # 'total': _elem_int(mac_stats, 'input-total-errors'),
                {'path': '././ethernet-mac-statistics/input-crc-errors', 'key': 'crc'},
                {'path': '././ethernet-mac-statistics/input-fifo-errors', 'key': 'fifo'},
            ]
            egress_error_fields = [
                # 'total': _elem_int(mac_stats, 'input-total-errors'),
                {'path': '././ethernet-mac-statistics/output-crc-errors', 'key': 'crc'},
                {'path': '././ethernet-mac-statistics/output-fifo-errors', 'key': 'fifo'},
            ]
    
            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
    
        for phy in ifc_doc.xpath(
                '//interface-information/physical-interface['
                '(name[(starts-with(normalize-space(), "xe-"))]'
                ' or name[(starts-with(normalize-space(), "ge-"))]'
                ' or name[(starts-with(normalize-space(), "et-"))]'
                ' or name[(starts-with(normalize-space(), "ae"))]'
                ' or name[(starts-with(normalize-space(), "irb"))])'
                ' and normalize-space(admin-status)="up"'
                ' and normalize-space(oper-status)="up"'
                ']'):
    
            counters = {
                'name': _ifc_name(phy),
                'brian': _brian_counters(phy)
            }
    
            if phy.xpath('./ethernet-mac-statistics'):
                counters['l2'] = _l2_counters(phy)
    
            yield counters
    
    
    def logical_interface_counters(ifc_doc):
    
        def _brian_counters(ifc_node):
            traffic = 'traffic-statistics' \
                if ifc_node.xpath('./traffic-statistics') \
                else 'lag-traffic-statistics/lag-bundle'
    
            ingress_fields = [
                {'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 = {
                'ingress': _counter_dict(ifc_node, ingress_fields),
                'egress': _counter_dict(ifc_node, egress_fields)
            }
    
            v6_ingress_fields = [
                {'path': './traffic-statistics/ipv6-transit-statistics/input-bytes', 'key': 'bytes'},
                {'path': './traffic-statistics/ipv6-transit-statistics/input-packets', 'key': 'packets'},
            ]
            v6_egress_fields = [
                {'path': './traffic-statistics/ipv6-transit-statistics/output-bytes', 'key': 'bytes'},
                {'path': './traffic-statistics/ipv6-transit-statistics/output-packets', 'key': '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
    
        for logical in ifc_doc.xpath(
                '//interface-information/physical-interface['
                '(name[(starts-with(normalize-space(), "xe-"))]'
                ' or name[(starts-with(normalize-space(), "ge-"))]'
                ' or name[(starts-with(normalize-space(), "et-"))]'
                ' or name[(starts-with(normalize-space(), "ae"))]'
                ' or name[(starts-with(normalize-space(), "irb"))])'
                ' and normalize-space(admin-status)="up"'
                ' and normalize-space(oper-status)="up"'
                ']/logical-interface'):
            yield {
                'name': _ifc_name(logical),
                'brian': _brian_counters(logical)
            }