Browse Source

upgrade

master
Mael G. 6 months ago
parent
commit
b069fdba8f
  1. 62
      README.md
  2. 62
      config.py.example
  3. 90
      diagram.py
  4. 18
      graphiclg.service
  5. 13
      requirements.txt
  6. 6
      static/css/bootstrap.min.css
  7. 209
      templates/index.html
  8. 287
      web.py
  9. 6
      wsgi.py

62
README.md

@ -1,32 +1,32 @@
# Graphic Looking Glass
GLG is a Python web looking glass for routers using FRRouting.
## Requirements
- Linux server
- Python 3.7+ with pip3 & python3-venv
- git
- graphviz
- Internet connectivity
- SSH client
## Production
```
git clone https://git.gnous.eu/mael/GraphicLG /opt/GraphicLG #Clone the repository
cd /opt/GraphicLG
python3 -m venv graphiclgenv #Create venv
source graphiclgenv/bin/activate #Enter the venv
pip3 install -r requirements.txt #Install requirements
mv config.py.example config.py #Copy config and edit it !
deactivate #Exit venv
sudo mv /opt/GraphicLG/graphiclg.service /etc/systemd/system/graphiclg.service
sudo useradd -r graphiclg #Create the graphiclg system account
sudo chown -R graphiclg:graphiclg /opt/GraphicLG/
sudo systemctl start graphiclg #Start GraphicLG service
sudo systemctl enable graphiclg #Enable GraphicLG on system startup
```
## Todo
- Better Readme / Production guide
- Other router support
# Graphic Looking Glass
GLG is a Python web looking glass for routers using FRRouting.
## Requirements
- Linux server
- Python 3.7+ with pip3 & python3-venv
- git
- graphviz
- Internet connectivity
- SSH client
## Production
```
git clone https://git.gnous.eu/mael/GraphicLG /opt/GraphicLG #Clone the repository
cd /opt/GraphicLG
python3 -m venv graphiclgenv #Create venv
source graphiclgenv/bin/activate #Enter the venv
pip3 install -r requirements.txt #Install requirements
mv config.py.example config.py #Copy config and edit it !
deactivate #Exit venv
sudo mv /opt/GraphicLG/graphiclg.service /etc/systemd/system/graphiclg.service
sudo useradd -r graphiclg #Create the graphiclg system account
sudo chown -R graphiclg:graphiclg /opt/GraphicLG/
sudo systemctl start graphiclg #Start GraphicLG service
sudo systemctl enable graphiclg #Enable GraphicLG on system startup
```
## Todo
- Better Readme / Production guide
- Other router support
- Theming

62
config.py.example

@ -1,32 +1,32 @@
# GraphicLG Configuration file
listen = "0.0.0.0" #Listen IP (0.0.0.0 for all)
lg_asn = "213253" #Your ASN as an STR
lg_name = "EnPLS Network" #Your ASN/Network name as an STR
lg_v6only = True
routers = {
"0": {
"id": "0",
"name": "Binouze",
"ip": "binouze.infra.org",
"auth-type": "ssh-key", #Can be ssh-plaintext or ssh-key
"username": "mael",
"key": "/home/.ssh/id_ed25519",
"port": "221"
},
"1": {
"id": "1",
"name": "Erable",
"ip": "erable.infra.org",
"auth-type": "ssh-key", #Can be ssh-plaintext or ssh-key
"username": "mael",
"key": "/home/.ssh/id_ed25519",
"port": "221"
}
}
# GraphicLG Configuration file
listen = "0.0.0.0" #Listen IP (0.0.0.0 for all)
lg_asn = "213253" #Your ASN as an STR
lg_name = "EnPLS Network" #Your ASN/Network name as an STR
lg_v6only = True
routers = {
"0": {
"id": "0",
"name": "Binouze",
"ip": "binouze.infra.org",
"auth-type": "ssh-key", #Can be ssh-plaintext or ssh-key
"username": "mael",
"key": "/home/.ssh/id_ed25519",
"port": "221"
},
"1": {
"id": "1",
"name": "Erable",
"ip": "erable.infra.org",
"auth-type": "ssh-key", #Can be ssh-plaintext or ssh-key
"username": "mael",
"key": "/home/.ssh/id_ed25519",
"port": "221"
}
}
default_router = "0"

90
diagram.py

