blob: 68e567ffeb22413fbee2cc665a316ccf23ee9eb8 [file] [log] [blame]
/***************************************************************************************************
extract url parameters into a map
***************************************************************************************************/
function parseURLParameters() {
var parameters = {};
var search = location.href.split('?')[1];
if (search) {
search.split('&').forEach(function (param) {
var key = param.split('=')[0];
var value = param.split('=')[1];
parameters[key] = decodeURIComponent(value);
});
}
return parameters;
}
/***************************************************************************************************
convenience function for moving an SVG element to the front so that it draws on top
***************************************************************************************************/
d3.selection.prototype.moveToFront = function() {
return this.each(function(){
this.parentNode.appendChild(this);
});
};
/***************************************************************************************************
standard function for generating the 'd' attribute for a path from an array of points
***************************************************************************************************/
var line = d3.svg.line()
.x(function(d) {
return d.x;
})
.y(function(d) {
return d.y;
});
/***************************************************************************************************
starts the "pending" animation
***************************************************************************************************/
function setPending(selection) {
selection.classed('pending', false);
setTimeout(function () {
selection.classed('pending', true);
}, 0);
}
/***************************************************************************************************
convert angle in degrees to radians
***************************************************************************************************/
function toRadians (degrees) {
return degrees * (Math.PI / 180);
}
/***************************************************************************************************
used to generate DOM element id for this link
***************************************************************************************************/
function makeLinkKey(link) {
return link['src-switch'] + '=>' + link['dst-switch'];
}
/***************************************************************************************************
used to generate DOM element id for this flow in the topology view
***************************************************************************************************/
function makeFlowKey(flow) {
return flow.srcDpid + '=>' + flow.dstDpid;
}
/***************************************************************************************************
used to generate DOM element id for this flow in the selected flows table
***************************************************************************************************/
function makeSelectedFlowKey(flow) {
return 'S' + makeFlowKey(flow);
}
/***************************************************************************************************
update the app header using the current model
***************************************************************************************************/
function updateHeader() {
d3.select('#lastUpdate').text(new Date().toLocaleString());
var activeSwitchCount = 0;
model.edgeSwitches.forEach(function (s) {
if (s.state === 'ACTIVE') {
activeSwitchCount += 1;
}
});
model.aggregationSwitches.forEach(function (s) {
if (s.state === 'ACTIVE') {
activeSwitchCount += 1;
}
});
model.coreSwitches.forEach(function (s) {
if (s.state === 'ACTIVE') {
activeSwitchCount += 1;
}
});
d3.select('#activeSwitches').text(activeSwitchCount);
d3.select('#activeFlows').text(model.flows.length);
}
/***************************************************************************************************
update the global linkmap
***************************************************************************************************/
function updateLinkMap(links) {
linkMap = {};
links.forEach(function (link) {
var srcDPID = link['src-switch'];
var dstDPID = link['dst-switch'];
var srcMap = linkMap[srcDPID] || {};
srcMap[dstDPID] = link;
linkMap[srcDPID] = srcMap;
});
}
/***************************************************************************************************
// removes links from the pending list that are now in the model
***************************************************************************************************/
function reconcilePendingLinks(model) {
links = [];
model.links.forEach(function (link) {
links.push(link);
delete pendingLinks[makeLinkKey(link)]
})
var linkId;
for (linkId in pendingLinks) {
links.push(pendingLinks[linkId]);
}
}
/***************************************************************************************************
used by both ring and map models
***************************************************************************************************/
function createRootSVG() {
var svg = d3.select('#svg-container').append('svg:svg');
svg.append("svg:defs").append("svg:marker")
.attr("id", "arrow")
.attr("viewBox", "0 -5 10 10")
.attr("refX", -1)
.attr("markerWidth", 5)
.attr("markerHeight", 5)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-3L10,0L0,3");
return svg;
}
/***************************************************************************************************
counts the number of flows which pass through each core<->core link
***************************************************************************************************/
function countCoreLinkFlows() {
var allCounts = {};
model.flows.forEach(function (f) {
if (f.dataPath && f.dataPath.flowEntries && f.dataPath.flowEntries.length > 1) {
var flowEntries = f.dataPath.flowEntries;
var i;
for (i = 0; i < flowEntries.length - 1; i += 1) {
var linkKey = flowEntries[i].dpid.value + '=>' + flowEntries[i+1].dpid.value;
if (!allCounts[linkKey]) {
allCounts[linkKey] = 1;
} else {
allCounts[linkKey] += 1;
}
}
}
});
var coreCounts = {};
var i, j;
for (i = 0; i < model.coreSwitches.length - 1; i += 1) {
for (j = i + 1; j < model.coreSwitches.length; j += 1) {
var si = model.coreSwitches[i];
var sj = model.coreSwitches[j];
var key1 = si.dpid + '=>' + sj.dpid;
var key2 = sj.dpid + '=>' + si.dpid;
var linkCount = 0;
if (allCounts[key1]) {
linkCount += allCounts[key1];
}
if (allCounts[key2]) {
linkCount += allCounts[key2];
}
coreCounts[key1] = linkCount;
}
}
return d3.entries(coreCounts);
}
/***************************************************************************************************
***************************************************************************************************/
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);
setTimeout(function () {
confirm.style('opacity', 1);
}, 0);
}
function dismiss() {
confirm.style('opacity', 0);
confirm.on('webkitTransitionEnd', function () {
confirm.style('display', 'none');
confirm.on('webkitTransitionEnd', null);
});
}
confirm.select('#confirm-ok').on('click', function () {
d3.select(this).on('click', null);
dismiss();
if (options) {
cb(select[0][0].value);
} else {
cb(true);
}
});
confirm.select('#confirm-cancel').on('click', function () {
d3.select(this).on('click', null);
dismiss();
cb(false);
});
show();
}