From 834b95629832fc3735756727b6c7cb1826d0e9d8 Mon Sep 17 00:00:00 2001 From: Christian Hofmaier Date: Tue, 16 Mar 2021 20:03:58 +0100 Subject: [rebootcontrol] Handle scheduler overlaps - Overlaps < 5 min are ignored, < 15 triggers reboot - Database: Make 1 line out of 2 --- modules-available/rebootcontrol/hooks/cron.inc.php | 6 +- .../rebootcontrol/inc/scheduler.inc.php | 112 ++++++++++++++------- modules-available/rebootcontrol/install.inc.php | 9 ++ 3 files changed, 85 insertions(+), 42 deletions(-) (limited to 'modules-available/rebootcontrol') diff --git a/modules-available/rebootcontrol/hooks/cron.inc.php b/modules-available/rebootcontrol/hooks/cron.inc.php index 3651c779..9c6cbaaf 100644 --- a/modules-available/rebootcontrol/hooks/cron.inc.php +++ b/modules-available/rebootcontrol/hooks/cron.inc.php @@ -14,10 +14,11 @@ if (in_array((int)date('G'), [6, 7, 9, 12, 15]) && in_array(date('i'), ['00', '0 $now = time(); $res = Database::simpleQuery("SELECT * FROM reboot_scheduler WHERE nextexecution < :now", ['now' => $now]); while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $options = json_decode($row['options'], true); // Calculate next_execution for the event. $location = Database::queryFirst("SELECT openingtime FROM `location` WHERE locationid = :lid", array('lid' => $row['locationid'])); - Scheduler::updateSchedule($row['locationid'], $row['action'], $row['options'], $location['openingtime']); + Scheduler::updateSchedule($row['locationid'], $options, $location['openingtime']); if ($row['nextexecution'] + 1200 < $now) continue; @@ -27,10 +28,9 @@ while ($row = $res->fetch(PDO::FETCH_ASSOC)) { settype($machine['locationid'], 'int'); $machines[] = $machine; } - // Options not yet used. - $options = json_decode($row['options']); if ($row['action'] === 'sd') RebootControl::execute($machines, RebootControl::SHUTDOWN, 0); else if ($row['action'] === 'wol') RebootControl::wakeMachines($machines); + else if ($row['action'] === 'rb') RebootControl::execute($machines, RebootControl::REBOOT, 0); } /* diff --git a/modules-available/rebootcontrol/inc/scheduler.inc.php b/modules-available/rebootcontrol/inc/scheduler.inc.php index 27a22646..0928d20f 100644 --- a/modules-available/rebootcontrol/inc/scheduler.inc.php +++ b/modules-available/rebootcontrol/inc/scheduler.inc.php @@ -3,63 +3,97 @@ class Scheduler { - public static function updateSchedule($locationid, $action, $options, $openingTimes) { + public static function updateSchedule($locationid, $options, $openingTimes) { if ($openingTimes == '') { - self::deleteSchedule($locationid, $action); + self::deleteSchedule($locationid); return false; } - $nextexec = self::calcNextexec($action, $options, $openingTimes); + $nextexec = self::calculateNext($options, $openingTimes); $json_options = json_encode($options); - self::upsert($locationid, $action, $nextexec, $json_options); + if ($nextexec !== false) self::upsert($locationid, $nextexec['action'], $nextexec['time'], $json_options); + else { + // All times are getting ignored because they are within 5 minutes of each other, delete possible db entries. + self::deleteSchedule($locationid); + } return true; } - public static function deleteSchedule($locationid, $action) { + public static function deleteSchedule($locationid) { Database::exec("DELETE FROM `reboot_scheduler` - WHERE locationid = :lid AND action = :act", array( - 'lid' => $locationid, - 'act' => $action - )); + WHERE locationid = :lid", array('lid' => $locationid)); } - private static function calcNextexec($action, $options, $openingTimes) { + private static function calculateNext($options, $openingTimes) { $openingTimes = json_decode($openingTimes, true); - $now = time(); $times = []; + $now = time(); + $openTimes = []; + $closeTimes = []; foreach ($openingTimes as $row) { - // Fetch hour and minutes of opening / closing time. - $hourmin = explode(':', ($action == 'wol' ? $row['openingtime'] : $row['closingtime'])); - // Calculate time based on offset. - $min = ($action == 'wol' ? $hourmin[0] * 60 + $hourmin[1] - $options['wol-offset'] : $hourmin[0] * 60 + $hourmin[1] + $options['sd-offset']); - // Calculate opening / closing time of each day. + if (!$options['wol'] && !$options['sd']) continue; + if ($options['wol']) { + $open = explode(':', $row['openingtime']); + $openTime = $open[0] * 60 + $open[1] - $options['wol-offset']; + } + if ($options['sd']) { + $close = explode(':', $row['closingtime']); + $closeTime = $close[0] * 60 + $close[1] + $options['sd-offset']; + } foreach ($row['days'] as $day) { - $next = strtotime(date('Y-m-d H:i', strtotime($day . ' ' . $min . ' minutes'))); - if ($next < $now) { - $times[] = strtotime(date('Y-m-d H:i', strtotime('next '.$day . ' ' . $min . ' minutes'))); - } else { - $times[] = $next; + if ($options['wol']) { + $nextOpen = strtotime(date('Y-m-d H:i', strtotime($day . ' ' . $openTime . ' minutes'))); + if ($nextOpen < $now) $openTimes[] = strtotime(date('Y-m-d H:i', strtotime('next '.$day . ' ' . $openTime . ' minutes'))); + else $openTimes[] = $nextOpen; + } + if ($options['sd']) { + $nextClose = strtotime(date('Y-m-d H:i', strtotime($day . ' ' . $closeTime . ' minutes'))); + if ($nextClose < $now) $closeTimes[] = strtotime(date('Y-m-d H:i', strtotime('next '.$day . ' ' . $closeTime . ' minutes'))); + else $closeTimes[] = $nextClose; } } } - // Iterate over days, use timestamp with smallest difference to now. - $res = 0; $smallestDiff = 0; - foreach ($times as $time) { - $diff = $time - $now; - if ($res == 0 || $diff < $smallestDiff) { - $smallestDiff = $diff; - $res = $time; + + sort($openTimes); + sort($closeTimes); + $res = array(); + + if ($options['wol'] && !$options['sd']) { + $res['action'] = 'wol'; + $res['time'] = $openTimes[0]; + return $res; + } else if ($options['sd'] && !$options['wol']) { + $res['action'] = 'sd'; + $res['time'] = $closeTimes[0]; + return $res; + } + + for ($i = 0; $i <= sizeof($openTimes); $i++) { + if (abs($openTimes[$i] - $closeTimes[$i]) < 300) { + // If difference is < 5 min, ignore both events. + continue; + } else if (abs($openTimes[$i] - $closeTimes[$i]) < 900) { + // If difference is < 15 min, reboot at the earlier time. + $res['action'] = 'rb'; + $res['time'] = $openTimes[$i] < $closeTimes[$i] ? $openTimes[$i] : $closeTimes[$i]; + } else { + // Use first event. + if ($openTimes[$i] < $closeTimes[$i]) { + $res['action'] = 'wol'; + $res['time'] = $openTimes[$i]; + } else { + $res['action'] = 'sd'; + $res['time'] =$closeTimes[$i]; + } } + return $res; } - return $res; + return false; } - - private static function upsert($locationid, $action, $nextexec, $options) { - $schedule = Database::queryFirst("SELECT locationid, action + $schedule = Database::queryFirst("SELECT locationid FROM `reboot_scheduler` - WHERE locationid = :lid AND action = :act", array( - 'lid' => $locationid, - 'act' => $action + WHERE locationid = :lid", array( + 'lid' => $locationid )); if ($schedule === false) { Database::exec("INSERT INTO `reboot_scheduler` (locationid, action, nextexecution, options) @@ -71,12 +105,12 @@ class Scheduler )); } else { Database::exec("UPDATE `reboot_scheduler` - SET nextexecution = :next, options = :opt - WHERE locationid = :lid AND action = :act", array( + SET action = :act, nextexecution = :next, options = :opt + WHERE locationid = :lid", array( + 'act' => $action, 'next' => $nextexec, 'opt' => $options, - 'lid' => $locationid, - 'act' => $action + 'lid' => $locationid )); } return true; diff --git a/modules-available/rebootcontrol/install.inc.php b/modules-available/rebootcontrol/install.inc.php index 7d4382d0..008d26aa 100644 --- a/modules-available/rebootcontrol/install.inc.php +++ b/modules-available/rebootcontrol/install.inc.php @@ -55,4 +55,13 @@ $output[] = tableAddConstraint('reboot_subnet_x_subnet', 'dstid', 'reboot_subnet $output[] = tableAddConstraint('reboot_scheduler', 'locationid', 'location', 'locationid', 'ON UPDATE CASCADE ON DELETE CASCADE'); +if (tableExists('reboot_scheduler')) { + Database::exec("ALTER TABLE `reboot_scheduler` DROP PRIMARY KEY , ADD PRIMARY KEY (`locationid`)"); +} +if (tableHasColumn('reboot_scheduler', 'action')) { + Database::exec("ALTER TABLE `reboot_scheduler` MODIFY COLUMN `action` ENUM('wol', 'sd', 'rb')"); +} + + + responseFromArray($output); \ No newline at end of file -- cgit v1.2.3-55-g7522