]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
baculum: Add status client API endpoint
authorMarcin Haba <marcin.haba@bacula.pl>
Sun, 28 Jul 2019 17:19:48 +0000 (19:19 +0200)
committerMarcin Haba <marcin.haba@bacula.pl>
Sat, 14 Dec 2019 14:55:27 +0000 (15:55 +0100)
gui/baculum/protected/API/Class/ComponentStatusModule.php
gui/baculum/protected/API/Class/StatusClient.php [new file with mode: 0644]
gui/baculum/protected/API/Class/StatusDirector.php
gui/baculum/protected/API/Class/StatusStorage.php
gui/baculum/protected/API/Pages/API/ComponentStatus.php
gui/baculum/protected/API/Pages/config.xml
gui/baculum/protected/API/endpoints.xml

index 8b2e60d796a2e1c2717f76be2afebad84e4e6f53..943599c74ed10c6711e77305be27faa8586d3afa 100644 (file)
@@ -3,7 +3,7 @@
  * Bacula(R) - The Network Backup Solution
  * Baculum   - Bacula web interface
  *
- * Copyright (C) 2013-2018 Kern Sibbald
+ * Copyright (C) 2013-2019 Kern Sibbald
  *
  * The main author of Baculum is Marcin Haba.
  * The original author of Bacula is Kern Sibbald, with contributions
@@ -29,11 +29,24 @@ Prado::using('Application.API.Class.APIModule');
 abstract class ComponentStatusModule extends APIModule {
 
        /**
-        * Method to get parsed status ready to show in API results.
+        * Get parsed status.
         *
-        * @param array $output array with parsed component status values
+        * @param string $director director name
+        * @param string $component_name component name
+        * @param string $type output type (e.g. header, running, terminated ...etc.)
+        * @return array ready array parsed component status output
         */
-       abstract public function getStatus(array $output);
+       abstract public function getStatus($director, $component_name = null, $type = null);
+
+
+       /**
+        * Parse component status.
+        *
+        * @param array $output component status output from bconsole
+        * @param string $type output type (e.g. header, running, terminated ...etc.)
+        * @return array parsed component status output
+        */
+       abstract public function parseStatus(array $output, $type);
 
        /**
         * Parse single component status line to find key=value pairs.
@@ -48,5 +61,13 @@ abstract class ComponentStatusModule extends APIModule {
                }
                return $ret;
        }
+
+       /**
+        * Validate status output type.
+        *
+        * @param string $type output type (e.g. header, running, terminated ...etc.)
+        * @return boolean true if output type is valid for component, otherwise false
+        */
+       abstract public function isValidOutputType($type);
 }
 ?>
