summaryrefslogtreecommitdiffstats
path: root/modules-available/remoteaccess
diff options
context:
space:
mode:
authorSimon Rettberg2022-05-31 15:42:07 +0200
committerSimon Rettberg2022-05-31 15:42:07 +0200
commit0c561cd07c82d09ec5f6f1aa0a92ead403d0952b (patch)
treedc52377dd6bd3a03fda18fff170dc1cde8001a4f /modules-available/remoteaccess
parent[remoteaccess] Include locationid in WOL task metadata (diff)
downloadslx-admin-0c561cd07c82d09ec5f6f1aa0a92ead403d0952b.tar.gz
slx-admin-0c561cd07c82d09ec5f6f1aa0a92ead403d0952b.tar.xz
slx-admin-0c561cd07c82d09ec5f6f1aa0a92ead403d0952b.zip
[remoteaccess] Avoid waking too many machines
If a location is assigned to multiple groups, we potentially wake too many machines, as the number of clients that received a WOL in that location when processing group A is not accounted for when peocessing arrives at location B. Keep track of the number of WOLed machines per location to be able to avoid this situation.
Diffstat (limited to 'modules-available/remoteaccess')
-rw-r--r--modules-available/remoteaccess/inc/remoteaccess.inc.php50
1 files changed, 39 insertions, 11 deletions
diff --git a/modules-available/remoteaccess/inc/remoteaccess.inc.php b/modules-available/remoteaccess/inc/remoteaccess.inc.php
index 1910c595..0e8b52b4 100644
--- a/modules-available/remoteaccess/inc/remoteaccess.inc.php
+++ b/modules-available/remoteaccess/inc/remoteaccess.inc.php
@@ -28,40 +28,63 @@ class RemoteAccess
return;
}
- $res = Database::simpleQuery("SELECT rg.groupid, rg.groupname, rg.wolcount, GROUP_CONCAT(rxl.locationid) AS locs FROM remoteaccess_group rg
+ $res = Database::simpleQuery("SELECT rg.groupid, rg.groupname, rg.wolcount, GROUP_CONCAT(rxl.locationid) AS locs
+ FROM remoteaccess_group rg
INNER JOIN remoteaccess_x_location rxl USING (groupid)
WHERE rg.active = 1
GROUP BY groupid");
// Consider machines we tried to wake in the past 90 seconds as online
$wolDeadline = time() - 90;
+ // Keep track of locations where we sent a WOL to clients, so we don't wake too many machines in locations
+ // that appear in multiple groups
+ $locationDone = [];
foreach ($res as $row) {
- if ($row['wolcount'] <= 0)
- continue;
// This can't really be anything but a CSV list, but better be safe
$locs = preg_replace('/[^0-9,]/', '', $row['locs']);
if (empty($locs))
continue;
+ $wantedCount = $row['wolcount'];
+ // Account for machines in the same location(s) already woken in a previous loop iteration
+ foreach (explode(',', $locs) as $lid) {
+ $wantedCount -= ($locationDone[$lid] ?? 0);
+ }
+ // Nothing to do?
+ if ($wantedCount <= 0)
+ continue;
+ // Subtract number of idle machines in those locations, or machines that have
+ // received a WOL in the past $wolDeadline (90) seconds.
$active = Database::queryFirst("SELECT Count(*) AS cnt FROM machine m
INNER JOIN remoteaccess_machine rm USING (machineuuid)
WHERE m.locationid IN ($locs) AND (m.state = 'IDLE' OR rm.woltime > $wolDeadline)");
- $active = (isset($active['cnt']) ? $active['cnt'] : 0);
- $wantNum = $row['wolcount'] - $active;
- if ($wantNum <= 0)
+ $active = ($active['cnt'] ?? 0);
+ $wantedCount -= $active;
+ if ($wantedCount <= 0)
continue;
- self::tryWakeMachines($locs, $wantNum);
+ $wokenMachines = self::tryWakeMachines($locs, $wantedCount);
+ // Update our lookup table of locations and number of machines woken up in them for additional iterations
+ if (!empty($wokenMachines)) {
+ $locations = array_count_values($wokenMachines);
+ foreach ($locations as $lid => $count) {
+ $locationDone[$lid] = ($locationDone[$lid] ?? 0) + $count;
+ }
+ }
}
}
- private static function tryWakeMachines($locs, $num)
+ private static function tryWakeMachines(string $locs, int $num): array
{
$res = Database::simpleQuery("SELECT m.machineuuid, m.macaddr, m.clientip, m.locationid FROM machine m
LEFT JOIN remoteaccess_machine rm USING (machineuuid)
WHERE m.locationid IN ($locs) AND m.state IN ('OFFLINE', 'STANDBY')
ORDER BY rm.woltime ASC");
$NOW = time();
+ // List of machines we did successfully try to send a WOL to
+ $retval = [];
+ // Loop while we still have machines to wake
while ($num > 0) {
$list = [];
+ // Try to gather as many machines as required
for ($i = 0; $i < $num && $row = $res->fetch(); ++$i) {
$list[] = $row;
Database::exec("INSERT INTO remoteaccess_machine (machineuuid, password, woltime)
@@ -72,6 +95,13 @@ class RemoteAccess
if (empty($list))
break; // No more clients in this location
RebootControl::wakeMachines($list, $fails);
+ $triedKv = array_column($list, 'locationid', 'machineuuid');
+ // Remove failed from list, flatten to list of machineuuid => location for return
+ foreach (array_column($fails, 'machineuuid') as $failMachineUuid) {
+ unset($triedKv[$failMachineUuid]);
+ }
+ $retval += $triedKv;
+ // Now reduce $num by how many machines were actually reachable, so we keep going in case there were fails
$num -= count($list) - count($fails);
if (!empty($fails)) {
$failIds = ArrayUtil::flattenByKey($fails, 'machineuuid');
@@ -80,9 +110,7 @@ class RemoteAccess
['faketime' => $NOW - 95, 'fails' => $failIds]);
}
}
- if ($num > 0) {
- error_log("Could not wake $num clients in ($locs)...");
- }
+ return $retval;
}
}