]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
baculum: Add search Bacula items endpoint
authorMarcin Haba <marcin.haba@bacula.pl>
Fri, 19 Aug 2022 12:13:22 +0000 (14:13 +0200)
committerMarcin Haba <marcin.haba@bacula.pl>
Thu, 17 Nov 2022 09:05:10 +0000 (10:05 +0100)
gui/baculum/protected/API/Modules/Bconsole.php
gui/baculum/protected/API/Modules/ConsoleOutputQueryPage.php
gui/baculum/protected/API/Pages/API/SearchItems.php [new file with mode: 0644]
gui/baculum/protected/API/Pages/API/endpoints.xml
gui/baculum/protected/API/openapi_baculum.json

index e6589226541372f00719d551497863ebcff5a0cd..cf50787ee0b5ea47e869e0b49da5321a19429aa6 100644 (file)
@@ -98,7 +98,8 @@ class Bconsole extends APIModule {
                '.ls',
                'setbandwidth',
                '.query',
-               '.jlist'
+               '.jlist',
+               '.search'
        );
 
        private $config;
index 23fa33e7cb273ea3672aa9051eedcf839100c193..053647e0f0b3648e9910c2a19d10168e72a5bdb0 100644 (file)
@@ -40,7 +40,7 @@ abstract class ConsoleOutputQueryPage extends ConsoleOutputPage {
        protected function parseOutputKeyValue(array $output) {
                $ret = [];
                for ($i = 0; $i < count($output); $i++) {
-                       if (preg_match('/(?P<key>\w+)=(?P<value>.+?)$/i', $output[$i], $matches) === 1) {
+                       if (preg_match('/(?P<key>\w+)=(?P<value>.*?)$/i', $output[$i], $matches) === 1) {
                                $ret[$matches['key']] = $matches['value'];
                        }
                }
diff --git a/gui/baculum/protected/API/Pages/API/SearchItems.php b/gui/baculum/protected/API/Pages/API/SearchItems.php
new file mode 100644 (file)
index 0000000..9e1fdca
--- /dev/null
@@ -0,0 +1,162 @@
+<?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\Logging;
+use Baculum\Common\Modules\Errors\BconsoleError;
+use Baculum\API\Modules\ConsoleOutputPage;
+use Baculum\API\Modules\ConsoleOutputQueryPage;
+
+/**
+ * List search results from .search Bconsole command.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category API
+ * @package Baculum API
+ */
+class SearchItems extends ConsoleOutputQueryPage {
+
+       const CATEGORY_ALL = 'all';
+       const CATEGORY_CLIENT = 'client';
+       const CATEGORY_JOB = 'job';
+       const CATEGORY_VOLUME = 'volume';
+
+       public function get() {
+               $misc = $this->getModule('misc');
+
+               $out_format = ConsoleOutputPage::OUTPUT_FORMAT_RAW;
+               if ($this->Request->contains('output') && $this->isOutputFormatValid($this->Request['output'])) {
+                       $out_format = $this->Request['output'];
+               }
+               $text = $this->Request->contains('text') && $misc->isValidName($this->Request['text']) ? $this->Request['text'] : null;
+               $category = self::CATEGORY_ALL; // default category
+               if ($this->Request->contains('category') && $this->categoryExists($this->Request['category'])) {
+                       $category = $this->Request['category'];
+               }
+
+               if (is_null($text)) {
+                       // no text, do nothing
+                       $this->output = [];
+                       $this->error = BconsoleError::ERROR_NO_ERRORS;
+                       return;
+               }
+
+               $params = [
+                       'text' => $text,
+                       'category' => $category
+               ];
+
+               $out = new \StdClass;
+               if ($out_format === ConsoleOutputPage::OUTPUT_FORMAT_RAW) {
+                       $out = $this->getRawOutput($params);
+               } elseif($out_format === ConsoleOutputPage::OUTPUT_FORMAT_JSON) {
+                       $out = $this->getJSONOutput($params);
+               }
+
+               if ($out->exitcode === 0) {
+                       $this->output = $out->output;
+                       $this->error = BconsoleError::ERROR_NO_ERRORS;
+               } else {
+                       $this->output = BconsoleError::MSG_ERROR_WRONG_EXITCODE . $out->output;
+                       $this->error = BconsoleError::ERROR_WRONG_EXITCODE;
+                       $this->getModule('logging')->log(
+                               Logging::CATEGORY_EXECUTE,
+                               $this->output . ", Error={$this->error}"
+                       );
+               }
+       }
+
+       /**
+        * Get search command output from console in raw format.
+        *
+        * @param array $params command parameters
+        * @return StdClass object with output and exitcode
+        */
+       protected function getRawOutput($params = []) {
+               $ret = $this->getModule('bconsole')->bconsoleCommand(
+                       $this->director,
+                       [
+                               '.search',
+                               'category="' . $params['category'] . '"',
+                               'text="' . $params['text'] . '"'
+                       ]
+               );
+               if ($ret->exitcode !== 0) {
+                       $ret->output = []; // don't provide errors to output, only in logs
+                       $this->getModule('logging')->log(
+                               Logging::CATEGORY_EXECUTE,
+                               'Wrong output from RAW search items: ' . implode(PHP_EOL, $ret->output)
+                       );
+               }
+               return $ret;
+       }
+
+       /**
+        * Get search items results in JSON format.
+        *
+        * @param array $params command parameters
+        * @return StdClass object with output and exitcode
+        */
+       protected function getJSONOutput($params = []) {
+               $ret = $this->getRawOutput($params);
+               if ($ret->exitcode === 0) {
+                       $result = $this->parseOutputKeyValue($ret->output);
+                       if (key_exists('error', $result) && $result['error'] === '0') {
+                               $ret->output = (object)[
+                                       'client' => !empty($result['client']) ? explode(',', $result['client']) : [],
+                                       'volume' => !empty($result['volume']) ? explode(',', $result['volume']) : [],
+                                       'job' => !empty($result['job']) ? explode(',', $result['job']) : []
+                               ];
+                       } else {
+                               $ret->output = []; // don't provide errors to output, only in logs
+                               $this->getModule('logging')->log(
+                                       Logging::CATEGORY_EXECUTE,
+                                       'Wrong output from .search command output: ' . implode(PHP_EOL, $result)
+                               );
+                       }
+               }
+               return $ret;
+       }
+
+       /**
+        * Check if category exists.
+        *
+        * @param string $category category
+        * @return bool true if exists, otherwise false
+        */
+       private function categoryExists($category) {
+               $cats = $this->getCategories();
+               return in_array($category, $cats);
+       }
+
+       /**
+        * Get all supported categories.
+        * return array category list
+        */
+       private function getCategories() {
+               return [
+                       self::CATEGORY_ALL,
+                       self::CATEGORY_VOLUME,
+                       self::CATEGORY_CLIENT,
+                       self::CATEGORY_JOB
+               ];
+       }
+}
index 102457dfe0194e31e87716a765738be91439cfe8..f0bdf9f618aa1e2b1dbba6578b5e1426db6eedda 100644 (file)
@@ -9,6 +9,7 @@
        <!-- API v2 -->
        <!-- general endpoint -->
        <url ServiceParameter="Welcome" pattern="api/v2/welcome/" />
+       <url ServiceParameter="SearchItems" pattern="api/v2/search/" />
        <!-- bconsole endpoints -->
        <url ServiceParameter="ConsoleCommand" pattern="api/v2/console/" />
        <!-- database endpoints -->
index 71a36ce4a11db2b07771e1c52d07ab123f1c4420..3c5af0a56f4ae330ccfe373a1677d97d5b3b1bc9 100644 (file)
                                        }
                                ]
                        }
