From: Marcin Haba Date: Mon, 28 Oct 2019 19:20:38 +0000 (+0100) Subject: baculum: Add list job files API endpoint X-Git-Tag: Release-9.6.0~108 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1acb7b7648878b7ab8b4e2f22cad7586e51ad6ee;p=thirdparty%2Fbacula.git baculum: Add list job files API endpoint --- diff --git a/gui/baculum/protected/API/Class/BList.php b/gui/baculum/protected/API/Class/BList.php new file mode 100644 index 000000000..5890dbc6f --- /dev/null +++ b/gui/baculum/protected/API/Class/BList.php @@ -0,0 +1,70 @@ +.+) \|$/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 index 000000000..525822635 --- /dev/null +++ b/gui/baculum/protected/API/Pages/API/JobFiles.php @@ -0,0 +1,89 @@ +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; + } + } +} +?> diff --git a/gui/baculum/protected/API/Pages/API/config.xml b/gui/baculum/protected/API/Pages/API/config.xml index e379d70d9..687a19d7d 100644 --- a/gui/baculum/protected/API/Pages/API/config.xml +++ b/gui/baculum/protected/API/Pages/API/config.xml @@ -37,7 +37,8 @@ - + + diff --git a/gui/baculum/protected/API/Pages/API/endpoints.xml b/gui/baculum/protected/API/Pages/API/endpoints.xml index 3a56d361f..e40e59047 100644 --- a/gui/baculum/protected/API/Pages/API/endpoints.xml +++ b/gui/baculum/protected/API/Pages/API/endpoints.xml @@ -64,6 +64,7 @@ + diff --git a/gui/baculum/protected/API/openapi_baculum.json b/gui/baculum/protected/API/openapi_baculum.json index 2c4d237c8..704cdfbe2 100644 --- a/gui/baculum/protected/API/openapi_baculum.json +++ b/gui/baculum/protected/API/openapi_baculum.json @@ -989,6 +989,96 @@ ] } }, + "/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"], diff --git a/gui/baculum/protected/Common/Class/Miscellaneous.php b/gui/baculum/protected/Common/Class/Miscellaneous.php index a83cbeaa0..678a82449 100644 --- a/gui/baculum/protected/Common/Class/Miscellaneous.php +++ b/gui/baculum/protected/Common/Class/Miscellaneous.php @@ -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); }