aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore
diff options
context:
space:
mode:
authorRoy Ben-Shabat <Roy@Twine-s.com>2019-11-26 17:33:02 +0200
committerRoy Ben-Shabat <Roy@Twine-s.com>2019-11-26 17:33:02 +0200
commitca29510e1e336c4d68aaa926cfea6eb72ce42779 (patch)
tree298c10a1567df22cf594054271dd5ce656f09c12 /Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore
parent6c43f97559613443e781917a827c3b644db03490 (diff)
downloadTango-ca29510e1e336c4d68aaa926cfea6eb72ce42779.tar.gz
Tango-ca29510e1e336c4d68aaa926cfea6eb72ce42779.zip
Working on backup/restore...
Diffstat (limited to 'Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore')
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupFile.cs18
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreStage.cs14
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/DefaultBackupManager.cs372
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/IBackupManager.cs10
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreResult.cs3
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreSettings.cs21
6 files changed, 424 insertions, 14 deletions
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupFile.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupFile.cs
index ebb7b9fcd..c687377a6 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupFile.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupFile.cs
@@ -1,4 +1,5 @@
-using System;
+using Newtonsoft.Json;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -14,6 +15,11 @@ namespace Tango.PPC.Common.BackupRestore
public class BackupFile
{
/// <summary>
+ /// Gets or sets the backup file version.
+ /// </summary>
+ public int Version { get; set; }
+
+ /// <summary>
/// Gets or sets the backup name.
/// </summary>
public String Name { get; set; }
@@ -61,5 +67,15 @@ namespace Tango.PPC.Common.BackupRestore
Settings = new BackupSettings();
JobFiles = new List<JobFile>();
}
+
+ public String ToJson()
+ {
+ return JsonConvert.SerializeObject(this);
+ }
+
+ public static BackupFile FromJson(String json)
+ {
+ return JsonConvert.DeserializeObject<BackupFile>(json);
+ }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreStage.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreStage.cs
index 3d5de1122..27fc06ec4 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreStage.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/BackupRestoreStage.cs
@@ -30,12 +30,22 @@ namespace Tango.PPC.Common.BackupRestore
CompressingFiles,
//Restore
- [Description("Validating backup...")]
- ValidatingBackup,
+ [Description("Extracting backup configuration...")]
+ ExtractingBackupConfiguration,
[Description("Extracting content...")]
ExtractingContent,
+ [Description("Restoring user settings...")]
+ RestoringSettings,
+ [Description("Restoring jobs...")]
+ RestoringJobs,
[Description("Restoring data...")]
RestoringDatabase,
+ [Description("Removing temporary files...")]
+ RemovingTemporaryFiles,
+ [Description("Restoring firmware version...")]
+ RestoringFirmware,
+ [Description("Rolling back changes...")]
+ RollingBackChanges,
[Description("Done")]
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/DefaultBackupManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/DefaultBackupManager.cs
index 31bdefd5d..05693c794 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/DefaultBackupManager.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/DefaultBackupManager.cs
@@ -16,6 +16,9 @@ using Tango.Settings;
using Tango.Core.DB;
using System.Data.SqlClient;
using Ionic.Zip;
+using Tango.BL.Entities;
+using Tango.PPC.Common.Authentication;
+using Tango.Integration.Upgrade;
namespace Tango.PPC.Common.BackupRestore
{
@@ -23,6 +26,7 @@ namespace Tango.PPC.Common.BackupRestore
{
private const string BACKUP_FILE_NAME = "Backup.json";
private const string DATABASE_FILE_NAME = "Tango.bak";
+ private const int VERSION = 1;
[TangoInject(TangoInjectMode.WhenAvailable)]
private IPPCApplicationManager _applicationManager;
@@ -30,6 +34,9 @@ namespace Tango.PPC.Common.BackupRestore
[TangoInject(TangoInjectMode.WhenAvailable)]
private IMachineProvider _machineProvider;
+ [TangoInject(TangoInjectMode.WhenAvailable)]
+ private IAuthenticationProvider _authenticationProvider;
+
public DefaultBackupManager()
{
TangoIOC.Default.Inject(this);
@@ -41,17 +48,19 @@ namespace Tango.PPC.Common.BackupRestore
{
return Task.Factory.StartNew(() =>
{
+ var tempFolder = TemporaryManager.CreateFolder();
+
try
{
//Basic
- LogManager.Log($"Starting backup operation to file {filePath}.");
+ LogManager.Log($"Starting backup operation to file '{filePath}'...");
LogManager.Log($"Backup settings:\n{settings.ToJsonString()}");
OnProgress(BackupRestoreStage.Initializing);
- var tempFolder = TemporaryManager.CreateFolder();
LogManager.Log($"Temporary folder created on {tempFolder.Path}.");
BackupFile backupFile = new BackupFile();
+ backupFile.Version = VERSION;
backupFile.Date = DateTime.Now;
backupFile.Settings = settings;
backupFile.Name = name;
@@ -134,7 +143,14 @@ namespace Tango.PPC.Common.BackupRestore
var dataSource = ObservablesContext.GetActualDataSource();
using (var dbManager = DbManager.FromDataSource(dataSource))
{
- dbManager.Backup(dataSource.Catalog, Path.Combine(tempFolder, DATABASE_FILE_NAME));
+ Directory.CreateDirectory("C:\\Backups");
+ var dbBackupFile = $"C:\\Backups\\{DATABASE_FILE_NAME}";
+ if (File.Exists(dbBackupFile))
+ {
+ File.Delete(dbBackupFile);
+ }
+ dbManager.Backup(dataSource.Catalog, dbBackupFile);
+ File.Move(dbBackupFile, Path.Combine(tempFolder, DATABASE_FILE_NAME));
}
}
catch (Exception ex)
@@ -145,7 +161,7 @@ namespace Tango.PPC.Common.BackupRestore
LogManager.Log("Database backup completed.");
}
- //Compression
+ //Backup.json
try
{
OnProgress(BackupRestoreStage.WritingConfiguration);
@@ -158,6 +174,7 @@ namespace Tango.PPC.Common.BackupRestore
throw new IOException("Error writing backup configuration file.", ex);
}
+ //Compression
LogManager.Log($"Generating {filePath}...");
using (ZipFile zip = new ZipFile())
{
@@ -176,22 +193,361 @@ namespace Tango.PPC.Common.BackupRestore
zip.Save(filePath);
}
+ tempFolder.Delete();
+
+
//Done
LogManager.Log("Backup operation completed!!!");
- OnProgress(BackupRestoreStage.Done);
+ OnProgress(BackupRestoreStage.Done, 100, 100, false);
}
catch (Exception ex)
{
- OnProgress(BackupRestoreStage.Error);
+ tempFolder.Delete();
+
+ OnProgress(BackupRestoreStage.Error, 100, 100, false);
LogManager.Log(ex, "Could not complete the backup operation.");
throw ex;
}
});
}
- public Task Restore(string filePath)
+ /// <summary>
+ /// Extracts the backup configuration from the specified backup file.
+ /// </summary>
+ /// <param name="filePath">The file path.</param>
+ /// <returns></returns>
+ public Task<BackupFile> ExtractBackupConfiguration(string filePath)
+ {
+ return Task.Factory.StartNew<BackupFile>(() =>
+ {
+ using (ZipFile zip = ZipFile.Read(filePath))
+ {
+ var reader = zip.Entries.SingleOrDefault(x => x.FileName == BACKUP_FILE_NAME).OpenReader();
+ String json = String.Empty;
+
+ using (StreamReader stReader = new StreamReader(reader))
+ {
+ json = stReader.ReadToEnd();
+ }
+
+ var backupFile = BackupFile.FromJson(json);
+ reader.Close();
+ reader.Dispose();
+ return backupFile;
+ }
+ });
+ }
+
+ public Task<RestoreResult> Restore(string filePath, RestoreSettings settings)
{
- throw new NotImplementedException();
+ TaskCompletionSource<RestoreResult> completionSource = new TaskCompletionSource<RestoreResult>();
+
+ String dbRollbackFile = null;
+
+ Task.Factory.StartNew(() =>
+ {
+ LogManager.Log($"Starting restore operation from file '{filePath}'...");
+ OnProgress(BackupRestoreStage.Initializing);
+
+ var tempFolder = TemporaryManager.CreateFolder();
+
+ var restoreResult = new RestoreResult() { FolderPath = tempFolder };
+
+ try
+ {
+ try
+ {
+ LogManager.Log("Creating database rollback file...");
+ var dataSource = ObservablesContext.GetActualDataSource();
+ using (var dbManager = DbManager.FromDataSource(dataSource))
+ {
+ Directory.CreateDirectory("C:\\Backups");
+ dbRollbackFile = $"C:\\Backups\\{Path.GetRandomFileName()}.bak";
+ LogManager.Log($"Creating database rollback to '{dbRollbackFile}'...");
+ dbManager.Backup(dataSource.Catalog, dbRollbackFile);
+ LogManager.Log("Database rollback created successfully.");
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidDataException("Error creating database rollback file.", ex);
+ }
+
+ //Basic
+ LogManager.Log("Extracting backup file configuration...");
+
+ BackupFile backupFile = null;
+
+ //Extract Configuration
+ try
+ {
+ OnProgress(BackupRestoreStage.ExtractingBackupConfiguration);
+ backupFile = ExtractBackupConfiguration(filePath).Result;
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Error extracting backup configuration.", ex);
+ }
+
+ //Validate Version
+ if (backupFile.Version > VERSION)
+ {
+ throw new NotSupportedException($"Backup file version {backupFile} is not supported.");
+ }
+
+ LogManager.Log($"Backup settings:\n{backupFile.Settings.ToJsonString()}");
+
+ if (backupFile.Settings.Mode == BackupMode.Jobs)
+ {
+ //Restore Jobs
+ OnProgress(BackupRestoreStage.RestoringJobs);
+ LogManager.Log("Starting jobs restore...");
+
+ using (ObservablesContext db = ObservablesContext.CreateDefault())
+ {
+ var jobs = db.Jobs.ToList();
+ var jobFiles = backupFile.JobFiles;
+
+ if (settings.AllowDeleteJobs)
+ {
+ try
+ {
+ LogManager.Log("Removing existing jobs...");
+ foreach (var job in jobs.ToList())
+ {
+ LogManager.Log($"Removing job '{job.Name}'...");
+ job.Delete(db);
+ jobs.Remove(job);
+ }
+
+ db.SaveChanges();
+ }
+ catch (Exception ex)
+ {
+ throw new Exception("Error removing existing jobs from database.", ex);
+ }
+ }
+
+ foreach (var jobFile in jobFiles)
+ {
+ LogManager.Log($"Importing job '{jobFile.Name}'...");
+
+ try
+ {
+ var existingJob = jobs.FirstOrDefault(x => x.Name == jobFile.Name);
+
+ if (existingJob != null)
+ {
+ if (settings.OverwriteExistingJobs)
+ {
+ try
+ {
+ LogManager.Log("Job already exist, overwriting...");
+
+ var newJob = Job.FromJobFile(jobFile, _machineProvider.Machine.Guid, _authenticationProvider.CurrentUser.Guid).Result;
+ newJob.Guid = existingJob.Guid;
+
+ existingJob.Delete(db);
+ jobs.Remove(existingJob);
+
+ db.SaveChanges();
+ db.Jobs.Add(newJob);
+ db.SaveChanges();
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException("Error overwriting job.", ex);
+ }
+ }
+ }
+ else
+ {
+ var newJob = Job.FromJobFile(jobFile, _machineProvider.Machine.Guid, _authenticationProvider.CurrentUser.Guid).Result;
+ db.Jobs.Add(newJob);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException("Error importing job.", ex);
+ }
+
+ OnProgress(BackupRestoreStage.RestoringJobs, jobFiles.IndexOf(jobFile) + 1, jobFiles.Count, false);
+ }
+
+ OnProgress(BackupRestoreStage.RestoringJobs);
+ db.SaveChanges();
+ }
+ }
+ else
+ {
+ //Extract zip file
+ LogManager.Log("Starting backup file extraction...");
+ OnProgress(BackupRestoreStage.ExtractingContent);
+ try
+ {
+ using (ZipFile zip = new ZipFile(filePath))
+ {
+ zip.ExtractProgress += (x, e) =>
+ {
+ if (e.EventType == ZipProgressEventType.Extracting_AfterExtractEntry)
+ {
+ LogManager.Log($"Extracting '{e.CurrentEntry.FileName}'...");
+ OnProgress(BackupRestoreStage.ExtractingContent, e.EntriesExtracted + 1, e.EntriesTotal, false);
+ }
+ };
+
+ zip.ParallelDeflateThreshold = -1;
+ zip.ExtractAll(tempFolder);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Error extracting backup content.", ex);
+ }
+
+ //Overwrite settings
+ LogManager.Log("Validating user settings...");
+ if (backupFile.SettingsFile != null)
+ {
+ try
+ {
+ LogManager.Log("Overwriting settings file...");
+ OnProgress(BackupRestoreStage.RestoringSettings);
+ File.WriteAllText(SettingsManager.Default.FilePath, backupFile.SettingsFile);
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Error overwriting user settings.", ex);
+ }
+ }
+ else
+ {
+ LogManager.Log("No user settings, skipping...");
+ }
+
+ //Restore database
+ var backupFilePath = Path.Combine(tempFolder, DATABASE_FILE_NAME);
+ LogManager.Log($"Looking for file database backup on '{backupFilePath}'...");
+ if (File.Exists(backupFilePath))
+ {
+ LogManager.Log("Restoring database...");
+ OnProgress(BackupRestoreStage.RestoringDatabase);
+ try
+ {
+ var dataSource = ObservablesContext.GetActualDataSource();
+ using (var dbManager = DbManager.FromDataSource(dataSource))
+ {
+ Directory.CreateDirectory("C:\\Backups");
+ var dbBackupFile = $"C:\\Backups\\{DATABASE_FILE_NAME}";
+ File.Copy(backupFilePath, dbBackupFile, true);
+ dbManager.Restore(dataSource.Catalog, dbBackupFile);
+ File.Delete(dbBackupFile);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Error restoring database backup", ex);
+ }
+
+ LogManager.Log("Database backup completed.");
+ }
+ else
+ {
+ LogManager.Log("Database backup file not found, skipping...");
+ }
+
+ //Remove extra files from application temp folder
+ OnProgress(BackupRestoreStage.RemovingTemporaryFiles);
+ LogManager.Log("Removing redundant files from temp folder...");
+ try
+ {
+ File.Delete(backupFilePath);
+ }
+ catch { }
+ try
+ {
+ File.Delete(Path.Combine(tempFolder, BACKUP_FILE_NAME));
+ }
+ catch { }
+
+ //Update firmware
+ var tfpFile = Path.Combine(tempFolder, "firmware_package.tfp");
+ LogManager.Log($"Looking for tfp file on '{tfpFile}'...");
+ if (File.Exists(tfpFile))
+ {
+ OnProgress(BackupRestoreStage.RestoringFirmware);
+ LogManager.Log("Restoring firmware version...");
+
+ var stream = new FileStream(tfpFile, FileMode.Open);
+
+ _machineProvider.MachineOperator.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU | FirmwareUpgradeModes.TFP_PACKAGE;
+
+ var handler = _machineProvider.MachineOperator.UpgradeFirmware(stream).Result;
+ handler.Failed += (_, ex) =>
+ {
+ stream.Dispose();
+ throw ex;
+ };
+ handler.Completed += (_, __) =>
+ {
+ OnProgress(BackupRestoreStage.RestoringFirmware, 100, 100, false);
+ stream.Dispose();
+ OnProgress(BackupRestoreStage.Done, 100, 100, false);
+ completionSource.SetResult(restoreResult);
+ };
+ handler.Canceled += (_, __) =>
+ {
+ stream.Dispose();
+ throw new Exception("The operation has been canceled.");
+ };
+ handler.Progress += (_, e) =>
+ {
+ OnProgress(BackupRestoreStage.RestoringFirmware, e.Current, e.Total, false);
+ };
+ }
+ else
+ {
+ LogManager.Log("Firmware package file not found, skipping...");
+ OnProgress(BackupRestoreStage.Done, 100, 100, false);
+ completionSource.SetResult(restoreResult);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log("Rolling back database changes...");
+
+ var dataSource = ObservablesContext.GetActualDataSource();
+ using (var dbManager = DbManager.FromDataSource(dataSource))
+ {
+ try
+ {
+ OnProgress(BackupRestoreStage.RollingBackChanges);
+ dbManager.Restore(dataSource.Catalog, dbRollbackFile);
+ LogManager.Log("Database restored successfully.");
+ }
+ catch (Exception e)
+ {
+ LogManager.Log(e, "Error rolling back database.");
+ }
+ finally
+ {
+ try
+ {
+ File.Delete(dbRollbackFile);
+ }
+ catch { }
+ }
+ }
+
+ tempFolder.Delete();
+ OnProgress(BackupRestoreStage.Error, 100, 100, false);
+ LogManager.Log(ex, "Could not complete the restore operation.");
+ completionSource.SetException(ex);
+ }
+ });
+
+ return completionSource.Task;
}
protected virtual void OnProgress(BackupRestoreStage stage, double progress = 0, double maxProgress = 100, bool isIntermediate = true)
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/IBackupManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/IBackupManager.cs
index 8ff8a434c..ae1884677 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/IBackupManager.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/IBackupManager.cs
@@ -29,7 +29,15 @@ namespace Tango.PPC.Common.BackupRestore
/// Restores a backup located in the specified file path.
/// </summary>
/// <param name="filePath">The file path.</param>
+ /// <param name="settings">The restore settings</param>
/// <returns></returns>
- Task Restore(String filePath);
+ Task<RestoreResult> Restore(String filePath, RestoreSettings settings);
+
+ /// <summary>
+ /// Extracts the backup configuration from the specified backup file.
+ /// </summary>
+ /// <param name="filePath">The file path.</param>
+ /// <returns></returns>
+ Task<BackupFile> ExtractBackupConfiguration(String filePath);
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreResult.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreResult.cs
index 0e1ac1a13..34d7c6298 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreResult.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreResult.cs
@@ -6,7 +6,8 @@ using System.Threading.Tasks;
namespace Tango.PPC.Common.BackupRestore
{
- class RestoreResult
+ public class RestoreResult
{
+ public String FolderPath { get; set; }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreSettings.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreSettings.cs
index 4e7bb2a55..a5b343302 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreSettings.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/BackupRestore/RestoreSettings.cs
@@ -3,10 +3,29 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using Tango.Core;
namespace Tango.PPC.Common.BackupRestore
{
- class RestoreSettings
+ public class RestoreSettings : ExtendedObject
{
+ private bool _allowDeleteJobs;
+ public bool AllowDeleteJobs
+ {
+ get { return _allowDeleteJobs; }
+ set { _allowDeleteJobs = value; RaisePropertyChangedAuto(); }
+ }
+
+ private bool _overwriteExistingJobs;
+ public bool OverwriteExistingJobs
+ {
+ get { return _overwriteExistingJobs; }
+ set { _overwriteExistingJobs = value; RaisePropertyChangedAuto(); }
+ }
+
+ public RestoreSettings()
+ {
+ OverwriteExistingJobs = true;
+ }
}
}