]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
baculum: Add volumes overview endpoint
authorMarcin Haba <marcin.haba@bacula.pl>
Wed, 22 Mar 2023 14:14:41 +0000 (15:14 +0100)
committerMarcin Haba <marcin.haba@bacula.pl>
Thu, 20 Apr 2023 10:00:26 +0000 (12:00 +0200)
gui/baculum/protected/API/Modules/VolumeManager.php
gui/baculum/protected/API/Pages/API/VolumesOverview.php [new file with mode: 0644]
gui/baculum/protected/API/Pages/API/endpoints.xml
gui/baculum/protected/API/openapi_baculum.json

index 5ac43e0af08f4c2b46a5a5e44d32372f0d068026..7889cf9e80debf339c2cc47f1860aad4c33a6933 100644 (file)
@@ -22,6 +22,8 @@
 
 namespace Baculum\API\Modules;
 
+use PDO;
+
 /**
  * Volume manager module.
  *
@@ -31,6 +33,69 @@ namespace Baculum\API\Modules;
  */
 class VolumeManager extends APIModule {
 
+       /**
+        * Volume types (voltype property)
+        */
+       const VOLTYPE_FILE = 1;
+       const VOLTYPE_TAPE = 2;
+       const VOLTYPE_DVD = 3;
+       const VOLTYPE_FIFO = 4;
+       const VOLTYPE_VTAPE_DEV = 5;
+       const VOLTYPE_FTP_DEV = 6;
+       const VOLTYPE_VTL_DEV = 7;
+       const VOLTYPE_ADATA = 8;
+       const VOLTYPE_ALIGNED_DEV = 9;
+       const VOLTYPE_DEDUP_OLD_DEV = 10;
+       const VOLTYPE_NULL_DEV = 11;
+       const VOLTYPE_VALIGNED_DEV = 12;
+       const VOLTYPE_VDEDUP_DEV = 13;
+       const VOLTYPE_CLOUD_DEV = 14;
+       const VOLTYPE_DEDUP_DEV = 15;
+
+       /**
+        * Get disk volume type identifiers.
+        *
+        * @return array disk volume type identifiers
+        */
+       private function getDiskVolTypes() {
+               return [
+                       self::VOLTYPE_FILE,
+                       self::VOLTYPE_ADATA,
+                       self::VOLTYPE_FIFO,
+                       self::VOLTYPE_ALIGNED_DEV,
+                       self::VOLTYPE_DEDUP_OLD_DEV,
+                       self::VOLTYPE_VALIGNED_DEV,
+                       self::VOLTYPE_VDEDUP_DEV,
+                       self::VOLTYPE_CLOUD_DEV,
+                       self::VOLTYPE_DEDUP_DEV
+               ];
+       }
+
+
+       /**
+        * Get tape volume type identifiers.
+        *
+        * @return array tape volume type identifiers
+        */
+       private function getTapeVolTypes() {
+               return [
+                       self::VOLTYPE_TAPE,
+                       self::VOLTYPE_VTL_DEV
+               ];
+       }
+
+
+       /**
+        * Get cloud volume type identifiers.
+        *
+        * @return array cloud volume type identifiers
+        */
+       private function getCloudVolTypes() {
+               return [
+                       self::VOLTYPE_CLOUD_DEV
+               ];
+       }
+
        public function getVolumes($criteria = array(), $limit_val = 0, $offset_val = 0) {
                $order_pool_id = 'PoolId';
                $order_volume = 'VolumeName';
@@ -68,6 +133,147 @@ LEFT JOIN Storage USING (StorageId)
                return $volumes;
        }
 
+       /**
+        * Get volume overview.
+        *
+        * @param array $criteria SQL criteria to get volume overview
+        * @param integer $limit_val item limit
+        * @param integer $offset_val offset value
+        * @return array volume overview
+        */
+       public function getMediaOverview($criteria = [], $limit_val = 0, $offset_val = 0) {
+               $limit = '';
+               if(is_int($limit_val) && $limit_val > 0) {
+                       $limit = " LIMIT $limit_val ";
+               }
+               $offset = '';
+               if (is_int($offset_val) && $offset_val > 0) {
+                       $offset = ' OFFSET ' . $offset_val;
+               }
+
+               $where = Database::getWhere($criteria);
+
+               // get volume type count
+               $sql = 'SELECT
+                       VolType AS voltype,
+                       COUNT(1) AS count
+                       FROM Media
+                       JOIN Pool USING (PoolId)
+                       ' . $where['where'] . '
+                       GROUP BY VolType ';
+
+               $statement = Database::runQuery($sql, $where['params']);
+               $voltype_count = $statement->fetchAll(PDO::FETCH_ASSOC);
+
+               $expire_query = '';
+               $db_params = $this->getModule('api_config')->getConfig('db');
+               if ($db_params['type'] === Database::PGSQL_TYPE) {
+                       $expire_query = 'CAST((DATE_PART(\'epoch\', Media.LastWritten) + Media.VolRetention) AS INTEGER)';
+               } elseif ($db_params['type'] === Database::MYSQL_TYPE) {
+                       $expire_query = 'CAST((UNIX_TIMESTAMP(Media.LastWritten) + Media.VolRetention) AS UNSIGNED)';
+               } elseif ($db_params['type'] === Database::SQLITE_TYPE) {
+                       $expire_query = 'CAST((strftime(\'%s\', Media.LastWritten) + Media.VolRetention) AS INTEGER)';
+               }
+
+               // get disk volume types (file, dedup and alighed together)
+               $vt_disk = $this->getDiskVolTypes();
+               $sql = 'SELECT
+                               VolumeName   AS volumename,
+                               Pool.Name    AS pool,
+                               Storage.Name AS storage,
+                               VolBytes     AS volbytes,
+                               VolABytes    AS volabytes,
+                               InChanger    AS inchanger,
+                               Slot         AS slot,
+                               CASE 
+                                       WHEN Media.VolStatus IN (\'Full\', \'Used\') THEN ' . $expire_query . '
+                                       ELSE 0
+                               END expiresin
+                       FROM Media
+                       JOIN Storage USING (StorageId)
+                       JOIN Pool USING (PoolId)
+                       ' .  (!empty($where['where']) ? $where['where'] . ' AND ' : ' WHERE ') . ' VolType IN (' . implode(',', $vt_disk) . ')
+                       ORDER BY VolStatus ASC, LastWritten DESC' . $limit . $offset;
+
+               $statement = Database::runQuery($sql, $where['params']);
+               $voltype_disk = $statement->fetchAll(PDO::FETCH_OBJ);
+
+               // get tape volume types
+               $vt_tape = $this->getTapeVolTypes();
+               $sql = 'SELECT
+                               VolumeName   AS volumename,
+                               Pool.Name    AS pool,
+                               Storage.Name AS storage,
+                               VolBytes     AS volbytes,
+                               VolABytes    AS volabytes,
+                               InChanger    AS inchanger,
+                               Slot         AS slot,
+                               CASE 
+                                       WHEN Media.VolStatus IN (\'Full\', \'Used\') THEN ' . $expire_query . '
+                                       ELSE 0
+                               END expiresin
+                       FROM Media
+                       JOIN Storage USING (StorageId)
+                       JOIN Pool USING (PoolId)
+                       ' .  (!empty($where['where']) ? $where['where'] . ' AND ' : ' WHERE ') . ' VolType IN (' . implode(',', $vt_tape) . ')
+                       ORDER BY VolStatus ASC, LastWritten DESC' . $limit . $offset;
+
+               $statement = Database::runQuery($sql, $where['params']);
+               $voltype_tape = $statement->fetchAll(PDO::FETCH_OBJ);
+
+               // get cloud volume types
+               $vt_cloud = $this->getCloudVolTypes();
+               $sql = 'SELECT
+                               VolumeName   AS volumename,
+                               Pool.Name    AS pool,
+                               Storage.Name AS storage,
+                               VolBytes     AS volbytes,
+                               VolABytes    AS volabytes,
+                               InChanger    AS inchanger,
+                               Slot         AS slot,
+                               CASE 
+                                       WHEN Media.VolStatus IN (\'Full\', \'Used\') THEN ' . $expire_query . '
+                                       ELSE 0
+                               END expiresin
+                       FROM Media
+                       JOIN Storage USING (StorageId)
+                       JOIN Pool USING (PoolId)
+                       ' .  (!empty($where['where']) ? $where['where'] . ' AND ' : ' WHERE ') . ' VolType IN (' . implode(',', $vt_cloud) . ')
+                       ORDER BY VolStatus ASC, LastWritten DESC' . $limit . $offset;
+
+               $statement = Database::runQuery($sql, $where['params']);
+               $voltype_cloud = $statement->fetchAll(PDO::FETCH_OBJ);
+
+               $disk_count = $tape_count = $cloud_count = 0;
+               for ($i = 0; $i < count($voltype_count); $i++) {
+                       $count = $voltype_count[$i]['count'];
+                       $voltype = $voltype_count[$i]['voltype'];
+                       if (in_array($voltype, $vt_disk)) {
+                               $disk_count += $count;
+                       } elseif (in_array($voltype, $vt_tape)) {
+                               $tape_count += $count;
+                       } elseif (in_array($voltype, $vt_cloud)) {
+                               $cloud_count += $count;
+                       }
+               }
+
+               $result = [
+                       'disk' => [
+                               'media' => $voltype_disk,
+                               'count' => $disk_count
+                       ],
+                       'tape' => [
+                               'media' => $voltype_tape,
+                               'count' => $tape_count
+                       ],
+                       'cloud' => [
+                               'media' => $voltype_cloud,
+                               'count' => $cloud_count
+                       ]
+               ];
+               return $result;
+       }
+
        public function getVolumesByPoolId($poolid) {
                $volumes = $this->getVolumes(array(
                        'Media.PoolId' => [[
diff --git a/gui/baculum/protected/API/Pages/API/VolumesOverview.php b/gui/baculum/protected/API/Pages/API/VolumesOverview.php
new file mode 100644 (file)
index 0000000..021110c
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum   - Bacula web interface
+ *
+ * Copyright (C) 2013-2023 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\API\Modules\BaculumAPIServer;
+use Baculum\Common\Modules\Errors\PoolError;
+
+/**
+ * Media/Volumes overview endpoint.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category API
+ * @package Baculum API
+ */
+class VolumesOverview extends BaculumAPIServer {
+       public function get() {
+               $misc = $this->getModule('misc');
+               $limit = $this->Request->contains('limit') ? intval($this->Request['limit']) : 0;
+               $offset = $this->Request->contains('offset') && $misc->isValidInteger($this->Request['offset']) ? (int)$this->Request['offset'] : 0;
+               $pools = $this->getModule('pool')->getPools();
+               $result = $this->getModule('bconsole')->bconsoleCommand(
+                       $this->director,
+                       ['.pool'],
+                       null,
+                       true
+               );
+               if ($result->exitcode === 0) {
+                       if (is_array($pools) && count($pools) > 0) {
+                               $params = [];
+                               $pools_output = [];
+                               foreach ($pools as $pool) {
+                                       if (in_array($pool->name, $result->output)) {
+                                               $pools_output[] = $pool->name;
+                                       }
+                               }
+                               $params['Pool.Name'] = [];
+                               $params['Pool.Name'][] = [
+                                       'operator' => 'IN',
+                                       'vals' => $pools_output
+                               ];
+                               $ret = $this->getModule('volume')->getMediaOverview(
+                                       $params,
+                                       $limit,
+                                       $offset
+                               );
+                               $this->output = $ret;
+                               $this->error = PoolError::ERROR_NO_ERRORS;
+                       } else {
+                               $this->output = PoolError::MSG_ERROR_POOL_DOES_NOT_EXISTS;
+                               $this->error = PoolError::ERROR_POOL_DOES_NOT_EXISTS;
+                       }
+               } else {
+                       $this->output = PoolError::MSG_ERROR_WRONG_EXITCODE;
+                       $this->error = PoolError::ERROR_WRONG_EXITCODE . ' Exitcode=> ' . $result->exitcode;
+               }
+       }
+}
+?>
index e9c6055bf1352f36ff0f5454ad0ba7faaf6cae65..32cd4160280f18b8a66dc50f27e38a73bcbdd770 100644 (file)
@@ -54,6 +54,7 @@
        <url ServiceParameter="VolumePurge" pattern="api/v2/volumes/{id}/purge/" parameters.id="\d+" />
        <url ServiceParameter="VolumesRequired" pattern="api/v2/volumes/required/{jobid}/{fileid}/" parameters.jobid="\d+" parameters.fileid="\d+" />
        <url ServiceParameter="JobsOnVolume" pattern="api/v2/volumes/{id}/jobs/" parameters.id="\d+" />
+       <url ServiceParameter="VolumesOverview" pattern="api/v2/volumes/overview/" />
        <url ServiceParameter="VolumeLabel" pattern="api/v2/volumes/label/" />
        <url ServiceParameter="VolumeLabelBarcodes" pattern="api/v2/volumes/label/barcodes/" />
        <url ServiceParameter="SlotsUpdate" pattern="api/v2/volumes/update/" />
index 5ceb4e003ba3c2e9c5bac63900822f341239bbb7..38662aa2af5564b187a72acdf260e12a1f682ea2 100644 (file)
                        },
                        "PluginM365EmailAttachment": {
                                "$ref": "#/definitions/PluginM365EmailAttachment"
+                       },
+                       "VolumeOverview": {
+                               "$ref": "#/definitions/VolumeOverview"
                        }
                },
                "parameters": {
                                ]
                        }
                },
+               "/api/v2/volumes/overview": {
+                       "get": {
+                               "tags": ["volumes"],
+                               "summary": "Get volumes overview.",
+                               "description": "Get volumes overview.",
+                               "responses": {
+                                       "200": { 
+                                               "description": "Volumes overview.",
+                                               "content": {
+                                                       "application/json": {
+                                                               "schema": {
+                                                                       "type": "object",
+                                                                       "properties": {
+                                                                               "output": {
+                                                                                       "type": "object",
+                                                                                       "properties": {
+                                                                                               "disk": {
+                                                                                                       "type": "object",
+                                                                                                       "properties": {
+                                                                                                               "media": {
+                                                                                                                       "type": "array",
+                                                                                                                       "items": {
+                                                                                                                               "$ref": "#/components/schemas/VolumeOverview"
+                                                                                                                       }
+                                                                                                               },
+                                                                                                               "count": {
+                                                                                                                       "type": "integer",
+                                                                                                                       "description": "Disk media count"
+                                                                                                               }
+                                                                                                       }
+                                                                                               },
+                                                                                               "tape": {
+                                                                                                       "type": "object",
+                                                                                                       "properties": {
+                                                                                                               "media": {
+                                                                                                                       "type": "array",
+                                                                                                                       "items": {
+                                                                                                                               "$ref": "#/components/schemas/VolumeOverview"
+                                                                                                                       }
+                                                                                                               },
+                                                                                                               "count": {
+                                                                                                                       "type": "integer",
+                                                                                                                       "description": "Tape media count"
+                                                                                                               }
+                                                                                                       }
+                                                                                               },
+                                                                                               "cloud": {
+                                                                                                       "type": "object",
+                                                                                                       "properties": {
+                                                                                                               "media": {
+                                                                                                                       "type": "array",
+                                                                                                                       "items": {
+                                                                                                                               "$ref": "#/components/schemas/VolumeOverview"
+                                                                                                                       }
+                                                                                                               },
+                                                                                                               "count": {
+                                                                                                                       "type": "integer",
+                                                                                                                       "description": "Cloud media count"
+                                                                                                               }
+                                                                                                       }
+                                                                                               }
+                                                                                       }
+                                                                               },
+                                                                               "error": {
+                                                                                       "type": "integer",
+                                                                                       "description": "Error code",
+                                                                                       "enum": [0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 40, 1000]
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               },
+                               "parameters": [
+                                       {
+                                               "$ref": "#/components/parameters/Limit"
+                                       },
+                                       {
+                                               "$ref": "#/components/parameters/Offset"
+                                       }
+                               ]
+                       }
+               },
                "/api/v2/volumes/{mediaid}/prune": {
                        "put": {
                                "tags": ["volumes"],
                                }
                        }
                },
+               "VolumeOverview": {
+                       "type": "object",
+                       "properties": {
+                               "volumename": {
+                                       "type": "string",
+                                       "description": "Volume name"
+                               },
+                               "pool": {
+                                       "type": "string",
+                                       "description": "Pool name"
+                               },
+                               "storage": {
+                                       "type": "string",
+                                       "description": "Storage name"
+                               },
+                               "volbytes": {
+                                       "type": "integer",
+                                       "description": "Volume bytes"
+                               },
+                               "volabytes": {
+                                       "type": "integer",
+                                       "description": "Volume compressed bytes"
+                               },
+                               "inchanger": {
+                                       "type": "integer",
+                                       "description": "InChanger flag"
+                               },
+                               "slot": {
+                                       "type": "integer",
+                                       "description": "Slot number"
+                               },
+                               "expiresin": {
+                                       "type": "integer",
+                                       "description": "When volume expire in UNIX timestamp form"
+                               }
+                       }
+               },
                "FileSet": {
                        "type": "object",
                        "properties": {