Netglass snapshot

This commit is contained in:
Jan 2021-05-16 17:40:37 +02:00
parent 688038b845
commit 90b644f921
8 changed files with 271 additions and 0 deletions

35
Dockerfile Normal file
View 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"]

View File

@ -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
View 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
View File

@ -0,0 +1,8 @@
version: '3'
services:
web:
build: .
image: netglass:dev
environment:
- NETBOX_URL
- NETBOX_TOKEN

111
netglass/netglass.py Executable file
View 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)

View File

@ -0,0 +1,2 @@
flask
pynetbox

44
nginx.conf Normal file
View 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;
}
}

5
run.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
service nginx start
./netglass.py