cleanup and support for editing links within the web app and rest api

This commit is contained in:
Blake J. Harnden 2018-05-11 10:23:06 -07:00
parent 5f6f718e92
commit f9200db939
8 changed files with 180 additions and 38 deletions

View file

@ -578,6 +578,7 @@ class PyCoreNet(PyCoreObj):
delay=netif.getparam("delay"), delay=netif.getparam("delay"),
bandwidth=netif.getparam("bw"), bandwidth=netif.getparam("bw"),
dup=netif.getparam("duplicate"), dup=netif.getparam("duplicate"),
per=netif.getparam("loss"),
jitter=netif.getparam("jitter") jitter=netif.getparam("jitter")
) )
@ -595,6 +596,7 @@ class PyCoreNet(PyCoreObj):
delay=netif.getparam("delay"), delay=netif.getparam("delay"),
bandwidth=netif.getparam("bw"), bandwidth=netif.getparam("bw"),
dup=netif.getparam("duplicate"), dup=netif.getparam("duplicate"),
per=netif.getparam("loss"),
jitter=netif.getparam("jitter") jitter=netif.getparam("jitter")
) )
netif.swapparams('_params_up') netif.swapparams('_params_up')

View file

@ -83,6 +83,7 @@ def link_config(network, interface, link_options, devname=None, interface_two=No
if not nodeutils.is_node(network, [NodeTypes.EMANE, NodeTypes.PHYSICAL]): if not nodeutils.is_node(network, [NodeTypes.EMANE, NodeTypes.PHYSICAL]):
config["devname"] = devname config["devname"] = devname
logger.info("configuring link for network(%s): %s", network.name, config)
network.linkconfig(**config) network.linkconfig(**config)

View file

@ -264,6 +264,7 @@ class PtpNet(LxBrNet):
delay=if1.getparam("delay"), delay=if1.getparam("delay"),
bandwidth=if1.getparam("bw"), bandwidth=if1.getparam("bw"),
dup=if1.getparam("duplicate"), dup=if1.getparam("duplicate"),
per=if1.getparam("loss"),
jitter=if1.getparam("jitter"), jitter=if1.getparam("jitter"),
interface1_id=if1.node.getifindex(if1), interface1_id=if1.node.getifindex(if1),
interface1_mac=if1.hwaddr, interface1_mac=if1.hwaddr,
@ -291,6 +292,7 @@ class PtpNet(LxBrNet):
delay=if1.getparam("delay"), delay=if1.getparam("delay"),
bandwidth=if1.getparam("bw"), bandwidth=if1.getparam("bw"),
dup=if1.getparam("duplicate"), dup=if1.getparam("duplicate"),
per=if1.getparam("loss"),
jitter=if1.getparam("jitter"), jitter=if1.getparam("jitter"),
unidirectional=1, unidirectional=1,
interface1_id=if2.node.getifindex(if2), interface1_id=if2.node.getifindex(if2),

View file

@ -1,5 +1,4 @@
import os import os
from functools import wraps from functools import wraps
from threading import Lock from threading import Lock
@ -12,7 +11,7 @@ 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, IpPrefixes from core.emulator.emudata import InterfaceData
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
@ -357,9 +356,41 @@ def add_link(session_id):
link_options.key = options_data.get("key") link_options.key = options_data.get("key")
link_options.opaque = options_data.get("opaque") 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) 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=["PUT"])
@synchronized
def edit_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")
options_data = data.get("options")
link_options = LinkOptions()
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")
session.update_link(node_one, node_two, link_options, interface_one, interface_two)
return jsonify(), 201 return jsonify(), 201

View file

