first initial working link for ptp and joining back and drawing the edge, also making use of old core icons to provide a basic look and feel, updated coloring to dark mode instead of info, seems to fit better

This commit is contained in:
Blake J. Harnden 2018-05-07 15:04:54 -07:00
parent b10c7fe502
commit 8e99af96a4
18 changed files with 1414 additions and 45 deletions

View file

@ -320,7 +320,9 @@ class IpPrefix(object):
addr = "" addr = ""
prefix_endpoint = -1 prefix_endpoint = -1
print "stop condition: %s" % (-(self.addrlen >> 3) - 1)
for i in xrange(-1, -(self.addrlen >> 3) - 1, -1): for i in xrange(-1, -(self.addrlen >> 3) - 1, -1):
print "i: %s" % i
prefix_endpoint = i prefix_endpoint = i
addr = chr(ord(self.prefix[i]) | (tmp & 0xff)) + addr addr = chr(ord(self.prefix[i]) | (tmp & 0xff)) + addr
tmp >>= 8 tmp >>= 8

View file

@ -1,3 +1,8 @@
import os
from functools import wraps
from threading import Lock
from flask import Flask from flask import Flask
from flask import jsonify from flask import jsonify
from flask import render_template from flask import render_template
@ -7,13 +12,16 @@ 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
from core.emulator.emudata import InterfaceData from core.emulator.emudata import InterfaceData, IpPrefixes
from core.emulator.emudata import LinkOptions from core.emulator.emudata import LinkOptions
from core.emulator.emudata import NodeOptions from core.emulator.emudata import NodeOptions
from core.enumerations import EventTypes from core.enumerations import EventTypes
from core.enumerations import LinkTypes 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
from core.misc.ipaddress import Ipv4Prefix, Ipv6Prefix
CORE_LOCK = Lock()
app = Flask(__name__) app = Flask(__name__)
app.config["SECRET_KEY"] = "core" app.config["SECRET_KEY"] = "core"
@ -22,6 +30,17 @@ socketio = SocketIO(app)
coreemu = CoreEmu() coreemu = CoreEmu()
def synchronized(function):
global CORE_LOCK
@wraps(function)
def wrapper(*args, **kwargs):
with CORE_LOCK:
return function(*args, **kwargs)
return wrapper
def link_data_str(link, key): def link_data_str(link, key):
value = link.get(key) value = link.get(key)
if value: if value:
@ -53,29 +72,53 @@ def home():
return render_template('index.html') return render_template('index.html')
@app.route("/ips", methods=["POST"])
def get_ips():
data = request.get_json() or {}
node_id = data["id"]
node_id = int(node_id)
ip4_prefix = data.get("ip4")
ip6_prefix = data.get("ip6")
ip4_prefixes = Ipv4Prefix(ip4_prefix)
ip6_prefixes = Ipv6Prefix(ip6_prefix)
return jsonify(
ip4=str(ip4_prefixes.addr(node_id)),
ip4mask=ip4_prefixes.prefixlen,
ip6=str(ip6_prefixes.addr(node_id)),
ip6mask=ip6_prefixes.prefixlen
)
@app.route("/sessions") @app.route("/sessions")
def get_sessions(): def get_sessions():
sessions = [] sessions = []
for session in coreemu.sessions.itervalues(): for session in coreemu.sessions.itervalues():
sessions.append({ sessions.append({
"id": session.session_id, "id": session.session_id,
"state": session.state,
"nodes": session.get_node_count() "nodes": session.get_node_count()
}) })
return jsonify(sessions=sessions) return jsonify(sessions=sessions)
@app.route("/sessions", methods=["POST"]) @app.route("/sessions", methods=["POST"])
@synchronized
def create_session(): def create_session():
session = coreemu.create_session() session = coreemu.create_session()
session.set_state(EventTypes.CONFIGURATION_STATE) session.set_state(EventTypes.CONFIGURATION_STATE)
response_data = jsonify( response_data = jsonify(
id=session.session_id, id=session.session_id,
state=session.state,
url="/sessions/%s" % session.session_id url="/sessions/%s" % session.session_id
) )
return response_data, 201 return response_data, 201
@app.route("/sessions/<int:session_id>", methods=["DELETE"]) @app.route("/sessions/<int:session_id>", methods=["DELETE"])
@synchronized
def delete_session(session_id): def delete_session(session_id):
result = coreemu.delete_session(session_id) result = coreemu.delete_session(session_id)
if result: if result:
@ -96,6 +139,7 @@ def get_session(session_id):
"id": node.objid, "id": node.objid,
"name": node.name, "name": node.name,
"type": nodeutils.get_node_type(node.__class__).value, "type": nodeutils.get_node_type(node.__class__).value,
"model": getattr(node, "type", None),
"position": { "position": {
"x": node.position.x, "x": node.position.x,
"y": node.position.y, "y": node.position.y,
@ -112,12 +156,14 @@ def get_session(session_id):
@app.route("/sessions/<int:session_id>/nodes", methods=["POST"]) @app.route("/sessions/<int:session_id>/nodes", methods=["POST"])
@synchronized
def create_node(session_id): def create_node(session_id):
session = coreemu.sessions.get(session_id) session = coreemu.sessions.get(session_id)
if not session: if not session:
return jsonify(error="session does not exist"), 404 return jsonify(error="session does not exist"), 404
data = request.get_json() or {} data = request.get_json() or {}
node_id = data.get("id")
node_type = data.get("type", NodeTypes.DEFAULT.value) node_type = data.get("type", NodeTypes.DEFAULT.value)
node_type = NodeTypes(node_type) node_type = NodeTypes(node_type)
logger.info("creating node: %s - %s", node_type.name, data) logger.info("creating node: %s - %s", node_type.name, data)
@ -130,7 +176,7 @@ def create_node(session_id):
node_options.opaque = data.get("opaque") node_options.opaque = data.get("opaque")
node_options.set_position(data.get("x"), data.get("y")) node_options.set_position(data.get("x"), data.get("y"))
node_options.set_location(data.get("lat"), data.get("lon"), data.get("alt")) node_options.set_location(data.get("lat"), data.get("lon"), data.get("alt"))
node = session.add_node(_type=node_type, node_options=node_options) node = session.add_node(_type=node_type, _id=node_id, node_options=node_options)
return jsonify( return jsonify(
id=node.objid, id=node.objid,
url="/sessions/%s/nodes/%s" % (session_id, node.objid) url="/sessions/%s/nodes/%s" % (session_id, node.objid)
@ -174,6 +220,7 @@ def get_node(session_id, node_id):
@app.route("/sessions/<int:session_id>/nodes/<node_id>", methods=["DELETE"]) @app.route("/sessions/<int:session_id>/nodes/<node_id>", methods=["DELETE"])
@synchronized
def delete_node(session_id, node_id): def delete_node(session_id, node_id):
session = coreemu.sessions.get(session_id) session = coreemu.sessions.get(session_id)
if not session: if not session:
@ -193,6 +240,7 @@ def delete_node(session_id, node_id):
@app.route("/sessions/<int:session_id>/state", methods=["PUT"]) @app.route("/sessions/<int:session_id>/state", methods=["PUT"])
@synchronized
def set_session_state(session_id): def set_session_state(session_id):
session = coreemu.sessions.get(session_id) session = coreemu.sessions.get(session_id)
if not session: if not session:
@ -204,6 +252,9 @@ def set_session_state(session_id):
session.set_state(state) session.set_state(state)
if state == EventTypes.INSTANTIATION_STATE: if state == EventTypes.INSTANTIATION_STATE:
# create session directory if it does not exist
if not os.path.exists(session.session_dir):
os.mkdir(session.session_dir)
session.instantiate() session.instantiate()
elif state == EventTypes.SHUTDOWN_STATE: elif state == EventTypes.SHUTDOWN_STATE:
session.shutdown() session.shutdown()
@ -218,6 +269,7 @@ def set_session_state(session_id):
@app.route("/sessions/<int:session_id>/links", methods=["POST"]) @app.route("/sessions/<int:session_id>/links", methods=["POST"])
@synchronized
def add_link(session_id): def add_link(session_id):
session = coreemu.sessions.get(session_id) session = coreemu.sessions.get(session_id)
if not session: if not session:
@ -282,6 +334,7 @@ def add_link(session_id):
@app.route("/sessions/<int:session_id>/links", methods=["DELETE"]) @app.route("/sessions/<int:session_id>/links", methods=["DELETE"])
@synchronized
def delete_link(session_id): def delete_link(session_id):
session = coreemu.sessions.get(session_id) session = coreemu.sessions.get(session_id)
if not session: if not session:

File diff suppressed because one or more lines are too long

BIN
webapp/static/core-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -3,8 +3,7 @@
} }
.navbar-text { .navbar-text {
color: #17a2b8 !important; color: #fff !important;
background-color: #fff;
font-weight: bold; font-weight: bold;
} }

199
webapp/static/coreip.js Normal file
View file

@ -0,0 +1,199 @@
const AF_INET = 2;
const AF_INET6 = 23;
function ord (string) {
var str = string + '';
var code = str.charCodeAt(0);
if (code >= 0xD800 && code <= 0xDBFF) {
// High surrogate (could change last hex to 0xDB7F to treat
// high private surrogates as single characters)
var hi = code;
if (str.length === 1) {
// This is just a high surrogate with no following low surrogate,
// so we return its value;
return code;
// we could also throw an error as it is not a complete character,
// but someone may want to know
}
var low = str.charCodeAt(1);
return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000
}
if (code >= 0xDC00 && code <= 0xDFFF) {
// Low surrogate
// This is just a low surrogate with no preceding high surrogate,
// so we return its value;
return code;
// we could also throw an error as it is not a complete character,
// but someone may want to know
}
return code
}
function inetNtop (a) {
var i = 0;
var m = '';
var c = [];
a += '';
if (a.length === 4) {
// IPv4
return [
a.charCodeAt(0),
a.charCodeAt(1),
a.charCodeAt(2),
a.charCodeAt(3)
].join('.');
} else if (a.length === 16) {
// IPv6
for (i = 0; i < 16; i++) {
c.push(((a.charCodeAt(i++) << 8) + a.charCodeAt(i)).toString(16));
}
return c.join(':')
.replace(/((^|:)0(?=:|$))+:?/g, function (t) {
m = (t.length > m.length) ? t : m;
return t;
})
.replace(m || ' ', '::');
} else {
// Invalid length
return false;
}
}
function inetPton(a) {
var r;
var m;
var x;
var i;
var j;
var f = String.fromCharCode;
// IPv4
m = a.match(/^(?:\d{1,3}(?:\.|$)){4}/);
if (m) {
m = m[0].split('.');
m = f(m[0]) + f(m[1]) + f(m[2]) + f(m[3]);
// Return if 4 bytes, otherwise false.
return m.length === 4 ? m : false;
}
r = /^((?:[\da-f]{1,4}(?::|)){0,8})(::)?((?:[\da-f]{1,4}(?::|)){0,8})$/;
// IPv6
m = a.match(r);
if (m) {
// Translate each hexadecimal value.
for (j = 1; j < 4; j++) {
// Indice 2 is :: and if no length, continue.
if (j === 2 || m[j].length === 0) {
continue;
}
m[j] = m[j].split(':');
for (i = 0; i < m[j].length; i++) {
m[j][i] = parseInt(m[j][i], 16);
// Would be NaN if it was blank, return false.
if (isNaN(m[j][i])) {
// Invalid IP.
return false;
}
m[j][i] = f(m[j][i] >> 8) + f(m[j][i] & 0xFF);
}
m[j] = m[j].join('');
}
x = m[1].length + m[3].length;
if (x === 16) {
return m[1] + m[3];
} else if (x < 16 && m[2].length > 0) {
return m[1] + (new Array(16 - x + 1))
.join('\x00') + m[3];
}
}
// Invalid IP
return false
}
class IpAddress {
constructor(addressFamily, address) {
console.log('ip address: ', addressFamily, address);
this.addressFamily = addressFamily;
this.address = address;
}
str() {
return inetNtop(this.address);
}
}
class IpPrefix {
constructor(addressFamily, prefix) {
this.addressFamily = addressFamily;
if (this.addressFamily === AF_INET) {
this.addressLength = 32;
} else if (this.addressFamily == AF_INET6) {
this.addressLength = 128;
} else {
throw Error(`invalid address family: ${addressFamily}`);
}
const values = prefix.split('/');
console.log(`split prefix: ${values}`);
if (values.length > 2) {
throw Error(`invalid prefix: ${prefix}`);
}
if (values.length === 2) {
this.prefixLength = parseInt(values[1]);
} else {
this.prefixLength = this.addressLength;
}
this.prefix = inetPton(values[0]);
if (this.addressLength > this.prefixLength) {
const addressBits = this.addressLength - this.prefixLength;
let netMask = ((1 << this.prefixLength) - 1) << addressBits;
prefix = '';
//in xrange(-1, -(addrbits >> 3) - 2, -1)
const stopValue = -(addressBits) - 2;
for (let i = -1; i > stopValue; --i) {
prefix = String.fromCharCode(ord(this.prefix[i]) & (netMask & 0xff)) + prefix;
netMask >>= 8;
}
this.prefix = this.prefix.slice(0, stopValue) + prefix;
}
}
getAddress(id) {
if (id in [-1, 0, 1] && this.addressLength === this.prefixLength) {
return new IpAddress(this.addressFamily, this.prefix);
}
const value = (1 << (this.addressLength - this.prefixLength)) - 1;
console.log(`address length(${this.addressLength}) prefix length(${this.prefixLength}) value(${value})`);
if (id === 0 || id > value || (this.addressFamily === AF_INET && id === value)) {
throw Error(`invalid id for prefix ${this.prefix}: ${id}`);
}
let address = '';
let prefixEndpoint = -1;
console.log('stop condition: ', -(this.addressLength >> 3) - 1);
for (let i = -1; i > -(this.addressLength >> 3) - 1; --i) {
console.log('i: ', i);
prefixEndpoint = i;
address = String.fromCharCode(ord(this.prefix[i]) | (id & 0xff)) + address;
console.log('address: ', address);
id >>= 8;
console.log('id: ', id);
if (!id) {
break
}
}
address = this.prefix.slice(0, prefixEndpoint) + address;
return new IpAddress(this.addressFamily, address);
}
}

View file

@ -1,25 +1,52 @@
const Ip4Prefix = '10.0.0.1/24';
const Ip6Prefix = '2001::/64';
const PtpNode = 12;
const NodeTypes = { const NodeTypes = {
// default router // default router
0: { 0: {
name: 'node' name: 'node',
display: 'Default'
}, },
// switch // switch
4: { 4: {
name: 'switch' name: 'switch',
display: 'Switch'
}, },
// hub // hub
5: { 5: {
name: 'hub' name: 'hub',
display: 'Hub'
}, },
// wlan // wlan
6: { 6: {
name: 'wlan' name: 'wlan',
display: 'WLAN'
},
12: {
name: 'ptp',
display: 'PTP'
} }
}; };
function getNodeType(nodeType) {
return NodeTypes[nodeType];
}
const NodeIcons = {
router: 'static/router.svg',
host: 'static/host.gif',
PC: 'static/pc.gif',
mdr: 'static/mdr.svg',
switch: 'static/lanswitch.svg',
hub: 'static/hub.svg',
wlan: 'static/wlan.gif'
};
class CoreNode { class CoreNode {
constructor(id, name, x, y) { constructor(id, type, name, x, y) {
this.id = id; this.id = id;
this.type = type;
this.name = name; this.name = name;
this.model = null; this.model = null;
this.canvas = null; this.canvas = null;
@ -32,35 +59,54 @@ class CoreNode {
this.lon = null; this.lon = null;
this.alt = null; this.alt = null;
this.emulation_id = null; this.emulation_id = null;
this.emulation_server = null; this.emulation_server = null
this.interfaces = {};
} }
getNetworkNode() { getNetworkNode() {
let iconName = getNodeType(this.type).name;
if (this.type === 0) {
iconName = this.model;
}
const icon = NodeIcons[iconName];
return { return {
id: this.id, id: this.id,
x: this.x, x: this.x,
y: this.y, y: this.y,
label: this.name, label: this.name,
node: this coreNode: this,
//color: '#FFF', //color: '#FFF',
//shape: 'image', shape: 'image',
//shapeProperties: { image: icon
// useBorderWithImage: true
//},
//image: nodeMode.image,
//type: nodeMode.nodeType
}; };
} }
json() {
return {
id: this.id,
type: this.type,
name: this.name,
model: this.model,
x: this.x,
y: this.y,
lat: this.lat,
lon: this.lon,
alt: this.alt
}
}
} }
class CoreNetwork { class CoreNetwork {
constructor(elementId) { constructor(elementId, coreRest) {
this.nodeType = NodeTypes['0']; this.coreRest = coreRest;
this.nodeModel = null; this.nodeType = 0;
this.nodeModel = 'router';
this.nodeId = 0; this.nodeId = 0;
this.container = document.getElementById(elementId); this.container = document.getElementById(elementId);
this.nodes = new vis.DataSet(); this.nodes = new vis.DataSet();
this.edges = new vis.DataSet(); this.edges = new vis.DataSet();
this.links = {};
this.networkData = { this.networkData = {
nodes: this.nodes, nodes: this.nodes,
edges: this.edges edges: this.edges
@ -88,18 +134,120 @@ class CoreNetwork {
this.edges.on('add', this.addEdge.bind(this)); this.edges.on('add', this.addEdge.bind(this));
} }
getCoreNodes() {
const coreNodes = [];
for (let node of this.nodes.get()) {
coreNodes.push(node.coreNode.json());
}
return coreNodes;
}
addCoreNode(node) {
const position = node.position;
const coreNode = new CoreNode(node.id, node.type, node.name, position.x, position.y);
coreNode.model = node.model;
this.nodes.add(coreNode.getNetworkNode());
}
nextNodeId() { nextNodeId() {
this.nodeId += 1; this.nodeId += 1;
return this.nodeId; return this.nodeId;
} }
joinedSessions(nodes) {
const self = this;
for (let node of nodes) {
if (node.type === PtpNode) {
continue;
}
this.addCoreNode(node);
}
for (let node of nodes) {
if (![4, 5, 6, 12].includes(node.type)) {
continue;
}
this.coreRest.getLinks(node.id)
.then(function(response) {
console.log('link response: ', response);
for (let linkData of response.links) {
self.createEdgeFromLink(linkData);
}
})
.catch(function(err) {
console.log('get link error: ', err);
});
}
if (nodes.length) {
this.nodeId = Math.max.apply(Math, nodes.map(function (node) {
return node.id
}));
} else {
this.nodeId = 0;
}
}
createEdgeFromLink(linkData) {
const fromNode = this.nodes.get(linkData.node1_id).coreNode;
const toNode = this.nodes.get(linkData.node2_id).coreNode;
const linkId = `${fromNode.id}-${toNode.id}`;
const interfaceOne = {
id: linkData.interface1_id,
ip4: linkData.interface1_ip4,
ip4mask: linkData.interface1_ip4_mask,
ip6: linkData.interface1_ip6,
ip6mask: linkData.interface1_ip6_mask
};
fromNode.interfaces[linkData.interface1_id] = interfaceOne;
const interfaceTwo = {
id: linkData.interface2_id,
ip4: linkData.interface2_ip4,
ip4mask: linkData.interface2_ip4_mask,
ip6: linkData.interface2_ip6,
ip6mask: linkData.interface2_ip6_mask
};
toNode.interfaces[linkData.interface2_id] = interfaceTwo;
this.links[linkId] = {
node_one: fromNode.id,
node_two: toNode.id,
interface_one: interfaceOne,
interface_two: interfaceTwo
};
const edge = {from: fromNode.id, to: toNode.id};
this.edges.add(edge);
}
async start() {
const nodes = coreNetwork.getCoreNodes();
for (let node of nodes) {
const response = await coreRest.createNode(node);
console.log('created node: ', response);
}
for (let linkId in this.links) {
const link = this.links[linkId];
const response = await coreRest.createLink(link);
console.log('created link: ', response);
}
return await coreRest.setSessionState(SessionStates.instantiation);
}
addNode(properties) { addNode(properties) {
console.log('add node event: ', properties); console.log('add node event: ', properties);
if (properties.nodes.length === 0) { if (properties.nodes.length === 0) {
const {x, y} = properties.pointer.canvas; const {x, y} = properties.pointer.canvas;
const nodeId = this.nextNodeId(); const nodeId = this.nextNodeId();
const name = `${this.nodeType.name}${nodeId}`; const nodeTypeData = getNodeType(this.nodeType);
const coreNode = new CoreNode(nodeId, name, x, y); const name = `${nodeTypeData.name}${nodeId}`;
const coreNode = new CoreNode(nodeId, this.nodeType, name, x, y);
coreNode.model = this.nodeModel; coreNode.model = this.nodeModel;
this.nodes.add(coreNode.getNetworkNode()); this.nodes.add(coreNode.getNetworkNode());
console.log('added node: ', coreNode.getNetworkNode()); console.log('added node: ', coreNode.getNetworkNode());
@ -115,10 +263,55 @@ class CoreNetwork {
this.edges.remove(edge.id); this.edges.remove(edge.id);
} }
const fromNode = this.nodes.get(edge.from).coreNode;
const toNode = this.nodes.get(edge.to).coreNode;
this.addEdgeLink(edge, fromNode, toNode)
.then(function() {
console.log('create edge link success!');
})
.catch(function(err) {
console.log('create link error: ', err);
});
// keep edge mode enabled // keep edge mode enabled
setTimeout(() => this.network.addEdgeMode(), 250); setTimeout(() => this.network.addEdgeMode(), 250);
} }
async addEdgeLink(edge, fromNode, toNode) {
const fromIps = await this.coreRest.getNodeIps(fromNode.id, Ip4Prefix, Ip6Prefix);
const toIps = await this.coreRest.getNodeIps(toNode.id, Ip4Prefix, Ip6Prefix);
console.log('link ips: ', fromIps, toIps);
const linkId = `${fromNode.id}-${toNode.id}`;
const interfaceOneId =Object.keys(fromNode.interfaces).length;
const interfaceOne = {
id: interfaceOneId,
ip4: fromIps.ip4,
ip4mask: fromIps.ip4mask,
ip6: fromIps.ip6,
ip6mask: fromIps.ip6mask
};
fromNode.interfaces[interfaceOneId] = interfaceOne;
const interfaceTwoId = Object.keys(toNode.interfaces).length;
const interfaceTwo = {
id: interfaceTwoId,
ip4: toIps.ip4,
ip4mask: toIps.ip4mask,
ip6: toIps.ip6,
ip6mask: toIps.ip6mask
};
toNode.interfaces[interfaceTwoId] = interfaceTwo;
this.links[linkId] = {
node_one: fromNode.id,
node_two: toNode.id,
interface_one: interfaceOne,
interface_two: interfaceTwo
};
}
linkMode(enabled) { linkMode(enabled) {
console.log('link mode:', enabled); console.log('link mode:', enabled);
if (enabled) { if (enabled) {
@ -129,7 +322,7 @@ class CoreNetwork {
} }
setNodeMode(nodeType, model) { setNodeMode(nodeType, model) {
this.nodeType = NodeTypes[nodeType]; this.nodeType = nodeType;
this.nodeModel = model || null; this.nodeModel = model || null;
} }
} }

View file

@ -1,9 +1,106 @@
const SessionStates = {
definition: 1,
configuration: 2,
instantiation: 3,
runtime: 4,
dataCollect: 5,
shutdown: 6
};
const SessionStateDisplay = {
1: 'Definition',
2: 'Configuration',
3: 'Instantiation',
4: 'Runtime',
5: 'Data Collect',
6: 'Shutdown'
};
async function sendJson(url, data, type) {
return await $.ajax({
url,
type,
data: JSON.stringify(data),
contentType: 'application/json',
dataType: 'json'
});
}
async function postJson(url, data) {
console.log('POST: ', url);
return await sendJson(url, data, 'POST');
}
async function putJson(url, data) {
console.log('PUT: ', url);
return await sendJson(url, data, 'PUT');
}
class CoreRest { class CoreRest {
constructor() { constructor() {
this.currentSession = null;
} }
async sessions(callback) { getStateName(state) {
const response = await $.getJSON('/sessions'); return SessionStateDisplay[state];
callback(response); }
async getSession() {
return await $.getJSON(`/sessions/${this.currentSession}`);
}
async getSessions() {
return await $.getJSON('/sessions');
}
async createSession() {
return await postJson('/sessions');
}
async shutdownSession() {
return await this.setSessionState(SessionStates.shutdown);
}
async setSessionState(state) {
return await putJson(`/sessions/${this.currentSession}/state`, {state})
}
async createNode(node) {
return await postJson(`/sessions/${this.currentSession}/nodes`, node);
}
async createLink(link) {
return await postJson(`/sessions/${this.currentSession}/links`, link);
}
async getLinks(nodeId) {
return await $.getJSON(`/sessions/${this.currentSession}/nodes/${nodeId}/links`)
}
async getNodeIps(nodeId, ip4Prefix, ip6Prefix) {
return await postJson('/ips', {
id: nodeId,
ip4: ip4Prefix,
ip6: ip6Prefix
});
}
async retrieveSession() {
let response = await this.getSessions();
const sessions = response.sessions;
console.log('current sessions: ', sessions);
const session = {id: 0, state: 0};
if (sessions.length) {
this.currentSession = sessions[0].id;
session.state = sessions[0].state;
} else {
response = await this.createSession();
this.currentSession = response.id;
session.state = response.state;
}
session.id = this.currentSession;
return session;
} }
} }

BIN
webapp/static/host.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

197
webapp/static/hub.svg Normal file
View file

@ -0,0 +1,197 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="146"
height="100"
id="svg3868"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="hub.svg">
<defs
id="defs3870">
<linearGradient
id="linearGradient5149">
<stop
id="stop5151"
offset="0"
style="stop-color:#5dacd1;stop-opacity:1;" />
<stop
id="stop5153"
offset="1"
style="stop-color:#1b4a78;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient4902">
<stop
style="stop-color:#484848;stop-opacity:1;"
offset="0"
id="stop4904" />
<stop
style="stop-color:#8f8f90;stop-opacity:0;"
offset="1"
id="stop4906" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective3876" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient4908"
x1="5.8083773"
y1="68.180191"
x2="84.600281"
y2="68.180191"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.269166,0,0,1.2912385,-2.3717949,931.82545)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient5039"
x1="105"
y1="55"
x2="140"
y2="55"
gradientUnits="userSpaceOnUse" />
<inkscape:perspective
id="perspective5049"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient3844"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.471308,0,0,0.471308,9.3066001,-238.48173)"
x1="175.71875"
y1="737.01562"
x2="470.00089"
y2="737.01562" />
<linearGradient
id="linearGradient12001">
<stop
style="stop-color:#1b4a78;stop-opacity:1;"
offset="0"
id="stop12003" />
<stop
style="stop-color:#5dacd1;stop-opacity:1;"
offset="1"
id="stop12005" />
</linearGradient>
<inkscape:perspective
id="perspective5135"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5165"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="91.162404"
inkscape:cy="35.126778"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="914"
inkscape:window-height="668"
inkscape:window-x="2157"
inkscape:window-y="214"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid4935"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
spacingx="0.5px"
spacingy="0.5px" />
</sodipodi:namedview>
<metadata
id="metadata3873">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-952.36218)">
<rect
style="fill:url(#linearGradient4908);fill-opacity:1.0;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
id="rect4390"
width="100"
height="45"
x="5"
y="997.36218" />
<path
style="fill:url(#linearGradient5039);stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;fill-opacity:1.0"
d="m 105,45 35,-40 0,45 -35,40 0,-45 z"
id="path5031"
transform="translate(0,952.36218)"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#3a78a0;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="M 105,45 5,45 40,5 140,5 105,45 z"
id="path5079"
transform="translate(0,952.36218)" />
<path
style="fill:#f2fdff;fill-opacity:0.70980392000000003;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 32.5,979.86218 7.5,-7.5 77.5,0 -7.5,7.5 -77.5,0 z"
id="path5255" />
<path
style="fill:#f2fdff;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:0.70980394"
d="m 45,27.5 -7.5,7.5 10,0 7.5,-7.5 -10,0 z"
id="path5259"
transform="translate(0,952.36218)" />
<path
id="path5261"
d="m 77.5,979.86218 -7.5,7.5 10,0 7.5,-7.5 -10,0 z"
style="fill:#f2fdff;fill-opacity:0.70980392;stroke:none" />
<path
style="fill:#f2fdff;fill-opacity:0.70980392;stroke:none"
d="m 70,964.86218 -7.5,7.5 10,0 7.5,-7.5 -10,0 z"
id="path5263" />
<path
id="path5265"
d="m 102.5,964.86218 -7.5,7.5 10,0 7.5,-7.5 -10,0 z"
style="fill:#f2fdff;fill-opacity:0.70980392;stroke:none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.4 KiB

190
webapp/static/lanswitch.svg Normal file
View file

@ -0,0 +1,190 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="146"
height="100"
id="svg3868"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="switch.svg">
<defs
id="defs3870">
<linearGradient
id="linearGradient5149">
<stop
id="stop5151"
offset="0"
style="stop-color:#5dacd1;stop-opacity:1;" />
<stop
id="stop5153"
offset="1"
style="stop-color:#1b4a78;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient4902">
<stop
style="stop-color:#484848;stop-opacity:1;"
offset="0"
id="stop4904" />
<stop
style="stop-color:#8f8f90;stop-opacity:0;"
offset="1"
id="stop4906" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective3876" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient4908"
x1="5.8083773"
y1="68.180191"
x2="84.600281"
y2="68.180191"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.269166,0,0,1.2912385,-2.3717949,931.82545)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient5039"
x1="105"
y1="55"
x2="140"
y2="55"
gradientUnits="userSpaceOnUse" />
<inkscape:perspective
id="perspective5049"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient3844"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.471308,0,0,0.471308,9.3066001,-238.48173)"
x1="175.71875"
y1="737.01562"
x2="470.00089"
y2="737.01562" />
<linearGradient
id="linearGradient12001">
<stop
style="stop-color:#1b4a78;stop-opacity:1;"
offset="0"
id="stop12003" />
<stop
style="stop-color:#5dacd1;stop-opacity:1;"
offset="1"
id="stop12005" />
</linearGradient>
<inkscape:perspective
id="perspective5135"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5165"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8284271"
inkscape:cx="91.162404"
inkscape:cy="35.126778"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="809"
inkscape:window-height="852"
inkscape:window-x="605"
inkscape:window-y="185"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid4935"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata3873">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-952.36218)">
<rect
style="fill:url(#linearGradient4908);fill-opacity:1.0;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
id="rect4390"
width="100"
height="45"
x="5"
y="997.36218" />
<path
style="fill:url(#linearGradient5039);stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;fill-opacity:1.0"
d="m 105,45 35,-40 0,45 -35,40 0,-45 z"
id="path5031"
transform="translate(0,952.36218)"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#3a78a0;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="M 105,45 5,45 40,5 140,5 105,45 z"
id="path5079"
transform="translate(0,952.36218)" />
<path
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none"
d="m 63.720704,982.3378 12.656877,-3.10116 12.656905,-3.10105 -3.523923,3.28427 18.978167,0.15164 -6.571234,6.1243 -18.978156,-0.15164 -3.523925,3.28426 -5.843452,-3.24896 -5.851259,-3.24166 z"
id="path13511" />
<path
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none"
d="M 80.174263,970.7443 74.367199,967.43074 68.560125,964.11719 65,967.36218 46.024691,967.0001 l -6.638732,6.05106 18.975309,0.36208 -3.560123,3.24499 12.690524,-2.96052 12.682595,-2.95341 z"
id="path13513" />
<path
id="path5195"
d="M 65.174263,985.7443 59.367199,982.43074 53.560125,979.11719 50,982.36218 31.024691,982.0001 l -6.638732,6.05106 18.975309,0.36208 -3.560123,3.24499 12.690524,-2.96052 12.682595,-2.95341 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
<path
id="path5209"
d="m 78.9675,967.75499 12.656877,-3.10116 12.656903,-3.10105 -3.52392,3.28427 18.97816,0.15164 -6.57123,6.1243 -18.978154,-0.15164 -3.523925,3.28426 -5.843452,-3.24896 -5.851259,-3.24166 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

178
webapp/static/mdr.svg Normal file
View file

@ -0,0 +1,178 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="146"
height="100"
id="svg13653"
sodipodi:version="0.32"
inkscape:version="0.47 r22583"
sodipodi:docname="router.svg"
version="1.0">
<defs
id="defs13655">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 99.931252 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="199.10001 : 99.931252 : 1"
inkscape:persp3d-origin="99.550003 : 66.620834 : 1"
id="perspective3835" />
<linearGradient
id="linearGradient12828">
<stop
id="stop12830"
offset="0"
style="stop-color:#484849;stop-opacity:1;" />
<stop
style="stop-color:#434344;stop-opacity:1;"
offset="0"
id="stop12862" />
<stop
id="stop12832"
offset="1.0000000"
style="stop-color:#8f8f90;stop-opacity:0.0000000;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient12828"
id="radialGradient13651"
cx="328.57144"
cy="602.7193"
fx="328.57144"
fy="602.7193"
r="147.14285"
gradientTransform="matrix(1,0,0,0.177184,0,495.9268)"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient12001">
<stop
style="stop-color:#1b4a78;stop-opacity:1;"
offset="0"
id="stop12003" />
<stop
style="stop-color:#5dacd1;stop-opacity:1;"
offset="1"
id="stop12005" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient13633"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.471308,0,0,0.471308,118.8781,123.5182)"
x1="175.71875"
y1="737.01562"
x2="470.00089"
y2="737.01562" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient3844"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.471308,0,0,0.471308,-45.6934,-239.9103)"
x1="175.71875"
y1="737.01562"
x2="470.00089"
y2="737.01562" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="118.57814"
inkscape:cy="50.488033"
inkscape:document-units="px"
inkscape:current-layer="layer1"
inkscape:window-width="1280"
inkscape:window-height="949"
inkscape:window-x="1631"
inkscape:window-y="29"
showgrid="false"
inkscape:window-maximized="0" />
<metadata
id="metadata13658">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-33.124945,-44.636248)">
<path
style="fill:url(#linearGradient3844);fill-opacity:1;stroke:none"
d="m 37.14136,72.27878 0,0.29457 c 0.006,-0.0975 0.0206,-0.19729 0.0295,-0.29457 l -0.0295,0 z m 138.62351,0 c 0.0302,0.33044 0.0589,0.66821 0.0589,1.00153 l 0,-1.00153 -0.0589,0 z m 0.0589,1.00153 c -1e-5,15.05224 -31.07495,27.26223 -69.35594,27.26223 -37.68286,1e-5 -68.3765,-11.82771 -69.32649,-26.55527 l 0,40.67979 c -0.0151,0.23376 -0.0147,0.45704 -0.0147,0.69223 0,0.22546 8.7e-4,0.45335 0.0147,0.67751 0.91151,14.74102 31.61889,26.59945 69.32649,26.59945 37.7076,0 68.41498,-11.85843 69.32648,-26.59945 l 0.0295,0 0,-0.50077 c 9.5e-4,-0.0587 0,-0.11794 0,-0.17674 0,-0.0588 9.4e-4,-0.11803 0,-0.17674 l 0,-41.90224 z"
id="path13626" />
<path
sodipodi:type="arc"
style="fill:#3a78a0;fill-opacity:1;stroke:none"
id="path11090"
sodipodi:cx="328.57144"
sodipodi:cy="602.7193"
sodipodi:rx="147.14285"
sodipodi:ry="26.071428"
d="m 475.71429,602.7193 c 0,14.39885 -65.87809,26.07143 -147.14285,26.07143 -81.26475,0 -147.14285,-11.67258 -147.14285,-26.07143 0,-14.39885 65.8781,-26.07143 147.14285,-26.07143 81.26476,0 147.14285,11.67258 147.14285,26.07143 z"
transform="matrix(0.471308,0,0,1.045917,-48.3838,-554.9944)" />
<g
id="g13565"
style="fill:#f2fdff;fill-opacity:0.71171169"
transform="matrix(0.84958,0.276715,-0.703617,0.334119,278.6313,-230.2001)">
<path
id="path13507"
d="m 328.66945,592.8253 -5.97867,10.35298 -5.97867,10.35297 6.18436,0 0,21.24074 11.53226,0 0,-21.24074 6.18435,0 -5.97867,-10.35297 -5.96496,-10.35298 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
<path
id="path13509"
d="m 328.66945,687.10951 -5.97867,-10.35298 -5.97867,-10.35297 6.18436,0 0,-21.24074 11.53226,0 0,21.24074 6.18435,0 -5.97867,10.35297 -5.96496,10.35298 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
<path
id="path13511"
d="m 333.74751,639.82449 10.35297,-5.97867 10.35297,-5.97867 0,6.18436 21.24074,0 0,11.53225 -21.24074,0 0,6.18436 -10.35297,-5.97867 -10.35297,-5.96496 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
<path
id="path13513"
d="m 323.35667,639.82449 -10.35297,-5.97867 -10.35298,-5.97867 0,6.18436 -21.24073,0 0,11.53225 21.24073,0 0,6.18436 10.35298,-5.97867 10.35297,-5.96496 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
</g>
<rect
style="fill:#f2fdff;fill-opacity:0.70980394;stroke:#000000;stroke-opacity:1"
id="rect6161"
width="91.923882"
height="37.476658"
x="52.679455"
y="60.048466"
transform="translate(33.124945,44.636248)"
rx="5.454824"
ry="5.454824" />
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:Bitstream Charter Bold"
x="91.107697"
y="135.0903"
id="text6673"><tspan
sodipodi:role="line"
id="tspan6675"
x="91.107697"
y="135.0903">MDR</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
webapp/static/pc.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

158
webapp/static/router.svg Normal file
View file

@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="146"
height="100"
id="svg13653"
sodipodi:version="0.32"
inkscape:version="0.47 r22583"
sodipodi:docname="router.svg"
version="1.0">
<defs
id="defs13655">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 99.931252 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="199.10001 : 99.931252 : 1"
inkscape:persp3d-origin="99.550003 : 66.620834 : 1"
id="perspective3835" />
<linearGradient
id="linearGradient12828">
<stop
id="stop12830"
offset="0"
style="stop-color:#484849;stop-opacity:1;" />
<stop
style="stop-color:#434344;stop-opacity:1;"
offset="0"
id="stop12862" />
<stop
id="stop12832"
offset="1.0000000"
style="stop-color:#8f8f90;stop-opacity:0.0000000;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient12828"
id="radialGradient13651"
cx="328.57144"
cy="602.7193"
fx="328.57144"
fy="602.7193"
r="147.14285"
gradientTransform="matrix(1,0,0,0.177184,0,495.9268)"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient12001">
<stop
style="stop-color:#1b4a78;stop-opacity:1;"
offset="0"
id="stop12003" />
<stop
style="stop-color:#5dacd1;stop-opacity:1;"
offset="1"
id="stop12005" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient13633"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.471308,0,0,0.471308,118.8781,123.5182)"
x1="175.71875"
y1="737.01562"
x2="470.00089"
y2="737.01562" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12001"
id="linearGradient3844"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.471308,0,0,0.471308,-45.6934,-239.9103)"
x1="175.71875"
y1="737.01562"
x2="470.00089"
y2="737.01562" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="118.57814"
inkscape:cy="50.488033"
inkscape:document-units="px"
inkscape:current-layer="layer1"
inkscape:window-width="1280"
inkscape:window-height="949"
inkscape:window-x="289"
inkscape:window-y="87"
showgrid="false"
inkscape:window-maximized="0" />
<metadata
id="metadata13658">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-33.124945,-44.636248)">
<path
style="fill:url(#linearGradient3844);fill-opacity:1;stroke:none"
d="m 37.14136,72.27878 0,0.29457 c 0.006,-0.0975 0.0206,-0.19729 0.0295,-0.29457 l -0.0295,0 z m 138.62351,0 c 0.0302,0.33044 0.0589,0.66821 0.0589,1.00153 l 0,-1.00153 -0.0589,0 z m 0.0589,1.00153 c -1e-5,15.05224 -31.07495,27.26223 -69.35594,27.26223 -37.68286,1e-5 -68.3765,-11.82771 -69.32649,-26.55527 l 0,40.67979 c -0.0151,0.23376 -0.0147,0.45704 -0.0147,0.69223 0,0.22546 8.7e-4,0.45335 0.0147,0.67751 0.91151,14.74102 31.61889,26.59945 69.32649,26.59945 37.7076,0 68.41498,-11.85843 69.32648,-26.59945 l 0.0295,0 0,-0.50077 c 9.5e-4,-0.0587 0,-0.11794 0,-0.17674 0,-0.0588 9.4e-4,-0.11803 0,-0.17674 l 0,-41.90224 z"
id="path13626" />
<path
sodipodi:type="arc"
style="fill:#3a78a0;fill-opacity:1;stroke:none"
id="path11090"
sodipodi:cx="328.57144"
sodipodi:cy="602.7193"
sodipodi:rx="147.14285"
sodipodi:ry="26.071428"
d="m 475.71429,602.7193 c 0,14.39885 -65.87809,26.07143 -147.14285,26.07143 -81.26475,0 -147.14285,-11.67258 -147.14285,-26.07143 0,-14.39885 65.8781,-26.07143 147.14285,-26.07143 81.26476,0 147.14285,11.67258 147.14285,26.07143 z"
transform="matrix(0.471308,0,0,1.045917,-48.3838,-554.9944)" />
<g
id="g13565"
style="fill:#f2fdff;fill-opacity:0.71171169"
transform="matrix(0.84958,0.276715,-0.703617,0.334119,278.6313,-230.2001)">
<path
id="path13507"
d="m 328.66945,592.8253 -5.97867,10.35298 -5.97867,10.35297 6.18436,0 0,21.24074 11.53226,0 0,-21.24074 6.18435,0 -5.97867,-10.35297 -5.96496,-10.35298 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
<path
id="path13509"
d="m 328.66945,687.10951 -5.97867,-10.35298 -5.97867,-10.35297 6.18436,0 0,-21.24074 11.53226,0 0,21.24074 6.18435,0 -5.97867,10.35297 -5.96496,10.35298 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
<path
id="path13511"
d="m 333.74751,639.82449 10.35297,-5.97867 10.35297,-5.97867 0,6.18436 21.24074,0 0,11.53225 -21.24074,0 0,6.18436 -10.35297,-5.97867 -10.35297,-5.96496 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
<path
id="path13513"
d="m 323.35667,639.82449 -10.35297,-5.97867 -10.35298,-5.97867 0,6.18436 -21.24073,0 0,11.53225 21.24073,0 0,6.18436 10.35298,-5.97867 10.35297,-5.96496 z"
style="fill:#f2fdff;fill-opacity:0.71171169;stroke:none" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because one or more lines are too long

BIN
webapp/static/wlan.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

View file

@ -9,8 +9,11 @@
</head> </head>
<body> <body>
<div class="container-fluid d-flex flex-column p-0"> <div class="container-fluid d-flex flex-column p-0">
<nav class="navbar navbar-expand navbar-dark bg-info mb-2"> <nav class="navbar navbar-expand navbar-dark bg-dark mb-2">
<span class="navbar-brand mb-0 h1">CORE</span> <div class="navbar-brand mb-0 h1">
<img src="static/core-icon.png" />
<span>CORE</span>
</div>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#nb-content" <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#nb-content"
aria-controls="nb-content" aria-expanded="false" aria-label="Toggle navigation"> aria-controls="nb-content" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
@ -51,16 +54,15 @@
</li> </li>
</ul> </ul>
<span class="navbar-text border rounded border-white p-2 mr-3">Session #</span> <span id="session-id" class="navbar-text p-2 mr-3">Session #</span>
<span class="navbar-text border rounded border-white p-2">Node: Default</span> <span id="node-display" class="navbar-text p-2">Node: Default</span>
</div> </div>
</nav> </nav>
<div class="row col"> <div class="row col">
<div class="col-2 col-xl-1"> <div class="col-2 col-xl-1">
<div class="btn-group-vertical w-100" role="group" aria-label="Side Menu"> <div class="btn-group-vertical w-100" role="group" aria-label="Side Menu">
<button type="button" class="btn btn-secondary">Select</button> <button id="run-button" type="button" class="btn btn-success">Start</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> <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"> <div class="btn-group dropright node-buttons" role="group">
<button id="menu-nodes" type="button" class="btn btn-secondary dropdown-toggle" <button id="menu-nodes" type="button" class="btn btn-secondary dropdown-toggle"
@ -100,25 +102,85 @@
<script src="static/bootstrap.min.js"></script> <script src="static/bootstrap.min.js"></script>
<script src="static/vis.min.js"></script> <script src="static/vis.min.js"></script>
<script src="static/socket.io.js"></script> <script src="static/socket.io.js"></script>
<script src="static/coreip.js"></script>
<script src="static/corerest.js"></script> <script src="static/corerest.js"></script>
<script src="static/corenetwork.js"></script> <script src="static/corenetwork.js"></script>
<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'); const $linkButton = $('#link-button');
const $nodeButtons = $('.node-buttons a');
const $sessionId = $('#session-id');
const $nodeDisplay = $('#node-display');
const $sessionsModel = $('#sessions-modal');
const $sessionsTable = $('#sessions-table');
const $runButton = $('#run-button');
function setRunButton(start) {
const $runButton = $('#run-button');
$runButton.removeClass('btn-danger btn-success');
if (start) {
$runButton.text('Start');
$runButton.addClass('btn-success');
} else {
$runButton.text('Stop');
$runButton.addClass('btn-danger');
}
}
// initial core setup
const coreRest = new CoreRest();
coreRest.retrieveSession()
.then(function (session) {
console.log('get session: ', session);
$sessionId.text(`Session: ${session.id}`);
if (session.state === SessionStates.runtime) {
setRunButton(false);
}
return coreRest.getSession();
})
.then(function(response) {
console.log('session: ', response);
const nodes = response.nodes;
coreNetwork.joinedSessions(nodes);
})
.catch(function (err) {
console.log('get session error: ', err);
});
const coreNetwork = new CoreNetwork('core-network', coreRest);
// page interactions
$linkButton.click(function () { $linkButton.click(function () {
const linkMode = !$(this).hasClass('active'); const linkMode = !$(this).hasClass('active');
coreNetwork.linkMode(linkMode); coreNetwork.linkMode(linkMode);
$(this).blur(); $(this).blur();
}); });
const $nodeButtons = $('.node-buttons a'); $runButton.click(function () {
const $this = $(this);
const start = $this.text() === 'Start';
if (start) {
coreNetwork.start()
.then(function() {
setRunButton(false);
})
.catch(function(err) {
console.log('start error: ', err);
});
} else {
coreRest.shutdownSession()
.then(function (response) {
console.log('shutdown session: ', response);
setRunButton(true);
})
.catch(function (err) {
console.log('shutdown error: ', err);
});
}
});
$nodeDisplay.text(`Node: ${coreNetwork.nodeModel}`);
$nodeButtons.click(function () { $nodeButtons.click(function () {
const $this = $(this); const $this = $(this);
const nodeType = $this.data('node'); const nodeType = $this.data('node');
@ -126,6 +188,35 @@
console.log('node creation: ', nodeType, model); console.log('node creation: ', nodeType, model);
console.log('clicked: ', this); console.log('clicked: ', this);
coreNetwork.setNodeMode(nodeType, model); coreNetwork.setNodeMode(nodeType, model);
const currentType = getNodeType(coreNetwork.nodeType);
const defaultType = getNodeType(0);
let nodeDisplay = currentType.display;
if (currentType.display === defaultType.display) {
nodeDisplay = coreNetwork.nodeModel;
}
$nodeDisplay.text(`Node: ${nodeDisplay}`);
});
// show sessions
$sessionsModel.on('shown.bs.modal', function () {
console.log('show sessions');
$sessionsTable.find('tbody tr').remove();
coreRest.getSessions()
.then(function (response) {
const sessions = response.sessions;
for (let session of sessions) {
console.log('show sessions: ', session);
const $idCell = $('<th>', {scope: 'row', text: session.id});
const $nodeCell = $('<td>', {text: session.nodes});
const stateName = coreRest.getStateName(session.state);
const $stateCell = $('<td>', {text: stateName});
const $row = $('<tr>').append([$idCell, $nodeCell, $stateCell]);
$sessionsTable.find('tbody').append($row);
}
})
.catch(function (err) {
console.log('error getting sessions: ', err);
});
}); });
console.log('connecting to ws'); console.log('connecting to ws');

View file

@ -1,17 +1,27 @@
<div id="sessions-modal" class="modal" tabindex="-1" role="dialog"> <div id="sessions-modal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document"> <div class="modal-dialog modal-lg" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header bg-dark text-white">
<h5 class="modal-title">Modal title</h5> <h5 class="modal-title">Sessions</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p>Modal body text goes here.</p> <table id="sessions-table" class="table table-hover">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Nodes</th>
<th scope="col">State</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-primary">Save changes</button> <button type="button" class="btn btn-primary">Join</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div> </div>
</div> </div>