Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show live data from nodes #51

Merged
merged 58 commits into from
May 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
4c9afa7
allow access to node-data.json
bortok Jun 12, 2022
4b5e9a8
fetch node-data from /collect
bortok Jul 9, 2022
012c910
node_data facts were flattened
bortok Jul 10, 2022
b0a81e1
fetch_device_data on load
bortok Jul 10, 2022
c6f1522
update node names with hostname data
bortok Jul 10, 2022
a248267
show vendor and model
bortok Jul 10, 2022
bbb3c9a
fetch api in fetch_device_data
bortok Jul 10, 2022
d44c2db
_update_topology_data
bortok Jul 10, 2022
affd4b9
device_data_autoupdate_toggle
bortok Jul 11, 2022
23c5ad9
mac-based interface identification (debug)
bortok Jul 17, 2022
960e763
refactored interface mac match
bortok Jul 17, 2022
7d41b3e
added exact interface name match
bortok Jul 17, 2022
1d7bdb7
lldp peer match implementation
bortok Jul 17, 2022
665c0fc
refactor interface_list loop to stop on match
bortok Jul 18, 2022
5f656eb
live/static labels footer toggle
bortok Jul 30, 2022
91e6e94
LinkWithAlignedLabels git sync
bortok Jul 30, 2022
d173a71
show static label if live one is undefined
bortok Jul 30, 2022
88fb3d1
live labels button is disabled by default
bortok Jul 31, 2022
28c35bd
label type buttons show as a group
bortok Jul 31, 2022
d847bbd
switch node names between static and live
bortok Aug 17, 2022
9c7dc4c
autolayout draft
bortok Aug 19, 2022
286be2e
update labels w/o topology reattach
bortok Aug 20, 2022
08fe5e1
shortened live interface names
bortok Aug 21, 2022
4442170
GraphiteLinkTooltipContent
bortok Aug 21, 2022
4f5b259
link tooltip as table
bortok Aug 21, 2022
e313d7f
link tooltip was 3-column table
bortok Aug 22, 2022
46f7949
commented out interfaces_ip
bortok Aug 22, 2022
7e7c914
fixed node_data interface match for multilink
bortok Aug 22, 2022
f85281d
interface match validations
bortok Aug 22, 2022
8bb5d33
use peer interface name from LLDP
bortok Aug 23, 2022
6cb99c4
IPv4 added to live data
bortok Sep 18, 2022
5ffe516
GraphiteLink class
bortok Sep 18, 2022
6351818
BadgeLinkLabel with live data
bortok Sep 18, 2022
d4cc335
replaced single src/tgtIP with arrays
bortok Oct 2, 2022
8822c75
IPs -> IPv4s
bortok Oct 2, 2022
05ce617
sourceIP/sourceIP -> sourceIPv4/targetIPv4
bortok Oct 3, 2022
4dc6967
fixed growing src/tgtIfIPv4Array on refreshes
bortok Oct 4, 2022
bd43a11
fix for unequal size of src/tgtIfIPv4Array
bortok Oct 4, 2022
5584413
added IPv6 to link tooltip
bortok Oct 4, 2022
340a3f2
fixed issue with link label types toggle clearing IPs
bortok Oct 4, 2022
7144cea
Merge pull request #46 from netreplica/node-data
bortok Oct 5, 2022
545beda
defaults to show live IPs
bortok Oct 5, 2022
5e21d65
automatically switch between static and live labels
bortok Oct 5, 2022
68e4ca5
Merge pull request #48 from netreplica/nanog-defaults
bortok Oct 5, 2022
3f8957d
added node-data to dockerfile in dev mode
bortok Oct 6, 2022
3923b82
alpine:3.15 to stay on Python3.9
bortok Oct 6, 2022
2e25567
moved default under common lab folder
bortok Oct 6, 2022
cf2b39e
multi-stage dockerfile
bortok Oct 6, 2022
4b3caec
added webssh-stage
bortok Oct 6, 2022
46a1358
uwsgi
bortok Oct 7, 2022
42d5419
run uwsgi as uwsgi user
bortok Oct 16, 2022
9e314a3
npm audit fix embedded
bortok Oct 16, 2022
64ec09d
note on custom lab prefix
bortok Oct 16, 2022
3761b3f
use submodule for node-data
bortok May 14, 2023
3b5e69b
env var doc
bortok May 14, 2023
598f556
check for "labels" property
bortok May 14, 2023
ae6a03b
handle multiple ways to mount labs in start.sh
bortok May 14, 2023
b65e4b7
show label controls
bortok May 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ docker/etc/lighttpd/lighttpd.conf
docker/etc/lighttpd/conf.d/dirlisting.conf
docker/etc/lighttpd/conf.d/mime.conf
docker/bin/clabg
docker/default/default.json
docker/default/topology-data.json
docker/webssh2/
docker/node-data/
docker/bootstrap-3.4.1-dist
docker/bin/envsubst
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "docker/node-data"]
path = docker/node-data
url = https://github.com/netreplica/node-data.git
93 changes: 76 additions & 17 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,59 @@
FROM alpine:latest
##########################################
# NODEDATA-IMAGE
##########################################

