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

@ -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>
@ -197,17 +197,16 @@
// 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,6 +253,11 @@
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');
} }
@ -258,8 +266,8 @@
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;
@ -309,23 +317,75 @@
$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');
} }
}); });
$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 () { $newSessionButton.click(function () {
coreNetwork.newSession() coreNetwork.newSession()
.then(function (session) { .then(function (session) {

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>