Compare commits

..

2 commits

Author SHA1 Message Date
a534568de5 [web] implement communication between web+node(s) 2022-12-21 22:52:56 +01:00
99809e1139 [node] implement everything... 2022-12-21 21:21:57 +01:00
9 changed files with 551 additions and 21 deletions

23
.vscode/launch.json vendored
View file

@ -1,12 +1,33 @@
{
"configurations": [
{
"name": "Python: Flask-node",
"type": "python",
"request": "launch",
"module": "flask",
"cwd": "${workspaceFolder}/nodes/",
"env": {
"FLASK_APP": "main.py",
"FLASK_DEBUG": "1",
"FLASK_RUN_HOST": "::",
"FLASK_RUN_PORT": "8142"
},
"args": ["run", "--no-debugger"],
"jinja": true,
"justMyCode": true
},
{
"name": "Python: Flask-backend",
"type": "python",
"request": "launch",
"module": "flask",
"cwd": "${workspaceFolder}/web/",
"env": { "FLASK_APP": "backend/main.py", "FLASK_DEBUG": "1", "FLASK_RUN_HOST": "0.0.0.0", "FLASK_RUN_PORT": "8042" },
"env": {
"FLASK_APP": "backend/main.py",
"FLASK_DEBUG": "1",
"FLASK_RUN_HOST": "0.0.0.0",
"FLASK_RUN_PORT": "8042"
},
"args": ["run", "--no-debugger"],
"jinja": true,
"justMyCode": true

3
nodes/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
venv
peerings.json
config.json

405
nodes/main.py Normal file
View file

@ -0,0 +1,405 @@
from flask import Flask, request, session, Response, render_template
from flask_restful import Resource, Api, reqparse
#import pandas as pd
#import ast
import json
import os
import base64
import logging
import random
import time
from functools import wraps
import subprocess
app = Flask(__name__)
api = Api(app)
# used as default value for create/update peering
class NotSpecified:
pass
class Config (dict):
def __init__(self, configfile: str = None):
if configfile:
self.configfile = configfile
else:
if os.path.exists("./node.config.json"):
self.configfile = "./node.config.json"
elif os.path.exists("./config.json"):
self.configfile = "./config.json"
elif os.path.exists("/etc/dn42-autopeer/node.config.json"):
self.configfile = "/etc/dn42-autopeer/node.config.json"
else:
raise FileNotFoundError(
"no config file found in ./node.config.json, ./config.json or /etc/dn42-autopeer/config.json")
self._load_config()
self.keys = self._config.keys
#self.__getitem__ = self._config.__getitem__
super().__init__(self)
def __contains__(self, o):
return self._config.__contains__(o)
def __delitem__(self, v):
raise NotImplementedError()
super().__delitem__(self, v)
def __getitem__(self, k):
return self._config[k]
def _load_config(self):
with open(self.configfile) as cf:
try:
self._config = json.load(cf)
except json.decoder.JSONDecodeError:
raise SyntaxError(f"no valid JSON found in '{cf.name}'")
print(self._config)
if not "debug-mode" in self._config:
self._config["debug-mode"] = False
if not "peerings" in self._config:
self._config["peerings"] = "./peerings"
if not "wg-configs" in self._config:
self._config["wg-configs"] = "/etc/wireguard/"
if not os.path.exists(self._config["wg-configs"]):
raise FileNotFoundError(f"specified wg-configs '{self._config['wg-configs']}' isn't a directory/doesn't exist")
if not "wg-commands" in self._config:
raise KeyError("wg-commands not specified")
# check if required keys is a subset of the specified keys
if not set(["enable", "disable","up","down"]) <= set(self._config["wg-commands"].keys()):
raise KeyError("at least one of 'enable','disable','up','down' in 'wg-commands' not specified")
if not "bird-peers" in self._config:
self._config["bird-peers"] = "/etc/bird/peers/"
if not os.path.exists(self._config["bird-peers"]):
raise FileNotFoundError(f"specified bird-peers '{self._config['bird-peers']}' isn't a directory/doesn't exist")
if not "bird-reload" in self._config:
self._config["bird-reload"] = "birdc configure"
if not "templates" in self._config:
self._config["templates"] = "./templates"
logging.info(self._config)
class PeeringManager:
def __init__(self, config):
self.__config = config
self.__peering_file = config["peerings"]
self.__load_peerings()
def __load_peerings(self):
if not os.path.exists(self.__peering_file):
with open(self.__peering_file, "x") as p:
json.dump({}, p)
try:
with open(self.__peering_file, "r") as p:
self.peerings = json.load(p)
except json.decoder.JSONDecodeError:
with open(self.__peering_file, "w") as p:
json.dump({}, p)
with open(self.__peering_file, "r") as p:
self.peerings = json.load(p)
# self.peerings = {}
# missing_peerings = False
# for peering in self._peerings:
# if os.path.exists(f"{self._peering_dir}/{peering}.json"):
# with open(f"{self._peering_dir}/{peering}.json") as peer_cfg:
# self.peerings[peering] = json.load(peer_cfg)
# else:
# logging.warning(f"peering with id {peering} doesn't exist. removing reference in `{self._peering_dir}/peerings.json`")
# self._peerings.remove(peering)
# missing_peerings = True
# if missing_peerings:
# with open(f"{self._peering_dir}/peerings.json","w") as p:
# json.dump(self._peerings, p, indent=4)
def __save_peerings(self):
with open(self.__peering_file, "w") as p:
json.dump(self.peerings, p, indent=4)
self._amounts = None
def __generate_wg_conf(self, peering: dict):
return render_template("wireguard.template.conf", peering=peering)
def __generate_bird_conf(self, peering:dict):
return render_template("bgp-peer.template.conf", peering=peering)
def __install_peering(self, mode: str, peering: dict):
if mode == "add":
wg_conf = self.__generate_wg_conf(peering)
bgp_conf = self.__generate_bird_conf(peering)
with open(f"{self.__config['wg-configs']}/dn42_{peering['MNT'][:-4].lower()}.conf", "w") as wg_file:
wg_file.write(wg_conf)
wg_enable = subprocess.run(self.__config["wg-commands"]["enable"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(wg_enable)
wg_up = subprocess.run(self.__config["wg-commands"]["up"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(wg_up)
time.sleep(5)
with open(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}.conf", "w") as bgp_file:
bgp_file.write(bgp_conf)
bgp_reload = subprocess.run(self.__config["bird-reload"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(bgp_reload)
return 200
elif mode == "update":
wg_conf = self.__generate_wg_conf(peering)
bgp_conf = self.__generate_bird_conf(peering)
with open(f"{self.__config['wg-configs']}/dn42_{peering['MNT'][:-4].lower()}.conf", "w") as wg_file:
wg_file.write(wg_conf)
wg_down = subprocess.run(self.__config["wg-commands"]["down"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(wg_down)
wg_up = subprocess.run(self.__config["wg-commands"]["up"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(wg_up)
time.sleep(5)
with open(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}.conf", "w") as bgp_file:
bgp_file.write(bgp_conf)
bgp_reload = subprocess.run(self.__config["bird-reload"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(bgp_reload)
return 200
elif mode == "delete":
os.remove(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}.conf")
bgp_reload = subprocess.run(self.__config["bird-reload"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(bgp_reload)
time.sleep(5)
wg_down = subprocess.run(self.__config["wg-commands"]["down"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(wg_down)
wg_disable = subprocess.run(self.__config["wg-commands"]["disable"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(wg_disable)
return 200
return 405
def get_peerings_by_asn(self, asn):
if asn in self.peerings:
return self.peerings[asn]
else:
return []
def exists(self, asn, MNT=NotSpecified, wg_key=NotSpecified, endpoint=NotSpecified, ipv6ll=NotSpecified, ipv4=NotSpecified, ipv6=NotSpecified, bgp_mp=NotSpecified, bgp_enh=NotSpecified):
"""checks if a peerings with specific data already exists"""
# check if mnt is specified, already exists in the database and if that mnt has the specified ASn -> if not: return False
if not asn in self.peerings:
return False
selected_peerings = self.peerings[asn]
# check if the ASn even has peerings
if len(selected_peerings) == 0:
return False
for p in selected_peerings:
if (not wg_key or p["wg_key"] == wg_key):
return True
return False
def add_peering(self, MNT, ASN, wg_key, node=NotSpecified, endpoint=NotSpecified, ipv6ll=NotSpecified, ipv4=NotSpecified, ipv6=NotSpecified, bgp_mp=NotSpecified, bgp_enh=NotSpecified):
asn = ASN
try:
if not asn in self.peerings:
self.peerings[asn] = []
except KeyError:
self.peerings[asn] = []
for p in self.peerings[asn]:
if p["wg_key"] == wg_key:
return False, 409
new_peering = {"MNT": MNT, "ASN": asn, "node": config["nodename"], "wg_key": wg_key, "endpoint": endpoint if endpoint != NotSpecified else None,
"ipv6ll": ipv6ll if ipv6ll != NotSpecified else None, "ipv4": ipv4 if ipv4 != NotSpecified else None, "ipv6": ipv6 if ipv6 != NotSpecified else None,
"bgp_mp": bgp_mp if bgp_mp != NotSpecified else True, "bgp_enh": bgp_enh if bgp_enh != NotSpecified else True}
self.peerings[asn].append(new_peering)
self.__save_peerings()
ret_code = self.__install_peering(mode="add", peering=new_peering)
if ret_code == 200:
return True, 201
else:
return False, ret_code
def update_peering(self, ASN, wg_key, MNT=NotSpecified, node=NotSpecified, endpoint=NotSpecified, ipv6ll=NotSpecified, ipv4=NotSpecified, ipv6=NotSpecified, bgp_mp=NotSpecified, bgp_enh=NotSpecified):
asn = ASN
try:
if not asn in self.peerings:
return False, 404
except KeyError:
return False, 404
success = False
for pNr in range(len(self.peerings[asn])):
if self.peerings[asn][pNr]["node"] == node:
old_peering = self.peerings[asn][pNr]
new_peering = self.peerings[asn][pNr] = {"MNT": MNT if MNT!=NotSpecified else old_peering["MNT"], "ASN": asn, "node": config["nodename"], "wg_key": wg_key,
"endpoint": endpoint if endpoint!=NotSpecified else old_peering["endpoint"], "ipv6ll": ipv6ll if ipv6ll != NotSpecified else old_peering["ipv6ll"], "ipv4": ipv4 if ipv4 != NotSpecified else old_peering["ipv4"], "ipv6": ipv6 if ipv6 != NotSpecified else old_peering["ipv6"], "bgp_mp": bgp_mp if bgp_mp != NotSpecified else old_peering["bgp_mp"], "bgp_enh": bgp_enh if bgp_enh != NotSpecified else old_peering["bgp_enh"]}
success = True
if not success:
return False, 404
self.__save_peerings()
ret_code = self.__install_peering(mode="update", peering=new_peering)
if ret_code == 200:
return True, 200
else:
return False, ret_code
def delete_peering(self, ASN, node, wg_key=None):
asn = ASN
if not self.exists(asn=asn, wg_key=wg_key):
return False, 404
for p in self.peerings[asn]:
if p["node"] == node:
if wg_key and p["wg_key"] != wg_key:
continue
self.peerings[asn].remove(p)
print(self.peerings)
self.__save_peerings()
ret_code = self.__install_peering(
mode="delete", peering=p)
if ret_code == 200:
return True, 201
else:
return False, ret_code
# if nothing got found (should have been catched by self.exists)
return False, 404
config = Config()
peerings = PeeringManager(config)
def check_ACL():
def wrapper(f):
@wraps(f)
def decorated(*args, **kwargs):
if request.remote_addr in config["ACL"]:
return f(*args, **kwargs)
else:
return Response(response="Unauthorized", status=401)
return decorated
return wrapper
class PeeringsRoute(Resource):
@check_ACL()
def get(self):
parser = reqparse.RequestParser() # initialize
parser.add_argument('ASN', required=True)
parser.add_argument('node', required=True, default=config["nodename"])
parser.add_argument('wg_key')
args = parser.parse_args() # parse arguments to dictionary
requested_peerings = peerings.get_peerings_by_asn(args["ASN"])
if requested_peerings:
return {"success": True, "asn": args["ASN"], "peerings": requested_peerings}, 200
else:
return {"success": False, "asn": args["ASN"], "error": "not found", "peerings": []}, 404
@check_ACL()
def post(self):
# print(request.get_json())
# {'MNT': 'LARE-MNT', 'ASN': '4242423035', 'node': 'node1',
# 'wg_key': 'uM92Rks/Em2n7QLGek6OUXyGO1P/hEelMQNLlW85J2o=',
# 'endpoint': None, 'ipv6ll': 'fe80::2', 'ipv4': None, 'ipv6': None,
# 'bgp_mp': True, 'bgp_enh': True}
parser = reqparse.RequestParser() # initialize
parser.add_argument('MNT', required=True)
parser.add_argument('ASN', required=True)
parser.add_argument('node', required=True, default=config["nodename"])
parser.add_argument('wg_key', required=True)
parser.add_argument('endpoint')
parser.add_argument('ipv6ll')
parser.add_argument('ipv4')
parser.add_argument('ipv6')
parser.add_argument('bgp_mp')
parser.add_argument('bgp_enh')
args = parser.parse_args() # parse arguments to dictionary
if not peerings.exists(asn=args["ASN"], wg_key=args["wg_key"]):
status, code = peerings.add_peering(**args)
if status:
return {"success": True, "new_peering": args}, 201
else:
pass
else:
code = 409
if code == 409:
error_msg = "already exists"
elif code == "50x":
error_msg = "abc"
return {"success": False, "error": error_msg}, code
@check_ACL()
def put(self):
parser = reqparse.RequestParser() # initialize
parser.add_argument('MNT', required=True)
parser.add_argument('ASN', required=True)
parser.add_argument('node', required=True, default=config["nodename"])
parser.add_argument('wg_key', required=True, nullable=False)
parser.add_argument('endpoint', store_missing=False)
parser.add_argument('ipv6ll', store_missing=False)
parser.add_argument('ipv4', store_missing=False)
parser.add_argument('ipv6', store_missing=False)
parser.add_argument('bgp_mp', store_missing=False)
parser.add_argument('bgp_enh', store_missing=False)
args = parser.parse_args() # parse arguments to dictionary
print(args)
if peerings.exists(asn=args["ASN"], wg_key=args["wg_key"]):
ret = peerings.update_peering(**args)
if ret:
return {"success": True, "new_peering": args}, 200
else:
return {"success": False, "new_peering": args}, 500
else:
return {"success": False, "error": "not found"}, 404
@check_ACL()
def delete(self):
parser = reqparse.RequestParser() # initialize
parser.add_argument('ASN', required=True, nullable=False)
parser.add_argument('node', required=True, default=config["nodename"])
parser.add_argument('wg_key', required=True, nullable=False)
args = parser.parse_args() # parse arguments to dictionary
if peerings.exists(asn=args["ASN"], wg_key=args["wg_key"]):
print(args)
ret, code = peerings.delete_peering(**args)
if ret:
return {"success": True, "deleted": args}
else:
code = 404
if code == 404:
error_msg = "not found"
return {"success": False, "error": error_msg}, code
api.add_resource(PeeringsRoute, '/peerings/')
app.template_folder = config["templates"]
def main():
if "production" in config and config["production"] == False:
logging.getLogger(__name__).setLevel(0)
app.run(host=config["listen"], port=config["port"],
debug=config["debug-mode"], threaded=True)
else:
from waitress import serve
logging.getLogger(__name__).setLevel(logging.INFO)
serve(app, host=config["listen"], port=config["port"])
if __name__ == "__main__":
main()

View file

@ -0,0 +1,21 @@
{
"MNT": "YOUR-MNT", // your MNT tag
"ASN": "424242000", //Your ASN (used to generate default peer ListenPorts)
"listen": "0.0.0.0",
"port": 8142,
"nodename": "node123",
"ACL": ["127.0.0.1"], // address of the server running the webinterface (for ACL); if "listen" is "::"(or other ipv6) and the the server is connecting via ipv4 use"::ffff:<ipv4>"
"peerings": "/path/to/peerings.json", // optional; default "$PWD/peerings.json", file to save existing peerings to
"production": true, //optional, default true;
"debug-mode": false, // optional; whethet to enable debugging; default false
"wg-configs": "/etc/wireguard/", // optional, default: "/etc/wireguard/"; directory where the wireguard configs are located
"wg-commands": { // {MNT} will get replaced with the lowercase mnter without "-MNT"
"enable": "systemctl enable wg-quick@dn42_{MNT}", //command to execute for enabling the wg-interface
"up": "systemctl start wg-quick@dn42_{MNT}", //command to execute for starting the wg-interface
"down": "systemctl stop wg-quick@dn42_{MNT}", //command to execute for stopping the wg-interface
"disable": "systemctl disable wg-quick@dn42_{MNT}" //command to execute for disabling the wg-interface
},
"bird-peers": "/etc/bird/peers/", // optional, default: "/etc/bird/peers/"; directory where bird peers are to be located
"bird-reload": "birdc configure", // optional, default: "birdc configure"; command to reconfigure bird or other bgp daemon
"templates": "templates" //optional, default "$PWD/templates"; directory where "wireguard.template.conf" and "bgo-peer.template.conf" are located
}

View file

@ -0,0 +1,36 @@
{% if peering["bgp_mp"] %}
protocol bgp dn42_{{peering["MNT"][:-4].lower()}} from dnpeers {
neighbor {{peering["ipv6ll"]}} as {{peering["ASN"]}};
interface "dn42_{{peering['MNT'][:-4].lower()}}";
passive off;
ipv4 {
{# TODO: implement pinging peer for bgp-communities
#import where dn42_import_filter(x,y,z);
#export where dn42_export_filter(x,y,z);
#}
extended next hop {% if peering["bgp_enh"] %}on{%else%}off{%endif%};
};
ipv6 {
{# TODO: implement pinging peer for bgp-communities
#import where dn42_import_filter(x,y,z);
#export where dn42_export_filter(x,y,z);
#}
extended next hop {% if peering["bgp_enh"] %}on{%else%}off{%endif%};
};
};
{%else%}
protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_4 from dnpeers {
neighbor {{peering["ipv4"]}} as {{peering["ASN"]}};
passive off;
#import where dn42_import_filter(x,y,z);
#export where dn42_export_filter(x,y,z);
};
protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_6 from dnpeers {
neighbor {{peering["ipv6"]}} as {{peering["ASN"]}};
passive off;
#import where dn42_import_filter(x,y,z);
#export where dn42_export_filter(x,y,z);
}
{%endif%}

View file

@ -0,0 +1,13 @@
[Interface]
PostUp = wg set %i private-key /etc/wireguard/dn42.priv
ListenPort = 2{{peering["ASN"][-4:]}}
{% if peering["ipv4"] %}PostUp = /sbin/ip addr add dev %i 172.22.125.130/32 peer {{peering["ipv4"]}}/32
{%endif%}{% if peering["ipv6"] %}PostUp = /sbin/ip addr add dev %i fe63:5d40:47e5::130/128 peer {{peering["ipv6"]}}/128
{%endif%}{% if peering["ipv6ll"] %}PostUp = /sbin/ip addr add dev %i fe80::3035:130/128 peer {{peering["ipv6ll"]}}/128{%endif%}
Table = off
# {{peering["MNT"]}}
[Peer]
PublicKey = {{peering["wg_key"]}}
{% if peering["endpoint"] %}Endpoint = {{peering["endpoint"]}}{%endif%}
AllowedIPs = {% if peering["ipv6ll"] %}{{peering["ipv6ll"]}}/128, {%endif%}fd00::/8, 172.20.0.0/14, 10.0.0.0/8

View file

@ -21,7 +21,7 @@
"port": 8042,
"domain": "example.org", // domain to use for kioubit verification service (with protocol)
"base-dir": "/", //optional:directury for which it is reachable (if behind some sort of reverse proxy) default "/"
"peerings": "/path/to/peering-config.json", // optional; default "$PWD/peerings", directory to save existing peerings to
"peerings": "/path/to/peerings.json", // optional; default "$PWD/peerings.json", file to save existing peerings to
"production": true, //optional, default true;
"debug-mode": false, // optional; whethet to enable debugging; default false
"flask-secret-key": "<secret-please-replace>", // secret key for session cookies

View file

@ -3,14 +3,28 @@ import os
import base64
import logging
import random
import threading
import requests
class NodeCommunicator:
def __init__(self, name:str, config):
def __init__(self, name: str, config):
self.name = name
self.__config = config
self.__api_addr = config["api-con"]
def update(self, action: str, peering: dict):
if action == "add":
print(requests.post(f"{self.__api_addr}peerings", json=peering))
elif action == "update":
print(requests.put(f"{self.__api_addr}peerings", json=peering))
elif action == "delete":
print(requests.delete(f"{self.__api_addr}peerings", json=peering))
else:
return 400
class PeeringManager:
def __init__(self, config):
@ -21,9 +35,11 @@ class PeeringManager:
self._nodes = {}
for node in self.__config["nodes"]:
self._nodes[node] = NodeCommunicator(name=node, config=self.__config["nodes"][node])
self._nodes[node] = NodeCommunicator(
name=node, config=self.__config["nodes"][node])
self._amounts = None
self._threads = []
def __load_peerings(self):
if not os.path.exists(self.__peering_file):
@ -56,12 +72,22 @@ class PeeringManager:
json.dump(self.peerings, p, indent=4)
self._amounts = None
def _update_nodes(self, mode, peering, new_peering=None):
def _update_nodes(self, action: str, peering, new_peering=None):
"""mode: "add","update","delete
peering: peering to send to node (included in peering)
new_peering: if mode=="update" the new peering to update to
"""
pass
if peering["node"] in self._nodes:
#thread = threading.Thread(target=
self._nodes[peering["node"]].update(#), kwargs={
action=action, peering = peering if not new_peering else new_peering#, })
)
#thread.start()
#self._threads.append(thread)
else:
return False
def _update_amounts(self):
__new = {}
for asn in self.peerings["asn"]:
@ -71,14 +97,14 @@ class PeeringManager:
__new[peering["node"]] += 1
self._amounts = __new
def amount_by_node(self, node_name: str):
if self._amounts ==None:
if self._amounts == None:
self._update_amounts()
try:
return self._amounts[node_name]
except KeyError:
return 0
def exists(self, asn, node, mnt=None, wg_key=None, endpoint=None, ipv6ll=None, ipv4=None, ipv6=None, bgp_mp=True, bgp_enh=True):
"""checks if a peerings with specific data already exists"""
# check if mnt is specified, already exists in the database and if that mnt has the specified ASn -> if not: return False
@ -130,9 +156,10 @@ class PeeringManager:
if peering["node"] == node:
return False
self.peerings["asn"][asn].append({"MNT": mnt, "ASN": asn, "node": node, "wg_key": wg_key, "endpoint": endpoint,
"ipv6ll": ipv6ll, "ipv4": ipv4, "ipv6": ipv6, "bgp_mp": bgp_mp, "bgp_enh": bgp_enh})
peering = {"MNT": mnt, "ASN": asn, "node": node, "wg_key": wg_key, "endpoint": endpoint,
"ipv6ll": ipv6ll, "ipv4": ipv4, "ipv6": ipv6, "bgp_mp": bgp_mp, "bgp_enh": bgp_enh}
self.peerings["asn"][asn].append(peering)
self._update_nodes("add", peering=peering)
self._save_peerings()
return True
@ -154,12 +181,15 @@ class PeeringManager:
success = False
for pNr in range(len(self.peerings["asn"][asn])):
if self.peerings["asn"][asn][pNr]["node"] == node:
self.peerings["asn"][asn][pNr] = {"MNT": mnt, "ASN": asn, "node": node, "wg_key": wg_key,
old_peering = self.peerings["asn"][asn][pNr]
new_peering = self.peerings["asn"][asn][pNr] = {"MNT": mnt, "ASN": asn, "node": node, "wg_key": wg_key,
"endpoint": endpoint, "ipv6ll": ipv6ll, "ipv4": ipv4, "ipv6": ipv6, "bgp_mp": bgp_mp, "bgp_enh": bgp_enh}
success = True
if not success:
return False
self._save_peerings()
self._update_nodes("update", old_peering, new_peering=new_peering)
return True
def delete_peering(self, asn, node, mnt, wg_key=None):
@ -171,6 +201,7 @@ class PeeringManager:
continue
self.peerings["asn"][asn].remove(p)
self._save_peerings()
self._update_nodes("delete", peering=p)
return True
# if nothing got found (should have been catched by self.exists)
return False

View file

@ -65,13 +65,6 @@ footer.flex {
/* # index.html */
/* based on https://codepen.io/t_afif/pen/RwQZLYb */
.progress-bar {
--w: 100px; /* the width*/
--background: lightgrey; /* the background color */
width: var(--w);
margin: 0 10px;
}
.progress {
-webkit-appearance: none;
-moz-appearance: none;
@ -81,6 +74,13 @@ footer.flex {
border-radius: 10em;
background: var(--background);
}
.progress-bar {
--w: 100px; /* the width*/
--background: lightgrey; /* the background color */
width: var(--w);
margin: 0 10px;
}
.progress-value[value]{
--color: /* the progress color */