Netglass snapshot
This commit is contained in:
parent
688038b845
commit
90b644f921
35
Dockerfile
Normal file
35
Dockerfile
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
FROM ubuntu:focal AS build
|
||||||
|
|
||||||
|
ENV TZ=Europe/Berlin
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
|
||||||
|
echo $TZ > /etc/timezone
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -qq --no-install-recommends\
|
||||||
|
apt-transport-https git nodejs npm && npm install --global yarn
|
||||||
|
|
||||||
|
WORKDIR /tmp
|
||||||
|
RUN git clone https://github.com/hopglass/hopglass
|
||||||
|
|
||||||
|
WORKDIR /tmp/hopglass
|
||||||
|
RUN yarn install && node_modules/grunt/bin/grunt
|
||||||
|
|
||||||
|
FROM ubuntu:focal
|
||||||
|
COPY --from=build /tmp/hopglass/build /hopglass
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -qq --no-install-recommends\
|
||||||
|
nginx python3-venv
|
||||||
|
|
||||||
|
ADD . /app
|
||||||
|
|
||||||
|
RUN cp /app/config.json /hopglass/config.json
|
||||||
|
|
||||||
|
RUN cd /etc/nginx &&\
|
||||||
|
cp /app/nginx.conf sites-available/hopglass;\
|
||||||
|
ln -s ../sites-available/hopglass sites-enabled/hopglass;\
|
||||||
|
rm sites-enabled/default || true
|
||||||
|
|
||||||
|
WORKDIR /app/netglass
|
||||||
|
RUN python3 -m venv flask &&\
|
||||||
|
flask/bin/pip install -U -r requirements.txt
|
||||||
|
|
||||||
|
CMD ["/app/run.sh"]
|
21
README.md
21
README.md
@ -0,0 +1,21 @@
|
|||||||
|
# Netbox: NetGlass
|
||||||
|
|
||||||
|
View cable topology in [hopglass](https://github.com/hopglass/hopglass) fashioned graph view.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
* A docker host
|
||||||
|
* docker-compose
|
||||||
|
|
||||||
|
## Prepare
|
||||||
|
|
||||||
|
Netglass acts as replacement for [hoplass-server](https://github.com/hopglass/hopglass-server) and uses NetBox's API to gather wiring information.
|
||||||
|
|
||||||
|
First go to your NetBox instance and create a read-only Token. Remember that tokens for users from LDAP backends do not work. Create a file `.env` with the contents
|
||||||
|
|
||||||
|
```
|
||||||
|
NETBOX_URL=https://netbox.your.tld
|
||||||
|
NETBOX_TOKEN=70bdtoken89nhuf
|
||||||
|
```
|
||||||
|
|
||||||
|
Run `docker-compose up -d` to build the hopglass instance.
|
45
config.json
Normal file
45
config.json
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"dataPath": "/data/",
|
||||||
|
"siteName": "NetGlass",
|
||||||
|
"mapSigmaScale": 0.5,
|
||||||
|
"showContact": false,
|
||||||
|
"maxAge": 365,
|
||||||
|
"domainNames": [
|
||||||
|
{"domain": "net", "name": "Testnetz"}
|
||||||
|
],
|
||||||
|
"mapLayers": [
|
||||||
|
{
|
||||||
|
"name": "OpenStreetMap.HOT"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_nodeInfos": [
|
||||||
|
{ "href": "https://map.eulenfunk.de/stats/dashboard/db/node-byid?var-nodeid={NODE_ID}",
|
||||||
|
"thumbnail": "https://map.eulenfunk.de/stats/render/dashboard-solo/db/node-byid?panelId=1&theme=light&width=600&height=300&var-nodeid={NODE_ID}"
|
||||||
|
},
|
||||||
|
{ "href": "https://map.eulenfunk.de/stats/dashboard/db/node-byid?var-nodeid={NODE_ID}",
|
||||||
|
"thumbnail": "https://map.eulenfunk.de/stats/render/dashboard-solo/db/node-byid?panelId=2&theme=light&width=600&height=500&var-nodeid={NODE_ID}"
|
||||||
|
},
|
||||||
|
{ "href": "https://map.eulenfunk.de/stats/dashboard/db/node-byid?var-nodeid={NODE_ID}",
|
||||||
|
"thumbnail": "https://map.eulenfunk.de/stats/render/dashboard-solo/db/node-byid?panelId=3&theme=light&width=600&height=200&var-nodeid={NODE_ID}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_globalInfos": [
|
||||||
|
{ "href": "https://map.eulenfunk.de/stats/dashboard/db/global?var-job=dus",
|
||||||
|
"thumbnail": "https://map.eulenfunk.de/stats/render/dashboard-solo/db/global?panelId=1&&theme=light&width=800&height=600&var-job=dus"
|
||||||
|
},
|
||||||
|
{ "href": "https://map.eulenfunk.de/stats/dashboard/db/global?var-job=dus",
|
||||||
|
"thumbnail": "https://map.eulenfunk.de/stats/render/dashboard-solo/db/global?panelId=8&&theme=light&width=800&height=600&var-job=dus"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_linkInfos": [
|
||||||
|
{ "href": "https://map.eulenfunk.de/stats/dashboard/db/links?var-source={SOURCE}&var-target={TARGET}",
|
||||||
|
"thumbnail": "https://map.eulenfunk.de/stats/render/dashboard-solo/db/links?panelId=1&&theme=light&width=800&height=600&var-source={SOURCE}&var-target={TARGET}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"siteNames": [
|
||||||
|
{ "site": "net", "name": "Location" }
|
||||||
|
],
|
||||||
|
"_hwImg": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
8
docker-compose.yml
Normal file
8
docker-compose.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
image: netglass:dev
|
||||||
|
environment:
|
||||||
|
- NETBOX_URL
|
||||||
|
- NETBOX_TOKEN
|
111
netglass/netglass.py
Executable file
111
netglass/netglass.py
Executable file
@ -0,0 +1,111 @@
|
|||||||
|
#!flask/bin/python3
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
from flask import Flask, jsonify
|
||||||
|
import pynetbox
|
||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
box = pynetbox.api(
|
||||||
|
os.environ.get('NETBOX_URL'),
|
||||||
|
token=os.environ['NETBOX_TOKEN']
|
||||||
|
)
|
||||||
|
|
||||||
|
netglass = Flask(__name__)
|
||||||
|
|
||||||
|
@netglass.route('/graph.json')
|
||||||
|
def net_graph():
|
||||||
|
links = []
|
||||||
|
nodes = []
|
||||||
|
dcim_port_types = ['dcim.interface','dcim.frontport','dcim.rearport']
|
||||||
|
|
||||||
|
def _isdevice(device):
|
||||||
|
if isinstance(device, pynetbox.models.dcim.Devices):
|
||||||
|
return device
|
||||||
|
|
||||||
|
for wire in box.dcim.cables.all():
|
||||||
|
term_a = wire.termination_a.device if wire.termination_a_type in dcim_port_types else None
|
||||||
|
term_b = wire.termination_b.device if wire.termination_b_type in dcim_port_types else None
|
||||||
|
|
||||||
|
for node in [term_a,term_b]:
|
||||||
|
if not _isdevice(node):
|
||||||
|
continue
|
||||||
|
if not next((n for n in nodes if n['node_id'] == str(node.id)), None):
|
||||||
|
nodes.append({
|
||||||
|
'node_id': str(node.id),
|
||||||
|
'id': str(node.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
link = {
|
||||||
|
'bidirect': True,
|
||||||
|
'source': next((
|
||||||
|
i for i, item in enumerate(nodes)
|
||||||
|
if term_a and item['node_id'] == str(term_a.id)), None),
|
||||||
|
'target': next((
|
||||||
|
i for i, item in enumerate(nodes)
|
||||||
|
if term_b and item['node_id'] == str(term_b.id)), None),
|
||||||
|
'tq': 1,
|
||||||
|
}
|
||||||
|
if link['source'] != None and link['target'] != None:
|
||||||
|
links.append(link)
|
||||||
|
|
||||||
|
return jsonify({'batadv':{
|
||||||
|
'directed': True,
|
||||||
|
'graph': None,
|
||||||
|
'multigraph': True,
|
||||||
|
'links': links,
|
||||||
|
'nodes': nodes,
|
||||||
|
},
|
||||||
|
'version': 1})
|
||||||
|
|
||||||
|
@netglass.route('/nodes.json')
|
||||||
|
def net_nodes():
|
||||||
|
nodes = []
|
||||||
|
for node in [
|
||||||
|
n for n in box.dcim.devices.all()
|
||||||
|
if isinstance(n, pynetbox.models.dcim.Devices)]:
|
||||||
|
lastseen = datetime.now().replace(microsecond=0)
|
||||||
|
addresses = []
|
||||||
|
for primary_ip in [node.primary_ip4,node.primary_ip6]:
|
||||||
|
if primary_ip:
|
||||||
|
addresses.append(re.sub(r'/\d+', '', primary_ip.address))
|
||||||
|
nodes.append({
|
||||||
|
'flags': {'online': True, 'gateway': False},
|
||||||
|
'firstseen': lastseen.isoformat(),
|
||||||
|
'lastseen': lastseen.isoformat(),
|
||||||
|
'nodeinfo': {
|
||||||
|
'hostname': node.display_name,
|
||||||
|
'node_id': str(node.id),
|
||||||
|
'network': { 'mac': node.name, 'addresses': addresses },
|
||||||
|
'mesh_interfaces': [node.name],
|
||||||
|
'mesh': {
|
||||||
|
'bat0': { 'interfaces': { 'other': [node.name] } }
|
||||||
|
},
|
||||||
|
'system': {
|
||||||
|
'site_code': 'net'
|
||||||
|
},
|
||||||
|
'traffic': {
|
||||||
|
'forward': {'packets': 0, 'bytes': 0},
|
||||||
|
'mgmt_rx': {'packets': 0, 'bytes': 0},
|
||||||
|
'mgmt_tx': {'packets': 0, 'bytes': 0},
|
||||||
|
'rx': {'packets': 0, 'bytes': 0},
|
||||||
|
'tx': {'packets': 0, 'bytes': 0, 'dropped': 0}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'statistics': {
|
||||||
|
'clients': 0,
|
||||||
|
'gateway': '',
|
||||||
|
'memory_usage': 0,
|
||||||
|
'rootfs_usage': 0,
|
||||||
|
'uptime': 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'nodes': nodes,
|
||||||
|
'timestamp': datetime.now().replace(microsecond=0).isoformat(),
|
||||||
|
'version': 2
|
||||||
|
})
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
netglass.run(debug=False, port=8080)
|
2
netglass/requirements.txt
Normal file
2
netglass/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
flask
|
||||||
|
pynetbox
|
44
nginx.conf
Normal file
44
nginx.conf
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
upstream netglass {
|
||||||
|
server 127.0.0.1:8080;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
listen [::]:80 default_server;
|
||||||
|
|
||||||
|
# SSL configuration
|
||||||
|
#
|
||||||
|
# listen 443 ssl default_server;
|
||||||
|
# listen [::]:443 ssl default_server;
|
||||||
|
#
|
||||||
|
# Note: You should disable gzip for SSL traffic.
|
||||||
|
# See: https://bugs.debian.org/773332
|
||||||
|
#
|
||||||
|
# Read up on ssl_ciphers to ensure a secure configuration.
|
||||||
|
# See: https://bugs.debian.org/765782
|
||||||
|
#
|
||||||
|
# Self signed certs generated by the ssl-cert package
|
||||||
|
# Don't use them in a production server!
|
||||||
|
#
|
||||||
|
# include snippets/snakeoil.conf;
|
||||||
|
|
||||||
|
root /hopglass;
|
||||||
|
|
||||||
|
location /data/ {
|
||||||
|
rewrite /data/(.*) /$1 break;
|
||||||
|
proxy_pass http://netglass;
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add index.php to the list if you are using PHP
|
||||||
|
index index.html index.htm index.nginx-debian.html;
|
||||||
|
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
# First attempt to serve request as file, then
|
||||||
|
# as directory, then fall back to displaying a 404.
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user