blob: 99d381c89e168c9b56f00ecfdea0960f0feabb4d [file] [log] [blame]
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
ONOS GUI -- Topology Instances Module.
Defines modeling of ONOS instances.
*/
(function () {
'use strict';
// injected refs
var $log, ps, sus, gs, ts, fs, flash;
// api from topo
var api;
/*
showMastership( id )
*/
// configuration
var instCfg = {
rectPad: 8,
nodeOx: 9,
nodeOy: 9,
nodeDim: 40,
birdOx: 19,
birdOy: 21,
birdDim: 21,
uiDy: 45,
titleDy: 30,
textYOff: 20,
textYSpc: 15
},
showLogicErrors = true,
idIns = 'topo-p-instance',
instOpts = {
edge: 'left',
width: 20
};
// internal state
var onosInstances,
onosOrder,
oiShowMaster,
oiBox,
themeListener;
// ==========================
function addInstance(data) {
var id = data.id;
if (onosInstances[id]) {
updateInstance(data);
return;
}
onosInstances[id] = data;
onosOrder.push(data);
updateInstances();
}
function updateInstance(data) {
var id = data.id,
d = onosInstances[id];
if (d) {
angular.extend(d, data);
updateInstances();
} else {
logicError('updateInstance: lookup fail: ID = "' + id + '"');
}
}
function removeInstance(data) {
var id = data.id,
d = onosInstances[id];
if (d) {
var idx = fs.find(id, onosOrder);
if (idx >= 0) {
onosOrder.splice(idx, 1);
}
delete onosInstances[id];
updateInstances();
} else {
logicError('removeInstance lookup fail. ID = "' + id + '"');
}
}
// ==========================
function computeDim(self) {
var css = window.getComputedStyle(self);
return {
w: sus.stripPx(css.width),
h: sus.stripPx(css.height)
};
}
function clickInst(d) {
var el = d3.select(this),
aff = el.classed('affinity');
if (!aff) {
setAffinity(el, d);
} else {
cancelAffinity();
}
}
function setAffinity(el, d) {
d3.selectAll('.onosInst')
.classed('mastership', true)
.classed('affinity', false);
el.classed('affinity', true);
// suppress all elements except nodes whose master is this instance
api.showMastership(d.id);
oiShowMaster = true;
}
function cancelAffinity() {
d3.selectAll('.onosInst')
.classed('mastership affinity', false);
api.showMastership(null);
oiShowMaster = false;
}
function instRectAttr(dim) {
var pad = instCfg.rectPad;
return {
x: pad,
y: pad,
width: dim.w - pad*2,
height: dim.h - pad*2,
rx: 6
};
}
function viewBox(dim) {
return '0 0 ' + dim.w + ' ' + dim.h;
}
function attachUiBadge(svg) {
gs.addGlyph(svg, 'uiAttached', 24, true, [28, instCfg.uiDy])
.classed('badgeIcon uiBadge', true);
}
function attachReadyBadge(svg) {
gs.addGlyph(svg, 'checkMark', 16, true, [12, instCfg.uiDy + 4])
.classed('badgeIcon readyBadge', true);
}
function instColor(id, online) {
return sus.cat7().getColor(id, !online, ts.theme());
}
// ==============================
function updateInstances() {
var onoses = oiBox.el().selectAll('.onosInst')
.data(onosOrder, function (d) { return d.id; }),
instDim = {w:0,h:0},
c = instCfg;
function nSw(n) {
return '# Switches: ' + n;
}
// operate on existing onos instances if necessary
onoses.each(function (d) {
var el = d3.select(this),
svg = el.select('svg');
instDim = computeDim(this);
// update online state
el.classed('online', d.online);
el.classed('notReady', !d.ready);
// update ui-attached state
svg.select('use.uiBadge').remove();
if (d.uiAttached) {
attachUiBadge(svg);
}
attachReadyBadge(svg, d.ready);
function updAttr(id, value) {
svg.select('text.instLabel.'+id).text(value);
}
updAttr('ip', d.ip);
updAttr('ns', nSw(d.switches));
});
// operate on new onos instances
var entering = onoses.enter()
.append('div')
.attr('class', 'onosInst')
.classed('online', function (d) { return d.online; })
.classed('notReady', function (d) { return !d.ready; })
.on('click', clickInst);
entering.each(function (d) {
var el = d3.select(this),
rectAttr,
svg;
instDim = computeDim(this);
rectAttr = instRectAttr(instDim);
svg = el.append('svg').attr({
width: instDim.w,
height: instDim.h,
viewBox: viewBox(instDim)
});
svg.append('rect').attr(rectAttr);
gs.addGlyph(svg, 'bird', 28, true, [14, 14])
.classed('badgeIcon', true);
if (d.uiAttached) {
attachUiBadge(svg);
}
attachReadyBadge(svg);
var left = c.nodeOx + c.nodeDim,
len = rectAttr.width - left,
hlen = len / 2,
midline = hlen + left;
// title
svg.append('text')
.attr({
class: 'instTitle',
x: midline,
y: c.titleDy
})
.text(d.id);
// a couple of attributes
var ty = c.titleDy + c.textYOff;
function addAttr(id, label) {
svg.append('text').attr({
class: 'instLabel ' + id,
x: midline,
y: ty
}).text(label);
ty += c.textYSpc;
}
addAttr('ip', d.ip);
addAttr('ns', nSw(d.switches));
});
// operate on existing + new onoses here
// set the affinity colors...
onoses.each(function (d) {
var el = d3.select(this),
rect = el.select('svg').select('rect'),
col = instColor(d.id, d.online);
rect.style('fill', col);
});
// adjust the panel size appropriately...
oiBox.width(instDim.w * onosOrder.length);
oiBox.height(instDim.h);
// remove any outgoing instances
onoses.exit().remove();
}
// ==========================
function logicError(msg) {
if (showLogicErrors) {
$log.warn('TopoInstService: ' + msg);
}
}
function initInst(_api_) {
api = _api_;
oiBox = ps.createPanel(idIns, instOpts);
oiBox.show();
onosInstances = {};
onosOrder = [];
oiShowMaster = false;
// we want to update the instances, each time the theme changes
themeListener = ts.addListener(updateInstances);
}
function destroyInst() {
ts.removeListener(themeListener);
themeListener = null;
ps.destroyPanel(idIns);
oiBox = null;
onosInstances = {};
onosOrder = [];
oiShowMaster = false;
}
function showInsts() {
oiBox.show();
}
function hideInsts() {
oiBox.hide();
}
function toggleInsts(x) {
var kev = (x === 'keyev'),
on,
verb;
if (kev) {
on = oiBox.toggle();
} else {
on = !!x;
if (on) {
showInsts();
} else {
hideInsts();
}
}
verb = on ? 'Show' : 'Hide';
flash.flash(verb + ' instances panel');
return on;
}
// ==========================
angular.module('ovTopo')
.factory('TopoInstService',
['$log', 'PanelService', 'SvgUtilService', 'GlyphService',
'ThemeService', 'FnService', 'FlashService',
function (_$log_, _ps_, _sus_, _gs_, _ts_, _fs_, _flash_) {
$log = _$log_;
ps = _ps_;
sus = _sus_;
gs = _gs_;
ts = _ts_;
fs = _fs_;
flash = _flash_;
return {
initInst: initInst,
destroyInst: destroyInst,
addInstance: addInstance,
updateInstance: updateInstance,
removeInstance: removeInstance,
cancelAffinity: cancelAffinity,
isVisible: function () { return oiBox.isVisible(); },
show: showInsts,
hide: hideInsts,
toggle: toggleInsts,
showMaster: function () { return oiShowMaster; }
};
}]);
}());