summaryrefslogtreecommitdiffstats
path: root/modules-available/vmstore/page.inc.php
diff options
context:
space:
mode:
Diffstat (limited to 'modules-available/vmstore/page.inc.php')
-rw-r--r--modules-available/vmstore/page.inc.php206
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