Added IP-KVM port status tracking.

Ylian Saint-Hilaire committed Dec 5, 2021 at 16:12 UTC 448aeec6e7cb58208ec9b540e728a955ca0e2768
4 files changed +48 -25
meshipkvm.js
+24 -15
@@ -9,12 +9,11 @@
9 function CreateIPKVMManager(parent) {
10 const obj = {};
11 const managedGroups = {} // meshid --> Manager
12 - const managedPorts = {} // nodeid --> PortInfo
12 + obj.managedPorts = {} // nodeid --> PortInfo
13
14 // Subscribe for mesh creation events
15 parent.AddEventDispatch(['server-createmesh', 'server-deletemesh'], obj);
16 obj.HandleEvent = function (source, event, ids, id) {
17 - console.log();
17 if ((event != null) && (event.action == 'createmesh') && (event.mtype == 4)) {
18 // Start managing this new device group
19 startManagement(parent.webserver.meshes[event.meshid]);
@@ -54,7 +53,7 @@ function CreateIPKVMManager(parent) {
53 for (var i = 0; i < manager.ports.length; i++) {
54 const port = manager.ports[i];
55 const nodeid = generateIpKvmNodeId(manager.meshid, port.PortId, manager.domainid);
57 - delete managedPorts[nodeid]; // Remove the managed port
56 + delete obj.managedPorts[nodeid]; // Remove the managed port
57 }
58
59 // Remove the manager
@@ -65,11 +64,13 @@ function CreateIPKVMManager(parent) {
64
65 // Called when a KVM device changes state
66 function onStateChanged(sender, state) {
67 + /*
68 console.log('State: ' + ['Disconnected', 'Connecting', 'Connected'][state]);
69 if (state == 2) {
70 console.log('DeviceModel:', sender.deviceModel);
71 console.log('FirmwareVersion:', sender.firmwareVersion);
72 }
73 + */
74 }
75
76 // Called when a KVM device changes state
@@ -78,8 +79,8 @@ function CreateIPKVMManager(parent) {
79 const port = sender.ports[updatedPorts[i]];
80 const nodeid = generateIpKvmNodeId(sender.meshid, port.PortId, sender.domainid);
81 if ((port.Status == 1) && (port.Class == 'KVM')) {
81 - console.log(port.PortNumber + ', ' + port.PortId + ', ' + port.Name + ', ' + port.Type + ', ' + ((port.StatAvailable == 0) ? 'Idle' : 'Connected'));
82 - if ((managedPorts[nodeid] == null) || (managedPorts[nodeid].name != port.Name)) {
82 + //console.log(port.PortNumber + ', ' + port.PortId + ', ' + port.Name + ', ' + port.Type + ', ' + ((port.StatAvailable == 0) ? 'Idle' : 'Connected'));
83 + if ((obj.managedPorts[nodeid] == null) || (obj.managedPorts[nodeid].name != port.Name)) {
84 parent.db.Get(nodeid, function (err, nodes) {
85 if ((err != null) || (nodes == null)) return;
86 const mesh = parent.webserver.meshes[sender.meshid];
@@ -104,26 +105,34 @@ function CreateIPKVMManager(parent) {
105 }
106
107 // Set the connectivity state if needed
107 - if (managedPorts[nodeid] == null) {
108 + if (obj.managedPorts[nodeid] == null) {
109 parent.SetConnectivityState(sender.meshid, nodeid, Date.now(), 1, 1, null, null);
109 - managedPorts[nodeid] = { name: port.Name, busy: false };
110 + obj.managedPorts[nodeid] = { name: port.Name };
111 }
112
113 // Update busy state
113 - const portInfo = managedPorts[nodeid];
114 - if (portInfo.busy != (port.StatAvailable != 0)) {
115 - console.log('Busy state', (port.StatAvailable != 0));
114 + const portInfo = obj.managedPorts[nodeid];
115 + if ((portInfo.sessions != null) != (port.StatAvailable != 0)) {
116 + if (port.StatAvailable != 0) { portInfo.sessions = { kvm: { 'busy': 1 } } } else { delete portInfo.sessions; }
117 +
118 + // Event the new sessions, this will notify everyone that agent sessions have changed
119 + var event = { etype: 'node', action: 'devicesessions', nodeid: nodeid, domain: sender.domainid, sessions: portInfo.sessions, nolog: 1 };
120 + parent.DispatchEvent(parent.webserver.CreateMeshDispatchTargets(sender.meshid, [nodeid]), obj, event);
121 }
122 });
123 } else {
124 // Update busy state
120 - const portInfo = managedPorts[nodeid];
121 - if (portInfo.busy != (port.StatAvailable != 0)) {
122 - console.log('Busy state', (port.StatAvailable != 0));
125 + const portInfo = obj.managedPorts[nodeid];
126 + if ((portInfo.sessions != null) != (port.StatAvailable != 0)) {
127 + if (port.StatAvailable != 0) { portInfo.sessions = { kvm: { 'busy': 1 } } } else { delete portInfo.sessions; }
128 +
129 + // Event the new sessions, this will notify everyone that agent sessions have changed
130 + var event = { etype: 'node', action: 'devicesessions', nodeid: nodeid, domain: sender.domainid, sessions: portInfo.sessions, nolog: 1 };
131 + parent.DispatchEvent(parent.webserver.CreateMeshDispatchTargets(sender.meshid, [nodeid]), obj, event);
132 }
133 }
134 } else {
126 - if (managedPorts[nodeid] != null) {
135 + if (obj.managedPorts[nodeid] != null) {
136 // This port is no longer connected
137 parent.ClearConnectivityState(sender.meshid, nodeid, 1, null, null);
138
@@ -141,7 +150,7 @@ function CreateIPKVMManager(parent) {
150 }
151
152 // Remove the managed port
144 - delete managedPorts[nodeid];
153 + delete obj.managedPorts[nodeid];
154 }
155 }
156 }
meshuser.js
+6
@@ -816,6 +816,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
816 const xagent = parent.wsagents[docs[i]._id];
817 if ((xagent != null) && (xagent.sessions != null)) { docs[i].sessions = xagent.sessions; }
818
819 + // Add IP-KVM sessions
820 + if (parent.parent.ipKvmManager != null) {
821 + const xipkvmport = parent.parent.ipKvmManager.managedPorts[docs[i]._id];
822 + if ((xipkvmport != null) && (xipkvmport.sessions != null)) { docs[i].sessions = xipkvmport.sessions; }
823 + }
824 +
825 r[meshid].push(docs[i]);
826 }
827 try { ws.send(JSON.stringify({ action: 'nodes', responseid: command.responseid, nodes: r, tag: command.tag })); } catch (ex) { }
views/default-mobile.handlebars
+10 -6
@@ -3136,9 +3136,13 @@
3136 if ((i == 'kvm') && (node.sessions.multidesk == null)) {
3137 x += '<u>' + "Remote Desktop" + '</u>';
3138 for (var j in node.sessions.kvm) {
3139 - var trash = '';
3140 - if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("kvm", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
3141 - x += addHtmlValue4(getUserName(j), ((node.sessions.kvm[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.kvm[j]))) + trash);
3139 + if (j.startsWith('user/')) {
3140 + var trash = '';
3141 + if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("kvm", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
3142 + x += addHtmlValue4(getUserName(j), ((node.sessions.kvm[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.kvm[j]))) + trash);
3143 + } else if (j == 'busy') {
3144 + x += addHtmlValue2("Device is busy", ((node.sessions.kvm[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.kvm[j]))));
3145 + }
3146 }
3147 } else if (i == 'multidesk') {
3148 x += '<u>' + "Remote Desktop" + '</u>';
@@ -3218,7 +3222,7 @@
3222 var states = [];
3223 if (node.state > 0 && node.state < powerStatetable.length) state.push(powerStatetable[node.state]);
3224 if (node.conn) {
3221 - if ((node.conn & 1) != 0) { if (node.mtype == 4) { states.push('<span>' + ((node.mtype == 4)?"IP-KVM":"Agent") + '</span>'); } else { states.push('<span>' + "IP KVM" + '</span>'); } }
3225 + if ((node.conn & 1) != 0) { states.push('<span>' + ((node.mtype == 4)?"IP-KVM":"Agent") + '</span>'); }
3226 if ((node.conn & 2) != 0) { states.push('<span>' + "CIRA" + '</span>'); }
3227 else if ((node.conn & 4) != 0) { states.push('<span>' + "Intel&reg; AMT" + '</span>'); }
3228 if ((node.conn & 8) != 0) { states.push('<span>' + "Relay" + '</span>'); }
@@ -3514,7 +3518,7 @@
3518
3519 x += '</table><br />';
3520 // Show action button, only show if we have permissions 4, 8, 64
3517 - if (((meshrights & (4 + 8 + 64)) != 0) && (node.mtype != 3)) { x += '<input type=button value="' + "Actions" + '" onclick=deviceActionFunction() />'; }
3521 + if (((meshrights & (4 + 8 + 64)) != 0) && (node.mtype < 3)) { x += '<input type=button value="' + "Actions" + '" onclick=deviceActionFunction() />'; }
3522 x += '<input type=button value=Notes onclick=showNotes(' + ((meshrights & 128) == 0) + ',"' + encodeURIComponent(node._id) + '") />';
3523 //if ((connectivity & 1) && (meshrights & 8) && (node.agent.id < 5)) { x += '<input type=button value=Toast onclick=deviceToastFunction() />'; }
3524
@@ -3542,7 +3546,7 @@
3546 // Set the node power state
3547 var powerstate = PowerStateStr(node.state);
3548 //if (node.state == 0) { powerstate = 'Unknown State'; }
3545 - if ((connectivity & 1) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += ((mesh.mtype == 4) ? "IP-KVM" : "Mesh Agent"); }
3549 + if ((connectivity & 1) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += ((node.mtype == 4) ? "IP-KVM" : "Mesh Agent"); }
3550 if ((connectivity & 2) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "Intel&reg; AMT connected"; }
3551 else if ((connectivity & 4) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "Intel&reg; AMT detected"; }
3552 if ((connectivity & 16) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "MQTT channel connected"; }
views/default.handlebars
+8 -4
@@ -3994,7 +3994,7 @@
3994 else if ((sort == 3) || (sort == 4)) { collapseName = 'tag:**xx**xx*TaG*xx**xx**'; }
3995 var collapsed = (sort != 3) & (sort != 4) & CollapsedGroups[collapseName];
3996 r += '<tr name=xxdevice2 colname=DevxCol' + collapseName + ' style=height:20px' + (collapsed?';display:none':'') + ' id=xv2' + node._id + '></tr>';
3997 - } else if (((view == 3) || (view == 5)) && (node.conn & 1) && (((meshrights & 8) || (meshrights & 256)) != 0) && ((node.agent.caps & 1) != 0)) { // Check if we have rights and agent is capable of KVM.
3997 + } else if (((view == 3) || (view == 5)) && (node.conn & 1) && (((meshrights & 8) || (meshrights & 256)) != 0) && (node.agent) && ((node.agent.caps & 1) != 0)) { // Check if we have rights and agent is capable of KVM.
3998 // Draw the device (TODO: See if we can replace this with a standin in the future)
3999 if ((Object.keys(checkedNodeids).length == 0) || checkedNodeids[node._id]) {
4000 r += updateDeviceViewHtml(view, null, node);
@@ -4494,9 +4494,13 @@
4494 if ((i == 'kvm') && (node.sessions.multidesk == null)) {
4495 x += '<u>' + "Remote Desktop" + '</u>';
4496 for (var j in node.sessions.kvm) {
4497 - var trash = '';
4498 - if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("kvm", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
4499 - x += addHtmlValue2(getUserName(j), ((node.sessions.kvm[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.kvm[j]))) + trash);
4497 + if (j.startsWith('user/')) {
4498 + var trash = '';
4499 + if (((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF))) { trash = ' <a href=# onclick=\'return endDeviceSession("kvm", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
4500 + x += addHtmlValue2(getUserName(j), ((node.sessions.kvm[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.kvm[j]))) + trash);
4501 + } else if (j == 'busy') {
4502 + x += addHtmlValue2("Device is busy", ((node.sessions.kvm[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.kvm[j]))));
4503 + }
4504 }
4505 } else if (i == 'multidesk') {
4506 x += '<u>' + "Remote Desktop" + '</u>';