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 Flask
|
||||||
from flask import jsonify
|
from flask import jsonify
|
||||||
from flask import request
|
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 import logger
|
||||||
from core.emulator.coreemu import CoreEmu
|
from core.emulator.coreemu import CoreEmu
|
||||||
|
@ -12,7 +17,12 @@ from core.enumerations import LinkTypes
|
||||||
from core.enumerations import NodeTypes
|
from core.enumerations import NodeTypes
|
||||||
from core.misc import nodeutils
|
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()
|
coreemu = CoreEmu()
|
||||||
|
|
||||||
|
@ -23,6 +33,31 @@ def link_data_str(link, key):
|
||||||
link[key] = str(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")
|
@app.route("/sessions")
|
||||||
def get_sessions():
|
def get_sessions():
|
||||||
sessions = []
|
sessions = []
|
||||||
|
@ -295,4 +330,5 @@ def get_node_links(session_id, node_id):
|
||||||
|
|
||||||
|
|
||||||
def start():
|
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