@ -1,45 +1,45 @@
# Explicitly provide key and passphrase
from paramiko import SSHClient, AutoAddPolicy
import time, re, os, os.path
from graphviz import Digraph
#Diagram paths generation
def gen_diagram(as_list, lg_asn, fname, self_graph=False):
img_path = f"static/img/{fname}"
g = Digraph('G', filename= img_path, format='svg', graph_attr={'rankdir':'LR', 'concentrate': 'true'})
as_path_count = 0
if(self_graph):
g.edge("AS" + lg_asn, "AS" + lg_asn + " ")
g.render()
return True
for as_path in as_list:
as_path = as_path.split(" ")
as_path.reverse()
original_asn = as_path[0]
border_asn = as_path[-1]
precedent_asn = original_asn
for asn in as_path:
if asn != original_asn:
g.edge("AS" + asn, "AS" + precedent_asn)
precedent_asn = asn
if asn == border_asn:
g.edge("AS" + lg_asn, "AS" + asn)
precedent_asn = asn
as_path_count += 1
#If empty as_path
if as_path_count == 0:
return False
g.render()
#Delete GraphViz file
try:
os.remove(img_path)
except Exception as e:
print(f"Can't remove Graphviz file. Ignoring...\n{e}")
return True
# Explicitly provide key and passphrase
from paramiko import SSHClient, AutoAddPolicy
import time, re, os, os.path
from graphviz import Digraph
#Diagram paths generation
def gen_diagram(as_list, lg_asn, fname, self_graph=False):
img_path = f"static/img/{fname}"
g = Digraph('G', filename= img_path, format='svg', graph_attr={'rankdir':'LR', 'concentrate': 'true'})
as_path_count = 0
if(self_graph):
g.edge("AS" + lg_asn, "AS" + lg_asn + " ")
g.render()
return True
for as_path in as_list:
as_path = as_path.split(" ")
as_path.reverse()
original_asn = as_path[0]
border_asn = as_path[-1]
precedent_asn = original_asn
for asn in as_path:
if asn != original_asn:
g.edge("AS" + asn, "AS" + precedent_asn)
precedent_asn = asn
if asn == border_asn:
g.edge("AS" + lg_asn, "AS" + asn)
precedent_asn = asn
as_path_count += 1
#If empty as_path
if as_path_count == 0:
return False
g.render()
#Delete GraphViz file
try:
os.remove(img_path)
except Exception as e:
print(f"Can't remove Graphviz file. Ignoring...\n{e}")
return True

18
graphiclg.service

@ -1,10 +1,10 @@
[Unit]
Description=Gunicorn instance to serve GraphicLG
After=network.target
[Service]
User=graphiclg
Group=www-data
WorkingDirectory=/opt/GraphicLG
Environment="PATH=/opt/GraphicLG/graphiclgenv/bin"
[Unit]
Description=Gunicorn instance to serve GraphicLG
After=network.target
[Service]
User=graphiclg
Group=www-data
WorkingDirectory=/opt/GraphicLG
Environment="PATH=/opt/GraphicLG/graphiclgenv/bin"
ExecStart=/opt/GraphicLG/graphiclgenv/bin/gunicorn --workers 3 --bind unix:graphiclg.sock -m 007 wsgi:app

13
requirements.txt

@ -1,6 +1,7 @@
Flask==1.1.2
flask_limiter==1.4
slugify
paramiko
graphviz
gunicorn
Flask==1.1.2
flask_limiter==1.4
slugify
paramiko
graphviz
gunicorn
wsgi

6
static/css/bootstrap.min.css
File diff suppressed because it is too large
View File

209
templates/index.html