+               },
+               "/api/v2/search": {
+                       "get": {
+                               "tags": ["tools"],
+                               "summary": "Search for Bacula items (clients, jobs, volumes)",
+                               "description": "Search for Bacula items (clients, jobs, volumes)",
+                               "responses": {
+                                       "200": {
+                                               "description": "Bacula items (clients, jobs and volumes)",
+                                               "content": {
+                                                       "application/json": {
+                                                               "schema": {
+                                                                       "type": "object",
+                                                                       "properties": {
+                                                                               "output": {
+                                                                                       "type": "object",
+                                                                                       "properties": {
+                                                                                               "client": {
+                                                                                                       "type": "array",
+                                                                                                       "description": "Bacula clients",
+                                                                                                       "items": {
+                                                                                                               "description": "Client name",
+                                                                                                               "type": "string"
+                                                                                                       }
+                                                                                               },
+                                                                                               "volume": {
+                                                                                                       "type": "array",
+                                                                                                       "description": "Bacula volumes",
+                                                                                                       "items": {
+                                                                                                               "description": "Volume names",
+                                                                                                               "type": "string"
+                                                                                                       }
+                                                                                               },
+                                                                                               "job": {
+                                                                                                       "type": "array",
+                                                                                                       "description": "Bacula jobs",
+                                                                                                       "items": {
+                                                                                                               "description": "Job names",
+                                                                                                               "type": "string"
+                                                                                                       }
+                                                                                               }
+                                                                                       }
+                                                                               },
+                                                                               "error": {
+                                                                                       "type": "integer",
+                                                                                       "description": "Error code",
+                                                                                       "enum": [0, 1, 4, 5, 6, 7, 11, 1000]
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               },
+                               "parameters": [
+                                       {
+                                               "name": "text",
+                                               "in": "query",
+                                               "required": false,
+                                               "description": "Text to search items",
+                                               "schema": {
+                                                       "type": "string"
+                                               }
+                                       },
+                                       {
+                                               "name": "category",
+                                               "in": "query",
+                                               "required": false,
+                                               "description": "Category to search (all, job, client, volume). Default is 'all'.",
+                                               "schema": {
+                                                       "type": "string"
+                                               }
+                                       },
+                                       {
+                                               "$ref": "#/components/parameters/Output"
+                                       }
+                               ]
+                       }
                }
        },
        "definitions": {