function getInfo() { global $config; if (!is_numeric($this->id)) { throw new Exception('backupStrategy->getInfo: ' . "Error: The ID for this object is not an integer."); } $conn = dbConnection::getInstance($this->log); $sql = "SELECT * FROM backup_strategies WHERE backup_strategy_id=" . $this->id; if (!($res = $conn->query($sql))) { throw new Exception('backupStrategy->getInfo: ' . "Error: Query: {$sql} \nFailed with MySQL Error: {$conn->error}"); } $info = $res->fetch_array(); return $info; }
function setKilled($bool = true) { if (!is_numeric($this->id)) { throw new Exception('backupJob->setKilled: ' . "Error: The ID for this object is not an integer."); } $conn = dbConnection::getInstance($this->log); if ($bool) { $killed = 1; } else { $killed = 0; } $sql = "UPDATE backup_jobs SET killed = " . $killed . " WHERE backup_job_id=" . $this->id; if (!$conn->query($sql)) { throw new DBException('backupJob->setKilled: ' . "Error: Query: {$sql} \nFailed with MySQL Error: {$conn->error}"); } return; }
function getChild() { global $config; if (!is_numeric($this->id)) { throw new Exception('backupSnapshot->getChild: ' . "Error: The ID for this object is not an integer."); } $conn = dbConnection::getInstance($this->log); $sql = "SELECT backup_snapshot_id FROM backup_snapshots WHERE status='COMPLETED' AND parent_snapshot_id=" . $this->id; if (!($res = $conn->query($sql))) { throw new Exception('backupSnapshot->getChild: ' . "Error: Query: {$sql} \nFailed with MySQL Error: {$conn->error}"); } if ($res->num_rows != 1) { throw new Exception('backupSnapshot->getChild: ' . "Error: Could not identify a single child of this backupSnapshot."); } $row = $res->fetch_array(); $backupGetter = new backupSnapshotGetter(); return $backupGetter->getById($row['backup_snapshot_id']); }
function destroy() { global $config; // Validate $this->__validate(); $this->setStatus('DELETING'); $this->deleteFiles(); // Remove the row from the DB. $conn = dbConnection::getInstance($this->log); $sql = "DELETE FROM materialized_snapshots WHERE materialized_snapshot_id=" . $this->id; if (!$conn->query($sql)) { throw new Exception('materializedSnapshot->destroy: ' . "Error: Query: {$sql} \nFailed with MySQL Error: {$conn->error}"); } if ($conn->affected_rows != 1) { throw new Exception('materializedSnapshot->destroy: ' . "Error: Failed to delete the Materialized Snapshot with ID " . $this->id . "."); } return true; }
/** * QueryBuilder::getCount() * * @return */ function getCount() { $where = sizeof($this->wheres) > 0 ? ' WHERE ' . implode(" \n AND \n\t", $this->wheres) : ''; $order = sizeof($this->orders) > 0 ? ' ORDER BY ' . implode(", ", $this->orders) : ''; $group = sizeof($this->groups) > 0 ? ' GROUP BY ' . implode(", ", $this->groups) : ''; $query = "SELECT count(*) FROM \n\t" . $this->class->databaseInfo->table . "\n " . implode("\n ", $this->joins) . $where . ' ' . $group . ' ' . $order . ' '; return dbConnection::getInstance($this->class->databaseInfo->connection)->fetchOne($query); }
function setParam($param, $value) { // Validate this... if (!is_numeric($this->id)) { throw new Exception('host->getScheduledBackupDisplay: ' . "Error: The ID for this object is not an integer."); } $conn = dbConnection::getInstance($this->log); switch (strtolower($param)) { case 'hostname': self::validateHostname($value); $backups = $this->getScheduledBackups(); if (sizeOf($backups) > 0) { // We have backups linked to this volume // Collect and print the information ... $info = $this->getInfo(); $errMsg = 'Error: Unable to edit the hostname for host with hostname: ' . $info['hostname'] . "\n\n" . $this->getScheduledBackupDisplay(); throw new ProcessingException($errMsg); } $sql = "UPDATE hosts SET hostname='" . $conn->real_escape_string($value) . "' WHERE host_id=" . $this->id; break; case 'description': self::validateHostDescription($value); $sql = "UPDATE hosts SET description='" . $conn->real_escape_string($value) . "' WHERE host_id=" . $this->id; break; case 'ssh_port': self::validateSSHPort($value); $sql = "UPDATE hosts SET ssh_port=" . $value . " WHERE host_id=" . $this->id; break; case 'active': self::validateActive($value); $sql = "UPDATE hosts SET active='" . $conn->real_escape_string(strtoupper($value)) . "' WHERE host_id=" . $this->id; break; case 'staging_path': self::validateStagingPath($value); $sql = "UPDATE hosts SET staging_path='" . $conn->real_escape_string($value) . "' WHERE host_id=" . $this->id; break; default: throw new InputException("Error: Unknown Host parameter: " . $param); break; } if (!$conn->query($sql)) { throw new DBException('host->setParam: ' . "Error: Query {$sql} \nFailed with MySQL Error: {$conn->error}"); } return; }
function destroy() { // Validate this... if (!is_numeric($this->id)) { throw new Exception('scheduledBackup->destroy: ' . "Error: The ID for this object is not an integer."); } $queueManager = new queueManager(); $queueManager->setLogStream($this->log); // We need to take over all queues for this backup and make sure nothing is running. $queues = array('scheduledBackup:' . $this->id, 'retentionApply:' . $this->id, 'postProcess:' . $this->id); foreach ($queues as $queue) { $ticket = $queueManager->getTicketNumber($queue); if (!$queueManager->checkFrontOfQueue($queue, $ticket)) { throw new ProcessingException("Error: Cannot remove the Scheduled Backup Task as it is currently running."); } } // Check to see if anything is running for this scheduledBackup $runningBackupGetter = new runningBackupGetter(); $runningBackupGetter->setLogStream($this->log); $runningBackups = $runningBackupGetter->getByScheduledBackup($this); if (sizeOf($runningBackups) > 0) { throw new ProcessingException("Error: Cannot remove the Scheduled Backup Task as it is currently running."); } // Get all snapshots and destroy them.. $groups = $this->getSnapshotGroupsNewestToOldest(); foreach ($groups as $group) { $snapshots = $group->getAllSnapshotsNewestToOldest(); foreach ($snapshots as $snapshot) { $snapshot->destroy(); } } // If we have a materialized snapshot - destroy that too if ($latestMaterialized = $this->getMostRecentCompletedMaterializedSnapshot()) { $latestMaterialized->destroy(); } $conn = dbConnection::getInstance($this->log); // Remove DB the entries for this scheduledBackup $sql = "DELETE sb.*, sbp.* FROM scheduled_backups sb JOIN scheduled_backup_params sbp USING (scheduled_backup_id) WHERE scheduled_backup_id = " . $this->id; if (!$conn->query($sql)) { throw new DBException('scheduledBackup->setParam: ' . "Error: Query {$sql} \nFailed with MySQL Error: {$conn->error}"); } return; }
function getStagingTmpdir() { global $config; if (!is_numeric($this->id)) { throw new Exception('runningBackup->getStagingTmpdir: ' . "Error: The ID for this object is not an integer."); } $conn = dbConnection::getInstance($this->log); $info = $this->getInfo(); // Collect the info we need to connect to the remote host $backupGetter = new scheduledBackupGetter(); $scheduledBackup = $backupGetter->getById($info['scheduled_backup_id']); $sbInfo = $scheduledBackup->getInfo(); $host = $scheduledBackup->getHost(); $hostInfo = $host->getInfo(); $this->remoteTempDir = new remoteTempDir(); $tempDir = $this->remoteTempDir->init($hostInfo['hostname'], $hostInfo['ssh_port'], $sbInfo['backup_user'], $hostInfo['staging_path'], 'xbm-'); // Put the path into the DB $sql = "UPDATE running_backups SET staging_tmpdir='" . $conn->real_escape_string($tempDir) . "' WHERE running_backup_id=" . $this->id; if (!$conn->query($sql)) { throw new Exception('runningBackup->getStagingTmpdir: ' . "Error: Query: {$sql} \nFailed with MySQL Error: {$conn->error}"); } return $tempDir; }
function upgrade() { // Look for running backups without checking schema version $runningBackupGetter = new runningBackupGetter(); $runningBackupGetter->setSchemaVersionChecks(false); // Get the runningbackups - this getter automatically removes stale entries, so it should only return truly running pids... $runningBackups = $runningBackupGetter->getAll(); $backupCount = sizeOf($runningBackups); // If we find running backups, abort with error if ($backupCount > 0) { throw new ProcessingException('schemaUpgrader->upgrade: ' . "Error: Detected " . $backupCount . " backup(s) currently running. Please retry upgrading the schema later."); } // Create a new DB connection getter that does not check the schema version... $conn = dbConnection::getInstance($this->log, false); $schemaVersion = $conn->getSchemaVersion(); switch (true) { // If the schema versions match - all is good - nothing to do case $schemaVersion == XBM_SCHEMA_VERSION: $this->resultMsg = "The schema version of the XtraBackup Manager database is already at the expected version number (" . XBM_SCHEMA_VERSION . ")."; return true; break; // If the schema version of the DB is higher than what we need - throw an error! // If the schema version of the DB is higher than what we need - throw an error! case $schemaVersion > XBM_SCHEMA_VERSION: $this->resultMsg = "The schema version of the XtraBackup Manager database (" . $schemaVersion . ") is higher than the expected version number (" . XBM_SCHEMA_VERSION . ")."; return false; break; // Schema version is < expected version - we need to upgrade! // Schema version is < expected version - we need to upgrade! case $schemaVersion < XBM_SCHEMA_VERSION: // Find all the files in the $XBM_AUTO_INSTALLDIR/sql/changes/ directory global $XBM_AUTO_INSTALLDIR; $files = glob($XBM_AUTO_INSTALLDIR . '/sql/changes/*'); // Sort them just in case the OS gives them back in a strange order asort($files); // Walk the array and build a list of scripts to run of files that are numeric and > $schemaVersion $toRun = array(); foreach ($files as $filename) { $basename = basename($filename); if (is_numeric($basename) && $basename > $schemaVersion && $basename <= XBM_SCHEMA_VERSION) { $toRun[] = $filename; } } // Walk over the array applying each schema update foreach ($toRun as $scriptName) { $version = basename($scriptName); // Run the script $sql = file_get_contents($scriptName); if (!($res = $conn->query($sql))) { throw new Exception('schemaUpgrader->upgrade: ' . "Error: Query: {$sql} \nFailed with MySQL Error: {$conn->error}"); } // Update the schemaVersion in the DB $sql = "UPDATE schema_version SET version=" . $version; if (!($res = $conn->query($sql))) { throw new Exception('schemaUpgrader->upgrade: ' . "Error: Query: {$sql} \nFailed with MySQL Error: {$conn->error}"); } } $this->resultMsg = "The XtraBackup Manager database schema was successfully upgraded to schema version " . XBM_SCHEMA_VERSION . "."; return true; break; // Catch all - should never get here // Catch all - should never get here default: $this->resultMsg = "An issue occurred when comparing schema versions. This probably indicated a bug in XtraBackup Manager."; return false; break; } // Catch all - should never get here $this->resultMsg = "An issue occurred when comparing schema versions. This probably indicated a bug in XtraBackup Manager."; return false; // Get the schema version }
/** * Logger::dbCheck() * Checks if the current database exists, and creates it if it's not there. */ public function dbCheck() { if (!$this->checked) { $this->log[] = "checking if db exists"; $this->checked = dbConnection::getInstance('Logger')->tableExists(Settings::Load()->Get('Logger', 'logtable')); if (!$this->checked) { $createquery = $this->createqueries[strtolower(Settings::Load()->Get('Logger', 'dbtype'))]; $createquery = str_replace('@logtable@', Settings::Load()->Get('Logger', 'logtable'), $createquery); $this->checked = dbConnection::getInstance('Logger')->query($createquery); if (!$this->checked) { die("Error creating logdatabase"); } Logger::Log("Log database created."); } } }
function getById($id) { global $config; $conn = dbConnection::getInstance($this->log); $sql = "SELECT backup_strategy_id FROM backup_strategies WHERE backup_strategy_id=" . $id; if (!($res = $conn->query($sql))) { throw new DBException('backupStrategyGetter->getById: ' . "Error: Query: {$sql} \nFailed with MySQL Error: {$conn->error}"); } if ($res->num_rows != 1) { return false; } $strategy = new backupStrategy($id); $strategy->setLogStream($this->log); return $strategy; }
function getAllSnapshotsNewestToOldest() { $this->__validate(); global $config; $conn = dbConnection::getInstance($this->log); $sql = "SELECT backup_snapshot_id FROM backup_snapshots WHERE status='COMPLETED' AND scheduled_backup_id=" . $this->scheduledBackupId . " \n\t\t\t\t\tAND snapshot_group_num=" . $this->snapshotGroupNum . " ORDER BY snapshot_time DESC"; if (!($res = $conn->query($sql))) { throw new Exception('backupSnapshotGroup->getAllSnapshotsNewestToOldest: ' . "Error: Query: {$sql} \nFailed with MySQL Error: {$conn->error}"); } $snapshots = array(); while ($row = $res->fetch_array()) { $snapshotGetter = new backupSnapshotGetter(); $snapshots[] = $snapshotGetter->getById($row['backup_snapshot_id']); } return $snapshots; }
function getScheduledBackups() { // Validate this... if (!is_numeric($this->id)) { throw new Exception('backupVolume->getScheduledBackups: ' . "Error: The ID for this object is not an integer."); } global $config; $conn = dbConnection::getInstance($this->log); $sql = "SELECT scheduled_backup_id FROM scheduled_backups WHERE backup_volume_id=" . $this->id; if (!($res = $conn->query($sql))) { throw new DBException('backupVolume->getScheduledBackups: ' . "Error: Query: {$sql} \nFailed with MySQL Error: {$conn->error}"); } $backupGetter = new scheduledBackupGetter(); $backupGetter->setLogStream($this->log); $backups = array(); while ($row = $res->fetch_array()) { $backups[] = $backupGetter->getById($row['scheduled_backup_id']); } return $backups; }
function applyRetentionPolicy(backupJob $job) { global $config; $scheduledBackup = $job->getScheduledBackup(); $this->infolog->write("Checking to see if any snapshots need to be merged into the seed backup.", XBM_LOG_INFO); if (!is_object($scheduledBackup)) { throw new Exception('continuousIncrementalBackupTaker->applyRetentionPolicy: ' . "Error: This function requires a scheduledBackup object as a parameter."); } $conn = dbConnection::getInstance($this->log); $sql = "SELECT backup_snapshot_id FROM backup_snapshots WHERE status='COMPLETED' AND scheduled_backup_id=" . $scheduledBackup->id . " AND snapshot_time IS NOT NULL ORDER BY snapshot_time ASC"; if (!($res = $conn->query($sql))) { throw new Exception('continuousIncrementalBackupTaker->applyRetentionPolicy: ' . "Error: Query: {$sql} \nFailed with MySQL Error: {$conn->error}"); } // Get info for this scheduledBackup $info = $scheduledBackup->getInfo(); // Get the params/options for this scheduledBackup $params = $scheduledBackup->getParameters(); // Validate them $this->validateParams($params); // Build service objects for later use $snapshotGetter = new backupSnapshotGetter(); $snapshotMerger = new backupSnapshotMerger(); // Check to see if the number of rows we have is more than the number of snapshots we should have at a max while ($res->num_rows > $params['max_snapshots']) { // Grab the first row - it is the SEED if (!($row = $res->fetch_array())) { throw new Exception('continuousIncrementalBackupTaker->applyRetentionPolicy: ' . "Error: Could not retrieve the object ID for the seed of Scheduled Backup ID " . $scheduledBackup->id); } $seedSnapshot = $snapshotGetter->getById($row['backup_snapshot_id']); // Grab the second row - it is the DELTA to be collapsed. if (!($row = $res->fetch_array())) { throw new Exception('continuousIncrementalBackupTaker->applyRetentionPolicy: ' . "Error: Could not retrieve the object ID for the seed of Scheduled Backup ID " . $scheduledBackup->id); } $deltaSnapshot = $snapshotGetter->getById($row['backup_snapshot_id']); $this->infolog->write("Merging deltas in Backup Snapshot ID #" . $deltaSnapshot->id . " with Backup Snapshot ID #" . $seedSnapshot->id . ".", XBM_LOG_INFO); // Merge them together $snapshotMerger->mergeSnapshots($seedSnapshot, $deltaSnapshot); // Check to see what merge work is needed now. $sql = "SELECT backup_snapshot_id FROM backup_snapshots WHERE status='COMPLETED' AND scheduled_backup_id=" . $scheduledBackup->id . " AND snapshot_time IS NOT NULL ORDER BY snapshot_time ASC"; if (!($res = $conn->query($sql))) { throw new Exception('continuousIncrementalBackupTaker->applyRetentionPolicy: ' . "Error: Query: {$sql} \nFailed with MySQL Error: {$conn->error}"); } } return true; }