diff --git a/gui/baculum/protected/API/Class/StatusClient.php b/gui/baculum/protected/API/Class/StatusClient.php
new file mode 100644 (file)
index 0000000..e44724d
--- /dev/null
@@ -0,0 +1,110 @@
+<?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.ComponentStatusModule');
+
+/**
+ * Module used to get and parse client status output.
+ */
+class StatusClient extends ComponentStatusModule {
+
+       /**
+        * Output types (output sections).
+        */
+       const OUTPUT_TYPE_HEADER = 'header';
+       const OUTPUT_TYPE_RUNNING = 'running';
+       const OUTPUT_TYPE_TERMINATED = 'terminated';
+
+       /**
+        * Get parsed client status.
+        *
+        * @param string $director director name
+        * @param string $component_name component name
+        * @param string $type output type (e.g. header, running, terminated ...etc.)
+        * @return array ready array parsed component status output
+        */
+       public function getStatus($director, $component_name = null, $type = null) {
+               $ret = array('output' => array(), 'error' => 0);
+               $result = $this->getModule('bconsole')->bconsoleCommand(
+                       $director,
+                       array('.status', 'client="' . $component_name . '"', $type),
+                       Bconsole::PTYPE_API_CMD
+               );
+               if ($result->exitcode === 0) {
+                       $ret['output'] = $this->parseStatus($result->output, $type);
+               } else {
+                       $ret['output'] = $result->output;
+               }
+               $ret['error'] = $result->exitcode;
+               return $ret;
+       }
+
+       /**
+        * Parse .api 2 client status output from bconsole.
+        *
+        * @param array $output bconsole status client output
+        * @param string $type output type (e.g. header, running, terminated ...etc.)
+        * @return array array with parsed client status values
+        */
+       public function parseStatus(array $output, $type) {
+               $result = array();
+               $line = null;
+               $opts = array();
+               for($i = 0; $i < count($output); $i++) {
+                       if (empty($output[$i])) {
+                               if  (count($opts) > 10) {
+                                       $result[] = $opts;
+                               }
+                               if (count($opts) > 0) {
+                                       $opts = array();
+                               }
+                       } else {
+                               $line = $this->parseLine($output[$i]);
+                               if (is_array($line)) {
+                                       $opts[$line['key']] = $line['value'];
+                               }
+                       }
+               }
+               if ($type === self::OUTPUT_TYPE_HEADER) {
+                       $result = array_pop($result);
+               }
+               return $result;
+       }
+
+       /**
+        * Validate status output type.
+        *
+        * @param string $type output type (e.g. header, running, terminated ...etc.)
+        * @return boolean true if output type is valid for component, otherwise false
+        */
+       public function isValidOutputType($type) {
+               return in_array(
+                       $type,
+                       array(
+                               self::OUTPUT_TYPE_HEADER,
+                               self::OUTPUT_TYPE_RUNNING,
+                               self::OUTPUT_TYPE_TERMINATED
+                       )
+               );
+       }
+}
+?>
index b931b16e7cc4e2f25022dff3cad2f5a4dddc0c0f..6d2a77b9f545731316e9d89e6f341a963fe6979f 100644 (file)
@@ -3,7 +3,7 @@
  * Bacula(R) - The Network Backup Solution
  * Baculum   - Bacula web interface
  *
- * Copyright (C) 2013-2018 Kern Sibbald
+ * Copyright (C) 2013-2019 Kern Sibbald
  *
  * The main author of Baculum is Marcin Haba.
  * The original author of Bacula is Kern Sibbald, with contributions
@@ -20,6 +20,7 @@
  * Bacula(R) is a registered trademark of Kern Sibbald.
  */
 
+Prado::using('Application.API.Class.Bconsole');
 Prado::using('Application.API.Class.ComponentStatusModule');
 
 /**
@@ -28,41 +29,106 @@ Prado::using('Application.API.Class.ComponentStatusModule');
 class StatusDirector extends ComponentStatusModule {
 
        /**
-        * Get director status by raw bconsole status director output.
+        * Output types (output sections).
+        */
+       const OUTPUT_TYPE_HEADER = 'header';
+       const OUTPUT_TYPE_SCHEDULED = 'scheduled';
+       const OUTPUT_TYPE_RUNNING = 'running';
+       const OUTPUT_TYPE_TERMINATED = 'terminated';
+
+
+       /**
+        * Get parsed director status.
+        *
+        * @param string $director director name
+        * @param string $component_name component name
+        * @param string $type output type (e.g. header, running, terminated ...etc.)
+        * @return array ready array parsed component status output
+        */
+       public function getStatus($director, $component_name = null, $type = null) {
+               $ret = array('output' => array(), 'error' => 0);
+               $result = $this->getModule('bconsole')->bconsoleCommand(
+                       $director,
+                       array('status', 'director'),
+                       Bconsole::PTYPE_API_CMD
+               );
+               if ($result->exitcode === 0) {
+                       $ret['output'] = $this->parseStatus($result->output, $type);
+                       if (is_string($type) && key_exists($type, $ret['output'])) {
+                               if ($type === self::OUTPUT_TYPE_HEADER) {
+                                       $ret['output'] = array_pop($ret['output'][$type]);
+                               } else {
+                                       $ret['output'] = $ret['output'][$type];
+                               }
+                       }
+               } else {
+                       $ret['output'] = $result->output;
+               }
+               $ret['error'] = $result->exitcode;
+               return $ret;
+       }
+
+       /**
+        * Parse .api 2 director status output from bconsole.
         *
         * @param array $output bconsole status director output
+        * @param string $type output type (e.g. header, running, terminated ...etc.)
         * @return array array with parsed director status values
         */
