]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
baculum: Add API changes to support restore from copy jobs
authorMarcin Haba <marcin.haba@bacula.pl>
Sun, 1 Dec 2019 13:22:43 +0000 (14:22 +0100)
committerMarcin Haba <marcin.haba@bacula.pl>
Sat, 14 Dec 2019 15:02:17 +0000 (16:02 +0100)
gui/baculum/protected/API/Class/JobManager.php
gui/baculum/protected/API/Pages/API/BVFSGetJobids.php
gui/baculum/protected/API/Pages/API/BVFSVersions.php
gui/baculum/protected/API/Pages/API/JobsRecent.php
gui/baculum/protected/API/openapi_baculum.json

index bd645342de9362621ec2ec58a72054609f3b664e..aee274213a94c53906df73ba0aa2c11150715afa 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
@@ -74,32 +74,101 @@ class JobManager extends APIModule {
                return JobRecord::finder()->deleteByjobid($id);
        }
 
-       public function getRecentJobids($jobname, $clientid, $filesetid) {
-               $sql = "name='$jobname' AND clientid='$clientid' AND filesetid='$filesetid' AND jobstatus IN ('T', 'W') AND level IN ('F', 'I', 'D')";
+       /**
+        * Find all compojobs required to do full restore.
+        *
+        * @param array $jobs jobid to start searching for jobs
+        * @return array compositional jobs regarding given jobid
+        */
+       private function findCompositionalJobs(array $jobs) {
+               $jobids = [];
+               $wait_on_full = false;
+               foreach($jobs as $job) {
+                       if($job->level == 'F') {
+                               $jobids[] = $job->jobid;
+                               break;
+                       } elseif($job->level == 'D' && $wait_on_full === false) {
+                               $jobids[] = $job->jobid;
+                               $wait_on_full = true;
+                       } elseif($job->level == 'I' && $wait_on_full === false) {
+                               $jobids[] = $job->jobid;
+                       }
+               }
+               return $jobids;
+       }
+
+       /**
+        * Get latest recent compositional jobids to do restore.
+        *
+        * @param string $jobname job name
+        * @param integer $clientid client identifier
+        * @param integer $filesetid fileset identifier
+        * @param boolean $inc_copy_job determine if include copy jobs to result
+        * @return array list of jobids required to do restore
+        */
+       public function getRecentJobids($jobname, $clientid, $filesetid, $inc_copy_job = false) {
+               $types = "('B')";
+               if ($inc_copy_job) {
+                       $types = "('B', 'C')";
+               }
+               $sql = "name='$jobname' AND clientid='$clientid' AND filesetid='$filesetid' AND type IN $types AND jobstatus IN ('T', 'W') AND level IN ('F', 'I', 'D')";
                $finder = JobRecord::finder();
                $criteria = new TActiveRecordCriteria;
-               $order = 'EndTime';
+               $order1 = 'RealEndTime';
+               $order2 = 'JobId';
                $db_params = $this->getModule('api_config')->getConfig('db');
                if ($db_params['type'] === Database::PGSQL_TYPE) {
-                   $order = strtolower($order);
+                   $order1 = strtolower($order1);
+                   $order2 = strtolower($order2);
                }
-               $criteria->OrdersBy[$order] = 'desc';
+               $criteria->OrdersBy[$order1] = 'desc';
+               $criteria->OrdersBy[$order2] = 'desc';
                $criteria->Condition = $sql;
                $jobs = $finder->findAll($criteria);
 
                $jobids = array();
-               $waitForFull = false;
-               if(!is_null($jobs)) {
-                       foreach($jobs as $job) {
-                               if($job->level == 'F') {
-                                       $jobids[] = $job->jobid;
-                                       break;
-                               } elseif($job->level == 'D' && $waitForFull === false) {
-                                       $jobids[] = $job->jobid;
-                                       $waitForFull = true;
-                               } elseif($job->level == 'I' && $waitForFull === false) {
-                                       $jobids[] = $job->jobid;
+               if(is_array($jobs)) {
+                       $jobids = $this->findCompositionalJobs($jobs);
+               }
+               return $jobids;
+       }
+
+       /**
+        * Get compositional jobids to do restore starting from given job (full/incremental/differential).
+        *
+        * @param integer $jobid job identifier of last job to do restore
+        * @return array list of jobids required to do restore
+        */
+       public function getJobidsToRestore($jobid) {
+               $jobids = [];
+               $bjob = JobRecord::finder()->findBySql(
+                       "SELECT * FROM job WHERE jobid = '$jobid' AND jobstatus IN ('T', 'W') AND type IN ('B', 'C') AND level IN ('F', 'I', 'D')"
+               );
+               if (is_object($bjob)) {
+                       if ($bjob->level != 'F') {
+                               $sql = "clientid=:clientid AND filesetid=:filesetid AND type IN ('B', 'C')" .
+                                       " AND jobstatus IN ('T', 'W') AND level IN ('F', 'I', 'D') " .
+                                       " AND starttime <= :starttime and jobid <= :jobid";
+                               $finder = JobRecord::finder();
+                               $criteria = new TActiveRecordCriteria;
+                               $order1 = 'JobId';
+                               $db_params = $this->getModule('api_config')->getConfig('db');
+                               if ($db_params['type'] === Database::PGSQL_TYPE) {
+                                       $order1 = strtolower($order1);
+                               }
+                               $criteria->OrdersBy[$order1] = 'desc';
+                               $criteria->Condition = $sql;
+                               $criteria->Parameters[':clientid'] = $bjob->clientid;
+                               $criteria->Parameters[':filesetid'] = $bjob->filesetid;
+                               $criteria->Parameters[':starttime'] = $bjob->endtime;
+                               $criteria->Parameters[':jobid'] = $bjob->jobid;
+                               $jobs = $finder->findAll($criteria);
+
+                               if(is_array($jobs)) {
+                                       $jobids = $this->findCompositionalJobs($jobs);
                                }
+                       } else {
+                               $jobids[] = $bjob->jobid;
                        }
                }
                return $jobids;
index 970dcd90e942c7e6c690ffc59068e3e60ce7f0ff..30ab404df501946ad0ea48eeba419937c92ef518 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
@@ -24,11 +24,30 @@ class BVFSGetJobids extends BaculumAPIServer {
 
        public function get() {
                $jobid = $this->Request->contains('jobid') ? intval($this->Request['jobid']) : 0;
+               $inc_copy_job = $this->Request->contains('inc_copy_job') ? intval($this->Request['inc_copy_job']) : 0;
                if ($jobid > 0) {
-                       $cmd = array('.bvfs_get_jobids', 'jobid="' . $jobid . '"');
-                       $result = $this->getModule('bconsole')->bconsoleCommand($this->director, $cmd);
-                       $this->output = $result->output;
-                       $this->error = $result->exitcode;
+                       $result = array();
+                       $error = BVFSError::ERROR_NO_ERRORS;
+                       if ($inc_copy_job == 1) {
+                               /**
+                                * To use copy jobs to restore here is used Baculum own method to get
+                                * all compositional jobs. It is because of a bug in .bvfs_get_jobids command
+                                * reported here:
+                                * http://bugs.bacula.org/view.php?id=2500
+                                */
+                               $jobids = $this->getModule('job')->getJobidsToRestore($jobid);
+                               $jobids_str = implode(',', $jobids); // implode to be compatible with Bvfs output
+                               if (!empty($jobids_str)) {
+                                       $result = array($jobids_str);
+                               }
+                       } else {
+                               $cmd = array('.bvfs_get_jobids', 'jobid="' . $jobid . '"');
+                               $jobids = $this->getModule('bconsole')->bconsoleCommand($this->director, $cmd);
+                               $result = $jobids->output;
+                               $error = $jobids->exitcode;
+                       }
+                       $this->output = $result;
+                       $this->error = $error;
                } else {
                        $this->output = BVFSError::MSG_ERROR_INVALID_JOBID;
                        $this->error = BVFSError::ERROR_INVALID_JOBID;
index 1d71185c1412b31282bc207dd1dfd9ed31c49d2f..9fd56f52eac022efe378395107d833c4e000d379 100644 (file)
@@ -3,7 +3,7 @@
  * Bacula(R) - The Network Backup Solution
  * Baculum   - Bacula web interface
  *
- * Copyright (C) 2013-2017 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
@@ -26,6 +26,7 @@ class BVFSVersions extends BaculumAPIServer {
                $jobid = $this->Request->contains('jobid') ? intval($this->Request['jobid']) : 0;
                $pathid = $this->Request->contains('pathid') ? intval($this->Request['pathid']) : 0;
                $filenameid = $this->Request->contains('filenameid') ? intval($this->Request['filenameid']) : 0;
+               $copies = $this->Request->contains('copies') ? intval($this->Request['copies']) : 0;
                $client = null;
                if ($this->Request->contains('client') && $this->getModule('misc')->isValidName($this->Request['client'])) {
                        $client = $this->Request['client'];
@@ -44,6 +45,9 @@ class BVFSVersions extends BaculumAPIServer {
                        'pathid="' . $pathid . '"',
                        'fnid="' . $filenameid . '"'
                );
+               if ($copies == 1) {
+                       $cmd[] = 'copies';
+               }
                $result = $this->getModule('bconsole')->bconsoleCommand($this->director, $cmd);
                $this->output = $result->output;
                $this->error = $result->exitcode;
index f38e79a8e566b03d6d39ef23fad663dba2fe8414..332bdd73e308af9b6112fd193ce128403a7f9a28 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
@@ -23,6 +23,7 @@
 class JobsRecent extends BaculumAPIServer {
        public function get() {
                $jobname = $this->Request->contains('name') ? $this->Request['name'] : '';
+               $inc_copy_job = $this->Request->contains('inc_copy_job') ? intval($this->Request['inc_copy_job']) : 0;
                $clientid = null;
                if ($this->Request->contains('clientid')) {
                        $clientid = intval($this->Request['clientid']);
@@ -55,7 +56,7 @@ class JobsRecent extends BaculumAPIServer {
                        if ($result->exitcode === 0) {
                                array_shift($result->output);
                                if (in_array($jobname, $result->output)) {
-                                       $jobs = $this->getModule('job')->getRecentJobids($jobname, $clientid, $filesetid);
+                                       $jobs = $this->getModule('job')->getRecentJobids($jobname, $clientid, $filesetid, $inc_copy_job);
                                        if (is_array($jobs)) {
                                                $this->output = $jobs;
                                                $this->error = JobError::ERROR_NO_ERRORS;
index 704cdfbe2d23e5443de4a23d5ed8be85d7c46443..4fcbbcfd5829d8bfa1d980568ab5807948d9f78e 100644 (file)
                                                "schema": {
                                                        "type": "integer"
                                                }
+                                       },
+                                       {
+                                               "name": "copies",
+                                               "in": "query",
+                                               "description": "If set to 1, lists copy job file versions together with backup job file versions",
+                                               "required": false,
+                                               "schema": {
+                                                       "type": "integer",
+                                                       "default": 0
+                                               }
                                        }
                                ]
                        }