napi
is a concept HTTP controller for network devices.
So you are a network engineer. You are responsible for a huge multivendor network.
To keep the network secure and easy to manage you want only the networking team to have access to network devices. But related teams (developers/SREs) want to collect data and/or even change network device configuration in some cases. It is probably not a good idea to manage access for all these external users, their credentials, SSH keys, permissions, etc. Tacacs could be a solution but you still have to set it up and manage it. And of course, nobody knows the CLI syntax of network boxes and is not eager to learn it. They just want the data!
The main idea of the project is to show how this situation might be solved by establishing a network controller to provide related teams with a familiar HTTP API. It follows DDD approach to keep thing separated, easy to manage, change and extend.
- 💪 Easy, secure, and vendor agnostic access your network
- 🚀 Async execution for improved performance powered by FastAPI
- 📊 Multicore load balancing via Uvicorn ASGI web server
- 🐳 Docker ready
- 🔒 Token based authorization
- 📚 Comprehensive API reference via Swagger and Redoc
- 📁 Beautiful documentation via MkDocs with material theme.
- 🛠️ Configurable via
.env
files andenv
variables - 💾 Completely stateless
Run the following command to run mkdocs
server:
make docs
Then open http://localhost:9000
for more details..
Before running napi
you need to setup source of thuth (SoT) - an inventory system. By default it has only Netbox support build in.
You can set it up on your own installation or use a community solution.
Don't forget to change view permissions to get full read access without a token by setting:
EXEMPT_VIEW_PERMISSIONS = ['*']
in the configuration/configuration.py
file.
And then run:
docker-compose up -d
After Netbox is started load example DB from examples/db_dump.sql.gz
with the following command:
gunzip -c db_dump.sql.gz | docker-compose exec -T postgres sh -c 'psql -U $POSTGRES_USER $POSTGRES_DB'
Now you are ready to try it out.
To try it out clone the repo:
git clone https://github.com/horseinthesky/napi.git
Next install dependencies (poetry is required):
make setup
Configuration is managed by pydantic's Settings module.
You must have .env
file to setup the following settings:
- nv_api_url (
NV_API_URL
env var) - an address of Netbox API - tenants (
TENANTS
env var) - comma separated list of tenants network devices belong to in SoT - domains (
DOMAINS
env var) - comma separated list of domains network devices has their fqnds from - endpoints (
ENDPOINTS
env var) - comma separated list of endpoints to use in the environment
Each setting must be prefixed with the corresponding "environment" value. Both in .env
file AND as env variable.
By default napi
runs in prod
environment. It is controlled by ENV
env variable.
Here is an example .env
file from the repo:
prod_nb_api_url=http://localhost:8000/api
prod_tenants=production
prod_domains=local
prod_endpoints=portswitcher,macgrabber
Finally run napi
server:
make run
By default it runs on port 8080 with several workers (number calculated via nproc
).
Before moving to further check out API documentation provided automatically by swagger
at localhost:8080/docs
.
As proof of concept napi
provides a few endpoints:
ping
- simple availability endpoint to put under your load balancer or k8s health checkportswitcher
- reconfigures DC fabric leaf switch downlinks (server-faced interfaces)macgrabber
- gets switch MAC addresses
Example:
xh get localhost:8080/ping
Always responds with:
{
"code": 200,
"status": "ok"
}
portswitcher
is an example of how you can provide an easy vendor agnostic way for your related teams to get/set a network device configuration (L2 interface is this case).
It works the same way with blackbox and whitebox switches. Example supports Huawei CE (Cloud Engine) switches and Nvidia Mellanox switches.
Example:
xh get localhost:8080/api/portswitcher switch=leaf1 interface=GE1/0/5 --bearer token
It expects input data describing the network device interface:
{
"switch": "string",
"interface": "string"
}
And returns an abstracted interface "state" - prod
/setup
:
{
"code": 200,
"status": "ok",
"result": {
"switch": "string",
"interface": "string",
"state": "prod"
}
}
These prod
/setup
states are the combination of interface mode (access/trunk) and VLANS on the interface. Check the DB example to see the details.
Example:
xh post localhost:8080/api/portswitcher switch=leaf1 interface=GE1/0/5 state=prod --bearer token
In the same way POST expects data describing the interface plus the desired "state":
{
"switch": "string",
"interface": "string",
"state": "prod"
}
The response is just the same as for GET:
{
"code": 0,
"status": "ok",
"result": {
"switch": "string",
"interface": "string",
"state": "prod"
}
}
The same goes for macgrabber
which is an example endpoint that provides an easy vendor agnostic way for your related teams to get/set MAC-addresses from a switch. No difference blackbox or whitebox.
Example:
xh get localhost:8080/api/macgrabber switch=leaf2 --bearer token
It expects input data of switch name and (optional) VLAN number:
{
"switch": "string",
"vlan": "string"
}
And returns a list of MAC-address info:
{
"code": 0,
"status": "ok",
"result": {
"switch": "string",
"macs": [
{
"vlan": "string",
"mac": "string",
"interface": "string"
}
]
}
}
napi
provides three main building blocks to solve any task you might imagine:
- token authorization
- inventory system
- network drivers
Authorization module uses static tokens but you can easily extend it to support JWT or PASETO.
Hash is stored in auth.yml
file to authorize the corresponding user.
groups:
group1: &group1
permissions:
- portswitcher
- macgrabber
users:
3c469e9d6c5875d37a43f353d4f88e61fcf812c66eee3457465a40b0da4153e0:
name: user1
<<: *group1
Example file includes one user user1
with a token
token to demonstrate the structure of user permissions.
You can generate new user tokens with this code snippet:
In [1]: import secrets, hashlib
In [2]: token = secrets.token_hex(16)
In [4]: token
Out[4]: 'bf3ae64635504b9b4ce71a6c7ab2bc8c'
In [5]: hashlib.sha256(token.encode('utf-8')).hexdigest()
Out[5]: '95fabfb1713ea5abb54767b2bea0fe49bbd29faad610df7c58c796408a29e7a5'
And assign permissions as in the example above.
Inventory module is designed to work with any SoT (Source of Truth) system.
As an example, it has Device
, Interface
, Vlans
, and SupportsGetDeviceInterface
abstractions to make the addition of new systems a breeze:
Drivers module provides drivers to interact with network devices via CLI or NETCONF protocol:
- CLI driver is powered by Carl Montanari's amazing scrapli library.
- NETCONF driver is powered by robust asyncssh library with in-house wrapper to make in NETCONF ready.