This commit is contained in:
matthewalanpenning 2023-10-07 19:30:17 -04:00
parent a463cd670c
commit f9eebfef5e
33 changed files with 15111 additions and 2575 deletions

View file

@ -1,3 +1,12 @@
# 0.3.0
- Improved Dockerfile for quicker builds
- Added version specific flask and werkzeug to requirements due to build error in dependencies
- Added a Network Zones page
- Added a Network page with DHCP Leases, Network Load Balancers, Forwards, and Peers
- Added missing parameters in API for creating a network
- Upgraded jQuery to version 3.7.1
- Updated layout to container and virtual machines pages
# 0.2.1
- Updated local xterm.js packages to version 5.1.0 and loading from local file
- Modified CSS to hide terminal scrollbar in chrome based browsers

View file

@ -1,13 +1,14 @@
FROM python:3.10.6
RUN mkdir -p /opt/lxconsole
ADD lxconsole /opt/lxconsole/lxconsole
COPY requirements.txt /opt/lxconsole/requirements.txt
COPY run.py /opt/lxconsole/run.py
COPY requirements.txt /opt/lxconsole/requirements.txt
RUN pip install --upgrade pip
RUN pip3 install -r /opt/lxconsole/requirements.txt
ADD lxconsole /opt/lxconsole/lxconsole
COPY run.py /opt/lxconsole/run.py
RUN apt update
RUN apt install sqlite3

View file

