Merge pull request #372 from pgreyson/master
iperf changes. add "K" action to kill a controller without wait state displaying
diff --git a/web/ons-demo/RELEASE_NOTES.txt b/web/ons-demo/RELEASE_NOTES.txt
index 369e9c4..3616d22 100644
--- a/web/ons-demo/RELEASE_NOTES.txt
+++ b/web/ons-demo/RELEASE_NOTES.txt
@@ -1,3 +1,8 @@
+** April 11, 2013 **
+- Use timestamps for iperf display
+This elimates spurious gaps when server responds slowly. However, gaps still appear if the server drops buffers entirely
+- Add "K" action for killing a controller
+
** April 9, 2013 **
- display number of flows for each core<->core link
- graphics tweaks
diff --git a/web/ons-demo/css/skin.default.css b/web/ons-demo/css/skin.default.css
index 9f1ecf1..2b07e33 100644
--- a/web/ons-demo/css/skin.default.css
+++ b/web/ons-demo/css/skin.default.css
@@ -167,7 +167,7 @@
path.flow {
fill: none;
- stroke-width: 5px;
+ stroke-width: 8px;
stroke: rgba(255, 255, 255, .35);
}
@@ -181,13 +181,13 @@
}
path.iperfdata {
- fill: none;
+ fill: #ccc;
stroke-width: 1px;
stroke: #ccc;
}
.iperf {
- background-color: #222;
+ background-color: black;
}
#selectedFlowsHeader .iperf {
@@ -293,7 +293,7 @@
text {
stroke: none;
fill: white;
- font-size: 16px;
+ font-size: 22px;
pointer-events: none;
}
@@ -469,7 +469,7 @@
}
.flowCount {
- font-size: 20px;
+ font-size: 24px;
fill: rgba(255, 255, 255, .75);
}
@@ -528,3 +528,21 @@
background-color: black;
}
+select {
+ margin-top: 1em;
+ -webkit-appearance: none;
+ border-radius: 0px;
+ font-size: 18px;
+ padding: .5em;
+ background-color: black;
+ color: white;
+ border: 1px solid white;
+}
+
+select:after {
+ content: 'a';
+ position: absolute;
+ right: 0px;
+ top: 0px;
+}
+
diff --git a/web/ons-demo/index.html b/web/ons-demo/index.html
index 1b88b4a..460188f 100644
--- a/web/ons-demo/index.html
+++ b/web/ons-demo/index.html
@@ -23,10 +23,11 @@
<div id='cluster-label'>ONOS Node Cluster</div>
<div id='controllers'></div>
<div id='actions'>
- <div id='action-all' class='action'>A</div>
<div id='action-local' class='action'>1</div>
- <div id='action-scale' class='action'>S</div>
<div id='action-reset' class='action'>R</div>
+ <div id='action-scale' class='action'>S</div>
+ <div id='action-all' class='action'>A</div>
+ <div id='action-kill' class='action'>K</div>
</div>
</div>
@@ -50,6 +51,7 @@
<div id='confirm-background'></div>
<div id='confirm-panel'>
<div id='confirm-prompt'>A PROMPT</div>
+ <select id='confirm-select'></select>
<div id='confirm-buttons'>
<div id='confirm-ok' class='confirm-button'>OK</div>
<div id='confirm-cancel' class='confirm-button'>CANCEL</div>
diff --git a/web/ons-demo/js/app.js b/web/ons-demo/js/app.js
index 2689b43..94c41e2 100644
--- a/web/ons-demo/js/app.js
+++ b/web/ons-demo/js/app.js
@@ -36,17 +36,24 @@
});
}
+// workaround for another Chrome v25 bug
+// viewbox transform stuff doesn't work in combination with browser zoom
+// also works in Chrome v27
+function zoomWorkaround() {
+ var zoom = window.document.body.clientWidth/window.document.width;
+ // workaround does not seem to be effective for transforming mouse coordinates
+ // map display does not use the transform stuff, so commenting out
+// d3.select('#svg-container').style('zoom', zoom);
+}
+
+d3.select(window).on('resize', zoomWorkaround);
+
appInit(function () {
// workaround for Chrome v25 bug
// if executed immediately, the view box transform logic doesn't work properly
// fixed in Chrome v27
setTimeout(function () {
-
- // workaround for another Chrome v25 bug
- // viewbox transform stuff doesn't work in combination with browser zoom
- // also works in Chrome v27
- d3.select('#svg-container').style('zoom', window.document.body.clientWidth/window.document.width);
-
+ zoomWorkaround();
sync();
}, 100);
});
diff --git a/web/ons-demo/js/constants.js b/web/ons-demo/js/constants.js
index 0ef0882..51b3aa6 100644
--- a/web/ons-demo/js/constants.js
+++ b/web/ons-demo/js/constants.js
@@ -25,6 +25,6 @@
***************************************************************************************************/
var widths = {
edge: 6,
- aggregation: 12,
- core: 18
+ aggregation: 16,
+ core: 20
}
\ No newline at end of file
diff --git a/web/ons-demo/js/controller.js b/web/ons-demo/js/controller.js
index 211fa97..f9b4baf 100644
--- a/web/ons-demo/js/controller.js
+++ b/web/ons-demo/js/controller.js
@@ -54,6 +54,14 @@
switchControllerCmd: function (cmd) {
var url = '/proxy/gui/switchctrl/' + cmd;
callURL(url);
+ },
+ resetCmd: function () {
+ var url = '/proxy/gui/reset';
+ callURL(url);
+ },
+ scaleCmd: function () {
+ var url = '/proxy/gui/scale';
+ callURL(url);
}
};
@@ -101,7 +109,11 @@
controllerFunctions.switchControllerCmd('local');
}
-function switchAll() {
- controllerFunctions.switchControllerCmd('all');
+function resetNetwork() {
+ controllerFunctions.resetCmd();
+}
+
+function scaleNetwork() {
+ controllerFunctions.scaleCmd();;
}
diff --git a/web/ons-demo/js/flows.js b/web/ons-demo/js/flows.js
index bc30d11..e21af0d 100644
--- a/web/ons-demo/js/flows.js
+++ b/web/ons-demo/js/flows.js
@@ -206,20 +206,48 @@
var updateRate = 2000; // ms
var pointsToDisplay = 1000;
- function makePoints() {
- var pts = [];
- var i;
- for (i=0; i < pointsToDisplay; ++i) {
- var sample = flow.iperfData.samples[i];
- var height = 28 * sample/1000000;
- if (height > 28)
- height = 28;
- pts.push({
- x: i * 1000/(pointsToDisplay-1),
- y: 30 - height
- })
+ function makeGraph(iperfData) {
+ var d = 'M0,0';
+
+ var now = flow.iperfData.startTime + (Date.now() - flow.iperfData.localNow)/1000;
+
+ if (iperfData.samples && iperfData.samples.length) {
+
+ var lastX;
+ var i = iperfData.samples.length - 1;
+ while (i) {
+ var sample = iperfData.samples[i];
+
+ var x = (1000 - (now - sample.time)*10);
+ // workaround for discontinuity in iperf data
+ if (x < 0) {
+ i -= 1;
+ continue;
+ }
+
+ var y = 28 * sample.value/1000000;
+ if (y > 28) {
+ y = 28;
+ }
+ if (i == iperfData.samples.length - 1) {
+ d = 'M' + x + ',30';
+ }
+
+ // handle gaps
+ // 1.5 for rounding error
+ if (lastX && lastX - x > 1.5) {
+ d += 'L' + lastX + ',30';
+ d += 'M' + x + ',30'
+ }
+ lastX = x;
+
+ d += 'L' + x + ',' + (30-y);
+
+ i -= 1;
+ }
+ d += 'L' + lastX + ',30';
}
- return pts;
+ return d;
}
if (flow.flowId) {
@@ -227,32 +255,23 @@
startIPerf(flow, duration, updateRate/interval);
flow.iperfDisplayInterval = setInterval(function () {
if (flow.iperfData) {
- while (flow.iperfData.samples.length < pointsToDisplay) {
- flow.iperfData.samples.push(0);
- }
var iperfPath = d3.select(document.getElementById(makeSelectedFlowKey(flow))).select('path');
- iperfPath.attr('d', line(makePoints()));
- flow.iperfData.samples.shift();
+ iperfPath.attr('d', makeGraph(flow.iperfData));
}
}, interval);
var animationTimeout;
+ flow.iperfData = {
+ samples: []
+ }
+ var lastTime;
flow.iperfFetchInterval = setInterval(function () {
+ console.log('Requesting iperf data');
getIPerfData(flow, function (data) {
try {
- if (!flow.iperfData) {
- flow.iperfData = {
- samples: []
- };
- var i;
- for (i = 0; i < pointsToDisplay; ++i) {
- flow.iperfData.samples.push(0);
- }
- }
-
var iperfData = JSON.parse(data);
// console.log(iperfData.timestamp);
@@ -268,12 +287,42 @@
stopFlowAnimation(flowSelection);
}, updateRate*1.5);
+ var endTime = Math.floor(iperfData['end-time']*10)/10;
+
+ var startTime = endTime - (iperfData.samples.length * interval/1000);
+ // set now on the first buffer
+ if (!flow.iperfData.startTime) {
+ flow.iperfData.startTime = startTime;
+ flow.iperfData.localNow = Date.now();
+ }
+
+ console.log('iperf buffer start time: ' + startTime);
+ if (lastTime && (startTime - lastTime) > updateRate/1000) {
+ console.log('iperf buffer gap: ' + startTime + ',' + lastTime);
+ }
+ lastTime = startTime;
+
+ // clear out the old data
while (flow.iperfData.samples.length > pointsToDisplay + iperfData.samples.length) {
flow.iperfData.samples.shift();
}
+ // if the client gets too out of sync, resynchronize
+ var clientNow = flow.iperfData.startTime + (Date.now() - flow.iperfData.localNow)/1000;
+ if (Math.abs(clientNow - startTime) > (updateRate/1000) * 2) {
+ console.log('resynchronizing now: ' + clientNow + ' => ' + startTime);
+ flow.iperfData.startTime = startTime;
+ flow.iperfData.localNow = Date.now();
+ }
+
+ var time = startTime;
iperfData.samples.forEach(function (s) {
- flow.iperfData.samples.push(s);
+ var sample = {
+ time: time,
+ value: s
+ };
+ flow.iperfData.samples.push(sample);
+ time += interval/1000;
});
}
flow.iperfData.timestamp = iperfData.timestamp;
@@ -282,7 +331,7 @@
}
// console.log(data);
});
- }, updateRate/2); // over sample to avoid gaps
+ }, updateRate*.5); // over sample to avoid gaps
}
}
diff --git a/web/ons-demo/js/init.js b/web/ons-demo/js/init.js
index f7bd55b..bf812aa 100644
--- a/web/ons-demo/js/init.js
+++ b/web/ons-demo/js/init.js
@@ -26,11 +26,29 @@
});
d3.select('#action-scale').on('click', function () {
- alert('scale')
+ var prompt = "Scale network?"
+ doConfirm(prompt, function (result) {
+ if (result) {
+ scaleNetwork();
+ }
+ });
});
d3.select('#action-reset').on('click', function () {
- alert('reset')
+ var prompt = "Reset network?"
+ doConfirm(prompt, function (result) {
+ if (result) {
+ resetNetwork();
+ }
+ });
+ });
+
+ d3.select('#action-kill').on('click', function () {
+ var prompt = "Kill ONOS node?";
+ var options = model.activeControllers;
+ doConfirm(prompt, function (result) {
+ controllerDown(result);
+ }, options);
});
createTopologyView(cb);
diff --git a/web/ons-demo/js/utils.js b/web/ons-demo/js/utils.js
index 265c661..1b4c38c 100644
--- a/web/ons-demo/js/utils.js
+++ b/web/ons-demo/js/utils.js
@@ -204,10 +204,24 @@
/***************************************************************************************************
***************************************************************************************************/
-function doConfirm(prompt, cb) {
+function doConfirm(prompt, cb, options) {
var confirm = d3.select('#confirm');
confirm.select('#confirm-prompt').text(prompt);
+ var select = d3.select(document.getElementById('confirm-select'));
+ if (options) {
+ select.style('display', 'block');
+ select.text('');
+ select.selectAll('option').
+ data(options)
+ .enter()
+ .append('option')
+ .attr('value', function (d) {return d})
+ .text(function (d) {return d});
+ } else {
+ select.style('display', 'none');
+ }
+
function show() {
confirm.style('display', '-webkit-box');
confirm.style('opacity', 0);
@@ -227,7 +241,11 @@
confirm.select('#confirm-ok').on('click', function () {
d3.select(this).on('click', null);
dismiss();
- cb(true);
+ if (options) {
+ cb(select[0][0].value);
+ } else {
+ cb(true);
+ }
});
confirm.select('#confirm-cancel').on('click', function () {