]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
baculum: Add list job files API endpoint
authorMarcin Haba <marcin.haba@bacula.pl>
Mon, 28 Oct 2019 19:20:38 +0000 (20:20 +0100)
committerMarcin Haba <marcin.haba@bacula.pl>
Sat, 14 Dec 2019 14:57:24 +0000 (15:57 +0100)
gui/baculum/protected/API/Class/BList.php [new file with mode: 0644]
gui/baculum/protected/API/Pages/API/JobFiles.php [new file with mode: 0644]
gui/baculum/protected/API/Pages/API/config.xml
gui/baculum/protected/API/Pages/API/endpoints.xml
gui/baculum/protected/API/openapi_baculum.json
gui/baculum/protected/Common/Class/Miscellaneous.php

diff --git a/gui/baculum/protected/API/Class/BList.php b/gui/baculum/protected/API/Class/BList.php
new file mode 100644 (file)
index 0000000..5890dbc
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum   - Bacula web interface
+ *
+ * Copyright (C) 2013-2019 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.
+ */
+
+
+Prado::using('Application.API.Class.APIModule');
+
+/**
+ * Tools used to show list files command output.
+ */
+class BList extends APIModule {
+
+       /**
+        * Pattern to get only files from list files output.
+        */
+       const LIST_FILES_OUTPUT_PATTERN = '/^\| (?!(filename|jobid|\d))(?P<path>.+) \|$/i';
+
+       /**
+        * Parse list files output.
+        *
+        * @param array $output raw list files output lines
+        * @return array parsed list files paths
+        */
+       public function parseListFilesOutput(array $output) {
+               $result = array();
+               for ($i = 0; $i < count($output); $i++) {
+                       if (preg_match(self::LIST_FILES_OUTPUT_PATTERN, $output[$i], $match) === 1) {
+                               $result[] = trim($match['path']);
+                       }
+               }
+               return $result;
+       }
+
+       /**
+        * Find file list items by given keyword.
+        *
+        * @param array $file_list file list
+        * @param string $keyword keyword to find
+        * @return array search result (items)
+        */
+       public function findFileListItems($file_list, $keyword) {
+               $result = array();
+               for ($i = 0; $i < count($file_list); $i++) {
+                       if (preg_match('!' . preg_quote($keyword, '!') . '!i', $file_list[$i], $match) === 1) {
+                               $result[] = $file_list[$i];
+                       }
+               }
+               return $result;
+       }
+
+}
+?>
diff --git a/gui/baculum/protected/API/Pages/API/JobFiles.php b/gui/baculum/protected/API/Pages/API/JobFiles.php
new file mode 100644 (file)
index 0000000..5258226
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum   - Bacula web interface
+ *
+ * Copyright (C) 2013-2019 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.
+ */
+
+/**
+ * List files from 'list files jobid=xx' bconsole command.
+ */
+class JobFiles extends BaculumAPIServer {
+
+       public function get() {
+               $misc = $this->getModule('misc');
+               $jobid = $this->Request->contains('id') ? intval($this->Request['id']) : 0;
+               $type = $this->Request->contains('type') && $misc->isValidListFilesType($this->Request['type']) ? $this->Request['type'] : null;
+               $offset = $this->Request->contains('offset') ? intval($this->Request['offset']) : 0;
+               $limit = $this->Request->contains('limit') ? intval($this->Request['limit']) : 0;
+               $search = $this->Request->contains('search') && $misc->isValidPath($this->Request['search']) ? $this->Request['search'] : null;
+
+               $result = $this->getModule('bconsole')->bconsoleCommand(
+                       $this->director,
+                       array('.jobs')
+               );
+               if ($result->exitcode === 0) {
+                       array_shift($result->output);
+                       $job = $this->getModule('job')->getJobById($jobid);
+                       if (is_object($job) && in_array($job->name, $result->output)) {
+                               $cmd = array('list', 'files');
+                               if (is_string($type)) {
+                                       /**
+                                        * NOTE: type param has to be used BEFORE jobid=xx, otherwise it doesn't work.
+                                        * This behavior is also described in Bacula source code (src/dird/ua_output.c).
+                                        */
+                                       $cmd[] = 'type="' . $type . '"';
+                               }
+                               $cmd[] = 'jobid="' . $jobid . '"';
+                               $result = $this->getModule('bconsole')->bconsoleCommand(
+                                       $this->director,
+                                       $cmd
+                               );
+                               if ($result->exitcode === 0) {
+                                       $file_list = $this->getModule('list')->parseListFilesOutput($result->output);
+                                       if (is_string($search)) {
+                                               // Find items
+                                               $file_list = $this->getModule('list')->findFileListItems($file_list, $search);
+                                       }
+                                       $total_items = count($file_list);
+                                       if ($offset > 0) {
+                                               if ($limit > 0) {
+                                                       $file_list = array_slice($file_list, $offset, $limit);
+                                               } else {
+                                                       $file_list = array_slice($file_list, $offset);
+                                               }
+                                       } elseif ($limit > 0) {
+                                               $file_list = array_slice($file_list, 0, $limit);
+                                       }
+                                       $this->output = array('items' => $file_list, 'total' => $total_items);
+                                       $this->error = GenericError::ERROR_NO_ERRORS;
+                               } else {
+                                       $this->output = $result->output;
+                                       $this->error = $result->exitcode;
+                               }
+                       } else {
+                               $this->output = JobError::MSG_ERROR_JOB_DOES_NOT_EXISTS;
+                               $this->error = JobError::ERROR_JOB_DOES_NOT_EXISTS;
+                       }
+               } else {
+                       $this->output = $result->output;
+                       $this->error = $result->exitcode;
+               }
+       }
+}
+?>
index e379d70d9ae8d63079fddc78ddcd6f731b760f37..687a19d7de486b520b54045c079fd93594cb2c93 100644 (file)
@@ -37,7 +37,8 @@
                <module id="status_fd" class="Application.API.Class.StatusClient" />
                <!-- component actions modules -->
                <module id="comp_actions" class="Application.API.Class.ComponentActions" />
