initial basic mockup on gui layout before integrating with api calls
This commit is contained in:
parent
aaa125a896
commit
b10c7fe502
15 changed files with 771 additions and 2 deletions
|
@ -1,6 +1,11 @@
|
|||
import os
|
||||
|
||||
from flask import Flask
|
||||
from flask import jsonify
|
||||
from flask import request
|
||||
from flask import render_template
|
||||
from flask_socketio import SocketIO
|
||||
from flask_socketio import emit
|
||||
|
||||
from core import logger
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
|
@ -12,7 +17,12 @@ from core.enumerations import LinkTypes
|
|||
from core.enumerations import NodeTypes
|
||||
from core.misc import nodeutils
|
||||
|
||||
app = Flask(__name__)
|
||||
_TEMPLATE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates")
|
||||
logger.info("template folder: %s", _TEMPLATE_PATH)
|
||||
|
||||
app = Flask(__name__, template_folder=_TEMPLATE_PATH)
|
||||
app.config["SECRET_KEY"] = "core"
|
||||
socketio = SocketIO(app)
|
||||
|
||||
coreemu = CoreEmu()
|
||||
|
||||
|
@ -23,6 +33,31 @@ def link_data_str(link, key):
|
|||
link[key] = str(value)
|
||||
|
||||
|
||||
@socketio.on("connect")
|
||||
def websocket_connect():
|
||||
emit("info", {"message": "You are connected!"})
|
||||
socketio.emit("node", {
|
||||
"id": 1,
|
||||
"x": 100,
|
||||
"y": 101
|
||||
})
|
||||
socketio.emit("node", {
|
||||
"id": 1,
|
||||
"x": 100,
|
||||
"y": 150
|
||||
})
|
||||
|
||||
|
||||
@socketio.on("disconnect")
|
||||
def websocket_disconnect():
|
||||
logger.info("websocket client disconnected")
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def home():
|
||||
return render_template('index.html')
|
||||
|
||||
|
||||
@app.route("/sessions")
|
||||
def get_sessions():
|
||||
sessions = []
|
||||
|
@ -295,4 +330,5 @@ def get_node_links(session_id, node_id):
|
|||
|
||||
|
||||
def start():
|
||||
app.run(host="0.0.0.0")
|
||||
socketio.run(app, host="0.0.0.0")
|
||||
# app.run(host="0.0.0.0")
|
||||
|
|
328
webapp/app.py
Normal file
328
webapp/app.py
Normal file
|
@ -0,0 +1,328 @@
|
|||
from flask import Flask
|
||||
from flask import jsonify
|
||||
from flask import render_template
|
||||
from flask import request
|
||||
from flask_socketio import SocketIO
|
||||
from flask_socketio import emit
|
||||
|
||||
from core import logger
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import InterfaceData
|
||||
from core.emulator.emudata import LinkOptions
|
||||
from core.emulator.emudata import NodeOptions
|
||||
from core.enumerations import EventTypes
|
||||
from core.enumerations import LinkTypes
|
||||
from core.enumerations import NodeTypes
|
||||
from core.misc import nodeutils
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config["SECRET_KEY"] = "core"
|
||||
socketio = SocketIO(app)
|
||||
|
||||
coreemu = CoreEmu()
|
||||
|
||||
|
||||
def link_data_str(link, key):
|
||||
value = link.get(key)
|
||||
if value:
|
||||
link[key] = str(value)
|
||||
|
||||
|
||||
@socketio.on("connect")
|
||||
def websocket_connect():
|
||||
emit("info", {"message": "You are connected!"})
|
||||
socketio.emit("node", {
|
||||
"id": 1,
|
||||
"x": 100,
|
||||
"y": 101
|
||||
})
|
||||
socketio.emit("node", {
|
||||
"id": 1,
|
||||
"x": 100,
|
||||
"y": 150
|
||||
})
|
||||
|
||||
|
||||
@socketio.on("disconnect")
|
||||
def websocket_disconnect():
|
||||
logger.info("websocket client disconnected")
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def home():
|
||||
return render_template('index.html')
|
||||
|
||||
|
||||
@app.route("/sessions")
|
||||
def get_sessions():
|
||||
sessions = []
|
||||
for session in coreemu.sessions.itervalues():
|
||||
sessions.append({
|
||||
"id": session.session_id,
|
||||
"nodes": session.get_node_count()
|
||||
})
|
||||
return jsonify(sessions=sessions)
|
||||
|
||||
|
||||
@app.route("/sessions", methods=["POST"])
|
||||
def create_session():
|
||||
session = coreemu.create_session()
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
response_data = jsonify(
|
||||
id=session.session_id,
|
||||
url="/sessions/%s" % session.session_id
|
||||
)
|
||||
return response_data, 201
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>", methods=["DELETE"])
|
||||
def delete_session(session_id):
|
||||
result = coreemu.delete_session(session_id)
|
||||
if result:
|
||||
return jsonify()
|
||||
else:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>")
|
||||
def get_session(session_id):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
nodes = []
|
||||
for node in session.objects.itervalues():
|
||||
nodes.append({
|
||||
"id": node.objid,
|
||||
"name": node.name,
|
||||
"type": nodeutils.get_node_type(node.__class__).value,
|
||||
"position": {
|
||||
"x": node.position.x,
|
||||
"y": node.position.y,
|
||||
"z": node.position.z
|
||||
},
|
||||
"url": "/sessions/%s/nodes/%s" % (session_id, node.objid)
|
||||
})
|
||||
state = EventTypes(session.state)
|
||||
|
||||
return jsonify(
|
||||
state=state.name,
|
||||
nodes=nodes
|
||||
)
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/nodes", methods=["POST"])
|
||||
def create_node(session_id):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
data = request.get_json() or {}
|
||||
node_type = data.get("type", NodeTypes.DEFAULT.value)
|
||||
node_type = NodeTypes(node_type)
|
||||
logger.info("creating node: %s - %s", node_type.name, data)
|
||||
|
||||
node_options = NodeOptions(
|
||||
name=data.get("name"),
|
||||
model=data.get("model")
|
||||
)
|
||||
node_options.icon = data.get("icon")
|
||||
node_options.opaque = data.get("opaque")
|
||||
node_options.set_position(data.get("x"), data.get("y"))
|
||||
node_options.set_location(data.get("lat"), data.get("lon"), data.get("alt"))
|
||||
node = session.add_node(_type=node_type, node_options=node_options)
|
||||
return jsonify(
|
||||
id=node.objid,
|
||||
url="/sessions/%s/nodes/%s" % (session_id, node.objid)
|
||||
), 201
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/nodes/<node_id>")
|
||||
def get_node(session_id, node_id):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
if node_id.isdigit():
|
||||
node_id = int(node_id)
|
||||
node = session.objects.get(node_id)
|
||||
if not node:
|
||||
return jsonify(error="node does not exist"), 404
|
||||
|
||||
interfaces = []
|
||||
for interface_id, interface in node._netif.iteritems():
|
||||
net_id = None
|
||||
if interface.net:
|
||||
net_id = interface.net.objid
|
||||
|
||||
interfaces.append({
|
||||
"id": interface_id,
|
||||
"netid": net_id,
|
||||
"name": interface.name,
|
||||
"mac": str(interface.hwaddr),
|
||||
"mtu": interface.mtu,
|
||||
"flowid": interface.flow_id
|
||||
})
|
||||
|
||||
return jsonify(
|
||||
name=node.name,
|
||||
type=nodeutils.get_node_type(node.__class__).value,
|
||||
model=node.type,
|
||||
interfaces=interfaces,
|
||||
linksurl="/sessions/%s/nodes/%s/links" % (session_id, node.objid)
|
||||
)
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/nodes/<node_id>", methods=["DELETE"])
|
||||
def delete_node(session_id, node_id):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
if node_id.isdigit():
|
||||
node_id = int(node_id)
|
||||
node = session.objects.get(node_id)
|
||||
if not node:
|
||||
return jsonify(error="node does not exist"), 404
|
||||
|
||||
result = session.delete_node(node_id)
|
||||
if result:
|
||||
return jsonify()
|
||||
else:
|
||||
return jsonify(error="failure to delete node"), 404
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/state", methods=["PUT"])
|
||||
def set_session_state(session_id):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
data = request.get_json()
|
||||
try:
|
||||
state = EventTypes(data["state"])
|
||||
session.set_state(state)
|
||||
|
||||
if state == EventTypes.INSTANTIATION_STATE:
|
||||
session.instantiate()
|
||||
elif state == EventTypes.SHUTDOWN_STATE:
|
||||
session.shutdown()
|
||||
elif state == EventTypes.DATACOLLECT_STATE:
|
||||
session.data_collect()
|
||||
elif state == EventTypes.DEFINITION_STATE:
|
||||
session.clear()
|
||||
|
||||
return jsonify()
|
||||
except KeyError:
|
||||
return jsonify(error="invalid state"), 404
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/links", methods=["POST"])
|
||||
def add_link(session_id):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
data = request.get_json()
|
||||
|
||||
node_one = data.get("node_one")
|
||||
node_two = data.get("node_two")
|
||||
|
||||
interface_one = None
|
||||
interface_one_data = data.get("interface_one")
|
||||
if interface_one_data:
|
||||
interface_one = InterfaceData(
|
||||
_id=interface_one_data.get("id"),
|
||||
name=interface_one_data.get("name"),
|
||||
mac=interface_one_data.get("mac"),
|
||||
ip4=interface_one_data.get("ip4"),
|
||||
ip4_mask=interface_one_data.get("ip4mask"),
|
||||
ip6=interface_one_data.get("ip6"),
|
||||
ip6_mask=interface_one_data.get("ip6mask"),
|
||||
)
|
||||
|
||||
interface_two = None
|
||||
interface_two_data = data.get("interface_two")
|
||||
if interface_two_data:
|
||||
interface_two = InterfaceData(
|
||||
_id=interface_two_data.get("id"),
|
||||
name=interface_two_data.get("name"),
|
||||
mac=interface_two_data.get("mac"),
|
||||
ip4=interface_two_data.get("ip4"),
|
||||
ip4_mask=interface_two_data.get("ip4mask"),
|
||||
ip6=interface_two_data.get("ip6"),
|
||||
ip6_mask=interface_two_data.get("ip6mask"),
|
||||
)
|
||||
|
||||
link_type = None
|
||||
link_type_value = data.get("type")
|
||||
if link_type_value is not None:
|
||||
link_type = LinkTypes(link_type_value)
|
||||
|
||||
options_data = data.get("options")
|
||||
link_options = LinkOptions(_type=link_type)
|
||||
if options_data:
|
||||
link_options.delay = options_data.get("delay")
|
||||
link_options.bandwidth = options_data.get("bandwidth")
|
||||
link_options.session = options_data.get("session")
|
||||
link_options.per = options_data.get("per")
|
||||
link_options.dup = options_data.get("dup")
|
||||
link_options.jitter = options_data.get("jitter")
|
||||
link_options.mer = options_data.get("mer")
|
||||
link_options.burst = options_data.get("burst")
|
||||
link_options.mburst = options_data.get("mburst")
|
||||
link_options.unidirectional = options_data.get("unidirectional")
|
||||
link_options.key = options_data.get("key")
|
||||
link_options.opaque = options_data.get("opaque")
|
||||
|
||||
link_options = LinkOptions()
|
||||
session.add_link(node_one, node_two, interface_one, interface_two, link_options=link_options)
|
||||
|
||||
return jsonify(), 201
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/links", methods=["DELETE"])
|
||||
def delete_link(session_id):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
data = request.get_json()
|
||||
node_one = data.get("node_one")
|
||||
node_two = data.get("node_two")
|
||||
interface_one = data.get("interface_one")
|
||||
interface_two = data.get("interface_two")
|
||||
session.delete_link(node_one, node_two, interface_one, interface_two)
|
||||
return jsonify()
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/nodes/<node_id>/links")
|
||||
def get_node_links(session_id, node_id):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
if node_id.isdigit():
|
||||
node_id = int(node_id)
|
||||
node = session.objects.get(node_id)
|
||||
if not node:
|
||||
return jsonify(error="node does not exist"), 404
|
||||
|
||||
links_data = node.all_link_data(0)
|
||||
links = []
|
||||
for link_data in links_data:
|
||||
link = link_data._asdict()
|
||||
del link["message_type"]
|
||||
link_data_str(link, "interface1_ip4")
|
||||
link_data_str(link, "interface1_ip6")
|
||||
link_data_str(link, "interface1_mac")
|
||||
link_data_str(link, "interface2_ip4")
|
||||
link_data_str(link, "interface2_ip6")
|
||||
link_data_str(link, "interface2_mac")
|
||||
links.append(link)
|
||||
|
||||
return jsonify(links=links)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
socketio.run(app, host="0.0.0.0", debug=True)
|
7
webapp/static/bootstrap.min.css
vendored
Normal file
7
webapp/static/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
webapp/static/bootstrap.min.css.map
Normal file
1
webapp/static/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
7
webapp/static/bootstrap.min.js
vendored
Normal file
7
webapp/static/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
15
webapp/static/core.css
Normal file
15
webapp/static/core.css
Normal file
|
@ -0,0 +1,15 @@
|
|||
.container-fluid {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.navbar-text {
|
||||
color: #17a2b8 !important;
|
||||
background-color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.vis-network {
|
||||
background-color: #f8f9fa !important;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 10px;
|
||||
}
|
135
webapp/static/corenetwork.js
Normal file
135
webapp/static/corenetwork.js
Normal file
|
@ -0,0 +1,135 @@
|
|||
const NodeTypes = {
|
||||
// default router
|
||||
0: {
|
||||
name: 'node'
|
||||
},
|
||||
// switch
|
||||
4: {
|
||||
name: 'switch'
|
||||
},
|
||||
// hub
|
||||
5: {
|
||||
name: 'hub'
|
||||
},
|
||||
// wlan
|
||||
6: {
|
||||
name: 'wlan'
|
||||
}
|
||||
};
|
||||
|
||||
class CoreNode {
|
||||
constructor(id, name, x, y) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.model = null;
|
||||
this.canvas = null;
|
||||
this.icon = null;
|
||||
this.opaque = null;
|
||||
this.services = [];
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.lat = null;
|
||||
this.lon = null;
|
||||
this.alt = null;
|
||||
this.emulation_id = null;
|
||||
this.emulation_server = null;
|
||||
}
|
||||
|
||||
getNetworkNode() {
|
||||
return {
|
||||
id: this.id,
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
label: this.name,
|
||||
node: this
|
||||
//color: '#FFF',
|
||||
//shape: 'image',
|
||||
//shapeProperties: {
|
||||
// useBorderWithImage: true
|
||||
//},
|
||||
//image: nodeMode.image,
|
||||
//type: nodeMode.nodeType
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class CoreNetwork {
|
||||
constructor(elementId) {
|
||||
this.nodeType = NodeTypes['0'];
|
||||
this.nodeModel = null;
|
||||
this.nodeId = 0;
|
||||
this.container = document.getElementById(elementId);
|
||||
this.nodes = new vis.DataSet();
|
||||
this.edges = new vis.DataSet();
|
||||
this.networkData = {
|
||||
nodes: this.nodes,
|
||||
edges: this.edges
|
||||
};
|
||||
this.networkOptions = {
|
||||
height: '95%',
|
||||
physics: false,
|
||||
interaction: {
|
||||
selectConnectedEdges: false
|
||||
},
|
||||
edges: {
|
||||
shadow: true,
|
||||
width: 3,
|
||||
smooth: false,
|
||||
color: {
|
||||
color: '#000000'
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
shadow: true
|
||||
}
|
||||
};
|
||||
this.network = new vis.Network(this.container, this.networkData, this.networkOptions);
|
||||
this.network.on('doubleClick', this.addNode.bind(this));
|
||||
this.edges.on('add', this.addEdge.bind(this));
|
||||
}
|
||||
|
||||
nextNodeId() {
|
||||
this.nodeId += 1;
|
||||
return this.nodeId;
|
||||
}
|
||||
|
||||
addNode(properties) {
|
||||
console.log('add node event: ', properties);
|
||||
if (properties.nodes.length === 0) {
|
||||
const {x, y} = properties.pointer.canvas;
|
||||
const nodeId = this.nextNodeId();
|
||||
const name = `${this.nodeType.name}${nodeId}`;
|
||||
const coreNode = new CoreNode(nodeId, name, x, y);
|
||||
coreNode.model = this.nodeModel;
|
||||
this.nodes.add(coreNode.getNetworkNode());
|
||||
console.log('added node: ', coreNode.getNetworkNode());
|
||||
}
|
||||
}
|
||||
|
||||
addEdge(_, properties) {
|
||||
const edgeId = properties.items[0];
|
||||
const edge = this.edges.get(edgeId);
|
||||
console.log('added edge: ', edgeId, edge);
|
||||
if (edge.from === edge.to) {
|
||||
console.log('removing cyclic edge');
|
||||
this.edges.remove(edge.id);
|
||||
}
|
||||
|
||||
// keep edge mode enabled
|
||||
setTimeout(() => this.network.addEdgeMode(), 250);
|
||||
}
|
||||
|
||||
linkMode(enabled) {
|
||||
console.log('link mode:', enabled);
|
||||
if (enabled) {
|
||||
this.network.addEdgeMode();
|
||||
} else {
|
||||
this.network.disableEditMode();
|
||||
}
|
||||
}
|
||||
|
||||
setNodeMode(nodeType, model) {
|
||||
this.nodeType = NodeTypes[nodeType];
|
||||
this.nodeModel = model || null;
|
||||
}
|
||||
}
|
9
webapp/static/corerest.js
Normal file
9
webapp/static/corerest.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
class CoreRest {
|
||||
constructor() {
|
||||
}
|
||||
|
||||
async sessions(callback) {
|
||||
const response = await $.getJSON('/sessions');
|
||||
callback(response);
|
||||
}
|
||||
}
|
2
webapp/static/jquery-3.3.1.min.js
vendored
Normal file
2
webapp/static/jquery-3.3.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
5
webapp/static/popper.min.js
vendored
Normal file
5
webapp/static/popper.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
8
webapp/static/socket.io.js
Normal file
8
webapp/static/socket.io.js
Normal file
File diff suppressed because one or more lines are too long
1
webapp/static/vis.min.css
vendored
Normal file
1
webapp/static/vis.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
47
webapp/static/vis.min.js
vendored
Normal file
47
webapp/static/vis.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
149
webapp/templates/index.html
Normal file
149
webapp/templates/index.html
Normal file
|
@ -0,0 +1,149 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>CORE</title>
|
||||
<link rel="stylesheet" type="text/css" href="static/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="static/vis.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="static/core.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid d-flex flex-column p-0">
|
||||
<nav class="navbar navbar-expand navbar-dark bg-info mb-2">
|
||||
<span class="navbar-brand mb-0 h1">CORE</span>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#nb-content"
|
||||
aria-controls="nb-content" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="nb-content">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="nb-file" role="button" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="false">
|
||||
File
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="nb-file">
|
||||
<a class="dropdown-item" href="#">Open File</a>
|
||||
<a class="dropdown-item" href="#">Save File</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="nb-session" role="button" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="false">
|
||||
Sessions
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="nb-session">
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#sessions-modal">View</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="nb-help" role="button" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="false">
|
||||
Help
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="nb-help">
|
||||
<a class="dropdown-item" href="https://github.com/coreemu/core">GitHub</a>
|
||||
<a class="dropdown-item" href="http://coreemu.github.io/core/">Documentation</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<span class="navbar-text border rounded border-white p-2 mr-3">Session #</span>
|
||||
<span class="navbar-text border rounded border-white p-2">Node: Default</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="row col">
|
||||
<div class="col-2 col-xl-1">
|
||||
<div class="btn-group-vertical w-100" role="group" aria-label="Side Menu">
|
||||
<button type="button" class="btn btn-secondary">Select</button>
|
||||
<button type="button" class="btn btn-secondary">Start</button>
|
||||
<button id="link-button" type="button" class="btn btn-secondary" data-toggle="button">Link Mode</button>
|
||||
<div class="btn-group dropright node-buttons" role="group">
|
||||
<button id="menu-nodes" type="button" class="btn btn-secondary dropdown-toggle"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Nodes
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="menu-nodes">
|
||||
<a class="dropdown-item" href="#" data-node="0" data-model="router">Router</a>
|
||||
<a class="dropdown-item" href="#" data-node="0" data-model="host">Host</a>
|
||||
<a class="dropdown-item" href="#" data-node="0" data-model="PC">PC</a>
|
||||
<a class="dropdown-item" href="#" data-node="0" data-model="mdr">MDR</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-group dropright node-buttons" role="group">
|
||||
<button id="menu-devices" type="button" class="btn btn-secondary dropdown-toggle"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Devices
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="menu-devices">
|
||||
<a class="dropdown-item" href="#" data-node="5">Hub</a>
|
||||
<a class="dropdown-item" href="#" data-node="4">Switch</a>
|
||||
<a class="dropdown-item" href="#" data-node="6">WLAN</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="core-network" class="col">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'sessions_modal.html' %}
|
||||
</div>
|
||||
|
||||
<script src="static/jquery-3.3.1.min.js"></script>
|
||||
<script src="static/popper.min.js"></script>
|
||||
<script src="static/bootstrap.min.js"></script>
|
||||
<script src="static/vis.min.js"></script>
|
||||
<script src="static/socket.io.js"></script>
|
||||
<script src="static/corerest.js"></script>
|
||||
<script src="static/corenetwork.js"></script>
|
||||
|
||||
<script>
|
||||
const core = new CoreRest();
|
||||
core.sessions(function (response) {
|
||||
console.log('session response: ', response);
|
||||
});
|
||||
|
||||
const coreNetwork = new CoreNetwork('core-network');
|
||||
|
||||
const $linkButton = $('#link-button');
|
||||
$linkButton.click(function () {
|
||||
const linkMode = !$(this).hasClass('active');
|
||||
coreNetwork.linkMode(linkMode);
|
||||
$(this).blur();
|
||||
});
|
||||
|
||||
const $nodeButtons = $('.node-buttons a');
|
||||
$nodeButtons.click(function () {
|
||||
const $this = $(this);
|
||||
const nodeType = $this.data('node');
|
||||
const model = $this.data('model');
|
||||
console.log('node creation: ', nodeType, model);
|
||||
console.log('clicked: ', this);
|
||||
coreNetwork.setNodeMode(nodeType, model);
|
||||
});
|
||||
|
||||
console.log('connecting to ws');
|
||||
const ws = io.connect();
|
||||
ws.on('connection', function () {
|
||||
console.log('connected!');
|
||||
});
|
||||
ws.on('error', function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
|
||||
ws.on('info', function (data) {
|
||||
console.log(data.message);
|
||||
});
|
||||
|
||||
ws.on('node', function (data) {
|
||||
console.log(data);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
19
webapp/templates/sessions_modal.html
Normal file
19
webapp/templates/sessions_modal.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
<div id="sessions-modal" class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Modal title</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Modal body text goes here.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary">Save changes</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in a new issue