From: Marcin Haba Date: Wed, 22 Mar 2023 14:14:41 +0000 (+0100) Subject: baculum: Add volumes overview endpoint X-Git-Tag: Release-13.0.3~104 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b5fe020f6080a9594b2f79472008fa97fed32831;p=thirdparty%2Fbacula.git baculum: Add volumes overview endpoint --- diff --git a/gui/baculum/protected/API/Modules/VolumeManager.php b/gui/baculum/protected/API/Modules/VolumeManager.php index 5ac43e0af..7889cf9e8 100644 --- a/gui/baculum/protected/API/Modules/VolumeManager.php +++ b/gui/baculum/protected/API/Modules/VolumeManager.php @@ -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 index 000000000..021110cdd --- /dev/null +++ b/gui/baculum/protected/API/Pages/API/VolumesOverview.php @@ -0,0 +1,76 @@ + + * @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; + } + } +} +?> diff --git a/gui/baculum/protected/API/Pages/API/endpoints.xml b/gui/baculum/protected/API/Pages/API/endpoints.xml index e9c6055bf..32cd41602 100644 --- a/gui/baculum/protected/API/Pages/API/endpoints.xml +++ b/gui/baculum/protected/API/Pages/API/endpoints.xml @@ -54,6 +54,7 @@ + diff --git a/gui/baculum/protected/API/openapi_baculum.json b/gui/baculum/protected/API/openapi_baculum.json index 5ceb4e003..38662aa2a 100644 --- a/gui/baculum/protected/API/openapi_baculum.json +++ b/gui/baculum/protected/API/openapi_baculum.json @@ -167,6 +167,9 @@ }, "PluginM365EmailAttachment": { "$ref": "#/definitions/PluginM365EmailAttachment" + }, + "VolumeOverview": { + "$ref": "#/definitions/VolumeOverview" } }, "parameters": { @@ -4016,6 +4019,90 @@ ] } }, + "/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"], @@ -9456,6 +9543,43 @@ } } }, + "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": {