@ -7,9 +7,11 @@ from . import cluster_members
from . import images
from . import container
from . import containers
from . import network
from . import networks
from . import network_acl
from . import network_acls
from . import network_zones
from . import profiles
from . import operations
from . import projects
@ -38,9 +40,11 @@ api.add_url_rule('/certificates/<endpoint>', view_func=certificates.api_certific
api.add_url_rule('/cluster-groups/<endpoint>', view_func=cluster_groups.api_cluster_groups_endpoint, methods=['GET', 'POST'])
api.add_url_rule('/cluster-members/<endpoint>', view_func=cluster_members.api_cluster_members_endpoint, methods=['GET', 'POST'])
api.add_url_rule('/images/<endpoint>', view_func=images.api_images_endpoint, methods=['GET', 'POST'])
api.add_url_rule('/network/<endpoint>', view_func=network.api_network_endpoint, methods=['GET', 'POST'])
api.add_url_rule('/networks/<endpoint>', view_func=networks.api_networks_endpoint, methods=['GET', 'POST'])
api.add_url_rule('/network-acl/<endpoint>', view_func=network_acl.api_network_acl_endpoint, methods=['GET', 'POST'])
api.add_url_rule('/network-acls/<endpoint>', view_func=network_acls.api_network_acls_endpoint, methods=['GET', 'POST'])
api.add_url_rule('/network-zones/<endpoint>', view_func=network_zones.api_network_zones_endpoint, methods=['GET', 'POST'])
api.add_url_rule('/profiles/<endpoint>', view_func=profiles.api_profiles_endpoint, methods=['GET', 'POST'])
api.add_url_rule('/operations/<endpoint>', view_func=operations.api_operations_endpoint, methods=['GET', 'POST'])
api.add_url_rule('/projects/<endpoint>', view_func=projects.api_projects_endpoint, methods=['GET', 'POST'])

View file

@ -24,6 +24,10 @@ def privilege_check(privilege, server_id = 0):
#'add_instance_usb_device',
#'add_instance',
#'add_network_acl',
#'add_network_zone',
#'add_network_load_balancer',
#'add_network_forward',
#'add_network_peer',
#'add_network',
#'add_profile',
#'add_project',
@ -52,6 +56,10 @@ def privilege_check(privilege, server_id = 0):
#'delete_instance',
#'delete_instance',
#'delete_network_acl',
#'delete_network_zone',
#'delete_network_load_balancer',
#'delete_network_forward',
#'delete_network_peer',
#'delete_network',
#'delete_operation',
#'delete_profile',
@ -68,6 +76,7 @@ def privilege_check(privilege, server_id = 0):
#'export_instance_backup',
'get_access_control',
'get_group',
'get_network_state',
'get_server_info',
'get_server_initial_project',
'get_server_resources',
@ -102,6 +111,11 @@ def privilege_check(privilege, server_id = 0):
'list_instances',
'list_logs',
'list_network_acls',
'list_network_forwards',
'list_network_leases',
'list_network_load_balancers',
'list_network_peers',
'list_network_zones',
'list_network_managed_devices',
'list_networks',
'list_operations',
@ -119,6 +133,11 @@ def privilege_check(privilege, server_id = 0):
'load_image',
'load_instance',
'load_network_acl',
'load_network_forward',
'load_network_lease',
'load_network_load_balancer',
'load_network_peer',
'load_network_zone',
'load_network',
'load_operation',
'load_profile',
@ -141,6 +160,11 @@ def privilege_check(privilege, server_id = 0):
#'update_image',
#'update_instance',
#'update_network_acl',
#'update_network_forward',
#'update_network_lease',
#'update_network_load_balancer',
#'update_network_peer',
#'update_network_zone',
#'update_network',
#'update_profile',
#'update_project',
@ -166,6 +190,10 @@ def privilege_check(privilege, server_id = 0):
#'add_instance_usb_device',
#'add_instance',
#'add_network_acl',
#'add_network_zone',
#'add_network_load_balancer',
#'add_network_forward',
#'add_network_peer',
#'add_network',
#'add_profile',
#'add_project',
@ -194,6 +222,10 @@ def privilege_check(privilege, server_id = 0):
#'delete_instance',
#'delete_instance',
#'delete_network_acl',
#'delete_network_zone',
#'delete_network_load_balancer',
#'delete_network_forward',
#'delete_network_peer',
#'delete_network',
#'delete_operation',
#'delete_profile',
@ -210,6 +242,7 @@ def privilege_check(privilege, server_id = 0):
'export_instance_backup',
'get_access_control',
'get_group',
'get_network_state',
'get_server_info',
'get_server_initial_project',
'get_server_resources',
@ -244,6 +277,11 @@ def privilege_check(privilege, server_id = 0):
'list_instances',
'list_logs',
'list_network_acls',
'list_network_forwards',
'list_network_leases',
'list_network_load_balancers',
'list_network_peers',
'list_network_zones',
'list_network_managed_devices',
'list_networks',
'list_operations',
@ -261,6 +299,11 @@ def privilege_check(privilege, server_id = 0):
'load_image',
'load_instance',
'load_network_acl',
'load_network_forward',
'load_network_lease',
'load_network_load_balancer',
'load_network_peer',
'load_network_zone',
'load_network',
'load_operation',
'load_profile',
@ -283,6 +326,11 @@ def privilege_check(privilege, server_id = 0):
#'update_image',
#'update_instance',
#'update_network_acl',
#'update_network_forward',
#'update_network_lease',
#'update_network_load_balancer',
#'update_network_peer',
#'update_network_zone',
#'update_network',
#'update_profile',
#'update_project',
@ -308,6 +356,10 @@ def privilege_check(privilege, server_id = 0):
'add_instance_usb_device',
'add_instance',
'add_network_acl',
'add_network_zone',
'add_network_load_balancer',
'add_network_forward',
'add_network_peer',
'add_network',
'add_profile',
'add_project',
@ -336,6 +388,10 @@ def privilege_check(privilege, server_id = 0):
'delete_instance',
'delete_instance',
'delete_network_acl',
'delete_network_zone',
'delete_network_load_balancer',
'delete_network_forward',
'delete_network_peer',
'delete_network',
'delete_operation',
'delete_profile',
@ -352,6 +408,7 @@ def privilege_check(privilege, server_id = 0):
'export_instance_backup',
'get_access_control',
'get_group',
'get_network_state',
'get_server_info',
'get_server_initial_project',
'get_server_resources',
@ -386,6 +443,11 @@ def privilege_check(privilege, server_id = 0):
'list_instances',
'list_logs',
'list_network_acls',
'list_network_forwards',
'list_network_leases',
'list_network_load_balancers',
'list_network_peers',
'list_network_zones',
'list_network_managed_devices',
'list_networks',
'list_operations',
@ -403,6 +465,11 @@ def privilege_check(privilege, server_id = 0):
'load_image',
'load_instance',
'load_network_acl',
'load_network_forward',
'load_network_lease',
'load_network_load_balancer',
'load_network_peer',
'load_network_zone',
'load_network',
'load_operation',
'load_profile',
@ -425,6 +492,11 @@ def privilege_check(privilege, server_id = 0):
'update_image',
'update_instance',
'update_network_acl',
'update_network_forward',
'update_network_lease',
'update_network_load_balancer',
'update_network_peer',
'update_network_zone',
'update_network',
'update_profile',
'update_project',
@ -450,6 +522,10 @@ def privilege_check(privilege, server_id = 0):
'add_instance_usb_device',
'add_instance',
'add_network_acl',
'add_network_zone',
'add_network_load_balancer',
'add_network_forward',
'add_network_peer',
'add_network',
'add_profile',
'add_project',
@ -478,6 +554,10 @@ def privilege_check(privilege, server_id = 0):
'delete_instance',
'delete_instance',
'delete_network_acl',
'delete_network_zone',
'delete_network_load_balancer',
'delete_network_forward',
'delete_network_peer',
'delete_network',
'delete_operation',
'delete_profile',
@ -494,6 +574,7 @@ def privilege_check(privilege, server_id = 0):
'export_instance_backup',
'get_access_control',
'get_group',
'get_network_state',
'get_server_info',
'get_server_initial_project',
'get_server_resources',
@ -528,6 +609,11 @@ def privilege_check(privilege, server_id = 0):
'list_instances',
'list_logs',
'list_network_acls',
'list_network_forwards',
'list_network_leases',
'list_network_load_balancers',
'list_network_peers',
'list_network_zones',
'list_network_managed_devices',
'list_networks',
'list_operations',
@ -545,6 +631,11 @@ def privilege_check(privilege, server_id = 0):
'load_image',
'load_instance',
'load_network_acl',
'load_network_forward',
'load_network_lease',
'load_network_load_balancer',
'load_network_peer',
'load_network_zone',
'load_network',
'load_operation',
'load_profile',
@ -567,6 +658,11 @@ def privilege_check(privilege, server_id = 0):
'update_image',
'update_instance',
'update_network_acl',
'update_network_forward',
'update_network_lease',
'update_network_load_balancer',
'update_network_peer',
'update_network_zone',
'update_network',
'update_profile',
'update_project',

392
lxconsole/api/network.py Normal file
View file

@ -0,0 +1,392 @@
from flask import jsonify, request
import json
import requests
import os
from lxconsole import db
from lxconsole.models import Server
from datetime import datetime
from flask_login import login_required
from lxconsole.api.access_controls import privilege_check
def get_client_crt():
return 'certs/client.crt'
def get_client_key():
return 'certs/client.key'
@login_required
def api_network_endpoint(endpoint):
if not privilege_check(endpoint, request.args.get('id')):
return jsonify({'data': [], 'metadata':[], 'error': 'not authorized', 'error_code': 403})
if endpoint == 'add_network_forward':
id = request.args.get('id')
project = request.args.get('project')
network = request.args.get('network')
server = Server.query.filter_by(id=id).first()
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + network + '/forwards?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
if request.form.get('json'):
data = request.form.get('json')
results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), data=data)
return jsonify(results.json())
data = {}
data.update({'listen_address': request.form.get('listen_address')})
data.update({'description': request.form.get('description')})
#config = {}
#config.update({'user.mykey': request.form.get('user.mykey')}) if request.form.get('user.mykey') else False
port = {}
port.update({'description': request.form.get('port_description')}) if request.form.get('port_description') else False
port.update({'listen_port': request.form.get('port_listen_port')}) if request.form.get('port_listen_port') else False
port.update({'protocol': request.form.get('port_protocol')}) if request.form.get('port_protocol') else False
port.update({'target_address': [ request.form.get('port_target_address') ]}) if request.form.get('port_target_address') else False
port.update({'target_port': [ request.form.get('port_target_port') ]}) if request.form.get('port_target_port') else False
#data.update({'config': config})
data.update({'ports': [ port ]})
results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), json=data)
return jsonify(results.json())
if endpoint == 'add_network_load_balancer':
id = request.args.get('id')
project = request.args.get('project')
network = request.args.get('network')
server = Server.query.filter_by(id=id).first()
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + network + '/load-balancers?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
if request.form.get('json'):
data = request.form.get('json')
results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), data=data)
return jsonify(results.json())
data = {}
data.update({'listen_address': request.form.get('listen_address')})
data.update({'description': request.form.get('description')})
backend = {}
backend.update({'description': request.form.get('backend_description')}) if request.form.get('backend_description') else False
backend.update({'name': request.form.get('backend_name')}) if request.form.get('backend_name') else False
backend.update({'target_address': request.form.get('backend_target_address')}) if request.form.get('backend_target_address') else False
backend.update({'target_port': request.form.get('backend_target_port')}) if request.form.get('backend_target_port') else False
#config = {}
#config.update({'user.mykey': request.form.get('user.mykey')}) if request.form.get('user.mykey') else False
port = {}
port.update({'description': request.form.get('port_description')}) if request.form.get('port_description') else False
port.update({'listen_port': request.form.get('port_listen_port')}) if request.form.get('port_listen_port') else False
port.update({'protocol': request.form.get('port_protocol')}) if request.form.get('port_protocol') else False
port.update({'target_backend': [ request.form.get('port_target_backend') ]}) if request.form.get('port_target_backend') else False
data.update({'backends': [ backend ]})
#data.update({'config': config})
data.update({'ports': [ port ]})
results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), json=data)
return jsonify(results.json())
if endpoint == 'add_network_peer':
id = request.args.get('id')
project = request.args.get('project')
network = request.args.get('network')
server = Server.query.filter_by(id=id).first()
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + network + '/peers?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
if request.form.get('json'):
data = request.form.get('json')
results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), data=data)
return jsonify(results.json())
data = {}
data.update({'name': request.form.get('name')})
data.update({'description': request.form.get('description')})
data.update({'target_network': request.form.get('target_network')})
data.update({'target_project': request.form.get('target_network')})
#config = {}
#config.update({'user.mykey': request.form.get('user.mykey')}) if request.form.get('user.mykey') else False
#data.update({'config': config})
results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), json=data)
return jsonify(results.json())
if endpoint == 'delete_network_forward':
id = request.args.get('id')
project = request.args.get('project')
network = request.args.get('network')
name = request.form.get('name')
server = Server.query.filter_by(id=id).first()
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + network + '/forwards/' + name + '?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
results = requests.delete(url, verify=server.ssl_verify, cert=(client_cert, client_key))
return jsonify(results.json())
if endpoint == 'delete_network_load_balancer':
id = request.args.get('id')
project = request.args.get('project')
network = request.args.get('network')
name = request.form.get('name')
server = Server.query.filter_by(id=id).first()
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + network + '/load-balancers/' + name + '?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
results = requests.delete(url, verify=server.ssl_verify, cert=(client_cert, client_key))
return jsonify(results.json())
if endpoint == 'delete_network_peer':
id = request.args.get('id')
project = request.args.get('project')
network = request.args.get('network')
name = request.form.get('name')
server = Server.query.filter_by(id=id).first()
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + network + '/peers/' + name + '?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
results = requests.delete(url, verify=server.ssl_verify, cert=(client_cert, client_key))
return jsonify(results.json())
if endpoint == 'get_network_state':
id = request.args.get('id')
project = request.args.get('project')
server = Server.query.filter_by(id=id).first()
name = request.args.get('name')
recursion = request.args.get('recursion')
if recursion == '1':
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + name + '/state?recursion=1&project=' + project
else:
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + name + '/state?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
try:
results = requests.get(url, verify=server.ssl_verify, cert=(client_cert, client_key), timeout=5)
results.raise_for_status()
except requests.exceptions.RequestException as errex:
return jsonify({'metadata': []})
return jsonify(results.json())
if endpoint == 'list_network_forwards':
id = request.args.get('id')
project = request.args.get('project')
server = Server.query.filter_by(id=id).first()
name = request.args.get('name')
recursion = request.args.get('recursion')
if recursion == '1':
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + name + '/forwards?recursion=1&project=' + project
else:
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + name + '/forwards?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
try:
results = requests.get(url, verify=server.ssl_verify, cert=(client_cert, client_key), timeout=5)
results.raise_for_status()
except requests.exceptions.RequestException as errex:
return jsonify({'metadata': []})
forwards = json.dumps(results.json())
forwards = json.loads(forwards)
if forwards['metadata']:
return jsonify(results.json())
else:
return jsonify({'metadata': []})
if endpoint == 'list_network_leases':
id = request.args.get('id')
project = request.args.get('project')
server = Server.query.filter_by(id=id).first()
name = request.args.get('name')
recursion = request.args.get('recursion')
if recursion == '1':
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + name + '/leases?recursion=1&project=' + project
else:
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + name + '/leases?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
try:
results = requests.get(url, verify=server.ssl_verify, cert=(client_cert, client_key), timeout=5)
results.raise_for_status()
except requests.exceptions.RequestException as errex:
return jsonify({'metadata': []})
leases = json.dumps(results.json())
leases = json.loads(leases)
if leases['metadata']:
return jsonify(results.json())
else:
return jsonify({'metadata': []})
if endpoint == 'list_network_load_balancers':
id = request.args.get('id')
project = request.args.get('project')
server = Server.query.filter_by(id=id).first()
name = request.args.get('name')
recursion = request.args.get('recursion')
if recursion == '1':
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + name + '/load-balancers?recursion=1&project=' + project
else:
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + name + '/load-balancers?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
try:
results = requests.get(url, verify=server.ssl_verify, cert=(client_cert, client_key), timeout=5)
results.raise_for_status()
except requests.exceptions.RequestException as errex:
return jsonify({'metadata': []})
load_balancers = json.dumps(results.json())
load_balancers = json.loads(load_balancers)
if load_balancers['metadata']:
return jsonify(results.json())
else:
return jsonify({'metadata': []})
if endpoint == 'list_network_peers':
id = request.args.get('id')
project = request.args.get('project')
server = Server.query.filter_by(id=id).first()
name = request.args.get('name')
recursion = request.args.get('recursion')
if recursion == '1':
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + name + '/peers?recursion=1&project=' + project
else:
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + name + '/peers?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
try:
results = requests.get(url, verify=server.ssl_verify, cert=(client_cert, client_key), timeout=5)
results.raise_for_status()
except requests.exceptions.RequestException as errex:
return jsonify({'metadata': []})
peers = json.dumps(results.json())
peers = json.loads(peers)
if peers['metadata']:
return jsonify(results.json())
else:
return jsonify({'metadata': []})
if endpoint == 'load_network_forward':
id = request.args.get('id')
project = request.args.get('project')
network = request.args.get('network')
name = request.form.get('name')
server = Server.query.filter_by(id=id).first()
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + network + '/forwards/' + name + '?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
results = requests.get(url, verify=server.ssl_verify, cert=(client_cert, client_key))
return jsonify(results.json())
if endpoint == 'load_network_load_balancer':
id = request.args.get('id')
project = request.args.get('project')
network = request.args.get('network')
name = request.form.get('name')
server = Server.query.filter_by(id=id).first()
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + network + '/load-balancers/' + name + '?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
results = requests.get(url, verify=server.ssl_verify, cert=(client_cert, client_key))
return jsonify(results.json())
if endpoint == 'load_network_peer':
id = request.args.get('id')
project = request.args.get('project')
network = request.args.get('network')
name = request.form.get('name')
server = Server.query.filter_by(id=id).first()
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + network + '/peers/' + name + '?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
results = requests.get(url, verify=server.ssl_verify, cert=(client_cert, client_key))
return jsonify(results.json())
if endpoint == 'update_network_forward':
id = request.args.get('id')
project = request.args.get('project')
name = request.args.get('name')
server = Server.query.filter_by(id=id).first()
network = request.args.get('network')
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + network + '/forwards/' + name + '?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
if request.form.get('json'):
data = request.form.get('json')
results = requests.put(url, verify=server.ssl_verify, cert=(client_cert, client_key), data=data)
return jsonify(results.json())
data = {}
data.update({'name': request.form.get('name')})
results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), json=data)
return jsonify(results.json())
if endpoint == 'update_network_load_balancer':
id = request.args.get('id')
project = request.args.get('project')
name = request.args.get('name')
server = Server.query.filter_by(id=id).first()
network = request.args.get('network')
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + network + '/load-balancers/' + name + '?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
if request.form.get('json'):
data = request.form.get('json')
results = requests.put(url, verify=server.ssl_verify, cert=(client_cert, client_key), data=data)
return jsonify(results.json())
data = {}
data.update({'name': request.form.get('name')})
results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), json=data)
return jsonify(results.json())
if endpoint == 'update_network_peer':
id = request.args.get('id')
project = request.args.get('project')
name = request.args.get('name')
server = Server.query.filter_by(id=id).first()
network = request.args.get('network')
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/networks/' + network + '/peers/' + name + '?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
if request.form.get('json'):
data = request.form.get('json')
results = requests.put(url, verify=server.ssl_verify, cert=(client_cert, client_key), data=data)
return jsonify(results.json())
data = {}
data.update({'name': request.form.get('name')})
results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), json=data)
return jsonify(results.json())