-               <!-- misc modules -->
+               <!-- bconsole command modules -->
                <module id="ls" class="Application.API.Class.Ls" />
+               <module id="list" class="Application.API.Class.BList" />
        </modules>
 </configuration>
index 3a56d361f62869466644a04b2cdc714a79433ae9..e40e59047fc9723284ab5f52b27870f1bd6e821c 100644 (file)
@@ -64,6 +64,7 @@
        <url ServiceParameter="JobRun" pattern="api/v1/jobs/run/" />
        <url ServiceParameter="JobCancel" pattern="api/v1/jobs/{id}/cancel/" parameters.id="\d+"/>
        <url ServiceParameter="JobTotals" pattern="api/v1/jobs/totals/" />
+       <url ServiceParameter="JobFiles" pattern="api/v1/jobs/{id}/files/" parameters.id="\d+" />
        <url ServiceParameter="RestoreRun" pattern="api/v1/jobs/restore/" />
        <!-- bvfs endpoints-->
        <url ServiceParameter="BVFSUpdate" pattern="api/v1/bvfs/update/" />
index 2c4d237c8a0c2e28e91196ab42b7811a5862e4a4..704cdfbe2d23e5443de4a23d5ed8be85d7c46443 100644 (file)
                                ]
                        }
                },
+               "/api/v1/jobs/{jobid}/files": {
+                       "get": {
+                               "tags": ["jobs"],
+                               "summary": "Show job files and directories",
+                               "description": "Get job file and directory list.",
+                               "responses": {
+                                       "200": {
+                                               "description": "Show job files output list",
+                                               "content": {
+                                                       "application/json": {
+                                                               "schema": {
+                                                                       "type": "object",
+                                                                       "properties": {
+                                                                               "output": {
+                                                                                       "type": "object",
+                                                                                       "properties": {
+                                                                                               "items": {
+                                                                                                       "type": "array",
+                                                                                                       "items": {
+                                                                                                               "type": "string",
+                                                                                                               "description": "Show job files output list"
+                                                                                                       }
+                                                                                               },
+                                                                                               "total": {
+                                                                                                       "type": "integer",
+                                                                                                       "description": "Total number of items"
+                                                                                               }
+                                                                                       }
+                                                                               },
+                                                                               "error": {
+                                                                                       "type": "integer",
+                                                                                       "description": "Error code",
+                                                                                       "enum": [0, 1, 2, 3, 4, 5, 6, 7, 11, 50, 1000]
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               },
+                               "parameters": [
+                                       {
+                                               "name": "jobid",
+                                               "in": "path",
+                                               "description": "Job identifier",
+                                               "required":  true,
+                                               "schema": {
+                                                       "type": "integer"
+                                               }
+                                       },
+                                       {
+                                               "name": "type",
+                                               "in": "query",
+                                               "description": "List item type",
+                                               "required":  false,
+                                               "schema": {
+                                                       "type": "string",
+                                                       "enum": ["deleted", "all"]
+                                               }
+                                       },
+                                       {
+                                               "name": "offset",
+                                               "in": "query",
+                                               "description": "Result items offset",
+                                               "required":  false,
+                                               "schema": {
+                                                       "type": "integer"
+                                               }
+                                       },
+                                       {
+                                               "name": "limit",
+                                               "in": "query",
+                                               "description": "Result items limit",
+                                               "required":  false,
+                                               "schema": {
+                                                       "type": "integer"
+                                               }
+                                       },
+                                       {
+                                               "name": "search",
+                                               "in": "query",
+                                               "description": "Search keyword in item list",
+                                               "required":  false,
+                                               "schema": {
+                                                       "type": "string"
+                                               }
+                                       }
+                               ]
+                       }
+               },
                "/api/v1/jobs/resnames": {
                        "get": {
                                "tags": ["jobs"],
index a83cbeaa042c6b4d7bf19371277addb483849823..678a82449ccc379537c39b1005e4a9da760b0dc3 100644 (file)
@@ -235,7 +235,7 @@ class Miscellaneous extends TModule {
        }
 
        public function isValidPath($path) {
-               return (preg_match('/^[\p{L}\p{N}\p{Z}\p{Sc}\[\]\-\'\/\\(){}:.#~_,+!$]{0,10000}$/u', $path) === 1);
+               return (preg_match('/^[\p{L}\p{N}\p{Z}\p{Sc}\p{Pd}\[\]\-\'\/\\(){}:.#~_,+!$]{0,10000}$/u', $path) === 1);
        }
 
        public function isValidReplace($replace) {
@@ -262,6 +262,10 @@ class Miscellaneous extends TModule {
                return (preg_match('/^[a-zA-Z0-9]+$/', $str) === 1);
        }
 
+       public function isValidListFilesType($type) {
+               return (preg_match('/^(all|deleted)$/', $type) === 1);
+       }
+
        public function escapeCharsToConsole($path) {
                return preg_replace('/([$])/', '\\\${1}', $path);
        }