@ -119,6 +119,11 @@ class CoreLink {
this.nodeTwo = nodeTwo; this.nodeTwo = nodeTwo;
this.interfaceOne = interfaceOne; this.interfaceOne = interfaceOne;
this.interfaceTwo = interfaceTwo; this.interfaceTwo = interfaceTwo;
this.bandwidth = null;
this.delay = null;
this.loss = null;
this.duplicate = null;
this.jitter = null;
} }
json() { json() {
@ -126,7 +131,14 @@ class CoreLink {
node_one: this.nodeOne, node_one: this.nodeOne,
node_two: this.nodeTwo, node_two: this.nodeTwo,
interface_one: this.interfaceOne, interface_one: this.interfaceOne,
interface_two: this.interfaceTwo interface_two: this.interfaceTwo,
options: {
bandwidth: this.bandwidth,
delay: this.delay,
per: this.loss,
dup: this.duplicate,
jitter: this.jitter
}
} }
} }
} }
@ -313,6 +325,11 @@ class CoreNetwork {
} }
const link = new CoreLink(fromNode.id, toNode.id, interfaceOne, interfaceTwo); const link = new CoreLink(fromNode.id, toNode.id, interfaceOne, interfaceTwo);
link.bandwidth = linkData.bandwidth;
link.delay = linkData.delay;
link.duplicate = linkData.dup;
link.loss = linkData.per;
link.jitter = linkData.jitter;
this.links[linkId] = link; this.links[linkId] = link;
const edge = { const edge = {

View file

@ -81,6 +81,10 @@ class CoreRest {
return await postJson(`/sessions/${this.currentSession}/links`, link); return await postJson(`/sessions/${this.currentSession}/links`, link);
} }
async editLink(link) {
return await putJson(`/sessions/${this.currentSession}/links`, link);
}
async getLinks(nodeId) { async getLinks(nodeId) {
return await $.getJSON(`/sessions/${this.currentSession}/nodes/${nodeId}/links`) return await $.getJSON(`/sessions/${this.currentSession}/nodes/${nodeId}/links`)
} }

View file

@ -11,7 +11,7 @@
<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-dark mb-2"> <nav class="navbar navbar-expand navbar-dark bg-dark mb-2">
<div class="navbar-brand mb-0 h1"> <div class="navbar-brand mb-0 h1">
<img src="static/core-icon.png" /> <img src="static/core-icon.png"/>
<span>CORE</span> <span>CORE</span>
</div> </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"
@ -58,7 +58,7 @@
<span id="session-id" class="navbar-text p-2 mr-3">Session #</span> <span id="session-id" class="navbar-text p-2 mr-3">Session #</span>
<div> <div>
<span id="node-display" class="navbar-text p-2">Create:</span> <span id="node-display" class="navbar-text p-2">Create:</span>
<img id="node-select" src="static/router.svg" /> <img id="node-select" src="static/router.svg"/>
</div> </div>
</div> </div>
</nav> </nav>
@ -113,12 +113,12 @@
{% include 'nodeedit_modal.html' %} {% include 'nodeedit_modal.html' %}
{% include 'linkedit_modal.html' %} {% include 'linkedit_modal.html' %}
<ul id="node-context" class="list-group invisible context"> <ul id="node-context" class="list-group context d-none">
<a class="list-group-item list-group-item-action" href="#" data-option="edit">Edit Node</a> <a class="list-group-item list-group-item-action" href="#" data-option="edit">Edit Node</a>
<a class="list-group-item list-group-item-action" href="#" data-option="services">Services</a> <a class="list-group-item list-group-item-action" href="#" data-option="services">Services</a>
</ul> </ul>
<ul id="edge-context" class="list-group invisible context"> <ul id="edge-context" class="list-group context d-none">
<a class="list-group-item list-group-item-action" href="#" data-option="edit">Edit Link</a> <a class="list-group-item list-group-item-action" href="#" data-option="edit">Edit Link</a>
</ul> </ul>
@ -155,7 +155,7 @@
const coreRest = new CoreRest(); const coreRest = new CoreRest();
const coreNetwork = new CoreNetwork('core-network', coreRest); const coreNetwork = new CoreNetwork('core-network', coreRest);
coreNetwork.initialSession() coreNetwork.initialSession()
.then(function(session) { .then(function (session) {
joinSession(session); joinSession(session);
}) })
.catch(function (err) { .catch(function (err) {
@ -195,19 +195,18 @@
} }
// handle network clicks // handle network clicks
coreNetwork.network.on('click', function(properties) { coreNetwork.network.on('click', function (properties) {
console.log('click properties: ', properties); console.log('click properties: ', properties);
$nodeContext.addClass('invisible'); $nodeContext.addClass('d-none');
$edgeContext.addClass('invisible'); $edgeContext.addClass('d-none');
$infoCard.removeClass('visible invisible'); $infoCard.removeClass('visible invisible');
if (properties.nodes.length === 1) { if (properties.nodes.length === 1) {
const nodeId = properties.nodes[0]; const nodeId = properties.nodes[0];
const node = coreNetwork.nodes.get(nodeId).coreNode; const node = coreNetwork.getCoreNode(nodeId);
$infoCardHeader.text(node.name); $infoCardHeader.text(node.name);
$infoCard.addClass('visible'); $infoCard.addClass('visible');
$infoCardTable.find('tbody tr').remove(); $infoCardTable.find('tbody tr').remove();
addInfoTable('Name', node.name);
addInfoTable('Model', node.model); addInfoTable('Model', node.model);
addInfoTable('X', node.x); addInfoTable('X', node.x);
addInfoTable('Y', node.y); addInfoTable('Y', node.y);
@ -226,13 +225,16 @@
const edgeId = properties.edges[0]; const edgeId = properties.edges[0];
const edge = coreNetwork.edges.get(edgeId); const edge = coreNetwork.edges.get(edgeId);
const link = edge.link; const link = edge.link;
const nodeOne = coreNetwork.getCoreNode(link.nodeOne);
const nodeTwo = coreNetwork.getCoreNode(link.nodeTwo);
console.log('clicked edge: ', link); console.log('clicked edge: ', link);
$infoCard.addClass('visible'); $infoCard.addClass('visible');
$infoCardHeader.text('Edge'); $infoCardHeader.text('Edge');
$infoCardTable.find('tbody tr').remove(); $infoCardTable.find('tbody tr').remove();
addInfoTable(link.nodeOne, null); addInfoTable(nodeOne.name, null);
const interfaceOne = link.interfaceOne; const interfaceOne = link.interfaceOne;
if (interfaceOne) { if (interfaceOne) {
addInfoTable('Interface', `eth${interfaceOne.id}`);
if (interfaceOne.ip4) { if (interfaceOne.ip4) {
addInfoTable('IP4', `${interfaceOne.ip4}/${interfaceOne.ip4mask}`); addInfoTable('IP4', `${interfaceOne.ip4}/${interfaceOne.ip4mask}`);
} }
@ -240,9 +242,10 @@
addInfoTable('IP6', `${interfaceOne.ip6}/${interfaceOne.ip6mask}`); addInfoTable('IP6', `${interfaceOne.ip6}/${interfaceOne.ip6mask}`);
} }
} }
addInfoTable(link.nodeTwo, null); addInfoTable(nodeTwo.name, null);
const interfaceTwo = link.interfaceTwo; const interfaceTwo = link.interfaceTwo;
if (interfaceTwo) { if (interfaceTwo) {
addInfoTable('Interface', `eth${interfaceTwo.id}`);
if (interfaceTwo.ip4) { if (interfaceTwo.ip4) {
addInfoTable('IP4', `${interfaceTwo.ip4}/${interfaceTwo.ip4mask}`); addInfoTable('IP4', `${interfaceTwo.ip4}/${interfaceTwo.ip4mask}`);
} }
@ -250,16 +253,21 @@
addInfoTable('IP6', `${interfaceTwo.ip6}/${interfaceTwo.ip6mask}`); addInfoTable('IP6', `${interfaceTwo.ip6}/${interfaceTwo.ip6mask}`);
} }
} }
addInfoTable('Bandwidth', edge.link.bandwidth);
addInfoTable('Delay', edge.link.delay);
addInfoTable('Duplicate', edge.link.duplicate);
addInfoTable('Loss', edge.link.loss);
addInfoTable('Jitter', edge.link.jitter);
} else { } else {
$infoCard.addClass('invisible'); $infoCard.addClass('invisible');
} }
}); });
coreNetwork.network.on('oncontext', function(properties) { coreNetwork.network.on('oncontext', function (properties) {
console.log('context event: ', properties); console.log('context event: ', properties);
properties.event.preventDefault(); properties.event.preventDefault();
$nodeContext.addClass('invisible'); $nodeContext.addClass('d-none');
$edgeContext.addClass('invisible'); $edgeContext.addClass('d-none');
const location = properties.pointer.DOM; const location = properties.pointer.DOM;
const x = properties.event.pageX; const x = properties.event.pageX;
@ -275,7 +283,7 @@
left: x, left: x,
top: y top: y
}); });
$nodeContext.removeClass('invisible'); $nodeContext.removeClass('d-none');
} else { } else {
const edgeId = coreNetwork.network.getEdgeAt(location); const edgeId = coreNetwork.network.getEdgeAt(location);
if (edgeId) { if (edgeId) {
@ -287,13 +295,13 @@
left: x, left: x,
top: y top: y
}); });
$edgeContext.removeClass('invisible'); $edgeContext.removeClass('d-none');
} }
} }
}); });
$nodeContext.click(function(event) { $nodeContext.click(function (event) {
$nodeContext.addClass('invisible'); $nodeContext.addClass('d-none');
console.log('node context click: ', event); console.log('node context click: ', event);
const nodeId = $nodeContext.data('node'); const nodeId = $nodeContext.data('node');
const node = coreNetwork.nodes.get(nodeId).coreNode; const node = coreNetwork.nodes.get(nodeId).coreNode;
@ -307,47 +315,99 @@
} }
}); });
$nodeEditButton.click(function() { $nodeEditButton.click(function () {
const $form = $nodeEditModal.find('form'); const $form = $nodeEditModal.find('form');
console.log('form data: ', $form.serialize()); console.log('node edit data: ', $form.serialize());
}); });
$edgeContext.click(function(event) { $edgeContext.click(function (event) {
$edgeContext.addClass('invisible'); $edgeContext.addClass('d-none');
console.log('edge context click: ', event); console.log('edge context click: ', event);
const edgeId = $edgeContext.data('edge'); const edgeId = $edgeContext.data('edge');
const edge = coreNetwork.edges.get(edgeId); const edge = coreNetwork.edges.get(edgeId);
const link = edge.link;
const $target = $(event.target); const $target = $(event.target);
const option = $target.data('option'); const option = $target.data('option');
console.log('edge context: ', edgeId, option); console.log('edge context: ', edgeId, option);
if (option === 'edit') { if (option === 'edit') {
// populate form with current link data
$linkEditModal.data('link', edgeId);
$linkEditModal.find('#link-bandwidth').val(link.bandwidth);
$linkEditModal.find('#link-delay').val(link.delay);
$linkEditModal.find('#link-per').val(link.loss);
$linkEditModal.find('#link-dup').val(link.duplicate);
$linkEditModal.find('#link-jitter').val(link.jitter);
// set modal name and show
$linkEditModal.find('.modal-title').text('Edit Edge'); $linkEditModal.find('.modal-title').text('Edit Edge');
$linkEditModal.modal('show'); $linkEditModal.modal('show');
} }
}); });
$newSessionButton.click(function() { $linkEditButton.click(function () {
const $form = $linkEditModal.find('form');
const formData = {};
$form.serializeArray().map(function (x) {
let value = x.value;
if (value === '') {
value = null;
} else if (!isNaN(value)) {
value = parseInt(value);
}
formData[x.name] = value;
});
console.log('link edit data: ', formData);
const edgeId = $linkEditModal.data('link');
const edge = coreNetwork.edges.get(edgeId);
const link = edge.link;
link.bandwidth = formData.bandwidth;
link.delay = formData.delay;
link.duplicate = formData.duplicate;
link.loss = formData.loss;
link.jitter = formData.jitter;
coreRest.isRunning()
.then(function (isRunning) {
if (isRunning) {
const linkEdit = link.json();
linkEdit.interface_one = linkEdit.interface_one.id;
linkEdit.interface_two = linkEdit.interface_two.id;
return coreRest.editLink(linkEdit);
}
})
.then(function (response) {
console.log('link edit success');
})
.catch(function (err) {
console.log('error editing link: ', err);
});
$linkEditModal.modal('hide');
});
$newSessionButton.click(function () {
coreNetwork.newSession() coreNetwork.newSession()
.then(function(session) { .then(function (session) {
joinSession(session); joinSession(session);
}) })
.catch(function(err) { .catch(function (err) {
console.log('error creating new session: ', err); console.log('error creating new session: ', err);
}); });
}); });
$sessionsTable.on('click', 'td', function(event) { $sessionsTable.on('click', 'td', function (event) {
const sessionId = $(this).parent('tr').data('session'); const sessionId = $(this).parent('tr').data('session');
console.log('clicked session to join: ', sessionId); console.log('clicked session to join: ', sessionId);
if (sessionId === coreRest.currentSession) { if (sessionId === coreRest.currentSession) {
console.log('same session, not changing'); console.log('same session, not changing');
} else { } else {
coreNetwork.joinSession(sessionId) coreNetwork.joinSession(sessionId)
.then(function(session) { .then(function (session) {
joinSession(session); joinSession(session);
$sessionsModal.modal('hide'); $sessionsModal.modal('hide');
}) })
.catch(function(err) { .catch(function (err) {
console.log('join session error: ', err); console.log('join session error: ', err);
}); });
} }
@ -364,10 +424,10 @@
const start = $this.text() === 'Start'; const start = $this.text() === 'Start';
if (start) { if (start) {
coreNetwork.start() coreNetwork.start()
.then(function() { .then(function () {
setRunButton(false); setRunButton(false);
}) })
.catch(function(err) { .catch(function (err) {
console.log('start error: ', err); console.log('start error: ', err);
}); });
} else { } else {

View file

@ -8,7 +8,32 @@
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p>edit stuff goes here</p> <form>
<div class="form-group">
<label for="link-bandwidth">Bandwidth</label>
<input id="link-bandwidth" name="bandwidth" class="form-control" type="text">
</div>
<div class="form-group">
<label for="link-delay">Delay</label>
<input id="link-delay" name="delay" class="form-control" type="text">
</div>
<div class="form-group">
<label for="link-loss">Loss</label>
<input id="link-loss" name="loss" class="form-control" type="text">
</div>
<div class="form-group">
<label for="link-duplicate">Duplicates</label>
<input id="link-duplicate" name="duplicate" class="form-control" type="text">
</div>
<div class="form-group">
<label for="link-jitter">Jitter</label>
<input id="link-jitter" name="jitter" class="form-control" type="text">
</div>
</form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button id="linkedit-button" type="button" class="btn btn-primary">Edit</button> <button id="linkedit-button" type="button" class="btn btn-primary">Edit</button>