View file

@ -0,0 +1,103 @@
from flask import jsonify, request
import requests
from lxconsole import db
from lxconsole.models import Server
from flask_login import login_required
from lxconsole.api.access_controls import privilege_check
def get_client_crt():
return 'certs/client.crt'
def get_client_key():
return 'certs/client.key'
@login_required
def api_network_zones_endpoint(endpoint):
if not privilege_check(endpoint, request.args.get('id')):
return jsonify({'data': [], 'metadata':[], 'error': 'not authorized', 'error_code': 403})
if endpoint == 'add_network_zone':
id = request.args.get('id')
project = request.args.get('project')
server = Server.query.filter_by(id=id).first()
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/network-zones?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
if request.form.get('json'):
data = request.form.get('json')
results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), data=data)
return jsonify(results.json())
data = {}
data.update({'name': request.form.get('name')})
data.update({'description': request.form.get('description')})
results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), json=data)
return jsonify(results.json())
if endpoint == 'delete_network_zone':
id = request.args.get('id')
project = request.args.get('project')
name = request.form.get('name')
server = Server.query.filter_by(id=id).first()
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/network-zones/' + name + '?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
results = requests.delete(url, verify=server.ssl_verify, cert=(client_cert, client_key))
return jsonify(results.json())
if endpoint == 'list_network_zones':
id = request.args.get('id')
project = request.args.get('project')
server = Server.query.filter_by(id=id).first()
recursion = request.args.get('recursion')
if recursion == '1':
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/network-zones?recursion=1&project=' + project
else:
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/network-zones?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
results = requests.get(url, verify=server.ssl_verify, cert=(client_cert, client_key))
return jsonify(results.json())
if endpoint == 'load_network_zone':
id = request.args.get('id')
project = request.args.get('project')
name = request.form.get('name')
server = Server.query.filter_by(id=id).first()
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/network-zones/' + name + '?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
results = requests.get(url, verify=server.ssl_verify, cert=(client_cert, client_key))
return jsonify(results.json())
if endpoint == 'update_network_zone':
id = request.args.get('id')
project = request.args.get('project')
name = request.args.get('name')
server = Server.query.filter_by(id=id).first()
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/network-zones/' + name + '?project=' + project
client_cert = get_client_crt()
client_key = get_client_key()
if request.form.get('json'):
data = request.form.get('json')
results = requests.put(url, verify=server.ssl_verify, cert=(client_cert, client_key), data=data)
return jsonify(results.json())
if request.form.get('name'):
data = {}
data.update({'name': request.form.get('name')})
results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), json=data)
return jsonify(results.json())
return False

View file

@ -34,8 +34,65 @@ def api_networks_endpoint(endpoint):
return jsonify(results.json())
data = {}
data.update({'name': request.form.get('name')})
data.update({'description': request.form.get('description')})
data.update({'name': request.form.get('name')}) if request.form.get('name') else False
data.update({'description': request.form.get('description')}) if request.form.get('description') else False
data.update({'type': request.form.get('type')}) if request.form.get('type') else False
data.update({'parent': request.form.get('parent')}) if request.form.get('parent') else False
data.update({'network': request.form.get('network')}) if request.form.get('network') else False
data.update({'mtu': request.form.get('mtu')}) if request.form.get('mtu') else False
data.update({'vlan': request.form.get('vlan')}) if request.form.get('vlan') else False
data.update({'bridge.driver': request.form.get('bridge.driver')}) if request.form.get('bridge.driver') else False
data.update({'bridge.external.interfaces': request.form.get('bridge.external.interfaces')}) if request.form.get('bridge.external.interfaces') else False
data.update({'bridge.hwaddr': request.form.get('bridge.hwaddr')}) if request.form.get('bridge.hwaddr') else False
data.update({'bridge.mode': request.form.get('bridge.mode')}) if request.form.get('bridge.mode') else False
data.update({'bridge.mtu': request.form.get('bridge.mtu')}) if request.form.get('bridge.mtu') else False
data.update({'dns.domain': request.form.get('dns.domain')}) if request.form.get('dns.domain') else False
data.update({'dns.mode': request.form.get('dns.mode')}) if request.form.get('dns.mode') else False
data.update({'dns.nameservers': request.form.get('dns.nameservers')}) if request.form.get('dns.nameservers') else False
data.update({'dns.search': request.form.get('dns.search')}) if request.form.get('dns.search') else False
data.update({'fan.overlay.subnet': request.form.get('fan.overlay.subnet')}) if request.form.get('fan.overlay.subnet') else False
data.update({'fan.type': request.form.get('fan.type')}) if request.form.get('fan.type') else False
data.update({'fan.underlay.subnet': request.form.get('fan.underlay.subnet')}) if request.form.get('fan.underlay.subnet') else False
data.update({'ipv4.address': request.form.get('ipv4.address')}) if request.form.get('ipv4.address') else False
data.update({'ipv4.dhcp': request.form.get('ipv4.dhcp')}) if request.form.get('ipv4.dhcp') else False
data.update({'ipv4.dhcp.expiry': request.form.get('ipv4.dhcp.expiry')}) if request.form.get('ipv4.dhcp.expiry') else False
data.update({'ipv4.dhcp.gateway': request.form.get('ipv4.dhcp.gateway')}) if request.form.get('ipv4.dhcp.gateway') else False
data.update({'ipv4.dhcp.ranges': request.form.get('ipv4.dhcp.ranges')}) if request.form.get('ipv4.dhcp.ranges') else False
data.update({'ipv4.firewall': request.form.get('ipv4.firewall')}) if request.form.get('ipv4.firewall') else False
data.update({'ipv4.nat.address': request.form.get('ipv4.nat.address')}) if request.form.get('ipv4.nat.address') else False
data.update({'ipv4.nat': request.form.get('ipv4.nat')}) if request.form.get('ipv4.nat') else False
data.update({'ipv4.nat.order': request.form.get('ipv4.nat.order')}) if request.form.get('ipv4.nat.order') else False
data.update({'ipv4.ovn.ranges': request.form.get('ipv4.ovn.ranges')}) if request.form.get('ipv4.ovn.ranges') else False
data.update({'ipv4.gateway': request.form.get('ipv4.gateway')}) if request.form.get('ipv4.gateway') else False
data.update({'ipv4.routes.anycast': request.form.get('ipv4.routes.anycast')}) if request.form.get('ipv4.routes.anycast') else False
data.update({'ipv4.routes': request.form.get('ipv4.routes')}) if request.form.get('ipv4.routes') else False
data.update({'ipv4.routing': request.form.get('ipv4.routing')}) if request.form.get('ipv4.routing') else False
data.update({'ipv6.address': request.form.get('ipv6.address')}) if request.form.get('ipv6.address') else False
data.update({'ipv6.dhcp': request.form.get('ipv6.dhcp')}) if request.form.get('ipv6.dhcp') else False
data.update({'ipv6.dhcp.expiry': request.form.get('ipv6.dhcp.expiry')}) if request.form.get('ipv6.dhcp.expiry') else False
data.update({'ipv6.dhcp.ranges': request.form.get('ipv6.dhcp.ranges')}) if request.form.get('ipv6.dhcp.ranges') else False
data.update({'ipv6.dhcp.stateful': request.form.get('ipv6.dhcp.stateful')}) if request.form.get('ipv6.dhcp.stateful') else False
data.update({'ipv6.firewall': request.form.get('ipv6.firewall')}) if request.form.get('ipv6.firewall') else False
data.update({'ipv6.nat.address': request.form.get('ipv6.nat.address')}) if request.form.get('ipv6.nat.address') else False
data.update({'ipv6.nat': request.form.get('ipv6.nat')}) if request.form.get('ipv6.nat') else False
data.update({'ipv6.nat.order': request.form.get('ipv6.nat.order')}) if request.form.get('ipv6.nat.order') else False
data.update({'ipv6.ovn.ranges': request.form.get('ipv6.ovn.ranges')}) if request.form.get('ipv6.ovn.ranges') else False
data.update({'ipv6.gateway': request.form.get('ipv6.gateway')}) if request.form.get('ipv6.gateway') else False
data.update({'ipv6.routes.anycast': request.form.get('ipv6.routes.anycast')}) if request.form.get('ipv6.routes.anycast') else False
data.update({'ipv6.routes': request.form.get('ipv6.routes')}) if request.form.get('ipv6.routes') else False
data.update({'ipv6.routing': request.form.get('ipv6.routing')}) if request.form.get('ipv6.routing') else False
data.update({'maas.subnet.ipv4': request.form.get('maas.subnet.ipv4')}) if request.form.get('maas.subnet.ipv4') else False
data.update({'maas.subnet.ipv6': request.form.get('maas.subnet.ipv6')}) if request.form.get('maas.subnet.ipv6') else False
data.update({'raw.dnsmasq': request.form.get('raw.dnsmasq')}) if request.form.get('raw.dnsmasq') else False
data.update({'ovn.ingress.mode': request.form.get('ovn.ingress.mode')}) if request.form.get('ovn.ingress.mode') else False
results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), json=data)
return jsonify(results.json())

View file

@ -643,8 +643,8 @@ def api_virtual_machine_endpoint(endpoint):
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/virtual-machines/' + name + '/exec?project=' + project
results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), json=data)
# Sleep a second to give time for a second reading
time.sleep(1)
# Sleep to allow time for copy to complete
time.sleep(.5)
#Get first /proc/stat information but from /tmp/stat
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/virtual-machines/' + name + '/files?project=' + project + '&path=/tmp/stat'
@ -660,7 +660,8 @@ def api_virtual_machine_endpoint(endpoint):
first_cpu_time = user_time + system_time + idle_time
first_idle_time = idle_time
# Sleep a second between reads
time.sleep(1)
# Copy /proc/stat to /tmp/stat and then read that file as a work around
data = {}
@ -678,8 +679,8 @@ def api_virtual_machine_endpoint(endpoint):
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/virtual-machines/' + name + '/exec?project=' + project
results = requests.post(url, verify=server.ssl_verify, cert=(client_cert, client_key), json=data)
# Sleep a second to give time for a second reading
time.sleep(1)
# Sleep to allow time for copy to complete
time.sleep(.5)
#Get second /proc/stat information
url = 'https://' + server.addr + ':' + str(server.port) + '/1.0/virtual-machines/' + name + '/files?project=' + project + '&path=/tmp/stat'

View file

