From cd32ef98d58bca41bd26ecda34058ed21561217e Mon Sep 17 00:00:00 2001 From: GNicho Date: Mon, 9 May 2016 17:00:06 +0200 Subject: [PATCH] Creating a VM Trying to create Test VM from within Duplicati Refactored some code backup up VM --- .../Main/Duplicati.Library.Main.csproj | 1 + .../Duplicati.Library.Snapshots.csproj | 3 +- Duplicati/Library/Snapshots/HyperVBackup.cs | 87 ------ Duplicati/Library/Snapshots/HyperVUtility.cs | 270 ++++++++++++++++++ .../Library/Snapshots/WindowsSnapshot.cs | 17 +- 5 files changed, 283 insertions(+), 95 deletions(-) delete mode 100644 Duplicati/Library/Snapshots/HyperVBackup.cs create mode 100644 Duplicati/Library/Snapshots/HyperVUtility.cs diff --git a/Duplicati/Library/Main/Duplicati.Library.Main.csproj b/Duplicati/Library/Main/Duplicati.Library.Main.csproj index a66515822..fd09f8f5d 100644 --- a/Duplicati/Library/Main/Duplicati.Library.Main.csproj +++ b/Duplicati/Library/Main/Duplicati.Library.Main.csproj @@ -41,6 +41,7 @@ + diff --git a/Duplicati/Library/Snapshots/Duplicati.Library.Snapshots.csproj b/Duplicati/Library/Snapshots/Duplicati.Library.Snapshots.csproj index 1d48574dd..a5992193d 100644 --- a/Duplicati/Library/Snapshots/Duplicati.Library.Snapshots.csproj +++ b/Duplicati/Library/Snapshots/Duplicati.Library.Snapshots.csproj @@ -38,6 +38,7 @@ false + ..\..\..\packages\NUnit.3.2.1\lib\net45\nunit.framework.dll True @@ -63,7 +64,7 @@ - + diff --git a/Duplicati/Library/Snapshots/HyperVBackup.cs b/Duplicati/Library/Snapshots/HyperVBackup.cs deleted file mode 100644 index 978344c8c..000000000 --- a/Duplicati/Library/Snapshots/HyperVBackup.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Management; -using System.Text; - -namespace Duplicati.Library.Snapshots -{ - - //Original code: HVBackup Project - public class HyperVBackup - { - private string _wmiQuery; - private readonly string _wmiScope; - private readonly string _vmIdField; - private readonly string _wmiHost = "localhost"; //We could backup remote Hyper-V Machines in the future - private readonly bool _wmiv2Namespace; - - public List RequestedVmNames = new List(); - readonly IDictionary _hyperVMachines = new Dictionary(); - - public HyperVBackup() - { - //Set the namespace depending off host OS - _wmiv2Namespace = Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 2; - //Set the scope to use in MWI. V2 for Server 2012 or newer. - _wmiScope = _wmiv2Namespace ? $"\\\\{_wmiHost}\\root\\virtualization\\v2" - : $"\\\\{_wmiHost}\\root\\virtualization"; - //Set the VM ID Selector Field for the WMI Query - _vmIdField = _wmiv2Namespace ? "VirtualSystemIdentifier" : "SystemName"; - - } - - public HyperVBackup(List reqVmNames) - { - _wmiv2Namespace = Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 2; - _wmiScope = _wmiv2Namespace ? $"\\\\{_wmiHost}\\root\\virtualization\\v2" - : $"\\\\{_wmiHost}\\root\\virtualization"; - _vmIdField = _wmiv2Namespace ? "VirtualSystemIdentifier" : "SystemName"; - //Store requested VM's - RequestedVmNames = reqVmNames; - } - - public IDictionary GetHyperVMachines() - { - //Set the WMI Query according the WMI Namespace - SetWmiQueryVmidField(); - //We MOSearcher to search for the Hyper-V Machines - using (ManagementObjectSearcher moSearcher = new ManagementObjectSearcher(new ManagementScope(_wmiScope), new ObjectQuery(_wmiQuery))) - { - using (var moCollection = moSearcher.Get()) - foreach (var mObject in moCollection) - using (mObject) - _hyperVMachines.Add((string)mObject[_vmIdField], (string)mObject["ElementName"]); - } - - return _hyperVMachines; - } - - private void SetWmiQueryVmidField() - { - _wmiQuery = _wmiv2Namespace ? "SELECT VirtualSystemIdentifier, ElementName FROM Msvm_VirtualSystemSettingData WHERE VirtualSystemType = 'Microsoft:Hyper-V:System:Realized'" - : "SELECT SystemName, ElementName FROM Msvm_VirtualSystemSettingData WHERE SettingType = 3"; - - //Temporary Stringbuilder to build up the VM selectors - var sb = new StringBuilder(); - - if (RequestedVmNames == null || !RequestedVmNames.Any()) return; - foreach (var vmName in RequestedVmNames) - { - if (sb.Length > 0) - sb.Append(" OR "); - sb.Append($"ElementName = \'{vmName.Replace("'", "''")}\'"); - } - _wmiQuery += $" AND ({sb})"; - } - //private string GetWMIScope(string host = "localhost") - //{ - // //string scopeFormatStr; - // //if (IsNameSpaceWMIV2) - // // scopeFormatStr = "\\\\{0}\\root\\virtualization\\v2"; - // //else - // // scopeFormatStr = "\\\\{0}\\root\\virtualization"; - // //return (string.Format(scopeFormatStr, host)); - //} - } -} diff --git a/Duplicati/Library/Snapshots/HyperVUtility.cs b/Duplicati/Library/Snapshots/HyperVUtility.cs new file mode 100644 index 000000000..4ca477b1f --- /dev/null +++ b/Duplicati/Library/Snapshots/HyperVUtility.cs @@ -0,0 +1,270 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management; +using System.Text; + +using System.Globalization; + +namespace Duplicati.Library.Snapshots +{ + + //Original code: HVBackup Project + public class HyperVUtility + { + private string _wmiQuery; + private readonly ManagementScope _wmiScope; + private readonly string _vmIdField; + private readonly string _wmiHost = "localhost"; //We could backup remote Hyper-V Machines in the future + private readonly bool _wmiv2Namespace; + + public List RequestedVmNames = new List(); + readonly IDictionary _hyperVMachines = new Dictionary(); + + public HyperVUtility() + { + //Set the namespace depending off host OS + _wmiv2Namespace = Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 2; + //Set the scope to use in MWI. V2 for Server 2012 or newer. + _wmiScope = _wmiv2Namespace + ? new ManagementScope(string.Format("\\\\{0}\\root\\virtualization\\v2", _wmiHost)) + : new ManagementScope(string.Format("\\\\{0}\\root\\virtualization", _wmiHost)); + //Set the VM ID Selector Field for the WMI Query + _vmIdField = _wmiv2Namespace ? "VirtualSystemIdentifier" : "SystemName"; + + } + + public HyperVUtility(List reqVmNames) + { + _wmiv2Namespace = Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 2; + _wmiScope = _wmiv2Namespace + ? new ManagementScope(string.Format("\\\\{0}\\root\\virtualization\\v2", _wmiHost)) + : new ManagementScope(string.Format("\\\\{0}\\root\\virtualization", _wmiHost)); + _vmIdField = _wmiv2Namespace ? "VirtualSystemIdentifier" : "SystemName"; + //Store requested VM's + RequestedVmNames = reqVmNames; + } + + /// + /// We query the Hyper-V for all requested Virtual Machines + /// + /// List of Hyper-V Machines + public IDictionary GetHyperVMachines() + { + ManagementObjectCollection moCollection = GetWmiObjects(WmiQueryHyperVMachines()); + foreach (ManagementBaseObject mObject in moCollection) + using (mObject) + _hyperVMachines.Add((string)mObject[_vmIdField], (string)mObject["ElementName"]); + + return _hyperVMachines; + } + + /// + /// We build up the wmi-query to retrieve all Virtual Machines + /// + /// WMI query + private string WmiQueryHyperVMachines() + { + string wmiQuery = _wmiv2Namespace + ? "SELECT VirtualSystemIdentifier, ElementName FROM Msvm_VirtualSystemSettingData WHERE VirtualSystemType = 'Microsoft:Hyper-V:System:Realized'" + : "SELECT SystemName, ElementName FROM Msvm_VirtualSystemSettingData WHERE SettingType = 3"; + + //Temporary Stringbuilder to build up the VM selectors + var sb = new StringBuilder(); + + if (RequestedVmNames == null || !RequestedVmNames.Any()) return null; + foreach (var vmName in RequestedVmNames) + { + if (sb.Length > 0) + sb.Append(" OR "); + sb.Append(string.Format("ElementName = \'{0}\'", vmName.Replace("'", "''"))); + } + wmiQuery += string.Format(" AND ({0})", sb); + + return wmiQuery; + } +#region Wmi helpers + /// + /// Executes the WMI Query + /// + /// + /// A collection of results + private ManagementObjectCollection GetWmiObjects(string query) + { + ManagementObjectSearcher moSearcher = new ManagementObjectSearcher(_wmiScope, new ObjectQuery(query)); + ManagementObjectCollection resultset = moSearcher.Get(); + return resultset; + } +#endregion Wmi helpers + + + + + + +#region Recovery Virtual Machines + public void CreateHyperVMachine() + { + ManagementObject sysMan = GetMsVM_VirtualSystemManagementService("MsVM_VirtualSystemManagementService", null); + } + + /// + /// Please refer to https://blogs.msdn.microsoft.com/sergeim/2008/06/03/prepare-vm-create-vm-programmatically-hyper-v-api-c-version/ + /// + /// + /// + /// + private ManagementObject GetMsVM_VirtualSystemManagementService(string className, string where) + { + var query = @where != null + ? string.Format("select * from {0} where {1}", className, where) + : string.Format("select * from {0}", className); + + ManagementObjectCollection resultset = GetWmiObjects(query); + + if (resultset.Count != 1) + throw new InvalidOperationException(string.Format("Cannot locate {0} where {1}", className, where)); + + try + { + ManagementObjectCollection.ManagementObjectEnumerator en = resultset.GetEnumerator(); + en.MoveNext(); + return en.Current as ManagementObject; + } + catch (Exception) + { + throw new InvalidOperationException(string.Format("Failure retrieving {0} where {1}", className, where)); + } + } + + #endregion + + /// + /// Please refer to https://blogs.msdn.microsoft.com/sergeim/2008/06/03/prepare-vm-create-vm-programmatically-hyper-v-api-c-version/ + /// + /// + /// + /// + public void CreateVm(string displayName) + { + string notes = "Created" + DateTime.Now; + + ManagementObject sysMan = GetMsVM_VirtualSystemManagementService(); + // Create VM with empty settings + ManagementBaseObject definition = sysMan.InvokeMethod(Constants.DefineVirtualSystem, sysMan.GetMethodParameters(Constants.DefineVirtualSystem), null); //Empty set + uint retCode = (uint)definition["returnvalue"]; + + if (retCode != Constants.ERROR_SUCCESS) + throw new InvalidOperationException("DefineVirtualSystem failed"); + + // Obtain WMI root\virtualization:ComputerSystem object. + // we will need "Name" of it, which is GUID + + string vmPath = definition["DefinedSystem"] as string; + ManagementObject computerSystemTemplate = new ManagementObject(vmPath); + string vmName = (string)computerSystemTemplate["name"]; + + // this is GUID; will need to locate settings for this VM + ManagementObject settings = GetMsvm_VirtualSystemSettingData(vmName); + + // Now, set settings of this MSVM_ComputerSystem as we like + + settings["elementname"] = displayName; + settings["notes"] = notes; + settings["BIOSGUID"] = new Guid(); + settings["BIOSSerialNumber"] = "1234567890"; + settings["BIOSNumLock"] = "true"; + + // settings["…"] = …; + // … set whatever you like; see list at + // http://msdn.microsoft.com/en-us/library/cc136944(VS.85).aspx + + settings.Put(); + + // Now, set the settings which were build above to newly created ComputerSystem + ManagementBaseObject inParams = sysMan.GetMethodParameters(Constants.ModifyVirtualSystem); + string settingsText = settings.GetText(TextFormat.WmiDtd20); + inParams["ComputerSystem"] = computerSystemTemplate; + inParams["SystemSettingData"] = settingsText; + ManagementBaseObject resultToCheck = sysMan.InvokeMethod(Constants.ModifyVirtualSystem, inParams, null); + + // Almost done – now apply the settings to newly created ComputerSystem + ManagementObject settingsAsSet = (ManagementObject)resultToCheck["ModifiedSettingData"]; + + // Optionally print settingsAsSet here + + Log(string.Format("Created: VM with name {0} and GUID name {1}", displayName, vmName)); + + } // CreateVm + + private ManagementObject GetMsVM_VirtualSystemManagementService() + { + return GetWmiObject("MsVM_VirtualSystemManagementService", null); + } + + private ManagementObject GetMsvm_VirtualSystemSettingData(string vmName) + { + return GetWmiObject("Msvm_VirtualSystemSettingData", string.Format("systemname = '{0}'", vmName)); + } + + #region Wmi Helpers + + private ManagementObject GetWmiObject(string classname, string where) + { + ManagementObjectCollection resultset = GetWmiObjects(classname, where); + + if (resultset.Count != 1) + throw new InvalidOperationException(string.Format("Cannot locate {0} where {1}", classname, where)); + + ManagementObjectCollection.ManagementObjectEnumerator en = resultset.GetEnumerator(); + en.MoveNext(); + ManagementObject result = en.Current as ManagementObject; + + if (result == null) throw new InvalidOperationException("Failure retrieving " +classname + " where " +where); + + return result; + } + + private ManagementObjectCollection GetWmiObjects(string className, string where) + { + string query; + ManagementScope scope = new ManagementScope(@"root\virtualization", null); + if (where != null) + { + query = string.Format("select * from {0} where {1}", className, where); + } else { + query = string.Format("select * from {0}", className); + } + + ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, new ObjectQuery(query)); + ManagementObjectCollection resultset = searcher.Get(); + return resultset; + } + #endregion Wmi helpers + + private static void Log(string message, params object[] data) + { + Console.WriteLine(message, data); + } + + private static void Oops(object sender, UnhandledExceptionEventArgs e) + { + Console.BackgroundColor = ConsoleColor.White; + Console.ForegroundColor = ConsoleColor.Black; + Exception ex = e.ExceptionObject as Exception; + Log(ex.Message); + Console.ResetColor(); + Log(ex.ToString()); + } + + } // class MainCreateVm + + class Constants + { + internal const string DefineVirtualSystem = "DefineVirtualSystem"; + internal const string ModifyVirtualSystem = "ModifyVirtualSystem"; + internal const uint ERROR_SUCCESS = 0; + internal const uint ERROR_INV_ARGUMENTS = 87; + } +} + diff --git a/Duplicati/Library/Snapshots/WindowsSnapshot.cs b/Duplicati/Library/Snapshots/WindowsSnapshot.cs index 011f31fe7..f98cfbd38 100644 --- a/Duplicati/Library/Snapshots/WindowsSnapshot.cs +++ b/Duplicati/Library/Snapshots/WindowsSnapshot.cs @@ -115,12 +115,15 @@ namespace Duplicati.Library.Snapshots //Do not hardcode the VM in the future, the front-end will provide this. var requestedVMs = new List {"TESTTS-CLNT"}; //Make an hyper V Backup object with the requested VMs - var hyperVBackup = new HyperVBackup(requestedVMs); + var hyperVUtility = new HyperVUtility(requestedVMs); //Retrieve all information about VMs - _hyperVMachines = hyperVBackup.GetHyperVMachines(); + _hyperVMachines = hyperVUtility.GetHyperVMachines(); ////Add the HyperV vss-writer; This is already implicitly included in the vss-writers //Enabling this vss-writer will disable all other vss-writers //m_backup.EnableWriterClasses(hyperVWriterGuid)); + + //attempt to create a vm + hyperVUtility.CreateVm("createtest"); } m_backup.StartSnapshotSet(); @@ -135,17 +138,17 @@ namespace Duplicati.Library.Snapshots //Gather information on all Vss writers m_backup.GatherWriterMetadata(); - //If we are backup up Hyper V Machines then we need to update our sourcepaths +#region HyperVBackup + //Update the sourcepaths if we're backing up Hyper-V Machines if (options.ContainsKey("hyperv-backup-vm")) { - //We will need the Hyper-V WriterMetaData to find the corresponding sourcepaths + //Find sourcepaths in Hyper-V WriterMetaData var m_backup_wmd = m_backup.WriterMetadata.FirstOrDefault(o => o.WriterId.Equals(HyperVWriterGuid)); if (m_backup_wmd != null) foreach (var component in m_backup_wmd.Components) { - //For all the components found cross reference the requested Hyper-V Machines to backup + //Cross reference the requested Hyper-V Machines to backup and add the sources if (!_hyperVMachines.ContainsKey(component.ComponentName)) continue; - //Each component is in fact a Hyper-V Machine, which contains several files. int m_sourcepathsStartPos = m_sourcepaths.Length; int i = 0; Array.Resize(ref m_sourcepaths, m_sourcepaths.Length + component.Files.Count()); @@ -159,7 +162,7 @@ namespace Duplicati.Library.Snapshots //Now clean up the sourcepaths m_sourcepaths = m_sourcepaths.Where(x => !string.IsNullOrEmpty(x)).ToArray(); } - +#endregion m_backup.FreeWriterMetadata(); }