FROM alpine:3.15 AS nodedata-image
# Install packages
RUN apk add --no-cache \
curl \
build-base \
python3 \
python3-dev \
&& rm -rf /var/cache/apk/* \
&& curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py \
&& python3 get-pip.py \
&& pip3 install virtualenv

# Node-data
ENV NODEDATA="/usr/local/node-data"
ENV VIRTUAL_ENV="${NODEDATA}/venv"
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
RUN mkdir -p ${NODEDATA}/instance && mkdir -p ${NODEDATA}/venv
COPY docker/node-data/ ${NODEDATA}/
COPY docker/nodedata.cfg.template ${NODEDATA}/nodedata.cfg.template
WORKDIR ${NODEDATA}
RUN python3 -m venv $VIRTUAL_ENV \
&& pip3 install --no-cache-dir -r requirements.txt -r requirements_prod.txt \
&& pip install -e .

##########################################
# WEBSSH-IMAGE
##########################################

FROM alpine:3.15 AS webssh-image

RUN apk add --no-cache \
npm \
&& rm -rf /var/cache/apk/*

# webssh2
ENV WEBSSH2=/usr/local/webssh2
RUN mkdir -p ${WEBSSH2}
WORKDIR ${WEBSSH2}
COPY docker/webssh2/ ${WEBSSH2}/
COPY docker/webssh2.config.template ${WEBSSH2}/config.template
# Add webssh2 user to run node.js
RUN addgroup --system webssh2 \
&& adduser -S -G webssh2 -H -s /bin/sh -h ${WEBSSH2} webssh2 \
&& chown webssh2:webssh2 ${WEBSSH2} \
&& npm install --production
# && npm audit fix

##########################################
# RELEASE-IMAGE
##########################################

FROM alpine:3.15 AS release-image

ENV LIGHTTPD_VERSION=1.4.64-r0

Expand All @@ -10,33 +65,37 @@ RUN apk add --no-cache \
git \
npm \
curl \
openssh-client \
python3 \
&& rm -rf /var/cache/apk/*

# Clone Graphite dependencies
ENV WWW_HOME=/htdocs
RUN git clone --single-branch https://github.com/netreplica/next-bower.git ${WWW_HOME}/next-bower

# Default configuration
# HTTPd configuration and assets
RUN mkdir -p $WWW_HOME/lab/default && mkdir -p $WWW_HOME/bootstrap-3.4.1-dist
COPY docker/etc/lighttpd/ /etc/lighttpd/
RUN mkdir -p $WWW_HOME/default
# Bootstrap
RUN mkdir -p $WWW_HOME/bootstrap-3.4.1-dist
COPY docker/bootstrap-3.4.1-dist/ $WWW_HOME/bootstrap-3.4.1-dist/

# Node-data
ENV NODEDATA=/usr/local/node-data
ENV VIRTUAL_ENV=${NODEDATA}/venv
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
COPY --from=nodedata-image /usr/local/node-data /usr/local/node-data
RUN addgroup --system uwsgi \
&& adduser -S -G uwsgi -H -s /bin/sh -h ${NODEDATA} uwsgi \
&& chown uwsgi:uwsgi ${NODEDATA}

# webssh2
ENV WEBSSH2=/usr/local/webssh2
RUN mkdir -p ${WEBSSH2}
WORKDIR ${WEBSSH2}
# Add webssh2 user to run node.js
RUN addgroup --system webssh2
RUN adduser -S -G webssh2 -H -s /bin/sh -h ${WEBSSH2} webssh2
RUN chown webssh2:webssh2 ${WEBSSH2}
COPY docker/webssh2/ ${WEBSSH2}/
COPY docker/webssh2.config.template ${WEBSSH2}/config.template
RUN npm install --production
COPY --from=webssh-image ${WEBSSH2} ${WEBSSH2}
RUN addgroup --system webssh2 \
&& adduser -S -G webssh2 -H -s /bin/sh -h ${WEBSSH2} webssh2 \
&& chown webssh2:webssh2 ${WEBSSH2}

# Default data
COPY docker/default/ $WWW_HOME/default/
COPY docker/default/ $WWW_HOME/lab/default/

# Scripts and binaries
COPY docker/bin/ /usr/local/bin/
Expand All @@ -50,8 +109,8 @@ EXPOSE 80/tcp
# Volume with configuration
#VOLUME /etc/lighttpd

# Volume with Containerlab topology assets
VOLUME ${WWW_HOME}/clab
# Volume with lab topology assets
VOLUME ${WWW_HOME}/lab

# Run lighttpd webserver
CMD ["start.sh"]
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ The easiest way to use Graphite with Containerlab is to add the following code t
env:
CLAB_SSH_CONNECTION: ${SSH_CONNECTION}
binds:
- __clabDir__/topology-data.json:/htdocs/default/default.json:ro
- __clabDir__/topology-data.json:/htdocs/lab/default/topology-data.json:ro
- __clabDir__/ansible-inventory.yml:/htdocs/lab/default/ansible-inventory.yml:ro
ports:
- 8080:80
exec:
- sh -c 'graphite_motd.sh 8080'
labels:
graph-hide: yes
````
```

Once added, deploy the topology with `sudo -E containerlab deploy -t <topology.yaml>`. Note `-E` parameter for `sudo` – it is needed to pass `SSH_CONNECTION` variable.

Expand All @@ -57,7 +58,7 @@ Graphite was conceived as part of [NANOG-84 Hackathon](https://www.nanog.org/eve

## Copyright notice

Copyright 2022 Netreplica Team
Copyright 2022-2023 Netreplica Team

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
17 changes: 17 additions & 0 deletions app/css/graphite.css
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,20 @@
transform-box: fill-box;
transform-origin: 100% 0%;
}
.m-fadeOut {
visibility: hidden;
opacity: 0;
transition: visibility 0s linear 300ms, opacity 300ms;
}
.m-fadedOut {
visibility: hidden;
opacity: 0;
}
.m-fadeIn {
visibility: visible;
opacity: 1;
transition: visibility 0s linear 0s, opacity 300ms;
}
.popover {
max-width: 400px;
}
16 changes: 10 additions & 6 deletions app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<link rel="icon" type="image/png" href="assets/favicon.png" />
</head>
<body>

<nav class="navbar navbar-inverse navbar-static-top">
<div class="container">
<div class="navbar-header">
Expand All @@ -34,9 +34,9 @@
</div>
</div>
</nav>
<div id="topology-diagram" class="container">


<div id="topology-diagram" class="container">
<div class="row">
<div class="col-md-14">
<div class="panel panel-default">
Expand All @@ -54,17 +54,21 @@ <h3 class="panel-title">
<div class="panel-footer">
<ul class="nav nav-pills">
<li role="presentation" class="disabled"><a href="#">Layout</a></li>
<li role="presentation" id="nav-auto" class="active"><a href="#" onClick="window.location.reload();">Auto</a></li>
<li role="presentation" id="nav-auto" class="active"><a href="#" onClick='autolayout()'>Auto</a></li>
<li role="presentation" id="nav-horizontal"><a href="#" onClick='horizontal()'>Horizontal</a></li>
<li role="presentation" id="nav-vertical"><a href="#" onClick='vertical()'>Vertical</a></li>
<!-- below is aligned to the right -->
<li role="presentation" id="nav-live" class="m-fadedOut active pull-right"><a href="#" onClick='label_types_live()'>Live</a></li>
<li role="presentation" id="nav-static" class="m-fadedOut pull-right"><a href="#" onClick='label_types_static()'>Static</a></li>
<li role="presentation" id="nav-labels" class="m-fadedOut disabled pull-right"><a href="#">Labels</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>

<div id="footnote" class="container">
<div id="footnote" class="container">
<div class="row">
<p class="text-muted pull-right">Copyright (c) 2022 <a href="https://twitter.com/netreplica" target="_blank">Netreplica</a></p>
</div>
Expand Down
66 changes: 21 additions & 45 deletions app/js/clab.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,6 @@
limitations under the License.
*/

