|
@@ -774,6 +774,7 @@ if (!colors['2500']) colors['2500'] = '#17C3B2';
|
|
|
if (showTerm) {
|
|
if (showTerm) {
|
|
|
w.all.forEach(function (n) {
|
|
w.all.forEach(function (n) {
|
|
|
var rows = portRows(n.ports).length;
|
|
var rows = portRows(n.ports).length;
|
|
|
|
|
+ if (!rows) rows = 2; // generic 24-port fallback
|
|
|
if (rows > portRowMax) portRowMax = rows;
|
|
if (rows > portRowMax) portRowMax = rows;
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
@@ -1405,6 +1406,14 @@ if (!colors['2500']) colors['2500'] = '#17C3B2';
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ function portIndexFromName(name) {
|
|
|
|
|
+ var s = String(name || '').toUpperCase();
|
|
|
|
|
+ if (/^(ETH-TRUNK|VLANIF|NULL|INLOOPBACK|STACK-PORT|LOOPBACK|TUNNEL|METH|VBDIF)/.test(s)) return 0;
|
|
|
|
|
+ var m = s.match(/(\d+)(?!.*\d)/);
|
|
|
|
|
+ var n = m ? parseInt(m[1], 10) : 0;
|
|
|
|
|
+ return n > 0 && n <= 1024 ? n : 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
function addTerminals(L, byId, els) {
|
|
function addTerminals(L, byId, els) {
|
|
|
termPortByMac = {};
|
|
termPortByMac = {};
|
|
|
var bySw = {}, termBySwPort = {};
|
|
var bySw = {}, termBySwPort = {};
|
|
@@ -1415,28 +1424,44 @@ if (!colors['2500']) colors['2500'] = '#17C3B2';
|
|
|
(bySw[tm.sw_ip] = bySw[tm.sw_ip] || []).push(tm);
|
|
(bySw[tm.sw_ip] = bySw[tm.sw_ip] || []).push(tm);
|
|
|
if (tm.port) termBySwPort[tm.sw_ip + '|' + pkey(tm.port)] = tm;
|
|
if (tm.port) termBySwPort[tm.sw_ip + '|' + pkey(tm.port)] = tm;
|
|
|
});
|
|
});
|
|
|
- function pushTermDot(sw, p, tm, x, y, id) {
|
|
|
|
|
- els.push({group: 'nodes', data: {
|
|
|
|
|
- id: id, type: 'term', bg: terminalOnline(tm) ? '#2ECC71' : '#95a5a6',
|
|
|
|
|
- mac: tm.mac, ip: tm.ip, online: terminalOnline(tm), speed: tm.speed,
|
|
|
|
|
- sw_name: tm.sw_name, port: tm.port, label: '', termText: terminalText(tm)
|
|
|
|
|
- }, position: {x: x, y: y}, selectable: false});
|
|
|
|
|
- els.push({group: 'edges', data: {id: 'te_' + tm.mac, type: 'term', source: sw, target: id}});
|
|
|
|
|
- }
|
|
|
|
|
Object.keys(byId).forEach(function (sw) {
|
|
Object.keys(byId).forEach(function (sw) {
|
|
|
var p = byId[sw]; if (!p || p.role === 'core' || p.role === 'main') return;
|
|
var p = byId[sw]; if (!p || p.role === 'core' || p.role === 'main') return;
|
|
|
var terms = bySw[sw] || [];
|
|
var terms = bySw[sw] || [];
|
|
|
var rawPorts = (p.node && p.node.ports) || [];
|
|
var rawPorts = (p.node && p.node.ports) || [];
|
|
|
|
|
+ var ports = rawPorts.slice();
|
|
|
if (!rawPorts.length) {
|
|
if (!rawPorts.length) {
|
|
|
- // Legacy/history snapshot without a port inventory: keep the compact
|
|
|
|
|
- // terminal dots as a fallback instead of inventing unused ports.
|
|
|
|
|
- terms.slice(0, 24).forEach(function (tm, k) {
|
|
|
|
|
- var col = k % 12, row = Math.floor(k / 12);
|
|
|
|
|
- pushTermDot(sw, p, tm, p.x - p.w / 2 + 16 + col * 9, p.y + 16 + row * 9, 't_' + tm.mac);
|
|
|
|
|
|
|
+ // The switch did not report a front-panel inventory (for example an
|
|
|
|
|
+ // old history snapshot). Fall back to a generic 24-port access panel
|
|
|
|
|
+ // laid out as two rows of 12. Terminals are placed into their real
|
|
|
|
|
+ // suffix position when the port name allows it; the remaining ports
|
|
|
|
|
+ // stay visible as unused hollow circles.
|
|
|
|
|
+ var used = {}, extra = [];
|
|
|
|
|
+ terms.forEach(function (tm) {
|
|
|
|
|
+ var idx = portIndexFromName(tm.port);
|
|
|
|
|
+ if (idx) used[idx] = tm; else extra.push(tm);
|
|
|
|
|
+ });
|
|
|
|
|
+ var maxIdx = 24;
|
|
|
|
|
+ Object.keys(used).forEach(function (k) { maxIdx = Math.max(maxIdx, parseInt(k, 10) || 0); });
|
|
|
|
|
+ ports = [];
|
|
|
|
|
+ for (var pi = 1; pi <= maxIdx; pi++) {
|
|
|
|
|
+ var ptm = used[pi];
|
|
|
|
|
+ var pspeed = ptm ? (parseFloat(ptm.speed) || 0) : 0;
|
|
|
|
|
+ ports.push({
|
|
|
|
|
+ name: ptm ? (ptm.port || ('GE1/0/' + pi)) : ('GE1/0/' + pi),
|
|
|
|
|
+ order: pi, speed: pspeed,
|
|
|
|
|
+ max: ptm ? portMaxFromName(ptm.port, pspeed) : 1000,
|
|
|
|
|
+ up: !!ptm, synthetic: true
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ extra.forEach(function (tm, k) {
|
|
|
|
|
+ var espeed = parseFloat(tm.speed) || 0;
|
|
|
|
|
+ ports.push({
|
|
|
|
|
+ name: tm.port || ('GE1/0/' + (maxIdx + k + 1)), order: 1000 + k,
|
|
|
|
|
+ speed: espeed, max: portMaxFromName(tm.port, espeed),
|
|
|
|
|
+ up: true, synthetic: true
|
|
|
|
|
+ });
|
|
|
});
|
|
});
|
|
|
- return;
|
|
|
|
|
}
|
|
}
|
|
|
- var ports = rawPorts.slice();
|
|
|
|
|
var known = {};
|
|
var known = {};
|
|
|
ports.forEach(function (pt) { known[pkey(pt.name)] = true; });
|
|
ports.forEach(function (pt) { known[pkey(pt.name)] = true; });
|
|
|
// A terminal may sit on a logical interface (for example an Eth-Trunk)
|
|
// A terminal may sit on a logical interface (for example an Eth-Trunk)
|