From: Marcin Haba Date: Mon, 5 Dec 2022 08:42:08 +0000 (+0100) Subject: baculum: Add job sum statistics endpoint X-Git-Tag: Release-13.0.2~43 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9f3421aadb0e1286636ef01af8638fca17d48a8e;p=thirdparty%2Fbacula.git baculum: Add job sum statistics endpoint --- diff --git a/gui/baculum/protected/API/Modules/JobManager.php b/gui/baculum/protected/API/Modules/JobManager.php index 3f619f66e..9cddf21dc 100644 --- a/gui/baculum/protected/API/Modules/JobManager.php +++ b/gui/baculum/protected/API/Modules/JobManager.php @@ -412,5 +412,24 @@ WHERE Client.ClientId='$clientid' $wh"; } return $result; } + + public function getNumberOfJobs($criteria) { + $where = Database::getWhere($criteria); + + $sql = 'SELECT + Type AS type, + SUM(1) AS total, + JobStatus AS jobstatus + FROM Job + ' . (!empty($where['where']) ? $where['where'] : '') . ' + GROUP BY type, jobstatus'; + + $connection = JobRecord::finder()->getDbConnection(); + $connection->setActive(true); + $pdo = $connection->getPdoInstance(); + $sth = $pdo->prepare($sql); + $sth->execute($where['params']); + return $sth->fetchAll(PDO::FETCH_ASSOC); + } } ?> diff --git a/gui/baculum/protected/API/Pages/API/JobStatsJobSum.php b/gui/baculum/protected/API/Pages/API/JobStatsJobSum.php new file mode 100644 index 000000000..1b69f7c8f --- /dev/null +++ b/gui/baculum/protected/API/Pages/API/JobStatsJobSum.php @@ -0,0 +1,160 @@ + + * @category API + * @package Baculum API + */ +class JobStatsJobSum extends BaculumAPIServer { + + public function get() { + $misc = $this->getModule('misc'); + $jobstatus = $this->Request->contains('jobstatus') && $this->Request['jobstatus'] ? $this->Request['jobstatus'] : ''; + $type = $this->Request->contains('type') && $misc->isValidJobType($this->Request['type']) ? $this->Request['type'] : ''; + $starttime_from = $this->Request->contains('starttime_from') && $misc->isValidInteger($this->Request['starttime_from']) ? (int)$this->Request['starttime_from'] : null; + $starttime_to = $this->Request->contains('starttime_to') && $misc->isValidInteger($this->Request['starttime_to']) ? (int)$this->Request['starttime_to'] : null; + $jobname = $this->Request->contains('jobname') && $misc->isValidName($this->Request['jobname']) ? $this->Request['jobname'] : ''; + $clientid = $this->Request->contains('clientid') ? $this->Request['clientid'] : ''; + + if (!empty($clientid) && !$misc->isValidId($clientid)) { + $this->output = JobError::MSG_ERROR_CLIENT_DOES_NOT_EXISTS; + $this->error = JobError::ERROR_CLIENT_DOES_NOT_EXISTS; + return; + } + $client = $this->Request->contains('client') ? $this->Request['client'] : ''; + if (!empty($client) && !$misc->isValidName($client)) { + $this->output = JobError::MSG_ERROR_CLIENT_DOES_NOT_EXISTS; + $this->error = JobError::ERROR_CLIENT_DOES_NOT_EXISTS; + return; + } + + $params = []; + $result = $this->getModule('bconsole')->bconsoleCommand( + $this->director, + ['.jobs'], + null, + true + ); + if ($result->exitcode === 0) { + $vals = []; + if (!empty($jobname) && in_array($jobname, $result->output)) { + $vals = [$jobname]; + } else { + $vals = $result->output; + } + if (count($vals) == 0) { + // no $vals criteria means that user has no job resource assigned. + $this->output = []; + $this->error = JobError::ERROR_NO_ERRORS; + return; + } + + $params['Job.Name'] = []; + $params['Job.Name'][] = [ + 'operator' => 'OR', + 'vals' => $vals + ]; + } + + $error = false; + // Client name and clientid filter + if (!empty($client) || !empty($clientid)) { + $result = $this->getModule('bconsole')->bconsoleCommand( + $this->director, + ['.client'] + ); + if ($result->exitcode === 0) { + array_shift($result->output); + $cli = null; + if (!empty($client)) { + $cli = $this->getModule('client')->getClientByName($client); + } elseif (!empty($clientid)) { + $cli = $this->getModule('client')->getClientById($clientid); + } + if (is_object($cli) && in_array($cli->name, $result->output)) { + $params['Job.ClientId'] = []; + $params['Job.ClientId'][] = [ + 'operator' => 'AND', + 'vals' => [$cli->clientid] + ]; + } else { + $error = true; + $this->output = JobError::MSG_ERROR_CLIENT_DOES_NOT_EXISTS; + $this->error = JobError::ERROR_CLIENT_DOES_NOT_EXISTS; + } + } else { + $error = true; + $this->output = $result->output; + $this->error = $result->exitcode; + } + } + + $jobstatuses = array_keys($misc->getJobState()); + $sts = str_split($jobstatus); + for ($i = 0; $i < count($sts); $i++) { + if (in_array($sts[$i], $jobstatuses)) { + if (!key_exists('Job.JobStatus', $params)) { + $params['Job.JobStatus'] = [[ + 'operator' => 'OR', + 'vals' => [] + ]]; + } + $params['Job.JobStatus'][0]['vals'][] = $sts[$i]; + } + } + + if (!empty($type)) { + $params['Job.Type'] = []; + $params['Job.Type'][] = [ + 'vals' => $type + ]; + } + + // Start time range + if (!empty($starttime_from) || !empty($starttime_to)) { + $params['Job.StartTime'] = []; + if (!empty($starttime_from)) { + $params['Job.StartTime'][] = [ + 'operator' => '>=', + 'vals' => date('Y-m-d H:i:s', $starttime_from) + ]; + } + if (!empty($starttime_to)) { + $params['Job.StartTime'][] = [ + 'operator' => '<=', + 'vals' => date('Y-m-d H:i:s', $starttime_to) + ]; + } + } + + if ($error === false) { + $jobs = $this->getModule('job')->getNumberOfJobs($params); + $this->output = $jobs; + $this->error = JobError::ERROR_NO_ERRORS; + } + } +} diff --git a/gui/baculum/protected/API/Pages/API/endpoints.xml b/gui/baculum/protected/API/Pages/API/endpoints.xml index 9cce05234..806d0b6df 100644 --- a/gui/baculum/protected/API/Pages/API/endpoints.xml +++ b/gui/baculum/protected/API/Pages/API/endpoints.xml @@ -79,6 +79,7 @@ + diff --git a/gui/baculum/protected/API/openapi_baculum.json b/gui/baculum/protected/API/openapi_baculum.json index 90ebede5e..88033bba4 100644 --- a/gui/baculum/protected/API/openapi_baculum.json +++ b/gui/baculum/protected/API/openapi_baculum.json @@ -2229,6 +2229,124 @@ ] } }, + "/api/v2/jobs/stats/sum": { + "get": { + "tags": ["jobs"], + "summary": "Total number of jobs with specific job type and job status.", + "consumes": [ "application/json" ], + "description": "Get total number of jobs with specific job type and job status.", + "responses": { + "200": { + "description": "Total number of jobs.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "output": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Job type", + "enum": ["B", "M", "V", "R", "I", "D", "A", "C", "c", "g"] + }, + "total": { + "type": "integer", + "description": "Total number of the specific type jobs." + }, + "jobstatus": { + "type": "string", + "description": "Job status. Note, some statuses can be not visible outside (used internally by Bacula)", + "enum": ["C", "R", "B", "T", "W", "E", "e", "f", "D", "A", "I", "F", "S", "m", "M", "s", "j", "c", "d", "t", "p", "i", "a", "l", "L"] + } + } + } + }, + "error": { + "type": "integer", + "description": "Error code", + "enum": [0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 1000] + } + } + } + } + } + } + }, + "parameters": [ + { + "name": "jobstatus", + "in": "query", + "description": "Job status letter(s). Possible multiple values like 'Ef' or 'Tef'", + "required": false, + "schema": { + "type": "string", + "description": "Job status. Note, some statuses can be not visible outside (used internally by Bacula)", + "enum": ["C", "R", "B", "T", "W", "E", "e", "f", "D", "A", "I", "F", "S", "m", "M", "s", "j", "c", "d", "t", "p", "i", "a", "l", "L"] + } + }, + { + "name": "type", + "in": "query", + "description": "Job type", + "required": false, + "schema": { + "type": "string", + "description": "Job type", + "enum": ["B", "M", "V", "R", "I", "D", "A", "C", "c", "g"] + } + }, + { + "name": "starttime_from", + "in": "query", + "required": false, + "description": "Start time from (UNIX timestamp format, seconds)", + "schema": { + "type": "integer" + } + }, + { + "name": "starttime_to", + "in": "query", + "required": false, + "description": "Start time to (UNIX timestamp format, seconds)", + "schema": { + "type": "integer" + } + }, + { + "name": "jobname", + "in": "query", + "description": "Job name", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "clientid", + "in": "query", + "description": "Client identifier (can be used instead of client value)", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "client", + "in": "query", + "description": "Client name (can be used instead clientid)", + "required": false, + "schema": { + "type": "string" + } + } + ] + } + }, "/api/v2/storages": { "get": { "tags": ["storages"],