const interface_full_name_map = {
'e1-': 'ethernet1-',
'Eth': 'Ethernet',
'Fa' : 'FastEthernet',
'Gi' : 'GigabitEthernet',
'Te' : 'TenGigabitEthernet',
'Ma' : 'Management'
};

function if_fullname(ifname) {
//TODO ifname = dequote(ifname)
for (k in interface_full_name_map){
var v = interface_full_name_map[k];
if (ifname.toLowerCase().startsWith(k.toLowerCase())) {
return ifname.toLowerCase().replace(k.toLowerCase(), v);
}
}
return ifname;
}

function if_shortname(ifname) {
//TODO ifname = dequote(ifname)
for (k in interface_full_name_map){
var v = interface_full_name_map[k];
if (ifname.toLowerCase().startsWith(v.toLowerCase())) {
return ifname.toLowerCase().replace(v.toLowerCase(), k);
}
}
return ifname;
}

function port_mode_node_name(n, i) {
return i + "@" + n;
}
Expand Down Expand Up @@ -96,27 +65,28 @@ function convert_clab_to_cmt(c){
function convert_clab_topology_data_to_cmt(c){
var cmt = {"nodes": [], "links": [], "type": "clab", "name": ""};
var node_id_map = {};

// topology name
if (c.hasOwnProperty("name")) {
cmt.name = c.name;
}

if (!c.hasOwnProperty("nodes")) {
return cmt;
}

var i = -1; // We will increment the index to 0 right away in the cycle below
for (var node in c.nodes) { // node is a string with a node name
i++;
var n = c.nodes[node]; // retrieve the full object

var cmt_node = {
// "id": int,
// "name": string,
// "fullname": string,
// "websshDeviceLink": string,
// "websshDeviceLinkIPv6": string,
// "model": string,
// "kind": string,
// "image": string,
// "group": string,
// "mgmtIPv4": string,
Expand All @@ -130,7 +100,7 @@ function convert_clab_topology_data_to_cmt(c){
cmt_node["id"] = i;
cmt_node["name"] = node;
cmt_node["icon"] = "router";

if (n.hasOwnProperty("labels")) {
if (n.labels.hasOwnProperty("graph-hide") && equals_true(n.labels["graph-hide"])) {
continue; // do not visualize this node
Expand All @@ -142,10 +112,11 @@ function convert_clab_topology_data_to_cmt(c){
cmt_node["layerSortPreference"] = n.labels["graph-level"];
}
}

cmt_node["model"] = n.kind;
cmt_node["image"] = n.image;
cmt_node["group"] = n.group;

cmt_node["fullname"] = n.longname;
cmt_node["kind"] = n.kind;
cmt_node["image"] = n.image;
cmt_node["group"] = n.group;

if (n.hasOwnProperty("mgmt-ipv4-address")) {
cmt_node["mgmtIPv4"] = n["mgmt-ipv4-address"];
Expand All @@ -163,8 +134,8 @@ function convert_clab_topology_data_to_cmt(c){
}
}

// This must be the last section, any other cmt_node properties shoud be set above
if (n.labels.hasOwnProperty("graph-mode") && n.labels["graph-mode"] == "port") {
// This must be the last section, any other cmt_node properties should be set above
if (n.hasOwnProperty("labels") && n.labels.hasOwnProperty("graph-mode") && n.labels["graph-mode"] == "port") {
// display each port of this node as it's own individual node
for (var l of c.links) {
// TODO handle when the same interface is encountered more than once
Expand All @@ -189,7 +160,7 @@ function convert_clab_topology_data_to_cmt(c){
cmt.nodes.push(cmt_node);
}
}

for (var i =0; i < c.links.length; i++) {
var l = c.links[i];
var src_i = node_id_map[l["a"]["node"]];
Expand All @@ -198,6 +169,9 @@ function convert_clab_topology_data_to_cmt(c){
var tgt_d_name = l["z"]["node"];
var src_i_name = l["a"]["interface"];
var tgt_i_name = l["z"]["interface"];
var src_i_mac = l["a"]["mac"];
var tgt_i_mac = l["z"]["mac"];
// for port mode display, do not show interface name as a label
if (node_id_map.hasOwnProperty(port_mode_node_name(l["a"]["node"], l["a"]["interface"]))) {
src_i = node_id_map[port_mode_node_name(l["a"]["node"], l["a"]["interface"])];
src_i_name = "";
Expand All @@ -213,8 +187,10 @@ function convert_clab_topology_data_to_cmt(c){
"source": src_i,
"target": tgt_i,
"srcIfName": src_i_name,
"srcIfMAC": src_i_mac,
"srcDevice": src_d_name,
"tgtIfName": tgt_i_name,
"tgtIfMAC": tgt_i_mac,
"tgtDevice": tgt_d_name,
})
}
Expand Down Expand Up @@ -258,7 +234,7 @@ function convert_clab_graph_to_cmt(c){
"name": n.name,
"websshDeviceLink": websshDeviceLink,
"websshDeviceLinkIPv6": websshDeviceLinkIPv6,
"model": n.kind,
"kind": n.kind,
"image": n.image,
"group": n.group,
"mgmtIPv4": mgmtIPv4,
Expand Down
Loading