@ -1,106 +1,105 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css', _scheme='http', _external=True) }}"/>
{%if not actiondata['ip_addr']%}
<!-- Primary Meta Tags -->
<title>AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass</title>
<meta name="title" content="AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass">
<meta name="description" content="The {{ config.lg_name }} Looking Glass allows everyone to perform BGP, PING and TRACEROUTE on his routers.">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:title" content="AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass">
<meta property="og:description" content="The {{ config.lg_name }} Looking Glass allows everyone to perform BGP, PING and TRACEROUTE on his routers.">
<!-- Twitter -->
<meta property="twitter:title" content="AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass">
<meta property="twitter:description" content="The {{ config.lg_name }} Looking Glass allows everyone to perform BGP, PING and TRACEROUTE on his routers.">
{%else%}
<!-- Primary Meta Tags -->
<title>AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass</title>
<meta name="title" content="AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass">
<meta name="description" content="Informations about {{ actiondata['ip_addr'] }} on AS{{ config.lg_asn }} / {{ config.lg_name }}.">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:title" content="AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass">
<meta property="og:description" content="Informations about {{ actiondata['ip_addr'] }} on AS{{ config.lg_asn }} / {{ config.lg_name }}">
<!-- Twitter -->
<meta property="twitter:title" content="AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass">
<meta property="twitter:description" content="Informations about {{ actiondata['ip_addr'] }} on AS{{ config.lg_asn }} / {{ config.lg_name }}">
{%endif%}
</head>
<body>
<nav class="navbar navbar-light bg-light">
<span class="navbar-brand mb-0 h1">AS{{ config.lg_asn }} LG</span>
</nav>
<div class="container-fluid mt-5">
<div class="card">
<h5 class="card-header">Looking Glass Tool</h5>
<div class="card-body">
<p class="card-text">The {{ config.lg_name }} Looking Glass allows everyone to perform BGP, PING and TRACEROUTE on his routers.</p>
<hr>
<form method="POST" action="./show">
<div class="form-row">
<div class="col">
<label for="action_type">Action</label>
<select class="form-control" id="action_type" required name="action_type">
<option value="1" {%if actiondata['action_type'] == "1" %}selected{%endif%}>show bgp</option>
<option value="2" {%if actiondata['action_type'] == "2" %}selected{%endif%}>ping</option>
<option value="3" {%if actiondata['action_type'] == "3" %}selected{%endif%}>traceroute</option>
</select>
</div>
<div class="col">
<label for="ip_addr">IP Address or prefix</label>
{%if config.lg_v6only %}
<input type="text" class="form-control" id="ip_addr" name="ip_addr" required placeholder="2001:DB8:CAFE::/64" value="{{ actiondata['ip_addr'] }}">
{%else%}
<input type="text" class="form-control" id="ip_addr" name="ip_addr" required placeholder="172.16.31.139/24 or 2001:DB8:cafe::/64" value="{{ actiondata['ip_addr'] }}">
{%endif%}
</div>
<div class="col">
<label for="router_id">Router</label>
<select class="form-control" name="router_id" required id="router_id">
{%for router in config.routers.items() %}
<option value="{{ router[1]['id'] }}" {%if actiondata['router_id'] == router[1]['id']%}selected{%endif%}>{{ router[1]["name"] }}</option>
{%endfor%}
</select>
</div>
</div>
<input class="btn btn-primary mt-1" type="submit" value="Perform">
</form>
</div>
</div>
</div>
{%if actiondata['ip_addr']%}
<div class="container-fluid mt-5">
<div class="card">
<h5 class="card-header">Looking Glass Result</h5>
<div class="card-body">
{%if graph%}
{%if graph['success']%}
<h5 class="card-title">Route to <code>{{ actiondata['ip_addr'] }}</code></h5>
<object type="image/svg+xml" data="/static/img/{{ graph['filename'] }}" class="img-fluid">
</object>
{%else%}
<h5 class="card-title">No route to <code>{{ actiondata['ip_addr'] }}</code></h5>
{%endif%}
{%endif%}
{%if cmd_res%}
<pre>{{cmd_res}}</pre>
{%endif%}
</div>
</div>
</div>
{% endif %}
</body>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/static/css/bootstrap.min.css"/>
{%if not actiondata['ip_addr']%}
<!-- Primary Meta Tags -->
<title>AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass</title>
<meta name="title" content="AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass">
<meta name="description" content="The {{ config.lg_name }} Looking Glass allows everyone to perform BGP, PING and TRACEROUTE on his routers.">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:title" content="AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass">
<meta property="og:description" content="The {{ config.lg_name }} Looking Glass allows everyone to perform BGP, PING and TRACEROUTE on his routers.">
<!-- Twitter -->
<meta property="twitter:title" content="AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass">
<meta property="twitter:description" content="The {{ config.lg_name }} Looking Glass allows everyone to perform BGP, PING and TRACEROUTE on his routers.">
{%else%}
<!-- Primary Meta Tags -->
<title>AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass</title>
<meta name="title" content="AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass">
<meta name="description" content="Informations about {{ actiondata['ip_addr'] }} on AS{{ config.lg_asn }} / {{ config.lg_name }}.">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:title" content="AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass">
<meta property="og:description" content="Informations about {{ actiondata['ip_addr'] }} on AS{{ config.lg_asn }} / {{ config.lg_name }}">
<!-- Twitter -->
<meta property="twitter:title" content="AS{{ config.lg_asn }} / {{ config.lg_name }} - Looking Glass">
<meta property="twitter:description" content="Informations about {{ actiondata['ip_addr'] }} on AS{{ config.lg_asn }} / {{ config.lg_name }}">
{%endif%}
</head>
<body>
<nav class="navbar navbar-light bg-light">
<span class="navbar-brand mb-0 h1"><a href="/">AS{{ config.lg_asn }} LG</a></span>
</nav>
<div class="container-fluid mt-5">
<div class="card">
<h5 class="card-header">Looking Glass Tool</h5>
<div class="card-body">
<p class="card-text">The {{ config.lg_name }} Looking Glass allows everyone to perform BGP, PING and TRACEROUTE on his routers.</p>
<hr>
<form method="POST" action="./show">
<div class="form-row">
<div class="col-md">
<label for="action_type">Action</label>
<select class="form-control" id="action_type" required name="action_type">
<option value="1" {%if actiondata['action_type'] == "1" %}selected{%endif%}>show bgp</option>
<option value="2" {%if actiondata['action_type'] == "2" %}selected{%endif%}>ping</option>
<option value="3" {%if actiondata['action_type'] == "3" %}selected{%endif%}>traceroute</option>
</select>
</div>
<div class="col-sm">
<label for="ip_addr">IP Address or prefix</label>
{%if config.lg_v6only %}
<input type="text" class="form-control" id="ip_addr" name="ip_addr" required placeholder="2001:DB8:CAFE::/64" value="{{ actiondata['ip_addr'] }}">
{%else%}
<input type="text" class="form-control" id="ip_addr" name="ip_addr" required placeholder="172.16.31.139/24 or 2001:DB8:cafe::/64" value="{{ actiondata['ip_addr'] }}">
{%endif%}
</div>
<div class="col-sm">
<label for="router_id">Router</label>
<select class="form-control" name="router_id" required id="router_id">
{%for router in config.routers.items() %}
<option value="{{ router[1]['id'] }}" {%if actiondata['router_id'] == router[1]['id']%}selected{%endif%}>{{ router[1]["name"] }}</option>
{%endfor%}
</select>
</div>
</div>
<input class="btn btn-primary mt-3" type="submit" value="Perform">
</form>
</div>
</div>
</div>
{%if actiondata['ip_addr']%}
<div class="container-fluid mt-5">
<div class="card">
<h5 class="card-header">Looking Glass Result</h5>
<div class="card-body">
{%if graph%}
{%if graph['success']%}
<h5 class="card-title">Route to <code>{{ actiondata['ip_addr'] }}</code></h5>
<object type="image/svg+xml" data="/static/img/{{ graph['filename'] }}" class="img-fluid">
</object>
{%else%}
<h5 class="card-title">No route to <code>{{ actiondata['ip_addr'] }}</code></h5>
{%endif%}
{%endif%}
{%if cmd_res%}
<pre>{{cmd_res}}</pre>
{%endif%}
</div>
</div>
</div>
{% endif %}
</body>
</html>

