diff options
Diffstat (limited to 'modules-available/vmstore/page.inc.php')
-rw-r--r-- | modules-available/vmstore/page.inc.php | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/modules-available/vmstore/page.inc.php b/modules-available/vmstore/page.inc.php index 60a9d60a..41e7e990 100644 --- a/modules-available/vmstore/page.inc.php +++ b/modules-available/vmstore/page.inc.php @@ -11,6 +11,19 @@ class Page_VmStore extends Page { User::load(); + if (User::hasPermission('edit')) { + Dashboard::addSubmenu('?do=vmstore', Dictionary::translate('menu_edit', true)); + } + if (User::hasPermission('benchmark')) { + Dashboard::addSubmenu('?do=vmstore&show=benchmark', Dictionary::translate('menu_benchmark', true)); + } + + if (Request::any('show') === 'benchmark') { + User::assertPermission('benchmark'); + $this->benchmarkDoPreprocess(); + return; + } + User::assertPermission('edit'); $action = Request::post('action'); @@ -22,6 +35,11 @@ class Page_VmStore extends Page protected function doRender() { + if (Request::any('show') === 'benchmark') { + $this->benchmarkDoRender(); + return; + } + $action = Request::post('action'); if ($action === 'setstore' && !Taskmanager::isFailed(Taskmanager::status($this->mountTask))) { Render::addTemplate('mount', array( @@ -68,4 +86,192 @@ class Page_VmStore extends Page } } + private function benchmarkDoPreprocess() + { + if (!Module::isAvailable('rebootcontrol')) { + ErrorHandler::traceError('rebootcontrol module not enabled'); + } + if (Request::post('action') === 'start') { + $this->benchmarkActionStart(); + } + } + + private function benchmarkDoRender() + { + switch (Request::get('action')) { + case 'select': + $this->benchmarkShowImageSelect(); + break; + case 'result': + $this->benchmarkShowResult(); + } + } + + private function benchmarkActionStart() + { + Module::isAvailable('dnbd3'); + $id = Request::post('id', Request::REQUIRED, 'string'); + $data = Session::get('benchmark-' . $id); + if (!isset($data['machines'])) { + Message::addError('invalid-benchmark-job', $id); + return; + } + if (isset($data['task'])) { + if ($data['task'] === 'inprogress') { + // Let's hope the proper ID gets written in a short while + sleep(1); + } + Util::redirect('?do=vmstore&show=benchmark&action=result&id=' . $id); + } + $nfs = !Dnbd3::isEnabled(); + $data['image'] = Request::post('image', Request::REQUIRED, 'string'); + // Save once first to minimize race window + $data['task'] = 'inprogress'; + Session::set('benchmark-' . $id, $data, 60); + Session::saveExtraData(); + $start = 0; + $data['task'] = VmStoreBenchmark::start($id, $data['machines'], $data['image'], $nfs, $start); + if ($data['task'] === null) { + $data['task'] = 'failed'; + } else { + // Test is 2x 30 seconds + $data['expected'] = $start + 60; + } + error_log('Saving: ' . json_encode($data)); + Session::set('benchmark-' . $id, $data, 60); + Util::redirect('?do=vmstore&show=benchmark&action=result&id=' . $id); + } + + private function benchmarkShowImageSelect() + { + $id = Request::get('id', Request::REQUIRED, 'string'); + $data = Session::get('benchmark-' . $id); + if (!isset($data['machines'])) { + Message::addError('invalid-benchmark-job', $id); + return; + } + if (isset($data['task'])) { + Message::addWarning('benchmark-already-started'); + Util::redirect('?do=vmstore&show=benchmark&action=result&id=' . $id); + } + Module::isAvailable('dnbd3'); + $lookup = Dnbd3::getActiveServers(); + $list = Dnbd3Rpc::getStatsMulti(array_keys($lookup), [Dnbd3Rpc::QUERY_IMAGES]); + if (empty($list)) { + Message::addError('dnbd3-failed'); + Util::redirect('?do=vmstore'); + } + $images = []; + foreach ($list as $json) { + foreach ($json['images'] as $img) { + $name = $img['name'] . ':' . $img['rid']; + if (!isset($images[$name])) { + $images[$name] = [ + 'users' => 0, + 'size' => Util::readableFileSize($img['size'], 1), + 'name' => $name, + 'id' => count($images) + ]; + } + $images[$name]['users'] += $img['users']; + } + } + ArrayUtil::sortByColumn($images, 'users', SORT_NUMERIC | SORT_DESC); + Render::addTemplate('benchmark-imgselect', ['id' => $id, 'list' => array_values($images)]); + } + + private function benchmarkShowResult() + { + $id = Request::get('id', Request::REQUIRED, 'string'); + $data = Session::get('benchmark-' . $id); + if (!isset($data['machines'])) { + Message::addError('invalid-benchmark-job', $id); + return; + } + if (!isset($data['task'])) { + Message::addWarning('select-image-first'); + Util::redirect('?do=vmstore&show=benchmark&action=select&id=' . $id); + } + if ($data['task'] === 'failed') { + Message::addError('benchmark-failed'); + return; + } + $remaining = 0; + if ($data['task'] !== 'done') { + $remaining = $data['expected'] - time(); + if ($remaining < 0) { + $remaining = 0; + } + $this->processRunningBenchmark($id, $data, $remaining === 0); + $refresh = $remaining; + Util::clamp($refresh, 2, 64); + } + $args = [ + 'id' => $id, + 'result' => json_encode($data['result'] ?? []), + 'wanted' => json_encode($data['machines']), + ]; + if ($remaining > 0) { + $args['remaining'] = $remaining; + $args['refresh'] = $refresh; + } + Module::isAvailable('js_chart'); + Render::addTemplate('benchmark-result', $args); + } + + private function processRunningBenchmark(string $id, array &$data, bool $timeout) + { + Module::isAvailable('rebootcontrol'); + $changed = false; + + $active = array_filter($data['machines'], function ($e) use ($data) { return !isset($data['result'][$e]); }); + if (empty($active)) { + $timeout = true; + } else { + $command = <<<EOF +grep -q '^Seq:' "/tmp/speedcheck-$id" && cat "/tmp/speedcheck-$id" +EOF; + $task = RebootControl::runScript($active, $command); + $task = Taskmanager::waitComplete($task, 4000); + if ($task === false) { + $data['task'] = 'failed'; + return; + } + if (!isset($data['result'])) { + $data['result'] = []; + } + $res =& $task['data']; + foreach ($res['result'] as $uuid => $out) { + if (isset($data['result'][$uuid])) + continue; + error_log(json_encode($out)); + // Not finished, ignore + if (($out['state'] !== 'DONE' || $out['exitCode'] !== 0) && !$timeout) + continue; + $changed = true; + unset($client); + $client = ['machineuuid' => $uuid]; + $data['result'][$uuid] =& $client; + if (preg_match_all("/^\+(\w{3}):(\d+),(.*)$/m", $out['stdout'], $modes, PREG_SET_ORDER)) { + foreach ($modes as $mode) { + $client[$mode[1]] = [ + 'start' => $mode[2], + 'values' => VmStoreBenchmark::parseBenchLine($mode[3]), + ]; + } + } + $m = Database::queryFirst('SELECT clientip, hostname FROM machine WHERE machineuuid = :uuid', + ['uuid' => $uuid]); + $client['name'] = empty($m['hostname']) ? $m['clientip'] : $m['hostname']; + } + } + if (count($data['result']) === count($data['machines']) || $timeout) { + $data['task'] = 'done'; + $changed = true; + } + if ($changed) { + Session::set('benchmark-' . $id, $data); + } + } + }
\ No newline at end of file |