@ -89,6 +89,16 @@ def network_acl():
def network_acls():
return render_template('network-acls.html', page_title='Network ACLs', page_user_id=current_user.id, page_username=current_user.username,)
@app.route("/network-zones")
@login_required
def network_zones():
return render_template('network-zones.html', page_title='Network Zones', page_user_id=current_user.id, page_username=current_user.username,)
@app.route("/network")
@login_required
def network():
return render_template('network.html', page_title='Network', page_user_id=current_user.id, page_username=current_user.username,)
@app.route("/networks")
@login_required
def networks():

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -62,3 +62,13 @@ code {
.terminal .xterm-viewport {
overflow-y: hidden;
}
td.details-control {
text-align:center;
color:#007bff ;
cursor: pointer;
}
tr.shown td.details-control {
text-align:center;
color:#ff0000 ;
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,4 @@
/*! DataTables Bootstrap 5 integration
* 2020 SpryMedia Ltd - datatables.net/license
*/
!function(t){var n,r;"function"==typeof define&&define.amd?define(["jquery","datatables.net"],function(e){return t(e,window,document)}):"object"==typeof exports?(n=require("jquery"),r=function(e,a){a.fn.dataTable||require("datatables.net")(e,a)},"undefined"==typeof window?module.exports=function(e,a){return e=e||window,a=a||n(e),r(e,a),t(a,0,e.document)}:(r(window,n),module.exports=t(n,window,window.document))):t(jQuery,window,document)}(function(x,e,r,o){"use strict";var i=x.fn.dataTable;return x.extend(!0,i.defaults,{dom:"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>><'row dt-row'<'col-sm-12'tr>><'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",renderer:"bootstrap"}),x.extend(i.ext.classes,{sWrapper:"dataTables_wrapper dt-bootstrap5",sFilterInput:"form-control form-control-sm",sLengthSelect:"form-select form-select-sm",sProcessing:"dataTables_processing card",sPageButton:"paginate_button page-item"}),i.ext.renderer.pageButton.bootstrap=function(d,e,s,a,l,c){function u(e,a){for(var t,n,r=function(e){e.preventDefault(),x(e.currentTarget).hasClass("disabled")||b.page()==e.data.action||b.page(e.data.action).draw("page")},o=0,i=a.length;o<i;o++)if(t=a[o],Array.isArray(t))u(e,t);else{switch(f=p="",t){case"ellipsis":p="&#x2026;",f="disabled";break;case"first":p=g.sFirst,f=t+(0<l?"":" disabled");break;case"previous":p=g.sPrevious,f=t+(0<l?"":" disabled");break;case"next":p=g.sNext,f=t+(l<c-1?"":" disabled");break;case"last":p=g.sLast,f=t+(l<c-1?"":" disabled");break;default:p=t+1,f=l===t?"active":""}p&&(n=-1!==f.indexOf("disabled"),n=x("<li>",{class:m.sPageButton+" "+f,id:0===s&&"string"==typeof t?d.sTableId+"_"+t:null}).append(x("<a>",{href:n?null:"#","aria-controls":d.sTableId,"aria-disabled":n?"true":null,"aria-label":w[t],role:"link","aria-current":"active"===f?"page":null,"data-dt-idx":t,tabindex:n?-1:d.iTabIndex,class:"page-link"}).html(p)).appendTo(e),d.oApi._fnBindAction(n,{action:t},r))}}var p,f,t,b=new i.Api(d),m=d.oClasses,g=d.oLanguage.oPaginate,w=d.oLanguage.oAria.paginate||{},e=x(e);try{t=e.find(r.activeElement).data("dt-idx")}catch(e){}var n=e.children("ul.pagination");n.length?n.empty():n=e.html("<ul/>").children("ul").addClass("pagination"),u(n,a),t!==o&&e.find("[data-dt-idx="+t+"]").trigger("focus")},i});

File diff suppressed because one or more lines are too long

10716
lxconsole/static/js/jquery/jquery-3.7.1.js vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -30,7 +30,6 @@ function configureNavbarForServers(){
function configureSidebarForServers(){
$("#clusterMembersLinkSidebar").hide()
$("#clusterGroupsLinkSidebar").hide()
$("#clusterGroupsLinkSidebar").hide()
$("#instanceSidebarLinks").hide()
$("#coreSidebarLinks").hide()
$("#networkSidebarLinks").hide()
@ -65,6 +64,7 @@ function applySidebarLinks() {
$("#clusterGroupsLinkSidebar").attr("href", "cluster-groups?id=" + encodeURI(serverId) + "&project=" + encodeURI(project));
$("#projectsLinkSidebar").attr("href", "projects?id=" + encodeURI(serverId) + "&project=" + encodeURI(project));
$("#networkAclsLinkSidebar").attr("href", "network-acls?id=" + encodeURI(serverId) + "&project=" + encodeURI(project));
$("#networkZonesLinkSidebar").attr("href", "network-zones?id=" + encodeURI(serverId) + "&project=" + encodeURI(project));
$("#operationsLinkSidebar").attr("href", "operations?id=" + encodeURI(serverId) + "&project=" + encodeURI(project));
$("#certificatesLinkSidebar").attr("href", "certificates?id=" + encodeURI(serverId) + "&project=" + encodeURI(project));
$("#simplestreamsLinkSidebar").attr("href", "simplestreams?id=" + encodeURI(serverId) + "&project=" + encodeURI(project));
@ -103,6 +103,10 @@ function applySidebarStyles() {
$('#networkAclsSpan').css('color','#fff');
$('#networkAclsIcon').css('color','#fff');
}
if (location.pathname == "/network-zones" || location.pathname == "/network-zone"){
$('#networkZonesSpan').css('color','#fff');
$('#networkZonesIcon').css('color','#fff');
}
if (location.pathname == "/operations"){
$('#operationsSpan').css('color','#fff');
$('#operationsIcon').css('color','#fff');

View file

@ -205,7 +205,6 @@
if (data.error_code >= 400){
alert(data.error);
}
$("#storagePoolNameEditInput").text("Name: " + name);
$("#jsonInput").val(JSON.stringify(data.metadata, null, 2));
$("#editModal").modal('show');
});

File diff suppressed because it is too large Load diff

View file

@ -4,21 +4,32 @@
<link rel="icon" type="image/png" href="{{ url_for('static', filename='assets/img/logo-light.svg') }}">
<title>lxconsole</title>
<!-- Google Font: Source Sans Pro -->
<link rel="stylesheet" href="../static/css/fonts/source_sans_pro.css">
<!-- Font Awesome -->
<link rel="stylesheet" href="../static/plugins/fontawesome-free/css/all.min.css">
<!-- DataTables -->
<link rel="stylesheet" href="../static/plugins/datatables-bs4/css/dataTables.bootstrap4.min.css">
<link rel="stylesheet" href="../static/plugins/datatables-responsive/css/responsive.bootstrap4.min.css">
<link rel="stylesheet" href="../static/plugins/datatables-buttons/css/buttons.bootstrap4.min.css">
<!-- Xterm.js style -->
<link rel="stylesheet" href="../static/css/xterm/xterm.css"/>
<!-- Theme style -->
<link rel="stylesheet" href="../static/dist/css/adminlte.min.css">
<!-- LXD Dashboard style -->
<link rel="stylesheet" href="../static/css/styles.css">
<!-- Bootstrap 5 - https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.2.3/css/bootstrap.min.css -->
<!-- <link rel="stylesheet" href="../static/css/bootstrap/bootstrap-5.2.3.min.css"> -->
<!-- Datatables Bootstrap 5 - https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css -->
<!--<link rel="stylesheet" href="../static/css/datatables/dataTables.bootstrap5.min.css">-->
</head>

View file

@ -40,7 +40,7 @@
<footer class="main-footer">
<div class="float-right d-none d-sm-block">
Version 0.2.1
Version 0.3.0
</div>
Copyright &copy; 2020-Present <a href="https://penninglabs.com">Penning Labs</a>. All rights reserved.
</footer>
@ -48,9 +48,15 @@
</div>
<!-- ./wrapper -->
<!-- jQuery -->
<script src="../static/plugins/jquery/jquery.min.js"></script>
<!-- jQuery - https://code.jquery.com/jquery-3.7.1.js -->
<script src="../static/js/jquery/jquery-3.7.1.js"></script>
<!-- jQuery Datatables - https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js -->
<!-- <script src="../static/js/datatables/jquery.dataTables-1.13.6.min.js"></script> -->
<!-- Datatables Bootstrap 5 - https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js -->
<!-- <script src="../static/js/datatables/dataTables.bootstrap5.min.js"></script> -->
<!-- Bootstrap 4 -->
<script src="../static/plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
@ -76,6 +82,7 @@
// url.search = search_params.toString();
// location = url.toString();
}
function changeServer(id){
window.location.href = 'server?id=' + id;
// var url = new URL(window.location);

View file

@ -13,7 +13,7 @@
<div class="col-12">
<p>Lxconsole is an open source management console providing a web-based user interface capable of managing multiple LXD servers from a single location.</p>
<p>
<strong>Version</strong>: <span id="versionNumber">v0.2.1</span> <br />
<strong>Version</strong>: <span id="versionNumber">v0.3.0</span> <br />
<strong>License</strong>: AGPL-3.0 <br />
<strong>URL</strong>: https://lxconsole.com <br />
</p>

View file

@ -0,0 +1,160 @@
<!-- Add Modal-->
<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Add Network Zone</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="form-tab" data-toggle="tab" href="#form" role="tab" aria-controls="form" aria-selected="true">Form</a>
</li>
<li class="nav-item">
<a class="nav-link" id="json-tab" data-toggle="tab" href="#json" role="tab" aria-controls="json" aria-selected="false">JSON</a>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="form" role="tabpanel" aria-labelledby="form-tab">
<form id="addForm">
<br />
<div class="row">
<label class="col-3 col-form-label text-right">Name: <span class="text-danger">*</span></label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="name">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='(Required) - Enter in the name of the network zone. Zones must be globally unique, even across projects.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Description: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="description">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network zone.'></i>
</div>
</div>
</form>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="addItem()" data-dismiss="modal">Submit</a>
</div>
</div>
<div class="tab-pane fade" id="json" role="tabpanel" aria-labelledby="json-tab">
<br />
<div class="row">
<div class="col-12">
<div class="form-group text-right">
<pre>
<textarea name="json" class="form-control" id="jsonCreateInput" rows="16" placeholder="Enter JSON data"></textarea>
</pre>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="createItemUsingJSON()" data-dismiss="modal">Submit</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Edit Modal-->
<div class="modal fade" id="editModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Edit Network Zone</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="json-edit-tab" data-toggle="tab" href="#json-edit" role="tab" aria-controls="json-edit" aria-selected="false">JSON</a>
</li>
<!-- <li class="nav-item">
<a class="nav-link" id="rename-edit-tab" data-toggle="tab" href="#rename-edit" role="tab" aria-controls="rename-edit" aria-selected="true">Rename</a>
</li> -->
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="json-edit" role="tabpanel" aria-labelledby="json-edit-tab">
<br />
<div class="row">
<label class="col-4 col-form-label" id="itemNameEditInput"></label>
<div class="col-12">
<div class="form-group text-right">
<pre>
<textarea name="json" class="form-control" id="jsonInput" rows="16" ></textarea>
</pre>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="updateItem()" data-dismiss="modal">Submit</a>
</div>
</div>
<div class="tab-pane fade" id="rename-edit" role="tabpanel" aria-labelledby="rename-edit-tab">
<form id="renameForm">
<br />
<div class="row">
<label class="col-2 col-form-label text-right">Name:</label>
<div class="col-8">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="name">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in the new name of the network zone.'></i>
</div>
</div>
</form>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="renameItem()" data-dismiss="modal">Submit</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Delete Modal-->
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Delete Network Zone</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="mb-2" id="deleteQuestionText">
Are you sure you want to delete this network zone?
</div>
<input type="hidden" id="networkZone" class="form-control" name="server_name">
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="deleteItem()" data-dismiss="modal">Yes</a>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,535 @@
<!-- Add Forward Modal-->
<div class="modal fade" id="addForwardModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Add Network Forward</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="add-forward-form-tab" data-toggle="tab" href="#addForwardform" role="tab" aria-controls="add-forward-form-tab" aria-selected="true">Form</a>
</li>
<li class="nav-item">
<a class="nav-link" id="add-forward-json-tab" data-toggle="tab" href="#addForwardJson" role="tab" aria-controls="add-forward-json-tab" aria-selected="false">JSON</a>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="addForwardform" role="tabpanel" aria-labelledby="form-tab">
<form id="addForwardForm">
<br />
<div class="row">
<label class="col-3 col-form-label text-right">Listen Address: <span class="text-danger">*</span></label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="listen_address">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='(Required) - Enter in the name of the listening network address.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Description: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="description">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network load balancer.'></i>
</div>
</div>
<hr>
<h5>Port:</h5>
<div class="row">
<label class="col-3 col-form-label text-right">Description: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="port_description">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the port.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Listen Port: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="port_listen_port">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network load balancer.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Protocol: </label>
<div class="col-7">
<div class="form-group">
<select class="form-control" name="port_protocol">
<option value="tcp">TCP</option>
<option value="udp">UDP</option>
</select>
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network load balancer.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Target Address: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="port_target_address">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network load balancer.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Target Port: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="port_target_port">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network load balancer.'></i>
</div>
</div>
</form>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="addItem('forward')" data-dismiss="modal">Submit</a>
</div>
</div>
<div class="tab-pane fade" id="addForwardJson" role="tabpanel" aria-labelledby="json-tab">
<br />
<div class="row">
<div class="col-12">
<div class="form-group text-right">
<pre>
<textarea name="json" class="form-control" id="jsonCreateForwardInput" rows="16" placeholder="Enter JSON data"></textarea>
</pre>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="createItemUsingJSON('forward')" data-dismiss="modal">Submit</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Add Load Balancer Modal-->
<div class="modal fade" id="addLoadBalancerModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Add Network Load Balancer</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="form-tab" data-toggle="tab" href="#form" role="tab" aria-controls="form" aria-selected="true">Form</a>
</li>
<li class="nav-item">
<a class="nav-link" id="json-tab" data-toggle="tab" href="#json" role="tab" aria-controls="json" aria-selected="false">JSON</a>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="form" role="tabpanel" aria-labelledby="form-tab">
<form id="addLoadBalancerForm">
<br />
<div class="row">
<label class="col-3 col-form-label text-right">Listen Address: <span class="text-danger">*</span></label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="listen_address">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='(Required) - Enter in the name of the listening network address.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Description: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="description">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network load balancer.'></i>
</div>
</div>
<hr>
<h5>Backend:</h5>
<div class="row">
<label class="col-3 col-form-label text-right">Description: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="backend_description">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the port.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Name: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="backend_name">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network load balancer.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Target Address: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="backend_target_address">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network load balancer.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Target Port: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="backend_target_port">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network load balancer.'></i>
</div>
</div>
<hr>
<h5>Port:</h5>
<div class="row">
<label class="col-3 col-form-label text-right">Description: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="port_description">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the port.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Listen Port: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="port_listen_port">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network load balancer.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Protocol: </label>
<div class="col-7">
<div class="form-group">
<select class="form-control" name="port_protocol">
<option value="tcp">TCP</option>
<option value="udp">UDP</option>
</select>
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network load balancer.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Target Backend: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="port_target_backend">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network load balancer.'></i>
</div>
</div>
</form>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="addItem('load_balancer')" data-dismiss="modal">Submit</a>
</div>
</div>
<div class="tab-pane fade" id="json" role="tabpanel" aria-labelledby="json-tab">
<br />
<div class="row">
<div class="col-12">
<div class="form-group text-right">
<pre>
<textarea name="json" class="form-control" id="jsonCreateLoadBalancerInput" rows="16" placeholder="Enter JSON data"></textarea>
</pre>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="createItemUsingJSON('load_balancer')" data-dismiss="modal">Submit</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Add Peer Modal-->
<div class="modal fade" id="addPeerModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Add Network Peer</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="add-peer-form-tab" data-toggle="tab" href="#addPeerform" role="tab" aria-controls="add-peer-form-tab" aria-selected="true">Form</a>
</li>
<li class="nav-item">
<a class="nav-link" id="add-peer-json-tab" data-toggle="tab" href="#addPeerJson" role="tab" aria-controls="add-peer-json-tab" aria-selected="false">JSON</a>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="addPeerform" role="tabpanel" aria-labelledby="form-tab">
<form id="addPeerForm">
<br />
<div class="row">
<label class="col-3 col-form-label text-right">Name: <span class="text-danger">*</span></label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="name">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='(Required) - Enter in the name of the listening network address.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Description: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="description">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network load balancer.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Target Network: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="target_network">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network load balancer.'></i>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label text-right">Target Project: </label>
<div class="col-7">
<div class="form-group">
<input type="text" class="form-control" placeholder="" name="target_project">
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Enter in a description for the network load balancer.'></i>
</div>
</div>
</form>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="addItem('peer')" data-dismiss="modal">Submit</a>
</div>
</div>
<div class="tab-pane fade" id="addPeerJson" role="tabpanel" aria-labelledby="json-tab">
<br />
<div class="row">
<div class="col-12">
<div class="form-group text-right">
<pre>
<textarea name="json" class="form-control" id="jsonCreatePeerInput" rows="16" placeholder="Enter JSON data"></textarea>
</pre>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="createItemUsingJSON('peer')" data-dismiss="modal">Submit</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Edit Forward Modal-->
<div class="modal fade" id="editForwardModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Edit Network Forward</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="json-edit-tab" data-toggle="tab" href="#json-edit" role="tab" aria-controls="json-edit" aria-selected="false">JSON</a>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="json-edit" role="tabpanel" aria-labelledby="json-edit-tab">
<br />
<div class="row">
<label class="col-4 col-form-label" id="forwardNameEditInput"></label>
<div class="col-12">
<div class="form-group text-right">
<pre>
<textarea name="json" class="form-control" id="forwardJsonInput" rows="16" ></textarea>
</pre>
<input type="hidden" id="forwardNameHiddenInput" name="forward_name_hidden">
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="updateForward()" data-dismiss="modal">Submit</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Edit Load Balancer Modal-->
<div class="modal fade" id="editLoadBalancerModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Edit Network Load Balancer</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="json-edit-tab" data-toggle="tab" href="#json-edit" role="tab" aria-controls="json-edit" aria-selected="false">JSON</a>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="json-edit" role="tabpanel" aria-labelledby="json-edit-tab">
<br />
<div class="row">
<label class="col-4 col-form-label" id="loadBalancerNameEditInput"></label>
<div class="col-12">
<div class="form-group text-right">
<pre>
<textarea name="json" class="form-control" id="loadBalancerJsonInput" rows="16" ></textarea>
</pre>
<input type="hidden" id="loadBalancerNameHiddenInput" name="load_balancer_name_hidden">
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="updateLoadBalancer()" data-dismiss="modal">Submit</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Edit Peer Modal-->
<div class="modal fade" id="editPeerModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Edit Network Peer</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="json-edit-tab" data-toggle="tab" href="#json-edit" role="tab" aria-controls="json-edit" aria-selected="false">JSON</a>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="json-edit" role="tabpanel" aria-labelledby="json-edit-tab">
<br />
<div class="row">
<label class="col-4 col-form-label" id="peerNameEditInput"></label>
<div class="col-12">
<div class="form-group text-right">
<pre>
<textarea name="json" class="form-control" id="peerJsonInput" rows="16" ></textarea>
</pre>
<input type="hidden" id="peerNameHiddenInput" name="peer_name_hidden">
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="#" onclick="updatePeer()" data-dismiss="modal">Submit</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -1889,7 +1889,7 @@
<div class="modal-dialog modal-xl" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Edit Container</h5>
<h5 class="modal-title" id="exampleModalLabel">Edit Virtual Machine</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>

View file

@ -257,7 +257,7 @@
</div>
</div>
<div class="col-1">
<i class="far fa-sm fa-question-circle" title='Select the LXD cluster member or @group to deploy this container on. Default: none'></i>
<i class="far fa-sm fa-question-circle" title='Select the LXD cluster member or @group to deploy this virtual machine on. Default: none'></i>
</div>
</div>

View file

@ -0,0 +1,243 @@
{% extends "main.html" %}
{% block header %}
<div class="row mb-2">
<div class="col-sm-6">
<h1>{{ page_title | safe }}</h1>
</div>
<div class="col-sm-6">
<a class="btn btn-outline-primary float-sm-right mr-4" href="#" data-toggle="modal" data-target="#addModal" title="Add Network Zone" aria-hidden="true">
<i class="fas fa-plus fa-sm fa-fw"></i> Network Zone
</a>
</div>
</div>
{% endblock header %}
{% block content %}
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">Network Zones</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" onclick="reloadPageContent()" title="Refresh">
<i class="fas fa-sync"></i>
</button>
</div>
</div>
<div class="card-body">
<table class="table table-hover" id="myDataTable" width="100%" cellspacing="0">
</table>
</div>
</div>
</div>
{% endblock content %}
{% block modal %}
{% include 'modals/network-zones.html' %}
{% endblock modal %}
{% block script %}
<script>
var reloadTime = 10000;
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const serverId = urlParams.get('id');
const project = urlParams.get('project');
var editedNetworkZone = ''
applySidebarStyles();
applySidebarLinks();
populateSidebarLinks();
populateNavbarLinks();
function reloadPageContent() {
//Clear the automatic page reload
clearTimeout(pageReloadTimeout);
//Reload the datatables content
$('#myDataTable').DataTable().ajax.reload(null, false);
//Set the automatic page reload
pageReloadTimeout = setTimeout(() => { reloadPageContent(); }, reloadTime);
}
function loadPageContent(){
//Display the current project
$("#selectedProject").text(project);
//Populate the Server dropdown
$.getJSON("../api/servers/list_servers?id="+serverId, function (data) {
data = data.data
for (var index = 0; index < data.length; index++) {
if (data[index].name == '')
optionText = data[index].addr
else
optionText = data[index].name
if (data[index].id == serverId)
$('#serverListNav').append('<option value="' + data[index].id + '" selected="selected">' + optionText + '</option>');
else
$('#serverListNav').append('<option value="' + data[index].id + '">' + optionText + '</option>');
}
})
//Populate the Project dropdown
$.getJSON("../api/projects/list_projects?id="+serverId+"&project="+project, function (data) {
data = data.metadata
for (var index = 0; index < data.length; index++) {
optionText = data[index].replace('/1.0/projects/','');
if (optionText == project)
$('#projectListNav').append('<option value="' + optionText + '" selected="selected">' + optionText + '</option>');
else
$('#projectListNav').append('<option value="' + optionText + '">' + optionText + '</option>');
}
})
// Configure Datatable
$('#myDataTable').DataTable({
ajax: {
url: "../api/network-zones/list_network_zones?id="+serverId+"&project=" + project + "&recursion=1",
dataType: "json",
dataSrc: "metadata",
contentType: "application/json"
},
columns: [
{ title: "Name", data: function (row, type, set) {
if (row.hasOwnProperty('name')) {
if (row.name)
return row.name
}
return '-'
},
},
{ title: "Description", data: function (row, type, set) {
if (row.hasOwnProperty('description')) {
if (row.description)
return row.description
}
return '-'
},
},
{ title: "Actions", data: function (row, type, set) {
links = ''
if (row.hasOwnProperty('name')) {
links += '<a href="#" onclick=editItem(\''+row.name+'\')><i class="fas fa-edit fa-lg" style="color:#ddd" title="Edit" aria-hidden="true"></i></a>' +
'&nbsp' + '&nbsp' +
'<a href="#" onclick=confirmDeleteItem(\''+row.name+'\')><i class="fas fa-trash-alt fa-lg" style="color:#ddd" title="Delete" aria-hidden="true"></i></a>'
}
return links
},
},
],
order: [],
});
//Set reload page content
pageReloadTimeout = setTimeout(() => { reloadPageContent(); }, reloadTime);
}
function addItem(){
console.log("Info: adding new item");
data = $('#addForm').serialize();
$.post("../api/network-zones/add_network_zone?id="+serverId+"&project="+project, data, function (data) {
console.log(data)
if (data.error_code >= 400){
alert(data.error);
}
//Sync type
reloadPageContent();
});
}
function confirmDeleteItem(name){
console.log("Info: confirming deletion of item " + name);
$("#deleteQuestionText").text("Are you sure you want to remove the network zone " + name + "?");
$("#networkZone").val(name);
$("#deleteModal").modal('show');
}
function createItemUsingJSON(){
var json = $("#jsonCreateInput").val();
console.log("Info: adding new network zone");
$.post("../api/network-zones/add_network_zone?id="+serverId+"&project="+project, { json: json }, function (data) {
console.log(data);
if (data.error_code >= 400){
alert(data.error);
}
//Sync type
reloadPageContent();
});
}
function deleteItem(){
name = $("#networkZone").val();
console.log("Info: deleting item " + name);
$.post("../api/network-zones/delete_network_zone?id=" + serverId + "&project=" + project, { name: name }, function (data) {
console.log(data);
if (data.error_code >= 400){
alert(data.error);
}
//Sync type
reloadPageContent();
});
}
function editItem(name){
editedNetworkZone = name
console.log("Info: loading network zone " + name);
$.post("../api/network-zones/load_network_zone?id=" + serverId + "&project=" + project, { name: name }, function (data) {
console.log(data);
if (data.error_code >= 400){
alert(data.error);
}
$("#jsonInput").val(JSON.stringify(data.metadata, null, 2));
$("#editModal").modal('show');
});
}
function renameItem(){
name = editedNetworkZone
console.log("Info: renaming network zone");
data = $('#renameForm').serialize();
$.post("../api/network-zones/update_network_zone?id=" + serverId + "&project=" + project + "&name=" + encodeURI(name), data, function (data) {
console.log(data)
if (data.error_code >= 400){
alert(data.error);
}
//Sync type
reloadPageContent();
});
}
function updateItem(){
name = editedNetworkZone
var updatedJSON = $("#jsonInput").val();
console.log("Info: updating netowrk zone");
$.post("../api/network-zones/update_network_zone?id=" + serverId + "&project=" + project + "&name=" + encodeURI(name), { json: updatedJSON }, function (data) {
console.log(data);
if (data.error_code >= 400){
alert(data.error);
}
//Sync type
reloadPageContent();
});
}
$(document).ready(function(){
//If id or project variables are missing redirect to servers page
if (!serverId || !project) {
window.location.href = 'servers';
}
else {
loadPageContent()
operationStatusCheck()
}
});
</script>
{% endblock script %}

View file

@ -0,0 +1,758 @@
{% extends "main.html" %}
{% block header %}
<div class="row mb-2">
<div class="col-sm-6">
<h1>{{ page_title | safe }} <span id="network_title"></span></h1>
</div>
<div class="col-sm-6">
<a class="btn btn-outline-primary dropdown-toggle float-sm-right ml-2 mr-4" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Actions">
Network Actions
</a>
<div class="dropdown-menu dropdown-menu-right shadow animated--fade-in" aria-labelledby="dropdownMenuLink">
<a class="dropdown-item text-primary" href="#" data-toggle="modal" data-target="#addLoadBalancerModal" id="addLoadBalancer"><i class="fas fa-save fa-sm fa-fw mr-2"></i>Add Load Balancer</a>
<a class="dropdown-item text-primary" href="#" data-toggle="modal" data-target="#addForwardModal" id="addForward"><i class="fas fa-save fa-sm fa-fw mr-2"></i>Add Forward</a>
<a class="dropdown-item text-primary" href="#" data-toggle="modal" data-target="#addPeerModal" id="addPeer"><i class="fas fa-save fa-sm fa-fw mr-2"></i>Add Peer</a>
</div>
</div>
</div>
{% endblock header %}
{% block content %}
<div class="col-12">
<nav>
<div class="nav nav-tabs" id="nav-page-tab" role="tablist">
<a class="nav-item nav-link active" id="nav-home-tab" data-toggle="tab" href="#nav-home" role="tab" aria-controls="nav-home" aria-selected="true">Home</a>
<a class="nav-item nav-link" id="nav-forwards-tab" data-toggle="tab" href="#nav-forwards" role="tab" aria-controls="nav-forwards" aria-selected="false">Forwards</a>
<a class="nav-item nav-link" id="nav-load-balancers-tab" data-toggle="tab" href="#nav-load-balancers" role="tab" aria-controls="nav-load-balancers" aria-selected="false">Load Balancers</a>
<a class="nav-item nav-link" id="nav-peers-tab" data-toggle="tab" href="#nav-peers" role="tab" aria-controls="nav-peers" aria-selected="false">Peers</a>
</div>
</nav>
<div class="tab-content" id="nav-page-content">
<div class="tab-pane fade show active" id="nav-home" role="tabpanel" aria-labelledby="nav-home-tab">
<br />
<div class="row">
<div class="col-9">
<!-- DHCP Leases Card-->
<div class="card" id="dhcpLeasesCard">
<div class="card-header">
<h3 class="card-title text-primary" data-card-widget="collapse">
DHCP Leases
</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover" id="dhcpLeasesTable" width="100%" cellspacing="0">
</table>
</div>
</div>
</div>
</div>
<div class="col-3">
<div class="accordion" id="accordionExample">
<!-- Network State Card-->
<div class="card">
<div class="card-header">
<h3 class="card-title text-primary" data-card-widget="collapse">
Network State
</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
</div>
</div>
<div class="card-body">
<table class="table table-sm h6">
<tr><td class="pr-3">HWAddr:</td> <td><span id="hwaddr">-</span></td></tr>
<tr><td class="pr-3">MTU:</td> <td><span id="mtu">-</span></td></tr>
<tr><td class="pr-3">State:</td> <td><span id="state">-</span></td></tr>
<tr><td class="pr-3">Type:</td> <td><span id="type">-</span></td></tr>
<tr><td class="pr-3">Bond:</td> <td><span id="bond">-</span></td></tr>
<tr><td class="pr-3">OVN:</td> <td><span id="ovn">-</span></td></tr>
<tr><td class="pr-3">VLAN:</td> <td><span id="vlan">-</span></td></tr>
</table>
</div>
</div>
<!-- Network Counters Card-->
<div class="card">
<div class="card-header">
<h3 class="card-title text-primary" data-card-widget="collapse">
Network Counters
</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
</div>
</div>
<div class="card-body">
<table class="table table-sm h6">
<tr><td class="pr-3">bytes_received:</td> <td><span id="counters_bytes_received">-</span></td></tr>
<tr><td class="pr-3">bytes_sent:</td> <td><span id="counters_bytes_sent">-</span></td></tr>
<tr><td class="pr-3">packets_received:</td> <td><span id="packets_bytes_received">-</span></td></tr>
<tr><td class="pr-3">packets_sent:</td> <td><span id="packets_bytes_sent">-</span></td></tr>
</table>
</div>
</div>
<!-- Network Bridge Card-->
<div class="card">
<div class="card-header">
<h3 class="card-title text-primary" data-card-widget="collapse">
Network Bridge
</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
</div>
</div>
<div class="card-body">
<table class="table table-sm h6">
<tr><td class="pr-3">forward_delay:</td> <td><span id="bridge_forward_delay">-</span></td></tr>
<tr><td class="pr-3">id:</td> <td><span id="bridge_id">-</span></td></tr>
<tr><td class="pr-3">stp:</td> <td><span id="bridge_stp">-</span></td></tr>
<tr><td class="pr-3">upper_devices:</td> <td><span id="bridge_upper_devices">-</span></td></tr>
<tr><td class="pr-3">vlan_default:</td> <td><span id="bridge_vlan_default">-</span></td></tr>
<tr><td class="pr-3">vlan_filtering:</td> <td><span id="bridge_vlan_filtering">-</span></td></tr>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="nav-forwards" role="tabpanel" aria-labelledby="nav-forwards-tab">
<br />
<div class="col-12">
<!-- Forwards Card-->
<div class="card" id="forwardsCard">
<div class="card-header">
<h3 class="card-title text-primary" data-card-widget="collapse">
Network Forwards <span class="text-secondary small">(Available only on Bridge or OVN networks)</span>
</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
<button type="button" class="btn btn-tool" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-bars"></i>
</button>
<div class="dropdown-menu dropdown-menu-right">
<a href="#" class="dropdown-item" data-toggle="modal" data-target="#addForwardModal" title="Create Forward">Create Forward</a>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover" id="forwardsTable" width="100%" cellspacing="0">
</table>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="nav-load-balancers" role="tabpanel" aria-labelledby="nav-load-balancers-tab">
<br />
<div class="col-12">
<!-- Load Balancers Card-->
<div class="card" id="loadBalancersCard">
<div class="card-header">
<h3 class="card-title text-primary" data-card-widget="collapse">
Network Load Balancers <span class="text-secondary small">(Available only on OVN networks)</span>
</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
<button type="button" class="btn btn-tool" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-bars"></i>
</button>
<div class="dropdown-menu dropdown-menu-right">
<a href="#" class="dropdown-item" data-toggle="modal" data-target="#addLoadBalancerModal" title="Create Load Balancer">Create Load Balancer</a>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover" id="loadBalancersTable" width="100%" cellspacing="0">
</table>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="nav-peers" role="tabpanel" aria-labelledby="nav-peers-tab">
<br />
<div class="col-12">
<!-- Peers Card-->
<div class="card" id="peersCard">
<div class="card-header">
<h3 class="card-title text-primary" data-card-widget="collapse">
Network Peers <span class="text-secondary small">(Available only on OVN networks)</span>
</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-plus"></i></button>
<button type="button" class="btn btn-tool" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-bars"></i>
</button>
<div class="dropdown-menu dropdown-menu-right">
<a href="#" class="dropdown-item" data-toggle="modal" data-target="#addPeerModal" title="Create Peer">Create Peer</a>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover" id="peersTable" width="100%" cellspacing="0">
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
{% block modal %}
{% include 'modals/network.html' %}
{% endblock modal %}
{% block script %}
<script>
var reloadTime = 10000;
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const serverId = urlParams.get('id');
const project = urlParams.get('project');
const network = urlParams.get('network');
const network_type = urlParams.get('network_type');
var editedNetwork
populateSidebarLinks();
populateNavbarLinks();
applySidebarStyles();
applySidebarLinks();
function reloadPageContent() {
//Clear the automatic page reload
clearTimeout(pageReloadTimeout);
//Load Network State
loadNetworkState()
loadDhcpLeasesTable()
loadLoadBalancersTable()
loadForwardsTable()
loadPeersTables()
//Set the automatic page reload
pageReloadTimeout = setTimeout(() => { reloadPageContent(); }, reloadTime);
}
function loadPageContent(){
//Populate the Server dropdown
$.getJSON("../api/servers/list_servers?id="+serverId, function (data) {
data = data.data
for (var index = 0; index < data.length; index++) {
if (data[index].name == '')
optionText = data[index].addr
else
optionText = data[index].name
if (data[index].id == serverId){
$('#serverListNav').append('<option value="' + data[index].id + '" selected="selected">' + optionText + '</option>');
}
else
$('#serverListNav').append('<option value="' + data[index].id + '">' + optionText + '</option>');
}
})
//Populate the Project dropdown
$.getJSON("../api/projects/list_projects?id="+serverId+"&project="+project, function (data) {
data = data.metadata
for (var index = 0; index < data.length; index++) {
optionText = data[index].replace('/1.0/projects/','');
if (optionText == project)
$('#projectListNav').append('<option value="' + optionText + '" selected="selected">' + optionText + '</option>');
else
$('#projectListNav').append('<option value="' + optionText + '">' + optionText + '</option>');
}
})
//Load Network State
loadNetworkState()
//Load Home Tab Tables
loadDhcpLeasesTable()
//Set reload page content
pageReloadTimeout = setTimeout(() => { reloadPageContent(); }, reloadTime);
}
function loadNetworkState() {
$.get("../api/network/get_network_state?id="+serverId+"&project="+project+"&name="+network+"&recursion=1", function (data) {
data = data.metadata;
console.log(data)
if (data.hasOwnProperty('hwaddr') && data.hwaddr) { $("#hwaddr").text(data.hwaddr); }
if (data.hasOwnProperty('mtu') && data.mtu) { $("#mtu").text(data.mtu); }
if (data.hasOwnProperty('state') && data.state) { $("#state").text(data.state); }
if (data.hasOwnProperty('type') && data.type) { $("#type").text(data.type); }
if (data.hasOwnProperty('bond') && data.bond) { $("#bond").text(data.bond); }
if (data.hasOwnProperty('ovn') && data.ovn) { $("#ovn").text(data.ovn); }
if (data.hasOwnProperty('vlan') && data.vlan) { $("#vlan").text(data.vlan); }
if (data.hasOwnProperty('counters') && data.counters){
dataCounters = data.counters;
if (dataCounters.hasOwnProperty('bytes_received') && dataCounters.bytes_received) { $("#counters_bytes_received").text(dataCounters['bytes_received']); }
if (dataCounters.hasOwnProperty('bytes_sent') && dataCounters.bytes_sent) { $("#counters_bytes_sent").text(dataCounters['bytes_sent']); }
if (dataCounters.hasOwnProperty('packets_received') && dataCounters.packets_received) { $("#packets_bytes_received").text(dataCounters['packets_received']); }
if (dataCounters.hasOwnProperty('packets_sent') && dataCounters.packets_sent) { $("#packets_bytes_sent").text(dataCounters['packets_sent']); }
}
if (data.hasOwnProperty('bridge') && data.bridge){
dataBridge = data.bridge;
if (dataBridge.hasOwnProperty('forward_delay') && dataBridge.forward_delay) { $("#bridge_forward_delay").text(dataBridge['forward_delay']); }
if (dataBridge.hasOwnProperty('id') && dataBridge.id) { $("#bridge_id").text(dataBridge['id']); }
if (dataBridge.hasOwnProperty('stp')) { $("#bridge_stp").text(dataBridge['stp']); } //Boolean
if (dataBridge.hasOwnProperty('upper_devices') && dataBridge.upper_devices) { $("#bridge_upper_devices").text(dataBridge['upper_devices'].join(', ')); } //Array
if (dataBridge.hasOwnProperty('id') && dataBridge.id) { $("#bridge_id").text(dataBridge['id']); }
if (dataBridge.hasOwnProperty('vlan_default') && dataBridge.vlan_default) { $("#bridge_vlan_default").text(dataBridge['vlan_default']); }
if (dataBridge.hasOwnProperty('vlan_filtering') && dataBridge.vlan_filtering) { $("#bridge_vlan_filtering").text(dataBridge['vlan_filtering']); }
}
});
}
function loadDhcpLeasesTable() {
if ( ! $.fn.DataTable.isDataTable( '#dhcpLeasesTable' ) ) {
//Load DHCP Leases Table
$('#dhcpLeasesTable').DataTable({
ajax: {
url: "../api/network/list_network_leases?id="+serverId+"&project="+project+"&name="+network,
dataType: "json",
dataSrc: "metadata",
contentType: "application/json"
},
rowId: 'id',
stateSave: true,
columns: [
{ title: "Address", data: function (row, type, set) {
if (row.hasOwnProperty('address')) {
if (row.address)
return row.address
}
return '-'
},
},
{ title: "Hostname", data: function (row, type, set) {
if (row.hasOwnProperty('hostname')) {
if (row.hostname)
return row.hostname
}
return '-'
},
},
{ title: "HWAddr", data: function (row, type, set) {
if (row.hasOwnProperty('hwaddr')) {
if (row.hwaddr)
return row.hwaddr
}
return '-'
},
},
{ title: "Location", data: function (row, type, set) {
if (row.hasOwnProperty('location')) {
if (row.location)
return row.location
}
return '-'
},
},
{ title: "Type", data: function (row, type, set) {
if (row.hasOwnProperty('type')) {
if (row.type)
return row.type
}
return '-'
},
},
],
order: [],
});
}
else {
$('#dhcpLeasesTable').DataTable().ajax.reload(null, false);
}
}
function loadLoadBalancersTable(){
if ( ! $.fn.DataTable.isDataTable( '#loadBalancersTable' ) ) {
//Load Load Balancers Table
var loadBalancersTable = $('#loadBalancersTable').DataTable({
ajax: {
url: "../api/network/list_network_load_balancers?id="+serverId+"&project="+project+"&name="+network+"&recursion=1",
dataType: "json",
dataSrc: "metadata",
contentType: "application/json"
},
stateSave: true,
columns: [
{ title: "Listen Address", data: function (row, type, set) {
if (row.hasOwnProperty('listen_address')) {
if (row.listen_address)
return row.listen_address
}
return '-'
},
},
{ title: "Description", data: function (row, type, set) {
if (row.hasOwnProperty('description')) {
if (row.description)
return row.description
}
return '-'
},
},
{ title: "Location", data: function (row, type, set) {
if (row.hasOwnProperty('location')) {
if (row.location)
return row.location
}
return '-'
},
},
{ title: "Actions", data: function (row, type, set) {
links = ''
if (row.hasOwnProperty('listen_address')) {
if (row.listen_address){
links = '<a href="#" onclick=editLoadBalancer(\''+row.listen_address+'\')><i class="fas fa-edit fa-lg" style="color:#ddd" title="Edit" aria-hidden="true"></i></a>' +
'&nbsp' + '&nbsp' +
'<a href="#" onclick=deleteItem(\''+row.listen_address+'\',\'load_balancer\')><i class="fas fa-trash-alt fa-lg" style="color:#ddd" title="Delete" aria-hidden="true"></i></a>'
}
}
return links
},
},
],
order: [],
});
}
else {
$('#loadBalancersTable').DataTable().ajax.reload(null, false);
}
}
function loadForwardsTable() {
if ( ! $.fn.DataTable.isDataTable( '#forwardsTable' ) ) {
//Load Forwards Table
var forwardsTable = $('#forwardsTable').DataTable({
ajax: {
url: "../api/network/list_network_forwards?id="+serverId+"&project="+project+"&name="+network+"&recursion=1",
dataType: "json",
dataSrc: "metadata",
contentType: "application/json"
},
stateSave: true,
columns: [
{ title: "Listen Address", data: function (row, type, set) {
if (row.hasOwnProperty('listen_address')) {
if (row.listen_address)
return row.listen_address
}
return '-'
},
},
{ title: "Description", data: function (row, type, set) {
if (row.hasOwnProperty('description')) {
if (row.description)
return row.description
}
return '-'
},
},
{ title: "Location", data: function (row, type, set) {
if (row.hasOwnProperty('location')) {
if (row.location)
return row.location
}
return '-'
},
},
{ title: "Actions", data: function (row, type, set) {
links = ''
if (row.hasOwnProperty('listen_address')) {
if (row.listen_address){
links = '<a href="#" onclick=editForward(\''+row.listen_address+'\')><i class="fas fa-edit fa-lg" style="color:#ddd" title="Edit" aria-hidden="true"></i></a>' +
'&nbsp' + '&nbsp' +
'<a href="#" onclick=deleteItem(\''+row.listen_address+'\',\'forward\')><i class="fas fa-trash-alt fa-lg" style="color:#ddd" title="Delete" aria-hidden="true"></i></a>'
}
}
return links
},
},
],
order: [],
});
}
else {
$('#forwardsTable').DataTable().ajax.reload(null, false);
}
}
function loadPeersTables() {
if ( ! $.fn.DataTable.isDataTable( '#peersTable' ) ) {
//Load Peers Table
$('#peersTable').DataTable({
ajax: {
url: "../api/network/list_network_peers?id="+serverId+"&project="+project+"&name="+network+"&recursion=1",
dataType: "json",
dataSrc: "metadata",
contentType: "application/json"
},
rowId: 'id',
stateSave: true,
columns: [
{ title: "Name", data: function (row, type, set) {
if (row.hasOwnProperty('name')) {
if (row.name)
return row.name
}
return '-'
},
},
{ title: "Description", data: function (row, type, set) {
if (row.hasOwnProperty('description')) {
if (row.description)
return row.description
}
return '-'
},
},
{ title: "Status", data: function (row, type, set) {
if (row.hasOwnProperty('status')) {
if (row.status)
return row.status
}
return '-'
},
},
{ title: "Target Network", data: function (row, type, set) {
if (row.hasOwnProperty('target_network')) {
if (row.target_network)
return row.target_network
}
return '-'
},
},
{ title: "Target Project", data: function (row, type, set) {
if (row.hasOwnProperty('target_project')) {
if (row.target_project)
return row.target_project
}
return '-'
},
},
{ title: "Actions", data: function (row, type, set) {
links = ''
if (row.hasOwnProperty('name')) {
if (row.name){
links = '<a href="#" onclick=editPeer(\''+row.name+'\')><i class="fas fa-edit fa-lg" style="color:#ddd" title="Edit" aria-hidden="true"></i></a>' +
'&nbsp' + '&nbsp' +
'<a href="#" onclick=deleteItem(\''+row.name+'\',\'peer\')><i class="fas fa-trash-alt fa-lg" style="color:#ddd" title="Delete" aria-hidden="true"></i></a>'
}
}
return links
},
},
],
order: [],
});
}
else {
$('#peersTable').DataTable().ajax.reload(null, false);
}
}
function addItem(type){
console.log("Info: adding new " + type);
if (type == 'forward')
data = $('#addForwardForm').serialize();
else if (type == 'load_balancer')
data = $('#addLoadBalancerForm').serialize();
else if (type == 'peer')
data = $('#addPeerForm').serialize();
else
var json = {}
$.post("../api/network/add_network_" + type + "?id="+serverId+"&project="+project+"&network="+network, data, function (data) {
console.log(data)
if (data.error_code >= 400){
alert(data.error);
}
//Sync type
reloadPageContent();
});
}
function createItemUsingJSON(type){
console.log("Info: adding new " + type);
if (type == 'forward')
var json = $("#jsonCreateForwardInput").val();
else if (type == 'load_balancer')
var json = $("#jsonCreateLoadBalancerInput").val();
else if (type == 'peer')
var json = $("#jsonCreatePeerInput").val();
else
var json = {}
$.post("../api/network/add_network?id="+serverId+"&project="+project+"&network="+network, { json: json }, function (data) {
console.log(data);
if (data.error_code >= 400){
alert(data.error);
}
//Sync type
reloadPageContent();
});
}
function deleteItem(name, type){
console.log("Info: confirming deletion of " + type + " " + name);
if (confirm("Are you sure you want to delete " + type + " " + name + "?") == true) {
console.log("Info: deleting " + type + " " + name);
$.post("../api/network/delete_network_" + type + "?id=" + serverId + "&project=" + project + "&network=" + network, { name: name }, function (data) {
console.log(data);
if (data.error_code >= 400){
alert(data.error);
}
//Sync type
setTimeout(() => { reloadPageContent(); }, 1000);
operationStatusCheck()
});
}
}
function editForward(name){
console.log("Info: loading forward " + name);
$.post("../api/network/load_network_forward?id=" + serverId + "&project=" + project + "&network=" + network, { name: name }, function (data) {
console.log(data);
if (data.error_code >= 400){
alert(data.error);
}
$("#forwardJsonInput").val(JSON.stringify(data.metadata, null, 2));
$("#forwardNameHiddenInput").val(name);
$("#editForwardModal").modal('show');
});
}
function editLoadBalancer(name){
console.log("Info: loading load balancer " + name);
$.post("../api/network/load_network_load_balancer?id=" + serverId + "&project=" + project + "&network=" + network, { name: name }, function (data) {
console.log(data);
if (data.error_code >= 400){
alert(data.error);
}
$("#loadBalancerJsonInput").val(JSON.stringify(data.metadata, null, 2));
$("#loadBalancerNameHiddenInput").val(name);
$("#editLoadBalancerModal").modal('show');
});
}
function editPeer(name){
console.log("Info: loading peer " + name);
$.post("../api/network/load_network_peer?id=" + serverId + "&project=" + project + "&network=" + network, { name: name }, function (data) {
console.log(data);
if (data.error_code >= 400){
alert(data.error);
}
$("#peerJsonInput").val(JSON.stringify(data.metadata, null, 2));
$("#peerNameHiddenInput").val(name);
$("#editPeerModal").modal('show');
});
}
function updateForward(){
var name = $("#forwardNameHiddenInput").val();
var updatedJSON = $("#forwardJsonInput").val();
console.log("Info: updating forward " + name);
$.post("../api/network/update_network_forward?id=" + serverId + "&project=" + project + "&network=" + network + "&name=" + encodeURI(name), { json: updatedJSON }, function (data) {
console.log(data);
if (data.error_code >= 400){
alert(data.error);
}
//Sync type
reloadPageContent();
});
}
function updateLoadBalancer(){
var name = $("#loadBalancerNameHiddenInput").val();
var updatedJSON = $("#loadBalancerJsonInput").val();
console.log("Info: updating load balancer " + name);
$.post("../api/network/update_network_load_balancer?id=" + serverId + "&project=" + project + "&network=" + network + "&name=" + encodeURI(name), { json: updatedJSON }, function (data) {
console.log(data);
if (data.error_code >= 400){
alert(data.error);
}
//Sync type
reloadPageContent();
});
}
function updatePeer(){
var name = $("#peerNameHiddenInput").val();
var updatedJSON = $("#peerJsonInput").val();
console.log("Info: updating peer " + name);
$.post("../api/network/update_network_peer?id=" + serverId + "&project=" + project + "&network=" + network + "&name=" + encodeURI(name), { json: updatedJSON }, function (data) {
console.log(data);
if (data.error_code >= 400){
alert(data.error);
}
//Sync type
reloadPageContent();
});
}
$(document).ready(function(){
//If id or project variables are missing redirect to servers page
if (!serverId || !project) {
window.location.href = 'servers';
}
else {
$("#network_title").text(network);
loadPageContent()
operationStatusCheck()
}
});
</script>
{% endblock script %}

View file

@ -103,13 +103,14 @@
columns: [
{ title: "Name", data: function (row, type, set) {
if (row.hasOwnProperty('name')) {
if (row.name && row.managed && row.type)
return '<a href="../network?id=' + serverId + '&project=' + project + '&network=' + row.name + '&network_type=' + row.type + '">' + row.name + '</a>';
if (row.name)
return row.name
}
return '-'
},
},
{ title: "Description", data: function (row, type, set) {
if (row.hasOwnProperty('description')) {
if (row.description)
@ -501,6 +502,7 @@
else {
loadPageContent()
operationStatusCheck()
changeNetworkTypeInput()
}
});

View file

@ -30,15 +30,7 @@
</li>
<li class="nav-item">
<a class="nav-link" id="clusterGroupsLinkSidebar" href="cluster-groups" style="display: none;">
<i id="clusterGroupsIcon" class="nav-icon fas fa-layer-group"></i>
<p id="clusterGroupsSpan">
Cluster Groups
</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link" id="clusterGroupsLinkSidebar" href="cluster-groups" style="display: none;">
<i id="clusterGroupsIcon" class="nav-icon fas fa-layer-group"></i>
<i id="clusterGroupsIcon" class="nav-icon fab fa-hive"></i>
<p id="clusterGroupsSpan">
Cluster Groups
</p>
@ -118,37 +110,14 @@
</a>
</li>
<li class="nav-item">
<a class="nav-link" id="networkForwardsLinkSidebar" href="network-forwards" style="display: none;">
<i id="networkForwardsIcon" class="nav-icon fas fa-shield-alt"></i>
<p id="networkForwardsSpan">
Network Forwards
</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link" id="networkLoadBalancersLinkSidebar" href="network-load-balancers" style="display: none;">
<i id="networkLoadBalancersIcon" class="nav-icon fas fa-shield-alt"></i>
<p id="networkLoadBalancersSpan">
Network Load Balancers
</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link" id="networkPeersLinkSidebar" href="network-peers" style="display: none;">
<i id="networkPeersIcon" class="nav-icon fas fa-shield-alt"></i>
<p id="networkPeersSpan">
Network Peers
</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link" id="networkZonesLinkSidebar" href="network-zones" style="display: none;">
<i id="networkZonesIcon" class="nav-icon fas fa-shield-alt"></i>
<a class="nav-link" id="networkZonesLinkSidebar" href="network-zones">
<i id="networkZonesIcon" class="nav-icon fas fa-globe"></i>
<p id="networkZonesSpan">
Network Zones
</p>
</a>
</li>
</ul>
<ul id="additionalSidebarLinks" class="nav nav-pills nav-sidebar flex-column user-panel py-2" style="display: none;" data-widget="treeview" role="menu" data-accordion="false">

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,10 @@
flask
flask-sqlalchemy
flask-bcrypt
flask-login
flask-login==0.6.2
flask-wtf
email-validator
requests
pyOpenSSL
gunicorn
gunicorn
werkzeug==2.3.0

View file

@ -1,10 +1,5 @@
# 0.3.0
- Add network-zones
- Add network-forwards
- Add network-load-balancers
- Add network peers
# 0.4.0
- Convert to bootstrap 5
- Add dark theme option
- Add bootstrap toast notifications
- Add warnings
@ -18,7 +13,7 @@
- Add logs page
# 0.7.0
- Add expandable datatables rows where appropriate for additional information
- Add offcanvas sidebars where appropriate for additional information
- Include rename tasks in tables with edit form on new tab
# 1.0.0