$list, "mode" => $mode, "minutes" => $minutes, "locationId" => $locationId, "sshkey" => SSHKey::getPrivateKey(), "port" => 9922, // Hard-coded, must match mgmt-sshd module )); if (!Taskmanager::isFailed($task)) { Property::addToList(RebootControl::KEY_TASKLIST, $locationId . '/' . $task["id"], 60 * 24); } return $task; } /** * @param int[]|null $locations filter by these locations * @return array list of active tasks for reboots/shutdowns. */ public static function getActiveTasks($locations = null) { if (is_array($locations) && in_array(0,$locations)) { $locations = null; } $list = Property::getList(RebootControl::KEY_TASKLIST); $return = []; foreach ($list as $entry) { $p = explode('/', $entry, 2); if (count($p) !== 2) { Property::removeFromList(RebootControl::KEY_TASKLIST, $entry); continue; } if (is_array($locations) && !in_array($p[0], $locations)) // Ignore continue; $id = $p[1]; $task = Taskmanager::status($id); if (!Taskmanager::isTask($task)) { Property::removeFromList(RebootControl::KEY_TASKLIST, $entry); continue; } $return[] = [ 'taskId' => $task['id'], 'locationId' => $task['data']['locationId'], 'time' => $task['data']['time'], 'mode' => $task['data']['mode'], 'clientCount' => count($task['data']['clients']), 'status' => $task['statusCode'], ]; } return $return; } /** * Execute given command or script on a list of hosts. The list of hosts is an array of structs containing * each a known machine-uuid and/or hostname, and optionally a port to use, which would otherwise default to 9922, * and optionally a username to use, which would default to root. * The command should be compatible with the remote user's default shell (most likely bash). * * @param array $clients [ { clientip: , machineuuid: , port: , username: }, ... ] * @param string $command Command or script to execute on client * @param int $timeout in seconds * @param string|false $privkey SSH private key to use to connect * @return array|false */ public static function runScript($clients, $command, $timeout = 5, $privkey = false) { $valid = []; $invalid = []; foreach ($clients as $client) { if (is_string($client)) { $invalid[strtoupper($client)] = []; // Assume machineuuid } elseif (!isset($client['clientip']) && !isset($client['machineuuid'])) { error_log('RebootControl::runScript called with list entry that has neither IP nor UUID'); } elseif (!isset($client['clientip'])) { $invalid[$client['machineuuid']] = $client; } else { $valid[] = $client; } } if (!empty($invalid)) { $res = Database::simpleQuery('SELECT machineuuid, clientip FROM machine WHERE machineuuid IN (:uuids)', ['uuids' => array_keys($invalid)]); while ($row = $res->fetch(PDO::FETCH_ASSOC)) { if (isset($invalid[$row['machineuuid']])) { $valid[] = $row + $invalid[$row['machineuuid']]; } else { $valid[] = $row; } } } if ($privkey === false) { $privkey = SSHKey::getPrivateKey(); } return Taskmanager::submit('RemoteExec', [ 'clients' => $valid, 'command' => $command, 'timeoutSeconds' => $timeout, 'sshkey' => $privkey, 'port' => 9922, // Fallback if no port given in client struct ]); } public static function connectionCheckCallback($task, $hostId) { $reachable = 0; if (isset($task['data']['result'])) { foreach ($task['data']['result'] as $res) { if ($res['exitCode'] == 0) { $reachable = 1; } } } Database::exec('UPDATE reboot_jumphost SET reachable = :reachable WHERE hostid = :id', ['id' => $hostId, 'reachable' => $reachable]); } }