]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
baculum: Add job sum statistics endpoint
authorMarcin Haba <marcin.haba@bacula.pl>
Mon, 5 Dec 2022 08:42:08 +0000 (09:42 +0100)
committerMarcin Haba <marcin.haba@bacula.pl>
Mon, 9 Jan 2023 12:34:42 +0000 (13:34 +0100)
gui/baculum/protected/API/Modules/JobManager.php
gui/baculum/protected/API/Pages/API/JobStatsJobSum.php [new file with mode: 0644]
gui/baculum/protected/API/Pages/API/endpoints.xml
gui/baculum/protected/API/openapi_baculum.json

index 3f619f66e0c018a8e3bb00cbefed2858845acb1b..9cddf21dc08cfcd7aa36058156bd5a96ff05f3cf 100644 (file)
@@ -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 (file)
index 0000000..1b69f7c
--- /dev/null
@@ -0,0 +1,160 @@
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum   - Bacula web interface
+ *
+ * Copyright (C) 2013-2022 Kern Sibbald
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+use Baculum\Common\Modules\Errors\JobError;
+
+/**
+ * Job count stats endpoint.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @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;
+               }
+       }
+}
index 9cce05234dc4e1e7558dab9a8fe0f93ebdae6a08..806d0b6df511b6289baf6ffddf3ca6f526087b83 100644 (file)
@@ -79,6 +79,7 @@
        <url ServiceParameter="RestoreRun" pattern="api/v2/jobs/restore/" />
        <url ServiceParameter="LlistPluginRestoreConf" pattern="api/v2/jobs/restore/plugin/config" />
        <url ServiceParameter="LlistPluginRestoreConfFields" pattern="api/v2/jobs/restore/plugin/config/fields" />
+       <url ServiceParameter="JobStatsJobSum" pattern="api/v2/jobs/stats/sum" />
        <!-- bvfs endpoints-->
        <url ServiceParameter="BVFSUpdate" pattern="api/v2/bvfs/update/" />
        <url ServiceParameter="BVFSLsDirs" pattern="api/v2/bvfs/lsdirs/" />
index 90ebede5e16b83cc9ea53140dcb71901dfb93272..88033bba4a4701beaf28d364c6c0407a898604b8 100644 (file)
                                ]
                        }
                },
+               "/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"],