-       public function getStatus(array $output) {
+       public function parseStatus(array $output, $type) {
                $result = array();
-               $section = null;
+               $type = null;
                $line = null;
-               $sections = array('header:', 'running:', 'terminated:');
+               $types = array(
+                       self::OUTPUT_TYPE_HEADER . ':',
+                       self::OUTPUT_TYPE_RUNNING . ':',
+                       self::OUTPUT_TYPE_TERMINATED . ':'
+               );
                $opts = array();
                for($i = 0; $i < count($output); $i++) {
-                       if (in_array($output[$i], $sections)) { // check if section
-                               $section = rtrim($output[$i], ':');
-                       } elseif ($section === 'header' && count($opts) == 0 && $output[$i] === '') {
+                       if (in_array($output[$i], $types)) { // check if type
+                               $type = rtrim($output[$i], ':');
+                       } elseif ($type === self::OUTPUT_TYPE_HEADER && count($opts) == 0 && $output[$i] === '') {
                                /**
-                                * special treating 'scheduled' section because this section
+                                * special treating 'scheduled' type because this type
                                 * is missing in the api status dir output.
                                 */
-                               $section = 'scheduled';
-                       } elseif (!empty($section)) {
+                               $type = self::OUTPUT_TYPE_SCHEDULED;
+                       } elseif (!empty($type)) {
                                $line = $this->parseLine($output[$i]);
                                if (is_array($line)) { // check if line
-                                       if (!key_exists($section, $result)) {
-                                               $result[$section] = array();
-                                               continue;
+                                       if (!key_exists($type, $result)) {
+                                               $result[$type] = array();
                                        }
                                        $opts[$line['key']] = $line['value'];
                                } elseif (count($opts) > 0) { // dump all parameters
-                                       $result[$section][] = $opts;
+                                       $result[$type][] = $opts;
                                        $opts = array();
                                }
                        }
                }
+               if ($type === self::OUTPUT_TYPE_HEADER) {
+                       $result = array_pop($result);
+               }
                return $result;
        }
+
+       /**
+        * Validate status output type.
+        *
+        * @param string $type output type (e.g. header, running, terminated ...etc.)
+        * @return boolean true if output type is valid for component, otherwise false
+        */
+       public function isValidOutputType($type) {
+               return in_array(
+                       $type,
+                       array(
+                               self::OUTPUT_TYPE_HEADER,
+                               self::OUTPUT_TYPE_SCHEDULED,
+                               self::OUTPUT_TYPE_RUNNING,
+                               self::OUTPUT_TYPE_TERMINATED
+                       )
+               );
+       }
 }
 ?>
index d3b7580e1d8d3dc83622aae7aca235e79b1aefec..ebbff12bf32983092618b11f758d4e400a8e0452 100644 (file)
@@ -3,7 +3,7 @@
  * Bacula(R) - The Network Backup Solution
  * Baculum   - Bacula web interface
  *
- * Copyright (C) 2013-2018 Kern Sibbald
+ * Copyright (C) 2013-2019 Kern Sibbald
  *
  * The main author of Baculum is Marcin Haba.
  * The original author of Bacula is Kern Sibbald, with contributions
 Prado::using('Application.API.Class.ComponentStatusModule');
 
 /**
- * Module used to parse and prepare storage status output.
+ * Module used to get and parse storage status output.
  */
 class StatusStorage extends ComponentStatusModule {
 
        /**
-        * Get director status by raw bconsole status director output.
+        * Output types (output sections).
+        */
+       const OUTPUT_TYPE_HEADER = 'header';
+       const OUTPUT_TYPE_RUNNING = 'running';
+       const OUTPUT_TYPE_TERMINATED = 'terminated';
+       const OUTPUT_TYPE_DEVICES = 'devices';
+
+       /**
+        * Get parsed storage status.
+        *
+        * @param string $director director name
+        * @param string $component_name component name
+        * @param string $type output type (e.g. header, running, terminated ...etc.)
+        * @return array ready array parsed component status output
+        */
+       public function getStatus($director, $component_name = null, $type = null) {
+               $ret = array('output' => array(), 'error' => 0);
+               $result = $this->getModule('bconsole')->bconsoleCommand(
+                       $director,
+                       array('.status', 'storage="' . $component_name . '"', $type),
+                       Bconsole::PTYPE_API_CMD
+               );
+               if ($result->exitcode === 0) {
+                       $ret['output'] = $this->parseStatus($result->output, $type);
+               } else {
+                       $ret['output'] = $result->output;
+               }
+               $ret['error'] = $result->exitcode;
+               return $ret;
+       }
+
+       /**
+        * Parse .api 2 storage status output from bconsole.
         *
-        * @param array $output bconsole status director output
+        * @param array $output bconsole status storage output
+        * @param string $type output type (e.g. header, running, terminated ...etc.)
         * @return array array with parsed storage status values
         */
-       public function getStatus(array $output) {
+       public function parseStatus(array $output, $type) {
                $result = array();
                $line = null;
                $opts = array();
-               $header = false;
                for($i = 0; $i < count($output); $i++) {
                        if (empty($output[$i])) {
                                if  (count($opts) > 10) {
@@ -46,8 +78,6 @@ class StatusStorage extends ComponentStatusModule {
                                if (count($opts) > 0) {
                                        $opts = array();
                                }
-                       } elseif ($output[$i] === 'header:') {
-                               $header = true;
                        } else {
                                $line = $this->parseLine($output[$i]);
                                if (is_array($line)) {
@@ -55,11 +85,28 @@ class StatusStorage extends ComponentStatusModule {
                                }
                        }
                }
-               if ($header) {
-                       // header is only one so get it without using list
+               if ($type === self::OUTPUT_TYPE_HEADER) {
                        $result = array_pop($result);
                }
                return $result;
        }
+
+       /**
+        * Validate status output type.
+        *
+        * @param string $type output type (e.g. header, running, terminated ...etc.)
+        * @return boolean true if output type is valid for component, otherwise false
+        */
+       public function isValidOutputType($type) {
+               return in_array(
+                       $type,
+                       array(
+                               self::OUTPUT_TYPE_HEADER,
+                               self::OUTPUT_TYPE_RUNNING,
+                               self::OUTPUT_TYPE_TERMINATED,
+                               self::OUTPUT_TYPE_DEVICES
+                       )
+               );
+       }
 }
 ?>
index c809eaba7cefab4688d8c98b7faea55fd28bb82b..e7c9c0db42aee8912fa2d2e2e3440cc00389d95a 100644 (file)
@@ -28,52 +28,37 @@ Prado::using('Application.API.Class.Bconsole');
 class ComponentStatus extends BaculumAPIServer {
 
        public function get() {
-               $status = array();
-               $misc = $this->getModule('misc');
                $component = $this->Request->contains('component') ? $this->Request['component'] : '';
-               $type = $this->Request->contains('type') && $misc->isValidName($this->Request['type']) ? $this->Request['type'] : '';
-
+               $component_name = $this->Request->contains('name') && $this->getModule('misc')->isValidName($this->Request['name']) ? $this->Request['name'] : null;
+               $type = null;
+               $status = null;
                switch($component) {
                        case 'director': {
-                               $result = $this->getModule('bconsole')->bconsoleCommand(
-                                       $this->director,
-                                       array('status', 'director'),
-                                       Bconsole::PTYPE_API_CMD
-                               );
-                               if ($result->exitcode === 0) {
-                                       $output = $this->getModule('status_dir')->getStatus($result->output);
-                                       if ($misc->isValidName($type) && key_exists($type, $output)) {
-                                               $this->output = $output[$type];
-                                       } else {
-                                               $this->output = $output;
-                                       }
-                               } else {
-                                       $this->output = $result->output;
-                               }
-                               $this->error = $result->exitcode;
+                               $status = $this->getModule('status_dir');
+                               $type = $this->Request->contains('type') && $status->isValidOutputType($this->Request['type']) ? $this->Request['type'] : '';
                                break;
                        }
                        case 'storage': {
-                               $storage = 'storage';
-                               if (empty($type)) {
-                                       $type = 'header'; // default list terminated jobs
-                               }
-                               if ($this->Request->contains('name') && $misc->isValidName($this->Request['name'])) {
-                                       $storage .= '="' . $this->Request['name'] . '"';
-                               }
-                               $result = $this->getModule('bconsole')->bconsoleCommand(
-                                       $this->director,
-                                       array('.status', $storage, $type),
-                                       Bconsole::PTYPE_API_CMD
-                               );
-                               if ($result->exitcode === 0) {
-                                       $this->output = $this->getModule('status_sd')->getStatus($result->output);
-                               } else {
-                                       $this->output = $result->output;
-                               }
-                               $this->error = $result->exitcode;
+                               $status = $this->getModule('status_sd');
+                               $type = $this->Request->contains('type') && $status->isValidOutputType($this->Request['type']) ? $this->Request['type'] : 'header';
                                break;
                        }
+                       case 'client':  {
+                               $status = $this->getModule('status_fd');
+                               $type = $this->Request->contains('type') && $status->isValidOutputType($this->Request['type']) ? $this->Request['type'] : 'header';
+                       }
+               }
+               if (is_object($status)) {
+                       $ret = $status->getStatus(
+                               $this->director,
+                               $component_name,
+                               $type
+                       );
+                       $this->output = $ret['output'];
+                       $this->error = $ret['error'];
+               } else {
+                       $this->output = GenericError::MSG_ERROR_INTERNAL_ERROR;
+                       $this->error =  GenericError::ERROR_INTERNAL_ERROR;
                }
        }
 }
index 2023d39a7671a3dc0c7dae9aee14db1531d7df50..d20e0d4418e85cd23bbc8685de87f835ad02f11e 100644 (file)
@@ -38,6 +38,7 @@
                <!-- component status modules -->
                <module id="status_dir" class="Application.API.Class.StatusDirector" />
                <module id="status_sd" class="Application.API.Class.StatusStorage" />
+               <module id="status_fd" class="Application.API.Class.StatusClient" />
                <!-- misc modules -->
                <module id="ls" class="Application.API.Class.Ls" />
        </modules>
index e934106c63f4fea3bbb34d2be0e321eea08974d4..45ee61ab1f5a00fce31328d25a9dcf646086b248 100644 (file)
@@ -90,7 +90,7 @@
        <url ServiceParameter="API.Config" pattern="api/v1/config/{component_type}/{resource_type}/" parameters.component_type="[a-z]+" parameters.resource_type="[a-zA-Z]+" />
        <url ServiceParameter="API.Config" pattern="api/v1/config/{component_type}/{resource_type}/{resource_name}/" parameters.component_type="[a-z]+" parameters.resource_type="[a-zA-Z]+" parameters.resource_name="[a-zA-Z0-9:.\-_ ]+" />
        <!-- component status endpoints -->
-       <url ServiceParameter="API.ComponentStatus" pattern="api/v1/status/{component}/" parameters.component="[a-z]+" />
+       <url ServiceParameter="API.ComponentStatus" pattern="api/v1/status/{component}/" parameters.component="(director|storage|client)" />