Metadata-Version: 2.1
Name: umnet-pyncs
Version: 0.1.17
Summary: custom python module for NCS helpers
Author: Nick Grundler
Author-email: grundler@umich.edu
Requires-Python: >=3.7,<4.0
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Requires-Dist: importlib-metadata (==6.0.0); python_version < "3.8"
Requires-Dist: multiprocessing-logging (>=0.3.3,<0.4.0)
Requires-Dist: netaddr (>=0.8.0,<0.9.0)
Requires-Dist: ntc-templates (>=3.0.0,<4.0.0)
Description-Content-Type: text/markdown

## Overview

This module is intended to be installed on the production NCS nodes and imported in other services/actions that need to gather state from the network.  It uses the NCS device manager and the standard python `multiprocessing` library to connect to devices in-parallel and issue commands, returning results as structured data.

## Usage information

Basic usage example in an NCS callback:

``` python
from umnet_pyncs.state import StateManager
...


class DemoAction(Action):
    @Action.action
    def cb_action(self, uinfo, name, kp, input, output, trans):
        ...
        with StateManager() as m:
            interfaces = m.get_state(al_devices, ["get-interface-details"])
            arp = m.get_state(dl_devices, ["get-arp-table"])
            ...
```

## Supported commands

Currently supported commands are:
- `get-mac-table`
- `get-arp-table`
- `get-interface-details`
- `get-transciever-details`
- `get-lldp-neighbors`
- `get-bfd-neighbors`
- `get-ospf-neighbors`

## Developer testing

Install the [ncs-pycli](https://pypi.org/project/ncs-pycli/) package into your local NSO development environment.  Run `ncs_pycli` to get an ipython shell with an open transaction:

``` python
import logging
from umnet_pyncs.state import StateManager

log = logging.getLogger()
log.setLevel(logging.DEBUG)

devices = [d for d in root.devices.device]

m = StateManager()

interfaces = m.get_state(devices, ["get-interface-details"])
    
print(interfaces)

m.__exit__()
```

For any given command, the various platform-specific models are responsible for implementing how the data is fetched and parsed from the remote device.  Each command corresponds to a method that can be invoked to retrieve the data, e.g. `get-interface-details` maps to the `get_interface_details()` instance method of the model(s).

For Cisco IOS and NXOS devices (which use CLI-based NEDs), the built-in NCS `live-status` action(s) are used to send raw CLI commands to the device.  For example, the `get_mac_address()` method will send a `show mac address-table` CLI command.  For both IOS and NXOS we use [ntc_templates](https://github.com/networktocode/ntc-templates) to parse the raw text output into structured data.

For Juniper devices, since the NED uses NETCONF for all device communications, we instead call the `<get-ethernet-switching-table-information>` RPC directly.  Since this RPC is modelled in YANG, we can then parse the results directly using the maagic API.

All the nitty-gritty details of parsing the data retrieved directly from the remote device is handled by the platform-specific model implementation for that device.  Each model normalizes the data using the dataclasses defined in [base.py](./umnet_pyncs/state/models/base.py).  The intention is to makes it simpler for NCS actions/services to use this module, as well as making it easier to develop/maintain.

**NB**: this implementation currently relies on an additional template for NXOS that handles parsing `show ip arp detail vrf all` -- see [PR# 1204](https://github.com/networktocode/ntc-templates/pull/1204).

## Developer testing

Install the [ipython-superuser](https://github.com/NSO-developer/ipython-superuser) package into your local NSO development environment.  Use the REPL e.g:

``` python
import logging
from umnet_pyncs.state import StateManager

logging.basicConfig(level=logging.DEBUG)

devices = [d for d in root.devices.device]

m = StateManager()

# NB: m.get_state() expects a list of devices
interfaces = m.get_state(devices, ["get-interface-details"])
    
print(interfaces)

m.__exit__()
```