287
web.py

@ -1,139 +1,150 @@
from flask import Flask, render_template, request
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from paramiko import SSHClient, AutoAddPolicy
from graphviz import Digraph
from slugify import slugify
import time, re, diagram, base64, sys
#Try to load config
try:
import config
except Exception as e:
sys.exit(f"Can't load config dot py. Exiting GraphicLG...\n{e}")
app = Flask(__name__)
#limit req to prevent f*cking spam
limiter = Limiter(
app,
key_func=get_remote_address
)
#Home page
@app.route('/', methods=['GET'])
def home():
return render_template('index.html', config = config, routers = config.routers.items(), actiondata={}, graph={}, cmd_res="")
@app.route('/<path:ip>', methods=['GET'])
def my_view_func(ip):
return show(short_url = True, ipaddr=ip)
@app.route('/show', methods=['POST'])
@limiter.limit("5 per minute")
def show(short_url = False, ipaddr = ""):
#Get data from form or URL
if(not short_url):
actiondata = {
"router_id": request.form.get('router_id'),
"ip_addr": request.form.get('ip_addr'),
"action_type": request.form.get('action_type'),
}
else:
actiondata = {
"router_id": config.default_router,
"ip_addr": ipaddr,
"action_type": '1',
}
#Check for valid IPv4 / IPv6 with or without CIDR
#Check for valid v6
if(not re.match('^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(\/((1(1[0-9]|2[0-8]))|([0-9][0-9])|([0-9])))?$', actiondata['ip_addr'])):
if(not config.lg_v6only): #if not v6 check for v4 as well
if(not re.match('^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(/(3[0-2]|2[0-9]|1[0-9]|[0-9]))?$', actiondata['ip_addr'])):
error_msg = "Please enter a valid IPv4 or IPv6 with or without his CIDR."
return render_template('index.html', config = config, actiondata=actiondata, graph={}, cmd_res=error_msg)
else:
error_msg = "Please enter a valid IPv6 with or without his CIDR."
return render_template('index.html', config = config, actiondata=actiondata, graph={}, cmd_res=error_msg)
#Init ssh client
client = SSHClient()
client.set_missing_host_key_policy(AutoAddPolicy())
#Get router from form data
rtr = config.routers[f"{actiondata['router_id']}"]
#Router connexion based on auth type
try:
if(rtr["auth-type"] == "ssh-key"):
client.connect(rtr["ip"], username=rtr["username"], key_filename=rtr["key"], port=rtr["port"])
else:
client.connect(rtr["ip"], username=rtr["username"], password=rtr["password"], port=rtr["port"])
except Exception as e:
error_msg = f"Can't connect to the router. {e}. Please contact the LG administrator"
return render_template('index.html', config = config, actiondata=actiondata, graph={}, cmd_res=error_msg)
#Show bgp IP_ADDR
if(actiondata['action_type'] == "1"):
#Launching CMD
stdin, stdout, stderr = client.exec_command(f'vtysh -c "sh bgp ipv6 {actiondata["ip_addr"]}"')
time.sleep(0.1)
conresult = stdout.read().decode("utf8")
#Parsing as_list (FRRouting / Cisco IOS) via a REGEX
as_list = re.findall(r" ([0-9][0-9 ]+)[\n]", conresult)
as_list1 = re.findall(r" ([0-9][0-9 ]+),", conresult)
as_list = as_list + as_list1
#Closing session
stdin.close()
stdout.close()
stderr.close()
client.close()
#Graph gen
filename = slugify(actiondata['ip_addr'])
result = diagram.gen_diagram(as_list, config.lg_asn, filename)
graph_details = {
"filename": filename + f".svg?{time.time()}",
"success": result
}
if(not graph_details['success']):
if(re.findall(r" Local", conresult)): #If local IPs
result = diagram.gen_diagram([], config.lg_asn, filename, True)
graph_details["success"] = result
return render_template('index.html', config = config, actiondata=actiondata, graph=graph_details, cmd_res=conresult)
#Ping
if(actiondata['action_type'] == "2"):
#Launching CMD
stdin, stdout, stderr = client.exec_command(f'ping -c3 {actiondata["ip_addr"]}')
#Traceroute
if(actiondata['action_type'] == "3"):
#Launching CMD
stdin, stdout, stderr = client.exec_command(f'traceroute {actiondata["ip_addr"]}')
time.sleep(0.1)
conresult = stdout.read().decode("utf8")
errResult = stderr.read().decode("utf8")
if(errResult != ""):
conresult = "Error : " + errResult
#Closing session
stdin.close()
stdout.close()
stderr.close()
client.close()
return render_template('index.html', config = config, actiondata=actiondata, graph={}, cmd_res=conresult)
if __name__ == '__main__':
from flask import Flask, render_template, request
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from paramiko import SSHClient, AutoAddPolicy
from graphviz import Digraph
from slugify import slugify
import time, re, diagram, base64, sys
#Try to load config
try:
import config
except Exception as e:
sys.exit(f"Can't load config dot py. Exiting GraphicLG...\n{e}")
app = Flask(__name__)
#limit req to prevent f*cking spam
limiter = Limiter(
app,
key_func=get_remote_address
)
#Home page
@app.route('/', methods=['GET'])
def home():
return render_template('index.html', config = config, routers = config.routers.items(), actiondata={}, graph={}, cmd_res="")
@app.route('/<path:ip>', methods=['GET'])
def my_view_func(ip):
return show(short_url = True, ipaddr=ip)
@app.route('/show', methods=['POST'])
@limiter.limit("5 per minute")
def show(short_url = False, ipaddr = ""):
#Get data from form or URL
if(not short_url):
actiondata = {
"router_id": request.form.get('router_id'),
"ip_addr": request.form.get('ip_addr'),
"action_type": request.form.get('action_type'),
}
else:
actiondata = {
"router_id": config.default_router,
"ip_addr": ipaddr,
"action_type": '1',
}
#Check for valid IPv4 / IPv6 with or without CIDR
#Check for valid v6
if(not re.match('^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(\/((1(1[0-9]|2[0-8]))|([0-9][0-9])|([0-9])))?$', actiondata['ip_addr'])):
if(not config.lg_v6only): #if not v6 check for v4 as well
if(not re.match('^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(/(3[0-2]|2[0-9]|1[0-9]|[0-9]))?$', actiondata['ip_addr'])):
error_msg = "Please enter a valid IPv4 or IPv6 with or without his CIDR."
return render_template('index.html', config = config, actiondata=actiondata, graph={}, cmd_res=error_msg)
else:
iptype = 2 # 1 = ipv6 / 2 = ipv4
else:
error_msg = "Please enter a valid IPv6 with or without his CIDR."
return render_template('index.html', config = config, actiondata=actiondata, graph={}, cmd_res=error_msg)
else:
iptype = 1 # 1 = ipv6 / 2 = ipv4
#Init ssh client
client = SSHClient()
client.set_missing_host_key_policy(AutoAddPolicy())
#Get router from form data
rtr = config.routers[f"{actiondata['router_id']}"]
#Router connexion based on auth type
try:
if(rtr["auth-type"] == "ssh-key"):
client.connect(rtr["ip"], username=rtr["username"], key_filename=rtr["key"], port=rtr["port"])
else:
client.connect(rtr["ip"], username=rtr["username"], password=rtr["password"], port=rtr["port"])
except Exception as e:
error_msg = f"Can't connect to the router. {e}. Please contact the LG administrator"
return render_template('index.html', config = config, actiondata=actiondata, graph={}, cmd_res=error_msg)
#Show bgp IP_ADDR
if(actiondata['action_type'] == "1"):
if(rtr["bgpcapable"]):
#Launching CMD
if(iptype == 2): #IPv4 cli command
stdin, stdout, stderr = client.exec_command(f'vtysh -c "sh bgp ipv6 {actiondata["ip_addr"]}"')
else: #IPv6 cli command
stdin, stdout, stderr = client.exec_command(f'vtysh -c "sh bgp {actiondata["ip_addr"]}"')
time.sleep(0.1)
conresult = stdout.read().decode("utf8")
#Parsing as_list (FRRouting) via a REGEX
as_list = re.findall(r" ([0-9][0-9 ]+)[\n]", conresult)
as_list1 = re.findall(r" ([0-9][0-9 ]+),", conresult)
as_list = as_list + as_list1
#Closing session
stdin.close()
stdout.close()
stderr.close()
client.close()
#Graph gen
filename = slugify(actiondata['ip_addr'])
result = diagram.gen_diagram(as_list, config.lg_asn, filename)
graph_details = {
"filename": filename + f".svg?{time.time()}",
"success": result
}
if(not graph_details['success']):
if(re.findall(r" Local", conresult)): #If local IPs
result = diagram.gen_diagram([], config.lg_asn, filename, True)
graph_details["success"] = result
return render_template('index.html', config = config, actiondata=actiondata, graph=graph_details, cmd_res=conresult)
else:
#If the router isn't capable of bgp
return render_template('index.html', config = config, actiondata=actiondata, graph={}, cmd_res="'show bgp' can't be executed since this router don't do BGP")
#Ping
if(actiondata['action_type'] == "2"):
#Launching CMD
stdin, stdout, stderr = client.exec_command(f'ping -c3 {actiondata["ip_addr"]}')
#Traceroute
if(actiondata['action_type'] == "3"):
#Launching CMD
stdin, stdout, stderr = client.exec_command(f'traceroute {actiondata["ip_addr"]}')
time.sleep(0.1)
conresult = stdout.read().decode("utf8")
errResult = stderr.read().decode("utf8")
if(errResult != ""):
conresult = "Error : " + errResult
#Closing session
stdin.close()
stdout.close()
stderr.close()
client.close()
return render_template('index.html', config = config, actiondata=actiondata, graph={}, cmd_res=conresult)
if __name__ == '__main__':
app.run(host='0.0.0.0')

6
wsgi.py

@ -1,4 +1,4 @@
from web import app
if __name__ == "__main__":
from web import app
if __name__ == "__main__":
app.run()
Loading…
Cancel
Save