From 3fd05f6851c42e7aee264edf30a84f8bbff66c9a Mon Sep 17 00:00:00 2001 From: titan68 Date: Wed, 4 Mar 2015 18:44:12 +0300 Subject: [PATCH 01/12] wsp-10323 Convert the VSP provider into one utilizing PowerShell. Step 1. --- .../Virtualization/BiosInfo.cs | 40 + .../Virtualization/ConcreteJobState.cs | 5 +- .../Virtualization/ControllerType.cs | 40 + .../Virtualization/MemoryInfo.cs | 44 + .../Virtualization/OperationalStatus.cs | 5 +- .../Virtualization/VirtualHardDiskFormat.cs | 40 + .../Virtualization/VirtualHardDiskInfo.cs | 10 + .../Virtualization/VirtualMachine.cs | 17 +- .../Virtualization/VirtualMachineState.cs | 31 +- .../Virtualization/VirtualSwitch.cs | 1 + .../WebsitePanel.Providers.Base.csproj | 4 + .../HyperV2012R2.cs | 2976 +++++++++++++++++ .../Properties/AssemblyInfo.cs | 21 + ...oviders.Virtualization.HyperV2012R2.csproj | 82 + .../Wmi.cs | 295 ++ .../HyperV.cs | 6 +- WebsitePanel/Sources/WebsitePanel.Server.sln | 14 +- 17 files changed, 3621 insertions(+), 10 deletions(-) create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/BiosInfo.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ControllerType.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/MemoryInfo.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Properties/AssemblyInfo.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Wmi.cs diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/BiosInfo.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/BiosInfo.cs new file mode 100644 index 00000000..f7fd4a59 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/BiosInfo.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2014, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace WebsitePanel.Providers.Virtualization +{ + public class BiosInfo + { + public bool NumLockEnabled { get; set; } + public string[] StartupOrder { get; set; } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ConcreteJobState.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ConcreteJobState.cs index 468136e8..b316ef8f 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ConcreteJobState.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ConcreteJobState.cs @@ -42,6 +42,9 @@ namespace WebsitePanel.Providers.Virtualization Completed = 7, Terminated = 8, Killed = 9, - Exception = 10 + Exception = 10, + + NotStarted = 11, + Failed = 12, } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ControllerType.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ControllerType.cs new file mode 100644 index 00000000..afe85d2a --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ControllerType.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2014, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace WebsitePanel.Providers.Virtualization +{ + public enum ControllerType + { + IDE = 0, + SCSI = 1 + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/MemoryInfo.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/MemoryInfo.cs new file mode 100644 index 00000000..7e79088a --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/MemoryInfo.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2014, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace WebsitePanel.Providers.Virtualization +{ + public class MemoryInfo + { + public bool DynamicMemoryEnabled { get; set; } + public Int64 Startup { get; set; } + public Int64 Minimum { get; set; } + public Int64 Maximum { get; set; } + public int Buffer { get; set; } + public int Priority { get; set; } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/OperationalStatus.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/OperationalStatus.cs index 808ff0dc..c10ebc26 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/OperationalStatus.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/OperationalStatus.cs @@ -35,9 +35,10 @@ namespace WebsitePanel.Providers.Virtualization public enum OperationalStatus { None = 0, - OK = 2, + Ok = 2, Error = 6, NoContact = 12, - LostCommunication = 13 + LostCommunication = 13, + Paused = 15 } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs new file mode 100644 index 00000000..8787c82d --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2014, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace WebsitePanel.Providers.Virtualization +{ + public enum VirtualHardDiskFormat + { + VHD = 1, + VHDX = 2 + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskInfo.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskInfo.cs index 75bd6f74..e327767f 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskInfo.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskInfo.cs @@ -40,5 +40,15 @@ namespace WebsitePanel.Providers.Virtualization public long MaxInternalSize { get; set; } public string ParentPath { get; set; } public VirtualHardDiskType DiskType { get; set; } + public bool SupportPersistentReservations { get; set; } + public ulong MaximumIOPS { get; set; } + public ulong MinimumIOPS { get; set; } + public ControllerType VHDControllerType { get; set; } + public int ControllerNumber { get; set; } + public int ControllerLocation { get; set; } + public string Name { get; set; } + public string Path { get; set; } + public VirtualHardDiskFormat DiskFormat { get; set; } + public bool Attached { get; set; } } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachine.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachine.cs index 22732d61..5f5847a8 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachine.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachine.cs @@ -69,8 +69,8 @@ namespace WebsitePanel.Providers.Virtualization public int CpuUsage { get; set; } [Persistent] - public int RamSize { get; set; } - public int RamUsage { get; set; } + public long RamSize { get; set; } + public long RamUsage { get; set; } [Persistent] public int HddSize { get; set; } public LogicalDisk[] HddLogicalDisks { get; set; } @@ -123,5 +123,18 @@ namespace WebsitePanel.Providers.Virtualization // for GetVirtualMachineEx used in import method public VirtualMachineNetworkAdapter[] Adapters { get; set; } + + [Persistent] + public VirtualHardDiskInfo[] Disks { get; set; } + + [Persistent] + public string Status { get; set; } + + [Persistent] + public string ReplicationState { get; set; } + + [Persistent] + public int Generation { get; set; } + } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineState.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineState.cs index 86ba3354..d7db3390 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineState.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineState.cs @@ -34,8 +34,9 @@ namespace WebsitePanel.Providers.Virtualization { public enum VirtualMachineState { + /* Unknown = 0, - Started = 2, // start + Running = 2, // start Off = 3, // turn off Reset = 10, // reset Paused = 32768, // pause @@ -47,5 +48,33 @@ namespace WebsitePanel.Providers.Virtualization Stopping = 32774, Deleted = 32775, Pausing = 32776 + */ + + Unknown = 0, + Other = 1, + Running = 2, + Off = 3, + Stopping = 4, + Saved = 6, + Paused = 9, + Starting = 10, + Reset = 11, + Saving = 32773, + Pausing = 32776, + Resuming = 32777, + FastSaved = 32779, + FastSaving = 32780, + RunningCritical = 32781, + OffCritical = 32782, + StoppingCritical = 32783, + SavedCritical = 32784, + PausedCritical = 32785, + StartingCritical = 32786, + ResetCritical = 32787, + SavingCritical = 32788, + PausingCritical = 32789, + ResumingCritical = 32790, + FastSavedCritical = 32791, + FastSavingCritical = 32792 } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualSwitch.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualSwitch.cs index 63bf3479..44c6906e 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualSwitch.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualSwitch.cs @@ -37,5 +37,6 @@ namespace WebsitePanel.Providers.Virtualization { [Persistent] public string SwitchId { get; set; } + public string SwitchType { get; set; } } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj index 752209d3..c52d1cdb 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj @@ -287,6 +287,7 @@ + Code @@ -297,6 +298,7 @@ Code + Code @@ -307,6 +309,7 @@ + @@ -321,6 +324,7 @@ Code + diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs new file mode 100644 index 00000000..71b11f28 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs @@ -0,0 +1,2976 @@ +// Copyright (c) 2014, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; + +using System.Management; +using System.Management.Automation; +using System.Management.Automation.Runspaces; + +using System.Reflection; +using System.Globalization; + +using System.Xml; +using WebsitePanel.Providers; +using WebsitePanel.Providers.HostedSolution; +using WebsitePanel.Providers.Utils; +using WebsitePanel.Server.Utils; + +using Vds = Microsoft.Storage.Vds; +using System.Configuration; +using System.Linq; + +namespace WebsitePanel.Providers.Virtualization +{ + public class HyperV2012R2 : HostingServiceProviderBase, IVirtualizationServer + { + #region Constants + private const string CONFIG_USE_DISKPART_TO_CLEAR_READONLY_FLAG = "WebsitePanel.HyperV.UseDiskPartClearReadOnlyFlag"; + private const string WMI_VIRTUALIZATION_NAMESPACE = @"root\virtualization"; + private const string WMI_CIMV2_NAMESPACE = @"root\cimv2"; + + private const int SWITCH_PORTS_NUMBER = 1024; + private const string LIBRARY_INDEX_FILE_NAME = "index.xml"; + private const string EXTERNAL_NETWORK_ADAPTER_NAME = "External Network Adapter"; + private const string PRIVATE_NETWORK_ADAPTER_NAME = "Private Network Adapter"; + private const string MANAGEMENT_NETWORK_ADAPTER_NAME = "Management Network Adapter"; + + private const string KVP_RAM_SUMMARY_KEY = "VM-RAM-Summary"; + private const string KVP_HDD_SUMMARY_KEY = "VM-HDD-Summary"; + private const Int64 Size1G = 0x40000000; + private const Int64 Size1M = 0x100000; + + #endregion + + #region Provider Settings + protected string ServerNameSettings + { + get { return ProviderSettings["ServerName"]; } + } + + public int AutomaticStartActionSettings + { + get { return ProviderSettings.GetInt("StartAction"); } + } + + public int AutomaticStartupDelaySettings + { + get { return ProviderSettings.GetInt("StartupDelay"); } + } + + public int AutomaticStopActionSettings + { + get { return ProviderSettings.GetInt("StopAction"); } + } + + public int AutomaticRecoveryActionSettings + { + get { return 1 /* restart */; } + } + + public int CpuReserveSettings + { + get { return ProviderSettings.GetInt("CpuReserve"); } + } + + public int CpuLimitSettings + { + get { return ProviderSettings.GetInt("CpuLimit"); } + } + + public int CpuWeightSettings + { + get { return ProviderSettings.GetInt("CpuWeight"); } + } + #endregion + + #region Fields + private Wmi _wmi = null; + + private Wmi wmi + { + get + { + if (_wmi == null) + _wmi = new Wmi(ServerNameSettings, WMI_VIRTUALIZATION_NAMESPACE); + return _wmi; + } + } + #endregion + + #region Constructors + public HyperV2012R2() + { + } + #endregion + + #region Virtual Machines + + public VirtualMachine GetVirtualMachine(string vmId) + { + return GetVirtualMachineInternal( vmId, false); + } + + public VirtualMachine GetVirtualMachineInternal(string vmId, bool extendedInfo) + { + + HostedSolutionLog.LogStart("GetVirtualMachine"); + HostedSolutionLog.DebugInfo("Virtual Machine: {0}", vmId); + + Runspace runSpace = null; + VirtualMachine vm = new VirtualMachine(); + + try + { + runSpace = OpenRunspace(); + Command cmd = new Command("Get-VM"); + + cmd.Parameters.Add("Id", vmId); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + vm.Name = GetPSObjectProperty(result[0], "Name").ToString(); + vm.State = (VirtualMachineState)Enum.Parse(typeof(VirtualMachineState), GetPSObjectProperty(result[0], "State").ToString()); + vm.CpuUsage = ConvertNullableToInt32(GetPSObjectProperty(result[0], "CpuUsage")); + vm.RamUsage = ConvertNullableToInt64(GetPSObjectProperty(result[0], "MemoryAssigned")); + vm.Uptime = TimeSpan.Parse(GetPSObjectProperty(result[0], "Uptime").ToString()).Ticks; + vm.Status = GetPSObjectProperty(result[0], "Status").ToString(); + vm.ReplicationState = GetPSObjectProperty(result[0], "ReplicationState").ToString(); + + vm.Heartbeat = GetVMHeartBeatStatus(runSpace, vm.Name); + + vm.CreatedDate = DateTime.Now; + + if (extendedInfo) + { + vm.CpuCores = GetVMProcessors(runSpace, vm.Name); + + MemoryInfo memoryInfo = GetVMMemory(runSpace, vm.Name); + vm.RamSize = memoryInfo.Startup; + + // BIOS + BiosInfo biosInfo = GetVMBios(runSpace, vm.Name); + vm.NumLockEnabled = biosInfo.NumLockEnabled; + + vm.BootFromCD = false; + if ((biosInfo.StartupOrder != null) && (biosInfo.StartupOrder.Length > 0)) + vm.BootFromCD = (biosInfo.StartupOrder[0] == "CD"); + + // DVD drive + cmd = new Command("Get-VMDvdDrive"); + cmd.Parameters.Add("VMName", vm.Name); + + result = ExecuteShellCommand(runSpace, cmd, false); + vm.DvdDriveInstalled = (result != null && result.Count > 0); + + // HDD + vm.Disks = GetVirtualHardDisks(runSpace, vm.Name); + + if ((vm.Disks != null) & (vm.Disks.GetLength(0) > 0)) + { + vm.VirtualHardDrivePath = vm.Disks[0].Path; + vm.HddSize = Convert.ToInt32(vm.Disks[0].FileSize); + } + + // network adapters + vm.Adapters = GetNetworkAdapters(runSpace, vm.Name); + return vm; + + } + + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetVirtualMachine", ex); + throw; + } + finally + { + CloseRunspace(runSpace); + } + + HostedSolutionLog.LogEnd("GetVirtualMachine"); + return vm; + + } + + internal OperationalStatus GetVMHeartBeatStatus(Runspace runSpace, string name) + { + + OperationalStatus status = OperationalStatus.None; + + Command cmd = new Command("Get-VMIntegrationService"); + + cmd.Parameters.Add("VMName", name); + cmd.Parameters.Add("Name", "HeartBeat"); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + var statusString = GetPSObjectProperty(result[0], "PrimaryOperationalStatus"); + + if (statusString != null) + status = (OperationalStatus)Enum.Parse(typeof(OperationalStatus), statusString.ToString()); + } + return status; + } + + public VirtualMachine GetVirtualMachineEx(string vmId) + { + return GetVirtualMachineInternal( vmId, true); + } + + + internal int GetVMProcessors(Runspace runSpace, string name) + { + + int procs = 0; + + Command cmd = new Command("Get-VMProcessor"); + + cmd.Parameters.Add("VMName", name); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + procs = Convert.ToInt32(GetPSObjectProperty(result[0], "Count")); + + } + return procs; + } + + internal MemoryInfo GetVMMemory(Runspace runSpace, string name) + { + MemoryInfo info = new MemoryInfo(); + + Command cmd = new Command("Get-VMMemory"); + + cmd.Parameters.Add("VMName", name); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + info.DynamicMemoryEnabled = Convert.ToBoolean(GetPSObjectProperty(result[0], "DynamicMemoryEnabled")); + info.Startup = Convert.ToInt64(GetPSObjectProperty(result[0], "Startup")); + info.Minimum = Convert.ToInt64(GetPSObjectProperty(result[0], "Minimum")); + info.Maximum = Convert.ToInt64(GetPSObjectProperty(result[0], "Maximum")); + info.Buffer = Convert.ToInt32(GetPSObjectProperty(result[0], "Buffer")); + info.Priority = Convert.ToInt32(GetPSObjectProperty(result[0], "Priority")); + } + return info; + } + + internal BiosInfo GetVMBios(Runspace runSpace, string name) + { + BiosInfo info = new BiosInfo(); + + Command cmd = new Command("Get-VMBios"); + + cmd.Parameters.Add("VMName", name); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + info.NumLockEnabled = Convert.ToBoolean(GetPSObjectProperty(result[0], "NumLockEnabled")); + + List startupOrders = new List(); + + foreach (var item in (IEnumerable)GetPSObjectProperty(result[0], "StartupOrder")) + startupOrders.Add(item.ToString()); + + info.StartupOrder = startupOrders.ToArray(); + } + return info; + } + + internal VirtualHardDiskInfo[] GetVirtualHardDisks(Runspace runSpace, string name) + { + + List disks = new List(); + + Command cmd = new Command("Get-VMHardDiskDrive"); + cmd.Parameters.Add("VMName", name); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + foreach(PSObject d in result) + { + VirtualHardDiskInfo disk = new VirtualHardDiskInfo(); + + disk.SupportPersistentReservations = Convert.ToBoolean(GetPSObjectProperty(d, "SupportPersistentReservations")); + disk.MaximumIOPS = Convert.ToUInt64(GetPSObjectProperty(d, "MaximumIOPS")); + disk.MinimumIOPS = Convert.ToUInt64(GetPSObjectProperty(d, "MinimumIOPS")); + disk.VHDControllerType = (ControllerType)Enum.Parse(typeof(ControllerType), GetPSObjectProperty(d, "ControllerType").ToString()); + disk.ControllerNumber = Convert.ToInt32(GetPSObjectProperty(d, "ControllerNumber")); + disk.ControllerLocation = Convert.ToInt32(GetPSObjectProperty(d, "ControllerLocation")); + disk.Path = GetPSObjectProperty(d, "Path").ToString(); + disk.Name = GetPSObjectProperty(d, "Name").ToString(); + + GetVirtualHardDiskDetail(runSpace, disk.Path, ref disk); + + disks.Add(disk); + } + } + return disks.ToArray(); + } + + internal void GetVirtualHardDiskDetail(Runspace runSpace, string path, ref VirtualHardDiskInfo disk) + { + if (!string.IsNullOrEmpty(path)) + { + Command cmd = new Command("Get-VHD"); + cmd.Parameters.Add("Path", path); + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + disk.DiskFormat = (VirtualHardDiskFormat)Enum.Parse(typeof(VirtualHardDiskFormat), GetPSObjectProperty(result[0], "VhdFormat").ToString()); + disk.DiskType = (VirtualHardDiskType)Enum.Parse(typeof(VirtualHardDiskType), GetPSObjectProperty(result[0], "VhdType").ToString()); + disk.ParentPath = (string)GetPSObjectProperty(result[0], "ParentPath"); + disk.MaxInternalSize = Convert.ToInt64(GetPSObjectProperty(result[0], "Size")) / Size1G; + disk.FileSize = Convert.ToInt64(GetPSObjectProperty(result[0], "FileSize")) / Size1G; + disk.Attached = Convert.ToBoolean(GetPSObjectProperty(result[0], "Attached")); + } + } + } + + + internal VirtualMachineNetworkAdapter[] GetNetworkAdapters(Runspace runSpace, string name) + { + List adapters = new List(); + + Command cmd = new Command("Get-VMNetworkAdapter"); + cmd.Parameters.Add("VMName", name); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + foreach(PSObject a in result) + { + VirtualMachineNetworkAdapter adapter = new VirtualMachineNetworkAdapter(); + + adapter.Name = GetPSObjectProperty(a, "Name").ToString(); + adapter.MacAddress = GetPSObjectProperty(a, "MacAddress").ToString(); + + adapters.Add(adapter); + } + } + return adapters.ToArray(); + } + + public List GetVirtualMachines() + { + HostedSolutionLog.LogStart("GetVirtualMachines"); + + Runspace runSpace = null; + List vmachines = new List(); + + try + { + runSpace = OpenRunspace(); + Command cmd = new Command("Get-VM"); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + foreach (PSObject current in result) + { + VirtualMachine vm = new VirtualMachine + { + VirtualMachineId = GetPSObjectProperty(current, "Id").ToString(), + Name = GetPSObjectProperty(current, "Name").ToString(), + State = (VirtualMachineState)Enum.Parse(typeof(VirtualMachineState), GetPSObjectProperty(current, "State").ToString()), + Uptime = GetPSObjectProperty(current, "UpTime").Ticks + }; + vmachines.Add(vm); + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetVirtualMachines", ex); + throw; + } + finally + { + CloseRunspace(runSpace); + } + + HostedSolutionLog.LogEnd("GetVirtualMachines"); + return vmachines; + + } + + public byte[] GetVirtualMachineThumbnailImage(string vmId, ThumbnailSize size) + { + //ManagementBaseObject objSummary = GetVirtualMachineSummaryInformation(vmId, (SummaryInformationRequest)size); + //wmi.Dump(objSummary); + //return GetTumbnailFromSummaryInformation(objSummary, size); + // TODO: + return (byte[]) (new ImageConverter()).ConvertTo(new Bitmap(80, 60), typeof (byte[])); + } + + private byte[] GetTumbnailFromSummaryInformation(ManagementBaseObject objSummary, ThumbnailSize size) + { + int width = 80; + int height = 60; + + if (size == ThumbnailSize.Medium160x120) + { + width = 160; + height = 120; + } + else if (size == ThumbnailSize.Large320x240) + { + width = 320; + height = 240; + } + + byte[] imgData = (byte[])objSummary["ThumbnailImage"]; + + // create new bitmap + Bitmap bmp = new Bitmap(width, height); + + if (imgData != null) + { + // lock bitmap + Rectangle rect = new Rectangle(0, 0, width, height); + BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format16bppRgb565); + + // get address of the first line + IntPtr ptr = bmpData.Scan0; + + // coby thumbnail bytes into bitmap + System.Runtime.InteropServices.Marshal.Copy(imgData, 0, ptr, imgData.Length); + + // unlock image + bmp.UnlockBits(bmpData); + } + else + { + // fill grey rectangle + Graphics g = Graphics.FromImage(bmp); + SolidBrush brush = new SolidBrush(Color.LightGray); + g.FillRectangle(brush, 0, 0, width, height); + } + + MemoryStream stream = new MemoryStream(); + bmp.Save(stream, ImageFormat.Png); + + stream.Flush(); + byte[] buffer = stream.ToArray(); + + bmp.Dispose(); + stream.Dispose(); + + return buffer; + } + + public VirtualMachine CreateVirtualMachine(VirtualMachine vm) + { + // evaluate paths + vm.RootFolderPath = FileUtils.EvaluateSystemVariables(vm.RootFolderPath); + vm.OperatingSystemTemplatePath = FileUtils.EvaluateSystemVariables(vm.OperatingSystemTemplatePath); + vm.VirtualHardDrivePath = FileUtils.EvaluateSystemVariables(vm.VirtualHardDrivePath); + + string vmID = null; + + // request management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // display name + ManagementObject objGlobalSettings = wmi.GetWmiClass("msvm_VirtualSystemGlobalSettingData").CreateInstance(); + objGlobalSettings["ElementName"] = vm.Name; + + // VM folders + objGlobalSettings["ExternalDataRoot"] = vm.RootFolderPath; + objGlobalSettings["SnapshotDataRoot"] = vm.RootFolderPath; + + wmi.Dump(objGlobalSettings); + + // startup/shutdown actions + if (AutomaticStartActionSettings != 100) + { + objGlobalSettings["AutomaticStartupAction"] = AutomaticStartActionSettings; + objGlobalSettings["AutomaticStartupActionDelay"] = String.Format("000000000000{0:d2}.000000:000", AutomaticStartupDelaySettings); + } + + if (AutomaticStopActionSettings != 100) + objGlobalSettings["AutomaticShutdownAction"] = AutomaticStopActionSettings; + + if (AutomaticRecoveryActionSettings != 100) + objGlobalSettings["AutomaticRecoveryAction"] = AutomaticRecoveryActionSettings; + + // create machine + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("DefineVirtualSystem"); + inParams["SystemSettingData"] = objGlobalSettings.GetText(TextFormat.CimDtd20); + inParams["ResourceSettingData"] = new string[] { }; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("DefineVirtualSystem", inParams, null); + ManagementObject objVM = wmi.GetWmiObjectByPath((string)outParams["DefinedSystem"]); + + // job + JobResult job = CreateJobResultFromWmiMethodResults(outParams); ; + + // read VM id + vmID = (string)objVM["Name"]; + + // update general settings + UpdateVirtualMachineGeneralSettings(vmID, objVM, + vm.CpuCores, + vm.RamSize, + vm.BootFromCD, + vm.NumLockEnabled); + + // hard disks + // load IDE 0 controller + ManagementObject objIDE0 = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Emulated IDE Controller'" + + " and InstanceID Like 'Microsoft:{0}%' and Address = 0", vmID); + + // load default hard disk drive + ManagementObject objDefaultHdd = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Synthetic Disk Drive'" + + " and InstanceID like '%Default'"); + ManagementObject objHdd = (ManagementObject)objDefaultHdd.Clone(); + objHdd["Parent"] = objIDE0.Path; + objHdd["Address"] = 0; + + // add HDD to VM resources + ManagementObject objAddedHDD = AddVirtualMachineResources(objVM, objHdd); + + // attach VHD + string fullVhdPath = vm.VirtualHardDrivePath; + ManagementObject objDefaultVHD = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Virtual Hard Disk'" + + " and InstanceID like '%Default'"); + ManagementObject objVhd = (ManagementObject)objDefaultVHD.Clone(); + objVhd["Parent"] = objAddedHDD.Path.Path; + objVhd["Connection"] = new string[] { fullVhdPath }; + + // add VHD to the system + AddVirtualMachineResources(objVM, objVhd); + + // DVD drive + if (vm.DvdDriveInstalled) + { + AddVirtualMachineDvdDrive(vmID, objVM); + } + + // add external adapter + if (vm.ExternalNetworkEnabled && !String.IsNullOrEmpty(vm.ExternalSwitchId)) + AddNetworkAdapter(objVM, vm.ExternalSwitchId, vm.Name, vm.ExternalNicMacAddress, EXTERNAL_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + + // add private adapter + if (vm.PrivateNetworkEnabled && !String.IsNullOrEmpty(vm.PrivateSwitchId)) + AddNetworkAdapter(objVM, vm.PrivateSwitchId, vm.Name, vm.PrivateNicMacAddress, PRIVATE_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + + // add management adapter + if (vm.ManagementNetworkEnabled && !String.IsNullOrEmpty(vm.ManagementSwitchId)) + AddNetworkAdapter(objVM, vm.ManagementSwitchId, vm.Name, vm.ManagementNicMacAddress, MANAGEMENT_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + + vm.VirtualMachineId = vmID; + return vm; + } + + public VirtualMachine UpdateVirtualMachine(VirtualMachine vm) + { + string vmId = vm.VirtualMachineId; + + // get VM object + ManagementObject objVM = GetVirtualMachineObject(vmId); + + // update general settings + UpdateVirtualMachineGeneralSettings(vmId, objVM, + vm.CpuCores, + vm.RamSize, + vm.BootFromCD, + vm.NumLockEnabled); + + // check DVD drive + ManagementObject objDvdDrive = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Synthetic DVD Drive'" + + " and InstanceID like 'Microsoft:{0}%' and Address = 0", vmId); + + if (vm.DvdDriveInstalled && objDvdDrive == null) + AddVirtualMachineDvdDrive(vmId, objVM); + else if (!vm.DvdDriveInstalled && objDvdDrive != null) + RemoveVirtualMachineResources(objVM, objDvdDrive); + + // External NIC + if (!vm.ExternalNetworkEnabled + && !String.IsNullOrEmpty(vm.ExternalNicMacAddress)) + { + // delete adapter + DeleteNetworkAdapter(objVM, vm.ExternalNicMacAddress); + + // reset MAC + vm.ExternalNicMacAddress = null; + } + else if (vm.ExternalNetworkEnabled + && !String.IsNullOrEmpty(vm.ExternalNicMacAddress)) + { + // add external adapter + AddNetworkAdapter(objVM, vm.ExternalSwitchId, vm.Name, vm.ExternalNicMacAddress, EXTERNAL_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + } + + + // Private NIC + if (!vm.PrivateNetworkEnabled + && !String.IsNullOrEmpty(vm.PrivateNicMacAddress)) + { + // delete adapter + DeleteNetworkAdapter(objVM, vm.PrivateNicMacAddress); + + // reset MAC + vm.PrivateNicMacAddress = null; + } + else if (vm.PrivateNetworkEnabled + && !String.IsNullOrEmpty(vm.PrivateNicMacAddress)) + { + // add private adapter + AddNetworkAdapter(objVM, vm.PrivateSwitchId, vm.Name, vm.PrivateNicMacAddress, PRIVATE_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + } + + return vm; + } + + private void UpdateVirtualMachineGeneralSettings(string vmId, ManagementObject objVM, int cpuCores, long ramMB, bool bootFromCD, bool numLockEnabled) + { + // request management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // VM resources + List vmConfig = new List(); + + // get system settings + ManagementObject objSettings = GetVirtualMachineSettingsObject(vmId); + + // BIOS (num lock) + objSettings["BIOSNumLock"] = numLockEnabled; + + // BIOS (boot order) + // BootOrder = 0 - Boot from floppy, 1 - Boot from CD, 2 - Boot from disk, 3 - PXE Boot + objSettings["BootOrder"] = bootFromCD ? new int[] { 1, 2, 3, 0 } : new int[] { 2, 1, 3, 0 }; + + // modify machine settings + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("ModifyVirtualSystem"); + inParams["ComputerSystem"] = objVM; + inParams["SystemSettingData"] = objSettings.GetText(TextFormat.CimDtd20); + ManagementBaseObject outParams = objVmsvc.InvokeMethod("ModifyVirtualSystem", inParams, null); + JobResult job = CreateJobResultFromWmiMethodResults(outParams); + + // setup CPU + ManagementObject objCpu = wmi.GetWmiObject("Msvm_ProcessorSettingData", "InstanceID Like 'Microsoft:{0}%'", vmId); + objCpu["VirtualQuantity"] = cpuCores; + objCpu["Limit"] = Convert.ToInt64(CpuLimitSettings * 1000); + objCpu["Reservation"] = Convert.ToInt64(CpuReserveSettings * 1000); + objCpu["Weight"] = CpuWeightSettings; + vmConfig.Add(objCpu.GetText(TextFormat.CimDtd20)); + + // setup RAM + ManagementObject objRam = wmi.GetWmiObject("Msvm_MemorySettingData", "InstanceID Like 'Microsoft:{0}%'", vmId); + objRam["VirtualQuantity"] = ramMB.ToString(); + objRam["Reservation"] = ramMB.ToString(); + objRam["Limit"] = ramMB.ToString(); + vmConfig.Add(objRam.GetText(TextFormat.CimDtd20)); + + // modify machine resources + inParams = objVmsvc.GetMethodParameters("ModifyVirtualSystemResources"); + inParams["ComputerSystem"] = objVM; + inParams["ResourceSettingData"] = vmConfig.ToArray(); + outParams = objVmsvc.InvokeMethod("ModifyVirtualSystemResources", inParams, null); + job = CreateJobResultFromWmiMethodResults(outParams); + } + + private void AddVirtualMachineDvdDrive(string vmId, ManagementObject objVM) + { + // load IDE 1 controller + ManagementObject objIDE1 = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Emulated IDE Controller'" + + " and InstanceID Like 'Microsoft:{0}%' and Address = 1", vmId); + + // load default hard disk drive + ManagementObject objDefaultDvd = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Synthetic DVD Drive'" + + " and InstanceID like '%Default'"); + ManagementObject objDvd = (ManagementObject)objDefaultDvd.Clone(); + objDvd["Parent"] = objIDE1.Path; + objDvd["Address"] = 0; + + // add DVD drive to VM resources + AddVirtualMachineResources(objVM, objDvd); + } + + private void AddNetworkAdapter(ManagementObject objVm, string switchId, string portName, string macAddress, string adapterName, bool legacyAdapter) + { + string nicClassName = GetNetworkAdapterClassName(legacyAdapter); + + string vmId = (string)objVm["Name"]; + + // check if already exists + ManagementObject objNic = wmi.GetWmiObject( + nicClassName, "InstanceID like 'Microsoft:{0}%' and Address = '{1}'", vmId, macAddress); + + if (objNic != null) + return; // exists - exit + + portName = String.Format("{0} - {1}", + portName, (adapterName == EXTERNAL_NETWORK_ADAPTER_NAME) ? "External" : "Private"); + + // Network service + ManagementObject objNetworkSvc = GetVirtualSwitchManagementService(); + + // default NIC + ManagementObject objDefaultNic = wmi.GetWmiObject(nicClassName, "InstanceID like '%Default'"); + + // find switch + ManagementObject objSwitch = wmi.GetWmiObject("msvm_VirtualSwitch", "Name = '{0}'", switchId); + + // create switch port + ManagementBaseObject inParams = objNetworkSvc.GetMethodParameters("CreateSwitchPort"); + inParams["VirtualSwitch"] = objSwitch; + inParams["Name"] = portName; + inParams["FriendlyName"] = portName; + inParams["ScopeOfResidence"] = ""; + + // invoke method + ManagementBaseObject outParams = objNetworkSvc.InvokeMethod("CreateSwitchPort", inParams, null); + + // process output parameters + ReturnCode code = (ReturnCode)Convert.ToInt32(outParams["ReturnValue"]); + if (code == ReturnCode.OK) + { + // created port + ManagementObject objPort = wmi.GetWmiObjectByPath((string)outParams["CreatedSwitchPort"]); + + // create NIC + ManagementObject objExtNic = (ManagementObject)objDefaultNic.Clone(); + objExtNic["Connection"] = new string[] { objPort.Path.Path }; + + if (!String.IsNullOrEmpty(macAddress)) + { + objExtNic["StaticMacAddress"] = true; + objExtNic["Address"] = macAddress; + } + else + { + objExtNic["StaticMacAddress"] = false; + } + objExtNic["ElementName"] = adapterName; + + if (!legacyAdapter) + objExtNic["VirtualSystemIdentifiers"] = new string[] { Guid.NewGuid().ToString("B") }; + + // add NIC + ManagementObject objCreatedExtNic = AddVirtualMachineResources(objVm, objExtNic); + } + } + + private string GetNetworkAdapterClassName(bool legacy) + { + return legacy ? "Msvm_EmulatedEthernetPortSettingData" : "Msvm_SyntheticEthernetPortSettingData"; + } + + private ManagementObject AddVirtualMachineResources(ManagementObject objVm, ManagementObject resource) + { + if (resource == null) + return resource; + + // request management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // add resources + string txtResource = resource.GetText(TextFormat.CimDtd20); + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("AddVirtualSystemResources"); + inParams["TargetSystem"] = objVm; + inParams["ResourceSettingData"] = new string[] { txtResource }; + ManagementBaseObject outParams = objVmsvc.InvokeMethod("AddVirtualSystemResources", inParams, null); + JobResult result = CreateJobResultFromWmiMethodResults(outParams); + + if (result.ReturnValue == ReturnCode.OK) + { + string[] wmiPaths = (string[])outParams["NewResources"]; + return wmi.GetWmiObjectByPath(wmiPaths[0]); + } + else if (result.ReturnValue == ReturnCode.JobStarted) + { + if (JobCompleted(result.Job)) + { + string[] wmiPaths = (string[])outParams["NewResources"]; + return wmi.GetWmiObjectByPath(wmiPaths[0]); + } + else + { + throw new Exception("Cannot add virtual machine resources"); + } + } + else + { + throw new Exception("Cannot add virtual machine resources: " + txtResource); + } + } + + private JobResult RemoveVirtualMachineResources(ManagementObject objVm, ManagementObject resource) + { + if (resource == null) + return null; + + // request management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // remove resources + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("RemoveVirtualSystemResources"); + inParams["TargetSystem"] = objVm; + inParams["ResourceSettingData"] = new string[] { resource.Path.Path }; + ManagementBaseObject outParams = objVmsvc.InvokeMethod("RemoveVirtualSystemResources", inParams, null); + JobResult result = CreateJobResultFromWmiMethodResults(outParams); + if (result.ReturnValue == ReturnCode.OK) + { + return result; + } + else if (result.ReturnValue == ReturnCode.JobStarted) + { + if (!JobCompleted(result.Job)) + { + throw new Exception("Cannot remove virtual machine resources"); + } + } + else + { + throw new Exception("Cannot remove virtual machine resources: " + resource.Path.Path); + } + + return result; + } + + public JobResult ChangeVirtualMachineState(string vmId, VirtualMachineRequestedState newState) + { + HostedSolutionLog.LogStart("ChangeVirtualMachineState"); + var jobResult = new JobResult(); + + var vm = GetVirtualMachine(vmId); + Runspace runSpace = null; + + try + { + runSpace = OpenRunspace(); + + string cmdTxt; + List paramList = new List(); + + switch (newState) + { + case VirtualMachineRequestedState.Start: + cmdTxt = "Start-VM"; + break; + case VirtualMachineRequestedState.Pause: + cmdTxt = "Suspend-VM"; + break; + case VirtualMachineRequestedState.Reset: + cmdTxt = "Restart-VM"; + break; + case VirtualMachineRequestedState.Resume: + cmdTxt = "Resume-VM"; + break; + case VirtualMachineRequestedState.ShutDown: + cmdTxt = "Stop-VM"; + break; + case VirtualMachineRequestedState.TurnOff: + cmdTxt = "Stop-VM"; + paramList.Add("TurnOff"); + break; + case VirtualMachineRequestedState.Save: + cmdTxt = "Save-VM"; + break; + default: + throw new ArgumentOutOfRangeException("newState"); + } + + Command cmd = new Command(cmdTxt); + + cmd.Parameters.Add("Name", vm.Name); + //cmd.Parameters.Add("AsJob"); + paramList.ForEach(p => cmd.Parameters.Add(p)); + + ExecuteShellCommand(runSpace, cmd, false); + jobResult = CreateSuccessJobResult(); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("ChangeVirtualMachineState", ex); + throw; + } + finally + { + CloseRunspace(runSpace); + } + + HostedSolutionLog.LogEnd("ChangeVirtualMachineState"); + + return jobResult; + } + + public ReturnCode ShutDownVirtualMachine(string vmId, bool force, string reason) + { + HostedSolutionLog.LogStart("ShutDownVirtualMachine"); + ReturnCode returnCode = ReturnCode.OK; + + var vm = GetVirtualMachine(vmId); + Runspace runSpace = null; + + try + { + runSpace = OpenRunspace(); + + Command cmd = new Command("Stop-VM"); + + cmd.Parameters.Add("Name", vm.Name); + if (force) cmd.Parameters.Add("Force"); + + ExecuteShellCommand(runSpace, cmd, false); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("ShutDownVirtualMachine", ex); + throw; + } + finally + { + CloseRunspace(runSpace); + } + + HostedSolutionLog.LogEnd("ShutDownVirtualMachine"); + + return returnCode; + } + + public List GetVirtualMachineJobs(string vmId) + { + List jobs = new List(); + + ManagementBaseObject objSummary = GetVirtualMachineSummaryInformation( + vmId, SummaryInformationRequest.AsynchronousTasks); + ManagementBaseObject[] objJobs = (ManagementBaseObject[])objSummary["AsynchronousTasks"]; + + if (objJobs != null) + { + foreach (ManagementBaseObject objJob in objJobs) + jobs.Add(CreateJobFromWmiObject(objJob)); + } + + return jobs; + } + + public JobResult RenameVirtualMachine(string vmId, string name) + { + // load virtual machine + ManagementObject objVm = GetVirtualMachineObject(vmId); + + // load machine settings + ManagementObject objVmSettings = GetVirtualMachineSettingsObject(vmId); + + // rename machine + objVmSettings["ElementName"] = name; + + // save + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("ModifyVirtualSystem"); + inParams["ComputerSystem"] = objVm.Path.Path; + inParams["SystemSettingData"] = objVmSettings.GetText(TextFormat.CimDtd20); + ManagementBaseObject outParams = objVmsvc.InvokeMethod("ModifyVirtualSystem", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult DeleteVirtualMachine(string vmId) + { + // load virtual machine object + ManagementObject objVm = GetVirtualMachineObject(vmId); + + // check state + VirtualMachine vm = GetVirtualMachine(vmId); + + // The virtual computer system must be in the powered off or saved state prior to calling this method. + if (vm.State == VirtualMachineState.Saved + || vm.State == VirtualMachineState.Off) + { + // delete network adapters and ports + DeleteNetworkAdapters(objVm); + + // destroy machine + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // get method + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("DestroyVirtualSystem"); + inParams["ComputerSystem"] = objVm; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("DestroyVirtualSystem", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + else + { + throw new Exception("The virtual computer system must be in the powered off or saved state prior to calling Destroy method."); + } + } + + private void DeleteNetworkAdapters(ManagementObject objVM) + { + string vmId = (string)objVM["Name"]; + + // delete synthetic adapters + foreach (ManagementObject objNic in wmi.GetWmiObjects("Msvm_SyntheticEthernetPortSettingData", "InstanceID like 'Microsoft:{0}%'", vmId)) + DeleteNetworkAdapter(objVM, objNic); + + // delete legacy adapters + foreach (ManagementObject objNic in wmi.GetWmiObjects("Msvm_EmulatedEthernetPortSettingData", "InstanceID like 'Microsoft:{0}%'", vmId)) + DeleteNetworkAdapter(objVM, objNic); + } + + private void DeleteNetworkAdapter(ManagementObject objVM, string macAddress) + { + // locate network adapter + ManagementObject objNic = wmi.GetWmiObject("CIM_ResourceAllocationSettingData", "Address = '{0}'", macAddress); + + // delete adapter + DeleteNetworkAdapter(objVM, objNic); + } + + private void DeleteNetworkAdapter(ManagementObject objVM, ManagementObject objNic) + { + if (objNic == null) + return; + + // delete corresponding switch port + string[] conn = (string[])objNic["Connection"]; + if (conn != null && conn.Length > 0) + DeleteSwitchPort(conn[0]); + + // delete adapter + RemoveVirtualMachineResources(objVM, objNic); + } + + private void DeleteSwitchPort(string portPath) + { + // Network service + ManagementObject objNetworkSvc = GetVirtualSwitchManagementService(); + + // create switch port + ManagementBaseObject inParams = objNetworkSvc.GetMethodParameters("DeleteSwitchPort"); + inParams["SwitchPort"] = portPath; + + // invoke method + objNetworkSvc.InvokeMethod("DeleteSwitchPort", inParams, null); + } + + public JobResult ExportVirtualMachine(string vmId, string exportPath) + { + // load virtual machine object + ManagementObject objVm = GetVirtualMachineObject(vmId); + + // check state + VirtualMachine vm = GetVirtualMachine(vmId); + + // The virtual computer system must be in the powered off or saved state prior to calling this method. + if (vm.State == VirtualMachineState.Off) + { + // export machine + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // get method + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("ExportVirtualSystem"); + inParams["ComputerSystem"] = objVm; + inParams["CopyVmState"] = true; + inParams["ExportDirectory"] = FileUtils.EvaluateSystemVariables(exportPath); + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("ExportVirtualSystem", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + else + { + throw new Exception("The virtual computer system must be in the powered off or saved state prior to calling Export method."); + } + } + #endregion + + #region Snapshots + public List GetVirtualMachineSnapshots(string vmId) + { + // get all VM setting objects + ManagementObject objVmSettings = GetVirtualMachineSettingsObject(vmId); + VirtualMachineSnapshot runningSnapshot = CreateSnapshotFromWmiObject(objVmSettings); + + // load snapshots + ManagementBaseObject objSummary = GetVirtualMachineSummaryInformation(vmId, SummaryInformationRequest.Snapshots); + ManagementBaseObject[] objSnapshots = (ManagementBaseObject[])objSummary["Snapshots"]; + + List snapshots = new List(); + + if (objSnapshots != null) + { + foreach (ManagementBaseObject objSnapshot in objSnapshots) + { + VirtualMachineSnapshot snapshot = CreateSnapshotFromWmiObject(objSnapshot); + snapshot.IsCurrent = (runningSnapshot.ParentId == snapshot.Id); + snapshots.Add(snapshot); + } + } + + return snapshots; + } + + public VirtualMachineSnapshot GetSnapshot(string snapshotId) + { + // load snapshot + ManagementObject objSnapshot = GetSnapshotObject(snapshotId); + return CreateSnapshotFromWmiObject(objSnapshot); + } + + public JobResult CreateSnapshot(string vmId) + { + // get VM management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // load virtual machine + ManagementObject objVm = GetVirtualMachineObject(vmId); + + // get method params + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("CreateVirtualSystemSnapshot"); + inParams["SourceSystem"] = objVm; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("CreateVirtualSystemSnapshot", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult RenameSnapshot(string vmId, string snapshotId, string name) + { + // load virtual machine + ManagementObject objVm = GetVirtualMachineObject(vmId); + + // load snapshot + ManagementObject objSnapshot = GetSnapshotObject(snapshotId); + + // rename snapshot + objSnapshot["ElementName"] = name; + + // save + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("ModifyVirtualSystem"); + inParams["ComputerSystem"] = objVm.Path.Path; + inParams["SystemSettingData"] = objSnapshot.GetText(TextFormat.CimDtd20); + ManagementBaseObject outParams = objVmsvc.InvokeMethod("ModifyVirtualSystem", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult ApplySnapshot(string vmId, string snapshotId) + { + // get VM management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // load virtual machine + ManagementObject objVm = GetVirtualMachineObject(vmId); + + // load snapshot + ManagementObject objSnapshot = GetSnapshotObject(snapshotId); + + ManagementObjectCollection objRelated = objVm.GetRelated("Msvm_SettingsDefineState"); + + // get method params + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("ApplyVirtualSystemSnapshot"); + inParams["ComputerSystem"] = objVm.Path.Path; + inParams["SnapshotSettingData"] = objSnapshot.Path.Path; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("ApplyVirtualSystemSnapshot", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult DeleteSnapshot(string snapshotId) + { + // get VM management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // load snapshot object + ManagementObject objSnapshot = GetSnapshotObject(snapshotId); + + // get method params + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("RemoveVirtualSystemSnapshot"); + inParams["SnapshotSettingData"] = objSnapshot.Path.Path; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("RemoveVirtualSystemSnapshot", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult DeleteSnapshotSubtree(string snapshotId) + { + // get VM management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // load snapshot object + ManagementObject objSnapshot = GetSnapshotObject(snapshotId); + + // get method params + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("RemoveVirtualSystemSnapshotTree"); + inParams["SnapshotSettingData"] = objSnapshot.Path.Path; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("RemoveVirtualSystemSnapshotTree", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public byte[] GetSnapshotThumbnailImage(string snapshotId, ThumbnailSize size) + { + ManagementBaseObject objSummary = GetSnapshotSummaryInformation(snapshotId, (SummaryInformationRequest)size); + return GetTumbnailFromSummaryInformation(objSummary, size); + } + #endregion + + #region DVD operations + public string GetInsertedDVD(string vmId) + { + // find CD/DVD disk + ManagementObject objDvd = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Virtual CD/DVD Disk'" + + " and InstanceID Like 'Microsoft:{0}%'", vmId); + + if (objDvd == null) + return null; + + string[] path = (string[])objDvd["Connection"]; + if (path != null && path.Length > 0) + return path[0]; + + return null; + } + + public JobResult InsertDVD(string vmId, string isoPath) + { + isoPath = FileUtils.EvaluateSystemVariables(isoPath); + + // find DVD drive + ManagementObject objDvdDrive = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Synthetic DVD Drive'" + + " and InstanceID Like 'Microsoft:{0}%'", vmId); + + // create CD/DVD disk + ManagementObject objDefaultDVD = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Virtual CD/DVD Disk'" + + " and InstanceID like '%Default'"); + ManagementObject objDvd = (ManagementObject)objDefaultDVD.Clone(); + objDvd["Parent"] = objDvdDrive.Path; + objDvd["Connection"] = new string[] { isoPath }; + + // get VM service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // get method + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("AddVirtualSystemResources"); + inParams["TargetSystem"] = GetVirtualMachineObject(vmId); + inParams["ResourceSettingData"] = new string[] { objDvd.GetText(TextFormat.CimDtd20) }; + + // execute method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("AddVirtualSystemResources", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult EjectDVD(string vmId) + { + // find CD/DVD disk + ManagementObject objDvd = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Virtual CD/DVD Disk'" + + " and InstanceID Like 'Microsoft:{0}%'", vmId); + + // get VM service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // get method + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("RemoveVirtualSystemResources"); + inParams["TargetSystem"] = GetVirtualMachineObject(vmId); + inParams["ResourceSettingData"] = new object[] { objDvd.Path.Path }; + + // execute method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("RemoveVirtualSystemResources", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + #endregion + + #region Virtual Switches + public List GetSwitches() + { + return GetSwitches(null, null); + } + + public List GetExternalSwitches(string computerName) + { + return GetSwitches(computerName, "External"); + } + + private List GetSwitches(string computerName, string type) + { + HostedSolutionLog.LogStart("GetSwitches"); + HostedSolutionLog.DebugInfo("ComputerName: {0}", computerName); + + Runspace runSpace = null; + List switches = new List(); + + try + { + runSpace = OpenRunspace(); + Command cmd = new Command("Get-VMSwitch"); + + if (!string.IsNullOrEmpty(computerName)) cmd.Parameters.Add("ComputerName", computerName); + if (!string.IsNullOrEmpty(type)) cmd.Parameters.Add("SwitchType", type); + + Collection result = ExecuteShellCommand(runSpace, cmd,false); + foreach (PSObject current in result) + { + VirtualSwitch sw = new VirtualSwitch(); + sw.SwitchId = GetPSObjectProperty(current, "Name").ToString(); + sw.Name = GetPSObjectProperty(current, "Name").ToString(); + sw.SwitchType = GetPSObjectProperty(current, "SwitchType").ToString(); + switches.Add(sw); + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetSwitches", ex); + throw; + } + finally + { + CloseRunspace(runSpace); + } + + HostedSolutionLog.LogEnd("GetSwitches"); + return switches; + + } + + public bool SwitchExists(string switchId) + { + ManagementObject objSwitch = wmi.GetWmiObject("msvm_VirtualSwitch", "Name = '{0}'", switchId); + return (objSwitch != null); + } + + public VirtualSwitch CreateSwitch(string name) + { + // generate ID for new virtual switch + string id = Guid.NewGuid().ToString(); + + // get switch management object + ManagementObject objNetworkSvc = GetVirtualSwitchManagementService(); + + ManagementBaseObject inParams = objNetworkSvc.GetMethodParameters("CreateSwitch"); + inParams["Name"] = id; + inParams["FriendlyName"] = name; + inParams["NumLearnableAddresses"] = SWITCH_PORTS_NUMBER; + + // invoke method + ManagementBaseObject outParams = objNetworkSvc.InvokeMethod("CreateSwitch", inParams, null); + + // process output parameters + ManagementObject objSwitch = wmi.GetWmiObjectByPath((string)outParams["CreatedVirtualSwitch"]); + return CreateSwitchFromWmiObject(objSwitch); + } + + public ReturnCode DeleteSwitch(string switchId) + { + // find requested switch + ManagementObject objSwitch = wmi.GetWmiObject("msvm_VirtualSwitch", "Name = '{0}'", switchId); + + if (objSwitch == null) + throw new Exception("Virtual switch with the specified ID was not found."); + + // get switch management object + ManagementObject objNetworkSvc = GetVirtualSwitchManagementService(); + + // get method params + ManagementBaseObject inParams = objNetworkSvc.GetMethodParameters("DeleteSwitch"); + inParams["VirtualSwitch"] = objSwitch.Path.Path; + + ManagementBaseObject outParams = (ManagementBaseObject)objNetworkSvc.InvokeMethod("DeleteSwitch", inParams, null); + return (ReturnCode)Convert.ToInt32(outParams["ReturnValue"]); + } + #endregion + + #region Library + public LibraryItem[] GetLibraryItems(string path) + { + path = Path.Combine(FileUtils.EvaluateSystemVariables(path), LIBRARY_INDEX_FILE_NAME); + + // convert to UNC if it is a remote computer + path = ConvertToUNC(path); + + if (!File.Exists(path)) + { + HostedSolutionLog.LogWarning("The folder does not contain 'index.xml' file: {0}", path); + return null; + } + + // create list + List items = new List(); + + // load xml + XmlDocument xml = new XmlDocument(); + xml.Load(path); + + XmlNodeList nodeItems = xml.SelectNodes("/items/item"); + + if (nodeItems.Count == 0) + HostedSolutionLog.LogWarning("index.xml found, but contains 0 items: {0}", path); + + foreach (XmlNode nodeItem in nodeItems) + { + LibraryItem item = new LibraryItem(); + item.Path = nodeItem.Attributes["path"].Value; + + // optional attributes + if (nodeItem.Attributes["diskSize"] != null) + item.DiskSize = Int32.Parse(nodeItem.Attributes["diskSize"].Value); + + if (nodeItem.Attributes["legacyNetworkAdapter"] != null) + item.LegacyNetworkAdapter = Boolean.Parse(nodeItem.Attributes["legacyNetworkAdapter"].Value); + + item.ProcessVolume = 0; // process (extend and sysprep) 1st volume by default + if (nodeItem.Attributes["processVolume"] != null) + item.ProcessVolume = Int32.Parse(nodeItem.Attributes["processVolume"].Value); + + if (nodeItem.Attributes["remoteDesktop"] != null) + item.RemoteDesktop = Boolean.Parse(nodeItem.Attributes["remoteDesktop"].Value); + + // inner nodes + item.Name = nodeItem.SelectSingleNode("name").InnerText; + item.Description = nodeItem.SelectSingleNode("description").InnerText; + + // sysprep files + XmlNodeList nodesSyspep = nodeItem.SelectNodes("provisioning/sysprep"); + List sysprepFiles = new List(); + foreach (XmlNode nodeSyspep in nodesSyspep) + { + if (nodeSyspep.Attributes["file"] != null) + sysprepFiles.Add(nodeSyspep.Attributes["file"].Value); + } + item.SysprepFiles = sysprepFiles.ToArray(); + + // vmconfig + XmlNode nodeVmConfig = nodeItem.SelectSingleNode("provisioning/vmconfig"); + if (nodeVmConfig != null) + { + if (nodeVmConfig.Attributes["computerName"] != null) + item.ProvisionComputerName = Boolean.Parse(nodeVmConfig.Attributes["computerName"].Value); + + if (nodeVmConfig.Attributes["administratorPassword"] != null) + item.ProvisionAdministratorPassword = Boolean.Parse(nodeVmConfig.Attributes["administratorPassword"].Value); + + if (nodeVmConfig.Attributes["networkAdapters"] != null) + item.ProvisionNetworkAdapters = Boolean.Parse(nodeVmConfig.Attributes["networkAdapters"].Value); + } + + items.Add(item); + } + + return items.ToArray(); + } + + private string ConvertToUNC(string path) + { + if (String.IsNullOrEmpty(ServerNameSettings) + || path.StartsWith(@"\\")) + return path; + + return String.Format(@"\\{0}\{1}", ServerNameSettings, path.Replace(":", "$")); + } + #endregion + + #region KVP + public List GetKVPItems(string vmId) + { + return GetKVPItems(vmId, "GuestExchangeItems"); + } + + public List GetStandardKVPItems(string vmId) + { + return GetKVPItems(vmId, "GuestIntrinsicExchangeItems"); + } + + private List GetKVPItems(string vmId, string exchangeItemsName) + { + List pairs = new List(); + + // load VM + ManagementObject objVm = GetVirtualMachineObject(vmId); + + ManagementObject objKvpExchange = null; + + try + { + objKvpExchange = wmi.GetRelatedWmiObject(objVm, "msvm_KvpExchangeComponent"); + } + catch + { + // TODO + // add logging... + + return pairs; + } + + // return XML pairs + string[] xmlPairs = (string[])objKvpExchange[exchangeItemsName]; + + if (xmlPairs == null) + return pairs; + + // join all pairs + StringBuilder sb = new StringBuilder(); + sb.Append(""); + foreach (string xmlPair in xmlPairs) + sb.Append(xmlPair); + sb.Append(""); + + // parse pairs + XmlDocument doc = new XmlDocument(); + doc.LoadXml(sb.ToString()); + + foreach (XmlNode nodeName in doc.SelectNodes("/result/INSTANCE/PROPERTY[@NAME='Name']/VALUE")) + { + string name = nodeName.InnerText; + string data = nodeName.ParentNode.ParentNode.SelectSingleNode("PROPERTY[@NAME='Data']/VALUE").InnerText; + pairs.Add(new KvpExchangeDataItem(name, data)); + } + + return pairs; + } + + public JobResult AddKVPItems(string vmId, KvpExchangeDataItem[] items) + { + // get KVP management object + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // create KVP items array + string[] wmiItems = new string[items.Length]; + + for (int i = 0; i < items.Length; i++) + { + ManagementClass clsKvp = wmi.GetWmiClass("Msvm_KvpExchangeDataItem"); + ManagementObject objKvp = clsKvp.CreateInstance(); + objKvp["Name"] = items[i].Name; + objKvp["Data"] = items[i].Data; + objKvp["Source"] = 0; + + // convert to WMI format + wmiItems[i] = objKvp.GetText(TextFormat.CimDtd20); + } + + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("AddKvpItems"); + inParams["TargetSystem"] = GetVirtualMachineObject(vmId); + inParams["DataItems"] = wmiItems; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("AddKvpItems", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult RemoveKVPItems(string vmId, string[] itemNames) + { + // get KVP management object + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // delete items one by one + for (int i = 0; i < itemNames.Length; i++) + { + ManagementClass clsKvp = wmi.GetWmiClass("Msvm_KvpExchangeDataItem"); + ManagementObject objKvp = clsKvp.CreateInstance(); + objKvp["Name"] = itemNames[i]; + objKvp["Data"] = ""; + objKvp["Source"] = 0; + + // convert to WMI format + string wmiItem = objKvp.GetText(TextFormat.CimDtd20); + + // call method + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("RemoveKvpItems"); + inParams["TargetSystem"] = GetVirtualMachineObject(vmId); + inParams["DataItems"] = new string[] { wmiItem }; + + // invoke method + objVmsvc.InvokeMethod("RemoveKvpItems", inParams, null); + } + return null; + } + + public JobResult ModifyKVPItems(string vmId, KvpExchangeDataItem[] items) + { + // get KVP management object + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // create KVP items array + string[] wmiItems = new string[items.Length]; + + for (int i = 0; i < items.Length; i++) + { + ManagementClass clsKvp = wmi.GetWmiClass("Msvm_KvpExchangeDataItem"); + ManagementObject objKvp = clsKvp.CreateInstance(); + objKvp["Name"] = items[i].Name; + objKvp["Data"] = items[i].Data; + objKvp["Source"] = 0; + + // convert to WMI format + wmiItems[i] = objKvp.GetText(TextFormat.CimDtd20); + } + + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("ModifyKvpItems"); + inParams["TargetSystem"] = GetVirtualMachineObject(vmId); + inParams["DataItems"] = wmiItems; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("ModifyKvpItems", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + #endregion + + #region Storage + public VirtualHardDiskInfo GetVirtualHardDiskInfo(string vhdPath) + { + ManagementObject objImgSvc = GetImageManagementService(); + + // get method params + ManagementBaseObject inParams = objImgSvc.GetMethodParameters("GetVirtualHardDiskInfo"); + inParams["Path"] = FileUtils.EvaluateSystemVariables(vhdPath); + + // execute method + ManagementBaseObject outParams = (ManagementBaseObject)objImgSvc.InvokeMethod("GetVirtualHardDiskInfo", inParams, null); + ReturnCode result = (ReturnCode)Convert.ToInt32(outParams["ReturnValue"]); + if (result == ReturnCode.OK) + { + // create XML + string xml = (string)outParams["Info"]; + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + + // read properties + VirtualHardDiskInfo vhd = new VirtualHardDiskInfo(); + vhd.DiskType = (VirtualHardDiskType)Enum.Parse(typeof(VirtualHardDiskType), GetPropertyValue("Type", doc), true); + vhd.FileSize = Int64.Parse(GetPropertyValue("FileSize", doc)); + vhd.InSavedState = Boolean.Parse(GetPropertyValue("InSavedState", doc)); + vhd.InUse = Boolean.Parse(GetPropertyValue("InUse", doc)); + vhd.MaxInternalSize = Int64.Parse(GetPropertyValue("MaxInternalSize", doc)); + vhd.ParentPath = GetPropertyValue("ParentPath", doc); + return vhd; + } + return null; + } + + private string GetPropertyValue(string propertyName, XmlDocument doc) + { + string xpath = string.Format(@"//PROPERTY[@NAME = '{0}']/VALUE/child::text()", propertyName); + XmlNode node = doc.SelectSingleNode(xpath); + return node != null ? node.Value : null; + } + + public MountedDiskInfo MountVirtualHardDisk(string vhdPath) + { + ManagementObject objImgSvc = GetImageManagementService(); + + // get method params + ManagementBaseObject inParams = objImgSvc.GetMethodParameters("Mount"); + inParams["Path"] = FileUtils.EvaluateSystemVariables(vhdPath); + + ManagementBaseObject outParams = (ManagementBaseObject)objImgSvc.InvokeMethod("Mount", inParams, null); + JobResult result = CreateJobResultFromWmiMethodResults(outParams); + + // load storage job + if (result.ReturnValue != ReturnCode.JobStarted) + throw new Exception("Failed to start Mount job with the following error: " + result.ReturnValue); ; + + ManagementObject objJob = wmi.GetWmiObject("msvm_StorageJob", "InstanceID = '{0}'", result.Job.Id); + + if (!JobCompleted(result.Job)) + throw new Exception("Failed to complete Mount job with the following error: " + result.Job.ErrorDescription); + + try + { + List volumes = new List(); + + // load output data + ManagementObject objImage = wmi.GetRelatedWmiObject(objJob, "Msvm_MountedStorageImage"); + + int pathId = Convert.ToInt32(objImage["PathId"]); + int portNumber = Convert.ToInt32(objImage["PortNumber"]); + int targetId = Convert.ToInt32(objImage["TargetId"]); + int lun = Convert.ToInt32(objImage["Lun"]); + + string diskAddress = String.Format("Port{0}Path{1}Target{2}Lun{3}", portNumber, pathId, targetId, lun); + + HostedSolutionLog.LogInfo("Disk address: " + diskAddress); + + // find mounted disk using VDS + Vds.Advanced.AdvancedDisk advancedDisk = null; + Vds.Pack diskPack = null; + + // first attempt + System.Threading.Thread.Sleep(3000); + HostedSolutionLog.LogInfo("Trying to find mounted disk - first attempt"); + FindVdsDisk(diskAddress, out advancedDisk, out diskPack); + + // second attempt + if (advancedDisk == null) + { + System.Threading.Thread.Sleep(20000); + HostedSolutionLog.LogInfo("Trying to find mounted disk - second attempt"); + FindVdsDisk(diskAddress, out advancedDisk, out diskPack); + } + + if (advancedDisk == null) + throw new Exception("Could not find mounted disk"); + + // check if DiskPart must be used to bring disk online and clear read-only flag + bool useDiskPartToClearReadOnly = false; + if (ConfigurationManager.AppSettings[CONFIG_USE_DISKPART_TO_CLEAR_READONLY_FLAG] != null) + useDiskPartToClearReadOnly = Boolean.Parse(ConfigurationManager.AppSettings[CONFIG_USE_DISKPART_TO_CLEAR_READONLY_FLAG]); + + // determine disk index for DiskPart + Wmi cimv2 = new Wmi(ServerNameSettings, WMI_CIMV2_NAMESPACE); + ManagementObject objDisk = cimv2.GetWmiObject("win32_diskdrive", + "Model='Msft Virtual Disk SCSI Disk Device' and ScsiTargetID={0} and ScsiLogicalUnit={1} and scsiPort={2}", + targetId, lun, portNumber); + + if (useDiskPartToClearReadOnly) + { + // *** Clear Read-Only and bring disk online with DiskPart *** + HostedSolutionLog.LogInfo("Clearing disk Read-only flag and bringing disk online"); + + if (objDisk != null) + { + // disk found + // run DiskPart + string diskPartResult = RunDiskPart(String.Format(@"select disk {0} +attributes disk clear readonly +online disk +exit", Convert.ToInt32(objDisk["Index"]))); + + HostedSolutionLog.LogInfo("DiskPart Result: " + diskPartResult); + } + } + else + { + // *** Clear Read-Only and bring disk online with VDS *** + // clear Read-Only + if ((advancedDisk.Flags & Vds.DiskFlags.ReadOnly) == Vds.DiskFlags.ReadOnly) + { + HostedSolutionLog.LogInfo("Clearing disk Read-only flag"); + advancedDisk.ClearFlags(Vds.DiskFlags.ReadOnly); + while ((advancedDisk.Flags & Vds.DiskFlags.ReadOnly) == Vds.DiskFlags.ReadOnly) + { + System.Threading.Thread.Sleep(100); + advancedDisk.Refresh(); + } + } + + // bring disk ONLINE + if (advancedDisk.Status == Vds.DiskStatus.Offline) + { + HostedSolutionLog.LogInfo("Bringing disk online"); + advancedDisk.Online(); + while (advancedDisk.Status == Vds.DiskStatus.Offline) + { + System.Threading.Thread.Sleep(100); + advancedDisk.Refresh(); + } + } + } + + // small pause after getting disk online + System.Threading.Thread.Sleep(3000); + + // get disk again + FindVdsDisk(diskAddress, out advancedDisk, out diskPack); + + // find volumes using VDS + HostedSolutionLog.LogInfo("Querying disk volumes with VDS"); + foreach (Vds.Volume volume in diskPack.Volumes) + { + string letter = volume.DriveLetter.ToString(); + if (letter != "") + volumes.Add(letter); + } + + // find volumes using WMI + if (volumes.Count == 0 && objDisk != null) + { + HostedSolutionLog.LogInfo("Querying disk volumes with WMI"); + foreach (ManagementObject objPartition in objDisk.GetRelated("Win32_DiskPartition")) + { + foreach (ManagementObject objVolume in objPartition.GetRelated("Win32_LogicalDisk")) + { + volumes.Add(objVolume["Name"].ToString().TrimEnd(':')); + } + } + } + + HostedSolutionLog.LogInfo("Volumes found: " + volumes.Count); + + // info object + MountedDiskInfo info = new MountedDiskInfo(); + info.DiskAddress = diskAddress; + info.DiskVolumes = volumes.ToArray(); + return info; + } + catch (Exception ex) + { + // unmount disk + UnmountVirtualHardDisk(vhdPath); + + // throw error + throw ex; + } + } + + private void FindVdsDisk(string diskAddress, out Vds.Advanced.AdvancedDisk advancedDisk, out Vds.Pack diskPack) + { + advancedDisk = null; + diskPack = null; + + Vds.ServiceLoader serviceLoader = new Vds.ServiceLoader(); + Vds.Service vds = serviceLoader.LoadService(ServerNameSettings); + vds.WaitForServiceReady(); + + foreach (Vds.Disk disk in vds.UnallocatedDisks) + { + if (disk.DiskAddress == diskAddress) + { + advancedDisk = (Vds.Advanced.AdvancedDisk)disk; + break; + } + } + + if (advancedDisk == null) + { + vds.HardwareProvider = false; + vds.SoftwareProvider = true; + + foreach (Vds.SoftwareProvider provider in vds.Providers) + foreach (Vds.Pack pack in provider.Packs) + foreach (Vds.Disk disk in pack.Disks) + if (disk.DiskAddress == diskAddress) + { + diskPack = pack; + advancedDisk = (Vds.Advanced.AdvancedDisk)disk; + break; + } + } + } + + public ReturnCode UnmountVirtualHardDisk(string vhdPath) + { + ManagementObject objImgSvc = GetImageManagementService(); + + // get method params + ManagementBaseObject inParams = objImgSvc.GetMethodParameters("Unmount"); + inParams["Path"] = FileUtils.EvaluateSystemVariables(vhdPath); + + ManagementBaseObject outParams = (ManagementBaseObject)objImgSvc.InvokeMethod("Unmount", inParams, null); + return (ReturnCode)Convert.ToInt32(outParams["ReturnValue"]); + } + + public JobResult ExpandVirtualHardDisk(string vhdPath, UInt64 sizeGB) + { + const UInt64 Size1G = 0x40000000; + + ManagementObject objImgSvc = GetImageManagementService(); + + // get method params + ManagementBaseObject inParams = objImgSvc.GetMethodParameters("ExpandVirtualHardDisk"); + inParams["Path"] = FileUtils.EvaluateSystemVariables(vhdPath); + inParams["MaxInternalSize"] = sizeGB * Size1G; + + ManagementBaseObject outParams = (ManagementBaseObject)objImgSvc.InvokeMethod("ExpandVirtualHardDisk", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult ConvertVirtualHardDisk(string sourcePath, string destinationPath, VirtualHardDiskType diskType) + { + sourcePath = FileUtils.EvaluateSystemVariables(sourcePath); + destinationPath = FileUtils.EvaluateSystemVariables(destinationPath); + + // check source file + if (!FileExists(sourcePath)) + throw new Exception("Source VHD cannot be found: " + sourcePath); + + // check destination folder + string destFolder = Path.GetDirectoryName(destinationPath); + if (!DirectoryExists(destFolder)) + CreateFolder(destFolder); + + ManagementObject objImgSvc = GetImageManagementService(); + + // get method params + ManagementBaseObject inParams = objImgSvc.GetMethodParameters("ConvertVirtualHardDisk"); + inParams["SourcePath"] = sourcePath; + inParams["DestinationPath"] = destinationPath; + inParams["Type"] = (UInt16)diskType; + + ManagementBaseObject outParams = (ManagementBaseObject)objImgSvc.InvokeMethod("ConvertVirtualHardDisk", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public void DeleteRemoteFile(string path) + { + if (DirectoryExists(path)) + DeleteFolder(path); // WMI way + else if (FileExists(path)) + DeleteFile(path); // WMI way + } + + public void ExpandDiskVolume(string diskAddress, string volumeName) + { + // find mounted disk using VDS + Vds.Advanced.AdvancedDisk advancedDisk = null; + Vds.Pack diskPack = null; + + FindVdsDisk(diskAddress, out advancedDisk, out diskPack); + + if (advancedDisk == null) + throw new Exception("Could not find mounted disk"); + + // find volume + Vds.Volume diskVolume = null; + foreach (Vds.Volume volume in diskPack.Volumes) + { + if (volume.DriveLetter.ToString() == volumeName) + { + diskVolume = volume; + break; + } + } + + if (diskVolume == null) + throw new Exception("Could not find disk volume: " + volumeName); + + // determine maximum available space + ulong oneMegabyte = 1048576; + ulong freeSpace = 0; + foreach (Vds.DiskExtent extent in advancedDisk.Extents) + { + if (extent.Type != Microsoft.Storage.Vds.DiskExtentType.Free) + continue; + + if (extent.Size > oneMegabyte) + freeSpace += extent.Size; + } + + if (freeSpace == 0) + return; + + // input disk + Vds.InputDisk inputDisk = new Vds.InputDisk(); + foreach (Vds.VolumePlex plex in diskVolume.Plexes) + { + inputDisk.DiskId = advancedDisk.Id; + inputDisk.Size = freeSpace; + inputDisk.PlexId = plex.Id; + + foreach (Vds.DiskExtent extent in plex.Extents) + inputDisk.MemberIndex = extent.MemberIndex; + + break; + } + + // extend volume + Vds.Async extendEvent = diskVolume.BeginExtend(new Vds.InputDisk[] { inputDisk }, null, null); + while (!extendEvent.IsCompleted) + System.Threading.Thread.Sleep(100); + diskVolume.EndExtend(extendEvent); + } + + // obsolete and currently is not used + private string RunDiskPart(string script) + { + // create temp script file name + string localPath = Path.Combine(GetTempRemoteFolder(), Guid.NewGuid().ToString("N")); + + // save script to remote temp file + string remotePath = ConvertToUNC(localPath); + File.AppendAllText(remotePath, script); + + // run diskpart + ExecuteRemoteProcess("DiskPart /s " + localPath); + + // delete temp script + try + { + File.Delete(remotePath); + } + catch + { + // TODO + } + + return ""; + } + + public string ReadRemoteFile(string path) + { + // temp file name on "system" drive available through hidden share + string tempPath = Path.Combine(GetTempRemoteFolder(), Guid.NewGuid().ToString("N")); + + HostedSolutionLog.LogInfo("Read remote file: " + path); + HostedSolutionLog.LogInfo("Local file temp path: " + tempPath); + + // copy remote file to temp file (WMI) + if (!CopyFile(path, tempPath)) + return null; + + // read content of temp file + string remoteTempPath = ConvertToUNC(tempPath); + HostedSolutionLog.LogInfo("Remote file temp path: " + remoteTempPath); + + string content = File.ReadAllText(remoteTempPath); + + // delete temp file (WMI) + DeleteFile(tempPath); + + return content; + } + + public void WriteRemoteFile(string path, string content) + { + // temp file name on "system" drive available through hidden share + string tempPath = Path.Combine(GetTempRemoteFolder(), Guid.NewGuid().ToString("N")); + + // write to temp file + string remoteTempPath = ConvertToUNC(tempPath); + File.WriteAllText(remoteTempPath, content); + + // delete file (WMI) + if (FileExists(path)) + DeleteFile(path); + + // copy (WMI) + CopyFile(tempPath, path); + + // delete temp file (WMI) + DeleteFile(tempPath); + } + #endregion + + #region Jobs + public ConcreteJob GetJob(string jobId) + { + HostedSolutionLog.LogStart("GetJob"); + HostedSolutionLog.DebugInfo("jobId: {0}", jobId); + + Runspace runSpace = null; + ConcreteJob job; + + try + { + runSpace = OpenRunspace(); + Command cmd = new Command("Get-Job"); + + if (!string.IsNullOrEmpty(jobId)) cmd.Parameters.Add("Id", jobId); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + job = CreateJobFromPSObject(result); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetJob", ex); + throw; + } + finally + { + CloseRunspace(runSpace); + } + + HostedSolutionLog.LogEnd("GetJob"); + return job; + } + + public List GetAllJobs() + { + List jobs = new List(); + + ManagementObjectCollection objJobs = wmi.GetWmiObjects("CIM_ConcreteJob"); + foreach (ManagementObject objJob in objJobs) + jobs.Add(CreateJobFromWmiObject(objJob)); + + return jobs; + } + + public ChangeJobStateReturnCode ChangeJobState(string jobId, ConcreteJobRequestedState newState) + { + ManagementObject objJob = GetJobWmiObject(jobId); + + // get method + ManagementBaseObject inParams = objJob.GetMethodParameters("RequestStateChange"); + inParams["RequestedState"] = (Int32)newState; + + // invoke method + ManagementBaseObject outParams = objJob.InvokeMethod("RequestStateChange", inParams, null); + return (ChangeJobStateReturnCode)Convert.ToInt32(outParams["ReturnValue"]); + } + + #endregion + + #region Configuration + public int GetProcessorCoresNumber() + { + Wmi w = new Wmi(ServerNameSettings, @"root\cimv2"); + ManagementObject objCpu = w.GetWmiObject("win32_Processor"); + return Convert.ToInt32(objCpu["NumberOfCores"]); + } + #endregion + + #region IHostingServiceProvier methods + public override string[] Install() + { + List messages = new List(); + + // TODO + + return messages.ToArray(); + } + + public override bool IsInstalled() + { + // check if Hyper-V role is installed and available for management + //Wmi root = new Wmi(ServerNameSettings, "root"); + //ManagementObject objNamespace = root.GetWmiObject("__NAMESPACE", "name = 'virtualization'"); + //return (objNamespace != null); + return true; + } + + public override void ChangeServiceItemsState(ServiceProviderItem[] items, bool enabled) + { + foreach (ServiceProviderItem item in items) + { + if (item is VirtualMachine) + { + // start/stop virtual machine + VirtualMachine vm = item as VirtualMachine; + ChangeVirtualMachineServiceItemState(vm, enabled); + } + } + } + + public override void DeleteServiceItems(ServiceProviderItem[] items) + { + foreach (ServiceProviderItem item in items) + { + if (item is VirtualMachine) + { + // delete virtual machine + VirtualMachine vm = item as VirtualMachine; + DeleteVirtualMachineServiceItem(vm); + } + else if (item is VirtualSwitch) + { + // delete switch + VirtualSwitch vs = item as VirtualSwitch; + DeleteVirtualSwitchServiceItem(vs); + } + } + } + + private void ChangeVirtualMachineServiceItemState(VirtualMachine vm, bool started) + { + try + { + VirtualMachine vps = GetVirtualMachine(vm.VirtualMachineId); + JobResult result = null; + + if (vps == null) + { + HostedSolutionLog.LogWarning(String.Format("Virtual machine '{0}' object with ID '{1}' was not found. Change state operation aborted.", + vm.Name, vm.VirtualMachineId)); + return; + } + + #region Start + if (started && + (vps.State == VirtualMachineState.Off + || vps.State == VirtualMachineState.Paused + || vps.State == VirtualMachineState.Saved)) + { + VirtualMachineRequestedState state = VirtualMachineRequestedState.Start; + if (vps.State == VirtualMachineState.Paused) + state = VirtualMachineRequestedState.Resume; + + result = ChangeVirtualMachineState(vm.VirtualMachineId, state); + + // check result + if (result.ReturnValue != ReturnCode.JobStarted) + { + HostedSolutionLog.LogWarning(String.Format("Cannot {0} '{1}' virtual machine: {2}", + state, vm.Name, result.ReturnValue)); + return; + } + + // wait for completion + if (!JobCompleted(result.Job)) + { + HostedSolutionLog.LogWarning(String.Format("Cannot complete {0} '{1}' of virtual machine: {1}", + state, vm.Name, result.Job.ErrorDescription)); + return; + } + } + #endregion + + #region Stop + else if (!started && + (vps.State == VirtualMachineState.Running + || vps.State == VirtualMachineState.Paused)) + { + if (vps.State == VirtualMachineState.Running) + { + // try to shutdown the system + ReturnCode code = ShutDownVirtualMachine(vm.VirtualMachineId, true, "Virtual Machine has been suspended from WebsitePanel"); + if (code == ReturnCode.OK) + return; + } + + // turn off + VirtualMachineRequestedState state = VirtualMachineRequestedState.TurnOff; + result = ChangeVirtualMachineState(vm.VirtualMachineId, state); + + // check result + if (result.ReturnValue != ReturnCode.JobStarted) + { + HostedSolutionLog.LogWarning(String.Format("Cannot {0} '{1}' virtual machine: {2}", + state, vm.Name, result.ReturnValue)); + return; + } + + // wait for completion + if (!JobCompleted(result.Job)) + { + HostedSolutionLog.LogWarning(String.Format("Cannot complete {0} '{1}' of virtual machine: {1}", + state, vm.Name, result.Job.ErrorDescription)); + return; + } + } + #endregion + } + catch (Exception ex) + { + HostedSolutionLog.LogError(String.Format("Error {0} Virtual Machine '{1}'", + started ? "starting" : "turning off", + vm.Name), ex); + } + } + + private void DeleteVirtualMachineServiceItem(VirtualMachine vm) + { + try + { + JobResult result = null; + VirtualMachine vps = GetVirtualMachine(vm.VirtualMachineId); + + if (vps == null) + { + HostedSolutionLog.LogWarning(String.Format("Virtual machine '{0}' object with ID '{1}' was not found. Delete operation aborted.", + vm.Name, vm.VirtualMachineId)); + return; + } + + #region Turn off (if required) + if (vps.State != VirtualMachineState.Off) + { + result = ChangeVirtualMachineState(vm.VirtualMachineId, VirtualMachineRequestedState.TurnOff); + // check result + if (result.ReturnValue != ReturnCode.JobStarted) + { + HostedSolutionLog.LogWarning(String.Format("Cannot Turn off '{0}' virtual machine before deletion: {1}", + vm.Name, result.ReturnValue)); + return; + } + + // wait for completion + if (!JobCompleted(result.Job)) + { + HostedSolutionLog.LogWarning(String.Format("Cannot complete Turn off '{0}' of virtual machine before deletion: {1}", + vm.Name, result.Job.ErrorDescription)); + return; + } + } + #endregion + + #region Delete virtual machine + result = DeleteVirtualMachine(vm.VirtualMachineId); + + // check result + if (result.ReturnValue != ReturnCode.JobStarted) + { + HostedSolutionLog.LogWarning(String.Format("Cannot delete '{0}' virtual machine: {1}", + vm.Name, result.ReturnValue)); + return; + } + + // wait for completion + if (!JobCompleted(result.Job)) + { + HostedSolutionLog.LogWarning(String.Format("Cannot complete deletion of '{0}' virtual machine: {1}", + vm.Name, result.Job.ErrorDescription)); + return; + } + #endregion + + #region Delete virtual machine + try + { + DeleteFile(vm.RootFolderPath); + } + catch (Exception ex) + { + HostedSolutionLog.LogError(String.Format("Cannot delete virtual machine folder '{0}'", + vm.RootFolderPath), ex); + } + #endregion + + } + catch (Exception ex) + { + HostedSolutionLog.LogError(String.Format("Error deleting Virtual Machine '{0}'", vm.Name), ex); + } + } + + private void DeleteVirtualSwitchServiceItem(VirtualSwitch vs) + { + try + { + // delete virtual switch + DeleteSwitch(vs.SwitchId); + } + catch (Exception ex) + { + HostedSolutionLog.LogError(String.Format("Error deleting Virtual Switch '{0}'", vs.Name), ex); + } + } + #endregion + + #region Private Methods + protected JobResult CreateSuccessJobResult() + { + JobResult result = new JobResult(); + + result.Job = new ConcreteJob(){JobState = ConcreteJobState.Completed}; + result.ReturnValue = ReturnCode.OK; + + return result; + } + protected JobResult CreateJobResultFromPSResults(Collection objJob) + { + if (objJob == null || objJob.Count == 0) + return null; + + JobResult result = new JobResult(); + + result.Job = CreateJobFromPSObject(objJob); + + result.ReturnValue = ReturnCode.JobStarted; + switch (result.Job.JobState) + { + case ConcreteJobState.Failed: + result.ReturnValue = ReturnCode.Failed; + break; + } + + return result; + } + + protected JobResult CreateJobResultFromWmiMethodResults(ManagementBaseObject outParams) + { + JobResult result = new JobResult(); + + // return value + result.ReturnValue = (ReturnCode)Convert.ToInt32(outParams["ReturnValue"]); + + // try getting job details job + try + { + ManagementBaseObject objJob = wmi.GetWmiObjectByPath((string)outParams["Job"]); + if (objJob != null && objJob.Properties.Count > 0) + { + result.Job = CreateJobFromWmiObject(objJob); + } + } + catch { /* dumb */ } + + return result; + } + + private ManagementObject GetJobWmiObject(string id) + { + return wmi.GetWmiObject("msvm_ConcreteJob", "InstanceID = '{0}'", id); + } + + private ManagementObject GetVirtualSystemManagementService() + { + return wmi.GetWmiObject("msvm_VirtualSystemManagementService"); + } + + private ManagementObject GetVirtualSwitchManagementService() + { + return wmi.GetWmiObject("msvm_VirtualSwitchManagementService"); + } + + protected ManagementObject GetImageManagementService() + { + return wmi.GetWmiObject("msvm_ImageManagementService"); + } + + private ManagementObject GetVirtualMachineObject(string vmId) + { + return wmi.GetWmiObject("msvm_ComputerSystem", "Name = '{0}'", vmId); + } + + private ManagementObject GetSnapshotObject(string snapshotId) + { + return wmi.GetWmiObject("Msvm_VirtualSystemSettingData", "InstanceID = '{0}'", snapshotId); + } + + + private VirtualMachineSnapshot CreateSnapshotFromWmiObject(ManagementBaseObject objSnapshot) + { + if (objSnapshot == null || objSnapshot.Properties.Count == 0) + return null; + + VirtualMachineSnapshot snapshot = new VirtualMachineSnapshot(); + snapshot.Id = (string)objSnapshot["InstanceID"]; + snapshot.Name = (string)objSnapshot["ElementName"]; + + string parentId = (string)objSnapshot["Parent"]; + if (!String.IsNullOrEmpty(parentId)) + { + int idx = parentId.IndexOf("Microsoft:"); + snapshot.ParentId = parentId.Substring(idx, parentId.Length - idx - 1); + } + snapshot.Created = wmi.ToDateTime((string)objSnapshot["CreationTime"]); + + return snapshot; + } + + private VirtualSwitch CreateSwitchFromWmiObject(ManagementObject objSwitch) + { + if (objSwitch == null || objSwitch.Properties.Count == 0) + return null; + + VirtualSwitch sw = new VirtualSwitch(); + sw.SwitchId = (string)objSwitch["Name"]; + sw.Name = (string)objSwitch["ElementName"]; + return sw; + } + + private ConcreteJob CreateJobFromPSObject(Collection objJob) + { + if (objJob == null || objJob.Count == 0) + return null; + + ConcreteJob job = new ConcreteJob(); + job.Id = GetPSObjectProperty(objJob[0], "Id").ToString(); + job.JobState = GetPSObjectPropertyEnum(objJob[0], "JobStateInfo"); + job.Caption = GetPSObjectProperty(objJob[0], "Name"); + job.Description = GetPSObjectProperty(objJob[0], "Command"); + job.StartTime = GetPSObjectProperty(objJob[0], "PSBeginTime"); + job.ElapsedTime = GetPSObjectProperty(objJob[0], "PSEndTime") ?? DateTime.Now; + + // PercentComplete + job.PercentComplete = 0; + var progress = (PSDataCollection)GetPSObjectProperty(objJob[0], "Progress"); + if (progress != null && progress.Count > 0) + job.PercentComplete = progress[0].PercentComplete; + + // Errors + var errors = (PSDataCollection)GetPSObjectProperty(objJob[0], "Error"); + if (errors != null && errors.Count > 0) + { + job.ErrorDescription = errors[0].ErrorDetails.Message + ". " + errors[0].ErrorDetails.RecommendedAction; + job.ErrorCode = errors[0].Exception != null ? -1 : 0; + } + + return job; + } + + private ConcreteJob CreateJobFromWmiObject(ManagementBaseObject objJob) + { + if (objJob == null || objJob.Properties.Count == 0) + return null; + + ConcreteJob job = new ConcreteJob(); + job.Id = (string)objJob["InstanceID"]; + job.JobState = (ConcreteJobState)Convert.ToInt32(objJob["JobState"]); + job.Caption = (string)objJob["Caption"]; + job.Description = (string)objJob["Description"]; + job.StartTime = wmi.ToDateTime((string)objJob["StartTime"]); + // TODO proper parsing of WMI time spans, e.g. 00000000000001.325247:000 + job.ElapsedTime = DateTime.Now; //wmi.ToDateTime((string)objJob["ElapsedTime"]); + job.ErrorCode = Convert.ToInt32(objJob["ErrorCode"]); + job.ErrorDescription = (string)objJob["ErrorDescription"]; + job.PercentComplete = Convert.ToInt32(objJob["PercentComplete"]); + return job; + } + + private ManagementBaseObject GetSnapshotSummaryInformation( + string snapshotId, + SummaryInformationRequest requestedInformation) + { + // find VM settings object + ManagementObject objVmSetting = GetSnapshotObject(snapshotId); + + // get summary + return GetSummaryInformation(objVmSetting, requestedInformation); + } + + private ManagementBaseObject GetVirtualMachineSummaryInformation( + string vmId, + params SummaryInformationRequest[] requestedInformation) + { + // find VM settings object + ManagementObject objVmSetting = GetVirtualMachineSettingsObject(vmId); + + // get summary + return GetSummaryInformation(objVmSetting, requestedInformation); + } + + private ManagementBaseObject GetSummaryInformation( + ManagementObject objVmSetting, params SummaryInformationRequest[] requestedInformation) + { + if (requestedInformation == null || requestedInformation.Length == 0) + throw new ArgumentNullException("requestedInformation"); + + // get management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + uint[] reqif = new uint[requestedInformation.Length]; + for (int i = 0; i < requestedInformation.Length; i++) + reqif[i] = (uint)requestedInformation[i]; + + // get method params + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("GetSummaryInformation"); + inParams["SettingData"] = new ManagementObject[] { objVmSetting }; + inParams["RequestedInformation"] = reqif; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("GetSummaryInformation", inParams, null); + return ((ManagementBaseObject[])outParams["SummaryInformation"])[0]; + } + + private ManagementObject GetVirtualMachineSettingsObject(string vmId) + { + return wmi.GetWmiObject("msvm_VirtualSystemSettingData", "InstanceID Like 'Microsoft:{0}%'", vmId); + } + + private bool JobCompleted(ConcreteJob job) + { + bool jobCompleted = true; + + while (job.JobState == ConcreteJobState.Starting || + job.JobState == ConcreteJobState.Running) + { + System.Threading.Thread.Sleep(200); + job = GetJob(job.Id); + } + + if (job.JobState != ConcreteJobState.Completed) + { + jobCompleted = false; + } + + return jobCompleted; + } + #endregion + + #region Remote File Methods + public bool FileExists(string path) + { + HostedSolutionLog.LogInfo("Check remote file exists: " + path); + + if (path.StartsWith(@"\\")) // network share + return File.Exists(path); + else + { + Wmi cimv2 = new Wmi(ServerNameSettings, WMI_CIMV2_NAMESPACE); + ManagementObject objFile = cimv2.GetWmiObject("CIM_Datafile", "Name='{0}'", path.Replace("\\", "\\\\")); + return (objFile != null); + } + } + + public bool DirectoryExists(string path) + { + if (path.StartsWith(@"\\")) // network share + return Directory.Exists(path); + else + { + Wmi cimv2 = new Wmi(ServerNameSettings, WMI_CIMV2_NAMESPACE); + ManagementObject objDir = cimv2.GetWmiObject("Win32_Directory", "Name='{0}'", path.Replace("\\", "\\\\")); + return (objDir != null); + } + } + + public bool CopyFile(string sourceFileName, string destinationFileName) + { + HostedSolutionLog.LogInfo("Copy file - source: " + sourceFileName); + HostedSolutionLog.LogInfo("Copy file - destination: " + destinationFileName); + + if (sourceFileName.StartsWith(@"\\")) // network share + { + if (!File.Exists(sourceFileName)) + return false; + + File.Copy(sourceFileName, destinationFileName); + } + else + { + if (!FileExists(sourceFileName)) + return false; + + // copy using WMI + Wmi cimv2 = new Wmi(ServerNameSettings, WMI_CIMV2_NAMESPACE); + ManagementObject objFile = cimv2.GetWmiObject("CIM_Datafile", "Name='{0}'", sourceFileName.Replace("\\", "\\\\")); + if (objFile == null) + throw new Exception("Source file does not exists: " + sourceFileName); + + objFile.InvokeMethod("Copy", new object[] { destinationFileName }); + } + return true; + } + + public void DeleteFile(string path) + { + if (path.StartsWith(@"\\")) + { + // network share + File.Delete(path); + } + else + { + // delete file using WMI + Wmi cimv2 = new Wmi(ServerNameSettings, "root\\cimv2"); + ManagementObject objFile = cimv2.GetWmiObject("CIM_Datafile", "Name='{0}'", path.Replace("\\", "\\\\")); + objFile.InvokeMethod("Delete", null); + } + } + + public void DeleteFolder(string path) + { + if (path.StartsWith(@"\\")) + { + // network share + try + { + FileUtils.DeleteFile(path); + } + catch { /* just skip */ } + FileUtils.DeleteFile(path); + } + else + { + // local folder + // delete sub folders first + ManagementObjectCollection objSubFolders = GetSubFolders(path); + foreach (ManagementObject objSubFolder in objSubFolders) + DeleteFolder(objSubFolder["Name"].ToString()); + + // delete this folder itself + Wmi cimv2 = new Wmi(ServerNameSettings, "root\\cimv2"); + ManagementObject objFolder = cimv2.GetWmiObject("Win32_Directory", "Name='{0}'", path.Replace("\\", "\\\\")); + objFolder.InvokeMethod("Delete", null); + } + } + + private ManagementObjectCollection GetSubFolders(string path) + { + if (path.EndsWith("\\")) + path = path.Substring(0, path.Length - 1); + + Wmi cimv2 = new Wmi(ServerNameSettings, "root\\cimv2"); + + return cimv2.ExecuteWmiQuery("Associators of {Win32_Directory.Name='" + + path + "'} " + + "Where AssocClass = Win32_Subdirectory " + + "ResultRole = PartComponent"); + } + + public void CreateFolder(string path) + { + ExecuteRemoteProcess(String.Format("cmd.exe /c md \"{0}\"", path)); + } + + public void ExecuteRemoteProcess(string command) + { + Wmi cimv2 = new Wmi(ServerNameSettings, "root\\cimv2"); + ManagementClass objProcess = cimv2.GetWmiClass("Win32_Process"); + + // run process + object[] methodArgs = { command, null, null, 0 }; + objProcess.InvokeMethod("Create", methodArgs); + + // process ID + int processId = Convert.ToInt32(methodArgs[3]); + + // wait until finished + // Create event query to be notified within 1 second of + // a change in a service + WqlEventQuery query = + new WqlEventQuery("__InstanceDeletionEvent", + new TimeSpan(0, 0, 1), + "TargetInstance isa \"Win32_Process\""); + + // Initialize an event watcher and subscribe to events + // that match this query + ManagementEventWatcher watcher = new ManagementEventWatcher(cimv2.GetScope(), query); + // times out watcher.WaitForNextEvent in 20 seconds + watcher.Options.Timeout = new TimeSpan(0, 0, 20); + + // Block until the next event occurs + // Note: this can be done in a loop if waiting for + // more than one occurrence + while (true) + { + ManagementBaseObject e = null; + + try + { + // wait untill next process finish + e = watcher.WaitForNextEvent(); + } + catch + { + // nothing has been finished in timeout period + return; // exit + } + + // check process id + int pid = Convert.ToInt32(((ManagementBaseObject)e["TargetInstance"])["ProcessID"]); + if (pid == processId) + { + //Cancel the subscription + watcher.Stop(); + + // exit + return; + } + } + } + + public string GetTempRemoteFolder() + { + Wmi cimv2 = new Wmi(ServerNameSettings, "root\\cimv2"); + ManagementObject objOS = cimv2.GetWmiObject("win32_OperatingSystem"); + string sysPath = (string)objOS["SystemDirectory"]; + + // remove trailing slash + if (sysPath.EndsWith("\\")) + sysPath = sysPath.Substring(0, sysPath.Length - 1); + + sysPath = sysPath.Substring(0, sysPath.LastIndexOf("\\") + 1) + "Temp"; + + return sysPath; + } + #endregion + + #region Hyper-V Cloud + public bool CheckServerState(string connString) + { + return !String.IsNullOrEmpty(connString); + } + #endregion Hyper-V Cloud + + #region PowerShell integration + private static InitialSessionState session = null; + + internal virtual Runspace OpenRunspace() + { + HostedSolutionLog.LogStart("OpenRunspace"); + + if (session == null) + { + session = InitialSessionState.CreateDefault(); + session.ImportPSModule(new string[] { "Hyper-V" }); + } + Runspace runSpace = RunspaceFactory.CreateRunspace(session); + // + runSpace.Open(); + // + runSpace.SessionStateProxy.SetVariable("ConfirmPreference", "none"); + HostedSolutionLog.LogEnd("OpenRunspace"); + return runSpace; + } + + internal void CloseRunspace(Runspace runspace) + { + try + { + if (runspace != null && runspace.RunspaceStateInfo.State == RunspaceState.Opened) + { + runspace.Close(); + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("Runspace error", ex); + } + } + + internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd) + { + return ExecuteShellCommand(runSpace, cmd, true); + } + + internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, bool useDomainController) + { + object[] errors; + return ExecuteShellCommand(runSpace, cmd, useDomainController, out errors); + } + + internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, out object[] errors) + { + return ExecuteShellCommand(runSpace, cmd, true, out errors); + } + + internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, bool useDomainController, out object[] errors) + { + HostedSolutionLog.LogStart("ExecuteShellCommand"); + List errorList = new List(); + + HostedSolutionLog.DebugCommand(cmd); + Collection results = null; + // Create a pipeline + Pipeline pipeLine = runSpace.CreatePipeline(); + using (pipeLine) + { + // Add the command + pipeLine.Commands.Add(cmd); + // Execute the pipeline and save the objects returned. + results = pipeLine.Invoke(); + + // Log out any errors in the pipeline execution + // NOTE: These errors are NOT thrown as exceptions! + // Be sure to check this to ensure that no errors + // happened while executing the command. + if (pipeLine.Error != null && pipeLine.Error.Count > 0) + { + foreach (object item in pipeLine.Error.ReadToEnd()) + { + errorList.Add(item); + string errorMessage = string.Format("Invoke error: {0}", item); + HostedSolutionLog.LogWarning(errorMessage); + } + } + } + pipeLine = null; + errors = errorList.ToArray(); + HostedSolutionLog.LogEnd("ExecuteShellCommand"); + return results; + } + + internal object GetPSObjectProperty(PSObject obj, string name) + { + return obj.Members[name].Value; + } + internal T GetPSObjectProperty(PSObject obj, string name) + { + return (T)obj.Members[name].Value; + } + internal T GetPSObjectPropertyEnum(PSObject obj, string name) where T : struct + { + return (T) Enum.Parse(typeof (T), GetPSObjectProperty(obj, name).ToString()); + } + + /// + /// Returns the identity of the object from the shell execution result + /// + /// + /// + internal string GetResultObjectIdentity(Collection result) + { + HostedSolutionLog.LogStart("GetResultObjectIdentity"); + if (result == null) + throw new ArgumentNullException("result", "Execution result is not specified"); + + if (result.Count < 1) + throw new ArgumentException("Execution result is empty", "result"); + + if (result.Count > 1) + throw new ArgumentException("Execution result contains more than one object", "result"); + + PSMemberInfo info = result[0].Members["Identity"]; + if (info == null) + throw new ArgumentException("Execution result does not contain Identity property", "result"); + + string ret = info.Value.ToString(); + HostedSolutionLog.LogEnd("GetResultObjectIdentity"); + return ret; + } + + internal string GetResultObjectDN(Collection result) + { + HostedSolutionLog.LogStart("GetResultObjectDN"); + if (result == null) + throw new ArgumentNullException("result", "Execution result is not specified"); + + if (result.Count < 1) + throw new ArgumentException("Execution result does not contain any object"); + + if (result.Count > 1) + throw new ArgumentException("Execution result contains more than one object"); + + PSMemberInfo info = result[0].Members["DistinguishedName"]; + if (info == null) + throw new ArgumentException("Execution result does not contain DistinguishedName property", "result"); + + string ret = info.Value.ToString(); + HostedSolutionLog.LogEnd("GetResultObjectDN"); + return ret; + } + #endregion + + + internal int ConvertNullableToInt32(object value) + { + return value == null ? 0 : Convert.ToInt32(value); + } + + internal long ConvertNullableToInt64(object value) + { + return value == null ? 0 : Convert.ToInt64(value); + } + + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Properties/AssemblyInfo.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..cb7f4edb --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Properties/AssemblyInfo.cs @@ -0,0 +1,21 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WebsitePanel.Providers.Virtualization.HyperV2012R2")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("WebsitePanel.Providers.Virtualization.HyperV2012R2")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("959c2614-2d69-4e4f-9e77-bd868e5afd4b")] diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj new file mode 100644 index 00000000..31e9533c --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj @@ -0,0 +1,82 @@ + + + + + Debug + AnyCPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC} + Library + Properties + WebsitePanel.Providers.Virtualization.HyperV2012R2 + WebsitePanel.Providers.Virtualization.HyperV2012R2 + v4.5 + 512 + + + + true + full + false + ..\WebsitePanel.Server\bin\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + ..\..\Lib\References\Microsoft\Microsoft.Storage.Vds.dll + + + + + + + + + + False + ..\..\Lib\References\Microsoft\Windows2012\System.Management.Automation.dll + + + + + + VersionInfo.cs + + + + + + + + {684c932a-6c75-46ac-a327-f3689d89eb42} + WebsitePanel.Providers.Base + + + {a06de5e4-4331-47e1-8f46-7b846146b559} + WebsitePanel.Providers.HostedSolution + + + {e91e52f3-9555-4d00-b577-2b1dbdd87ca7} + WebsitePanel.Server.Utils + + + + + \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Wmi.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Wmi.cs new file mode 100644 index 00000000..8ab33885 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Wmi.cs @@ -0,0 +1,295 @@ +// Copyright (c) 2014, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.Collections.Generic; +using System.Text; +using System.Management; +using System.Diagnostics; + +namespace WebsitePanel.Providers.Virtualization +{ + internal class Wmi + { + string nameSpace = null; + string computerName = null; + ManagementScope scope = null; + + public Wmi(string nameSpace) : this(nameSpace, null) + { + } + + public Wmi(string computerName, string nameSpace) + { + this.nameSpace = nameSpace; + this.computerName = computerName; + } + + internal ManagementObjectCollection ExecuteWmiQuery(string query, params object[] args) + { + if (args != null && args.Length > 0) + query = String.Format(query, args); + + ManagementObjectSearcher searcher = new ManagementObjectSearcher(GetScope(), + new ObjectQuery(query)); + return searcher.Get(); + } + + internal ManagementObject GetWmiObject(string className, string filter, params object[] args) + { + ManagementObjectCollection col = GetWmiObjects(className, filter, args); + ManagementObjectCollection.ManagementObjectEnumerator enumerator = col.GetEnumerator(); + return enumerator.MoveNext() ? (ManagementObject)enumerator.Current : null; + } + + internal ManagementObject GetWmiObject(string className) + { + return GetWmiObject(className, null); + } + + internal ManagementObjectCollection GetWmiObjects(string className, string filter, params object[] args) + { + string query = "select * from " + className; + if (!String.IsNullOrEmpty(filter)) + query += " where " + filter; + return ExecuteWmiQuery(query, args); + } + + internal ManagementObjectCollection GetWmiObjects(string className) + { + return GetWmiObjects(className, null); + } + + internal ManagementObject GetWmiObjectByPath(string path) + { + return new ManagementObject(GetScope(), new ManagementPath(path), null); + } + + internal ManagementClass GetWmiClass(string className) + { + return new ManagementClass(GetScope(), new ManagementPath(className), null); + } + + internal ManagementObject GetRelatedWmiObject(ManagementObject obj, string className) + { + ManagementObjectCollection col = obj.GetRelated(className); + ManagementObjectCollection.ManagementObjectEnumerator enumerator = col.GetEnumerator(); + enumerator.MoveNext(); + return (ManagementObject)enumerator.Current; + } + + internal void Dump(ManagementBaseObject obj) + { +#if DEBUG + foreach (PropertyData prop in obj.Properties) + { + string typeName = prop.Value == null ? "null" : prop.Value.GetType().ToString(); + Debug.WriteLine(prop.Name + ": " + prop.Value + " (" + typeName + ")"); + } +#endif + } + + // Converts a given datetime in DMTF format to System.DateTime object. + internal System.DateTime ToDateTime(string dmtfDate) + { + System.DateTime initializer = System.DateTime.MinValue; + int year = initializer.Year; + int month = initializer.Month; + int day = initializer.Day; + int hour = initializer.Hour; + int minute = initializer.Minute; + int second = initializer.Second; + long ticks = 0; + string dmtf = dmtfDate; + System.DateTime datetime = System.DateTime.MinValue; + string tempString = string.Empty; + if (String.IsNullOrEmpty(dmtf)) + { + return DateTime.MinValue; + } + else if ((dmtf.Length != 25)) + { + throw new System.ArgumentOutOfRangeException(); + } + try + { + tempString = dmtf.Substring(0, 4); + if (("****" != tempString)) + { + year = int.Parse(tempString); + } + tempString = dmtf.Substring(4, 2); + if (("**" != tempString)) + { + month = int.Parse(tempString); + } + tempString = dmtf.Substring(6, 2); + if (("**" != tempString)) + { + day = int.Parse(tempString); + } + tempString = dmtf.Substring(8, 2); + if (("**" != tempString)) + { + hour = int.Parse(tempString); + } + tempString = dmtf.Substring(10, 2); + if (("**" != tempString)) + { + minute = int.Parse(tempString); + } + tempString = dmtf.Substring(12, 2); + if (("**" != tempString)) + { + second = int.Parse(tempString); + } + tempString = dmtf.Substring(15, 6); + if (("******" != tempString)) + { + ticks = (long.Parse(tempString) * ((long)((System.TimeSpan.TicksPerMillisecond / 1000)))); + } + if (((((((((year < 0) + || (month < 0)) + || (day < 0)) + || (hour < 0)) + || (minute < 0)) + || (minute < 0)) + || (second < 0)) + || (ticks < 0))) + { + throw new System.ArgumentOutOfRangeException(); + } + } + catch (System.Exception e) + { + throw new System.ArgumentOutOfRangeException(null, e.Message); + } + + if (year == 0 + && month == 0 + && day == 0 + && hour == 0 + && minute == 0 + && second == 0 + && ticks == 0) + return DateTime.MinValue; + + datetime = new System.DateTime(year, month, day, hour, minute, second, 0); + datetime = datetime.AddTicks(ticks); + System.TimeSpan tickOffset = System.TimeZone.CurrentTimeZone.GetUtcOffset(datetime); + int UTCOffset = 0; + int OffsetToBeAdjusted = 0; + long OffsetMins = ((long)((tickOffset.Ticks / System.TimeSpan.TicksPerMinute))); + tempString = dmtf.Substring(22, 3); + if ((tempString != "******")) + { + tempString = dmtf.Substring(21, 4); + try + { + UTCOffset = int.Parse(tempString); + } + catch (System.Exception e) + { + throw new System.ArgumentOutOfRangeException(null, e.Message); + } + OffsetToBeAdjusted = ((int)((OffsetMins - UTCOffset))); + datetime = datetime.AddMinutes(((double)(OffsetToBeAdjusted))); + } + return datetime; + } + + // Converts a given System.DateTime object to DMTF datetime format. + internal string ToDmtfDateTime(System.DateTime date) + { + string utcString = string.Empty; + System.TimeSpan tickOffset = System.TimeZone.CurrentTimeZone.GetUtcOffset(date); + long OffsetMins = ((long)((tickOffset.Ticks / System.TimeSpan.TicksPerMinute))); + if ((System.Math.Abs(OffsetMins) > 999)) + { + date = date.ToUniversalTime(); + utcString = "+000"; + } + else + { + if ((tickOffset.Ticks >= 0)) + { + utcString = string.Concat("+", ((System.Int64)((tickOffset.Ticks / System.TimeSpan.TicksPerMinute))).ToString().PadLeft(3, '0')); + } + else + { + string strTemp = ((System.Int64)(OffsetMins)).ToString(); + utcString = string.Concat("-", strTemp.Substring(1, (strTemp.Length - 1)).PadLeft(3, '0')); + } + } + string dmtfDateTime = ((System.Int32)(date.Year)).ToString().PadLeft(4, '0'); + dmtfDateTime = string.Concat(dmtfDateTime, ((System.Int32)(date.Month)).ToString().PadLeft(2, '0')); + dmtfDateTime = string.Concat(dmtfDateTime, ((System.Int32)(date.Day)).ToString().PadLeft(2, '0')); + dmtfDateTime = string.Concat(dmtfDateTime, ((System.Int32)(date.Hour)).ToString().PadLeft(2, '0')); + dmtfDateTime = string.Concat(dmtfDateTime, ((System.Int32)(date.Minute)).ToString().PadLeft(2, '0')); + dmtfDateTime = string.Concat(dmtfDateTime, ((System.Int32)(date.Second)).ToString().PadLeft(2, '0')); + dmtfDateTime = string.Concat(dmtfDateTime, "."); + System.DateTime dtTemp = new System.DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, 0); + long microsec = ((long)((((date.Ticks - dtTemp.Ticks) + * 1000) + / System.TimeSpan.TicksPerMillisecond))); + string strMicrosec = ((System.Int64)(microsec)).ToString(); + if ((strMicrosec.Length > 6)) + { + strMicrosec = strMicrosec.Substring(0, 6); + } + dmtfDateTime = string.Concat(dmtfDateTime, strMicrosec.PadLeft(6, '0')); + dmtfDateTime = string.Concat(dmtfDateTime, utcString); + return dmtfDateTime; + } + + public ManagementScope GetScope() + { + if (scope != null) + return scope; + + // create new scope + if (String.IsNullOrEmpty(computerName)) + { + // local + scope = new ManagementScope(nameSpace); + } + else + { + // remote + ConnectionOptions options = new ConnectionOptions(); + + string path = String.Format(@"\\{0}\{1}", computerName, nameSpace); + scope = new ManagementScope(path, options); + } + + // connect + scope.Connect(); + return scope; + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV/HyperV.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV/HyperV.cs index d035f0d9..2658df64 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV/HyperV.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV/HyperV.cs @@ -488,7 +488,7 @@ namespace WebsitePanel.Providers.Virtualization return vm; } - private void UpdateVirtualMachineGeneralSettings(string vmId, ManagementObject objVM, int cpuCores, int ramMB, bool bootFromCD, bool numLockEnabled) + private void UpdateVirtualMachineGeneralSettings(string vmId, ManagementObject objVM, int cpuCores, long ramMB, bool bootFromCD, bool numLockEnabled) { // request management service ManagementObject objVmsvc = GetVirtualSystemManagementService(); @@ -1989,10 +1989,10 @@ exit", Convert.ToInt32(objDisk["Index"]))); #region Stop else if (!started && - (vps.State == VirtualMachineState.Started + (vps.State == VirtualMachineState.Running || vps.State == VirtualMachineState.Paused)) { - if (vps.State == VirtualMachineState.Started) + if (vps.State == VirtualMachineState.Running) { // try to shutdown the system ReturnCode code = ShutDownVirtualMachine(vm.VirtualMachineId, true, "Virtual Machine has been suspended from WebsitePanel"); diff --git a/WebsitePanel/Sources/WebsitePanel.Server.sln b/WebsitePanel/Sources/WebsitePanel.Server.sln index 8f1323d3..2b573470 100644 --- a/WebsitePanel/Sources/WebsitePanel.Server.sln +++ b/WebsitePanel/Sources/WebsitePanel.Server.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.30723.0 +VisualStudioVersion = 12.0.21005.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Caching Application Block", "Caching Application Block", "{C8E6F2E4-A5B8-486A-A56E-92D864524682}" ProjectSection(SolutionItems) = preProject @@ -154,6 +154,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebsitePanel.Providers.Host EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebsitePanel.Providers.Mail.IceWarp", "WebsitePanel.Providers.Mail.IceWarp\WebsitePanel.Providers.Mail.IceWarp.csproj", "{95EA2D6E-278C-4A74-97DB-946362C4DEEA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebsitePanel.Providers.Virtualization.HyperV2012R2", "WebsitePanel.Providers.Virtualization.HyperV-2012R2\WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj", "{EE40516B-93DF-4CAB-80C4-64DCE0B751CC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -774,6 +776,16 @@ Global {95EA2D6E-278C-4A74-97DB-946362C4DEEA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {95EA2D6E-278C-4A74-97DB-946362C4DEEA}.Release|Mixed Platforms.Build.0 = Release|Any CPU {95EA2D6E-278C-4A74-97DB-946362C4DEEA}.Release|x86.ActiveCfg = Release|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Debug|x86.ActiveCfg = Debug|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Release|Any CPU.Build.0 = Release|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From d9d41827f69589625a23211e3a32c1db3139e54b Mon Sep 17 00:00:00 2001 From: me Date: Wed, 4 Mar 2015 20:04:18 +0400 Subject: [PATCH 02/12] =?UTF-8?q?wsp-10323=20=D0=A1onvert=20the=20VSP=20pr?= =?UTF-8?q?ovider=20into=20one=20utilizing=20PowerShell=20-=20Step=201.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebsitePanel/Database/update_db.sql | 9 +- WebsitePanel/Sources/.nuget/NuGet.targets | 4 +- .../VirtualizationServerController.cs | 42 +- .../Virtualization/BiosInfo.cs | 40 + .../Virtualization/ConcreteJobState.cs | 5 +- .../Virtualization/ControllerType.cs | 40 + .../Virtualization/MemoryInfo.cs | 44 + .../Virtualization/OperationalStatus.cs | 5 +- .../Virtualization/VirtualHardDiskFormat.cs | 40 + .../Virtualization/VirtualHardDiskInfo.cs | 10 + .../Virtualization/VirtualMachine.cs | 17 +- .../Virtualization/VirtualMachineState.cs | 31 +- .../Virtualization/VirtualSwitch.cs | 1 + .../WebsitePanel.Providers.Base.csproj | 4 + .../HyperV2012R2.cs | 2976 +++++++++++++++++ .../Properties/AssemblyInfo.cs | 21 + ...oviders.Virtualization.HyperV2012R2.csproj | 82 + .../Wmi.cs | 295 ++ .../HyperV.cs | 6 +- WebsitePanel/Sources/WebsitePanel.Server.sln | 14 +- .../WebsitePanel/UserControls/Gauge.ascx.cs | 10 +- .../UserControls/QuotaViewer.ascx.cs | 2 +- .../VpsDetailsGeneral.ascx.resx | 6 + .../VPS/VpsDetailsGeneral.ascx.cs | 12 +- 24 files changed, 3677 insertions(+), 39 deletions(-) create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/BiosInfo.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ControllerType.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/MemoryInfo.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Properties/AssemblyInfo.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Wmi.cs diff --git a/WebsitePanel/Database/update_db.sql b/WebsitePanel/Database/update_db.sql index 27d8a6f9..8ae258b7 100644 --- a/WebsitePanel/Database/update_db.sql +++ b/WebsitePanel/Database/update_db.sql @@ -1,4 +1,4 @@ -USE [${install.database}] +USE [${install.database}] GO -- update database version DECLARE @build_version nvarchar(10), @build_date datetime @@ -8745,3 +8745,10 @@ AND SI.ItemName = @ItemName AND ((@GroupName IS NULL) OR (@GroupName IS NOT NULL AND RG.GroupName = @GroupName)) RETURN GO + +-- Hyper-V 2012 R2 +IF NOT EXISTS (SELECT * FROM [dbo].[Providers] WHERE [ProviderName] = 'HyperV2012R2') +BEGIN +INSERT [dbo].[Providers] ([ProviderID], [GroupID], [ProviderName], [DisplayName], [ProviderType], [EditorControl], [DisableAutoDiscovery]) VALUES (350, 30, N'HyperV2012R2', N'Microsoft Hyper-V 2012 R2', N'WebsitePanel.Providers.Virtualization.HyperV2012R2, WebsitePanel.Providers.Virtualization.HyperV2012R2', N'HyperV', 1) +END +GO \ No newline at end of file diff --git a/WebsitePanel/Sources/.nuget/NuGet.targets b/WebsitePanel/Sources/.nuget/NuGet.targets index 428c8dad..b2066df9 100644 --- a/WebsitePanel/Sources/.nuget/NuGet.targets +++ b/WebsitePanel/Sources/.nuget/NuGet.targets @@ -97,9 +97,9 @@ - + Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />--> diff --git a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Code/Virtualization/VirtualizationServerController.cs b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Code/Virtualization/VirtualizationServerController.cs index 75ea31ef..fb69ef50 100644 --- a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Code/Virtualization/VirtualizationServerController.cs +++ b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Code/Virtualization/VirtualizationServerController.cs @@ -1074,10 +1074,14 @@ namespace WebsitePanel.EnterpriseServer } } - private static void CheckNumericQuota(PackageContext cntx, List errors, string quotaName, int currentVal, int val, string messageKey) + private static void CheckNumericQuota(PackageContext cntx, List errors, string quotaName, long currentVal, long val, string messageKey) { CheckQuotaValue(cntx, errors, quotaName, currentVal, val, messageKey); } + private static void CheckNumericQuota(PackageContext cntx, List errors, string quotaName, int currentVal, int val, string messageKey) + { + CheckQuotaValue(cntx, errors, quotaName, Convert.ToInt64(currentVal), Convert.ToInt64(val), messageKey); + } private static void CheckNumericQuota(PackageContext cntx, List errors, string quotaName, int val, string messageKey) { @@ -1094,7 +1098,7 @@ namespace WebsitePanel.EnterpriseServer CheckQuotaValue(cntx, errors, quotaName, 0, -1, messageKey); } - private static void CheckQuotaValue(PackageContext cntx, List errors, string quotaName, int currentVal, int val, string messageKey) + private static void CheckQuotaValue(PackageContext cntx, List errors, string quotaName, long currentVal, long val, string messageKey) { if (!cntx.Quotas.ContainsKey(quotaName)) return; @@ -1111,7 +1115,7 @@ namespace WebsitePanel.EnterpriseServer errors.Add(messageKey); else if (quota.QuotaTypeId == 2) { - int maxValue = quota.QuotaAllocatedValue - quota.QuotaUsedValue + currentVal; + long maxValue = quota.QuotaAllocatedValue - quota.QuotaUsedValue + currentVal; if(val > maxValue) errors.Add(messageKey + ":" + maxValue); } @@ -1795,8 +1799,9 @@ namespace WebsitePanel.EnterpriseServer else if (state == VirtualMachineRequestedState.Reboot) { // shutdown first - ResultObject shutdownResult = ChangeVirtualMachineState(itemId, VirtualMachineRequestedState.ShutDown); - if(!shutdownResult.IsSuccess) + ResultObject shutdownResult = ChangeVirtualMachineState(itemId, + VirtualMachineRequestedState.ShutDown); + if (!shutdownResult.IsSuccess) { TaskManager.CompleteResultTask(res); return shutdownResult; @@ -1817,20 +1822,29 @@ namespace WebsitePanel.EnterpriseServer JobResult result = vps.ChangeVirtualMachineState(machine.VirtualMachineId, state); - // check return - if (result.ReturnValue != ReturnCode.JobStarted) + if (result.Job.JobState == ConcreteJobState.Completed) { LogReturnValueResult(res, result); - TaskManager.CompleteResultTask(res); + TaskManager.CompleteTask(); return res; } - - // wait for completion - if (!JobCompleted(vps, result.Job)) + else { - LogJobResult(res, result.Job); - TaskManager.CompleteResultTask(res); - return res; + // check return + if (result.ReturnValue != ReturnCode.JobStarted) + { + LogReturnValueResult(res, result); + TaskManager.CompleteResultTask(res); + return res; + } + + // wait for completion + if (!JobCompleted(vps, result.Job)) + { + LogJobResult(res, result.Job); + TaskManager.CompleteResultTask(res); + return res; + } } } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/BiosInfo.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/BiosInfo.cs new file mode 100644 index 00000000..f7fd4a59 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/BiosInfo.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2014, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace WebsitePanel.Providers.Virtualization +{ + public class BiosInfo + { + public bool NumLockEnabled { get; set; } + public string[] StartupOrder { get; set; } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ConcreteJobState.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ConcreteJobState.cs index 468136e8..b316ef8f 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ConcreteJobState.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ConcreteJobState.cs @@ -42,6 +42,9 @@ namespace WebsitePanel.Providers.Virtualization Completed = 7, Terminated = 8, Killed = 9, - Exception = 10 + Exception = 10, + + NotStarted = 11, + Failed = 12, } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ControllerType.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ControllerType.cs new file mode 100644 index 00000000..afe85d2a --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/ControllerType.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2014, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace WebsitePanel.Providers.Virtualization +{ + public enum ControllerType + { + IDE = 0, + SCSI = 1 + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/MemoryInfo.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/MemoryInfo.cs new file mode 100644 index 00000000..7e79088a --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/MemoryInfo.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2014, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace WebsitePanel.Providers.Virtualization +{ + public class MemoryInfo + { + public bool DynamicMemoryEnabled { get; set; } + public Int64 Startup { get; set; } + public Int64 Minimum { get; set; } + public Int64 Maximum { get; set; } + public int Buffer { get; set; } + public int Priority { get; set; } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/OperationalStatus.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/OperationalStatus.cs index 808ff0dc..c10ebc26 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/OperationalStatus.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/OperationalStatus.cs @@ -35,9 +35,10 @@ namespace WebsitePanel.Providers.Virtualization public enum OperationalStatus { None = 0, - OK = 2, + Ok = 2, Error = 6, NoContact = 12, - LostCommunication = 13 + LostCommunication = 13, + Paused = 15 } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs new file mode 100644 index 00000000..8787c82d --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2014, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace WebsitePanel.Providers.Virtualization +{ + public enum VirtualHardDiskFormat + { + VHD = 1, + VHDX = 2 + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskInfo.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskInfo.cs index 75bd6f74..e327767f 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskInfo.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskInfo.cs @@ -40,5 +40,15 @@ namespace WebsitePanel.Providers.Virtualization public long MaxInternalSize { get; set; } public string ParentPath { get; set; } public VirtualHardDiskType DiskType { get; set; } + public bool SupportPersistentReservations { get; set; } + public ulong MaximumIOPS { get; set; } + public ulong MinimumIOPS { get; set; } + public ControllerType VHDControllerType { get; set; } + public int ControllerNumber { get; set; } + public int ControllerLocation { get; set; } + public string Name { get; set; } + public string Path { get; set; } + public VirtualHardDiskFormat DiskFormat { get; set; } + public bool Attached { get; set; } } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachine.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachine.cs index 22732d61..5f5847a8 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachine.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachine.cs @@ -69,8 +69,8 @@ namespace WebsitePanel.Providers.Virtualization public int CpuUsage { get; set; } [Persistent] - public int RamSize { get; set; } - public int RamUsage { get; set; } + public long RamSize { get; set; } + public long RamUsage { get; set; } [Persistent] public int HddSize { get; set; } public LogicalDisk[] HddLogicalDisks { get; set; } @@ -123,5 +123,18 @@ namespace WebsitePanel.Providers.Virtualization // for GetVirtualMachineEx used in import method public VirtualMachineNetworkAdapter[] Adapters { get; set; } + + [Persistent] + public VirtualHardDiskInfo[] Disks { get; set; } + + [Persistent] + public string Status { get; set; } + + [Persistent] + public string ReplicationState { get; set; } + + [Persistent] + public int Generation { get; set; } + } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineState.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineState.cs index 86ba3354..d7db3390 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineState.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineState.cs @@ -34,8 +34,9 @@ namespace WebsitePanel.Providers.Virtualization { public enum VirtualMachineState { + /* Unknown = 0, - Started = 2, // start + Running = 2, // start Off = 3, // turn off Reset = 10, // reset Paused = 32768, // pause @@ -47,5 +48,33 @@ namespace WebsitePanel.Providers.Virtualization Stopping = 32774, Deleted = 32775, Pausing = 32776 + */ + + Unknown = 0, + Other = 1, + Running = 2, + Off = 3, + Stopping = 4, + Saved = 6, + Paused = 9, + Starting = 10, + Reset = 11, + Saving = 32773, + Pausing = 32776, + Resuming = 32777, + FastSaved = 32779, + FastSaving = 32780, + RunningCritical = 32781, + OffCritical = 32782, + StoppingCritical = 32783, + SavedCritical = 32784, + PausedCritical = 32785, + StartingCritical = 32786, + ResetCritical = 32787, + SavingCritical = 32788, + PausingCritical = 32789, + ResumingCritical = 32790, + FastSavedCritical = 32791, + FastSavingCritical = 32792 } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualSwitch.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualSwitch.cs index 63bf3479..44c6906e 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualSwitch.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualSwitch.cs @@ -37,5 +37,6 @@ namespace WebsitePanel.Providers.Virtualization { [Persistent] public string SwitchId { get; set; } + public string SwitchType { get; set; } } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj index 752209d3..c52d1cdb 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj @@ -287,6 +287,7 @@ + Code @@ -297,6 +298,7 @@ Code + Code @@ -307,6 +309,7 @@ + @@ -321,6 +324,7 @@ Code + diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs new file mode 100644 index 00000000..71b11f28 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs @@ -0,0 +1,2976 @@ +// Copyright (c) 2014, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; + +using System.Management; +using System.Management.Automation; +using System.Management.Automation.Runspaces; + +using System.Reflection; +using System.Globalization; + +using System.Xml; +using WebsitePanel.Providers; +using WebsitePanel.Providers.HostedSolution; +using WebsitePanel.Providers.Utils; +using WebsitePanel.Server.Utils; + +using Vds = Microsoft.Storage.Vds; +using System.Configuration; +using System.Linq; + +namespace WebsitePanel.Providers.Virtualization +{ + public class HyperV2012R2 : HostingServiceProviderBase, IVirtualizationServer + { + #region Constants + private const string CONFIG_USE_DISKPART_TO_CLEAR_READONLY_FLAG = "WebsitePanel.HyperV.UseDiskPartClearReadOnlyFlag"; + private const string WMI_VIRTUALIZATION_NAMESPACE = @"root\virtualization"; + private const string WMI_CIMV2_NAMESPACE = @"root\cimv2"; + + private const int SWITCH_PORTS_NUMBER = 1024; + private const string LIBRARY_INDEX_FILE_NAME = "index.xml"; + private const string EXTERNAL_NETWORK_ADAPTER_NAME = "External Network Adapter"; + private const string PRIVATE_NETWORK_ADAPTER_NAME = "Private Network Adapter"; + private const string MANAGEMENT_NETWORK_ADAPTER_NAME = "Management Network Adapter"; + + private const string KVP_RAM_SUMMARY_KEY = "VM-RAM-Summary"; + private const string KVP_HDD_SUMMARY_KEY = "VM-HDD-Summary"; + private const Int64 Size1G = 0x40000000; + private const Int64 Size1M = 0x100000; + + #endregion + + #region Provider Settings + protected string ServerNameSettings + { + get { return ProviderSettings["ServerName"]; } + } + + public int AutomaticStartActionSettings + { + get { return ProviderSettings.GetInt("StartAction"); } + } + + public int AutomaticStartupDelaySettings + { + get { return ProviderSettings.GetInt("StartupDelay"); } + } + + public int AutomaticStopActionSettings + { + get { return ProviderSettings.GetInt("StopAction"); } + } + + public int AutomaticRecoveryActionSettings + { + get { return 1 /* restart */; } + } + + public int CpuReserveSettings + { + get { return ProviderSettings.GetInt("CpuReserve"); } + } + + public int CpuLimitSettings + { + get { return ProviderSettings.GetInt("CpuLimit"); } + } + + public int CpuWeightSettings + { + get { return ProviderSettings.GetInt("CpuWeight"); } + } + #endregion + + #region Fields + private Wmi _wmi = null; + + private Wmi wmi + { + get + { + if (_wmi == null) + _wmi = new Wmi(ServerNameSettings, WMI_VIRTUALIZATION_NAMESPACE); + return _wmi; + } + } + #endregion + + #region Constructors + public HyperV2012R2() + { + } + #endregion + + #region Virtual Machines + + public VirtualMachine GetVirtualMachine(string vmId) + { + return GetVirtualMachineInternal( vmId, false); + } + + public VirtualMachine GetVirtualMachineInternal(string vmId, bool extendedInfo) + { + + HostedSolutionLog.LogStart("GetVirtualMachine"); + HostedSolutionLog.DebugInfo("Virtual Machine: {0}", vmId); + + Runspace runSpace = null; + VirtualMachine vm = new VirtualMachine(); + + try + { + runSpace = OpenRunspace(); + Command cmd = new Command("Get-VM"); + + cmd.Parameters.Add("Id", vmId); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + vm.Name = GetPSObjectProperty(result[0], "Name").ToString(); + vm.State = (VirtualMachineState)Enum.Parse(typeof(VirtualMachineState), GetPSObjectProperty(result[0], "State").ToString()); + vm.CpuUsage = ConvertNullableToInt32(GetPSObjectProperty(result[0], "CpuUsage")); + vm.RamUsage = ConvertNullableToInt64(GetPSObjectProperty(result[0], "MemoryAssigned")); + vm.Uptime = TimeSpan.Parse(GetPSObjectProperty(result[0], "Uptime").ToString()).Ticks; + vm.Status = GetPSObjectProperty(result[0], "Status").ToString(); + vm.ReplicationState = GetPSObjectProperty(result[0], "ReplicationState").ToString(); + + vm.Heartbeat = GetVMHeartBeatStatus(runSpace, vm.Name); + + vm.CreatedDate = DateTime.Now; + + if (extendedInfo) + { + vm.CpuCores = GetVMProcessors(runSpace, vm.Name); + + MemoryInfo memoryInfo = GetVMMemory(runSpace, vm.Name); + vm.RamSize = memoryInfo.Startup; + + // BIOS + BiosInfo biosInfo = GetVMBios(runSpace, vm.Name); + vm.NumLockEnabled = biosInfo.NumLockEnabled; + + vm.BootFromCD = false; + if ((biosInfo.StartupOrder != null) && (biosInfo.StartupOrder.Length > 0)) + vm.BootFromCD = (biosInfo.StartupOrder[0] == "CD"); + + // DVD drive + cmd = new Command("Get-VMDvdDrive"); + cmd.Parameters.Add("VMName", vm.Name); + + result = ExecuteShellCommand(runSpace, cmd, false); + vm.DvdDriveInstalled = (result != null && result.Count > 0); + + // HDD + vm.Disks = GetVirtualHardDisks(runSpace, vm.Name); + + if ((vm.Disks != null) & (vm.Disks.GetLength(0) > 0)) + { + vm.VirtualHardDrivePath = vm.Disks[0].Path; + vm.HddSize = Convert.ToInt32(vm.Disks[0].FileSize); + } + + // network adapters + vm.Adapters = GetNetworkAdapters(runSpace, vm.Name); + return vm; + + } + + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetVirtualMachine", ex); + throw; + } + finally + { + CloseRunspace(runSpace); + } + + HostedSolutionLog.LogEnd("GetVirtualMachine"); + return vm; + + } + + internal OperationalStatus GetVMHeartBeatStatus(Runspace runSpace, string name) + { + + OperationalStatus status = OperationalStatus.None; + + Command cmd = new Command("Get-VMIntegrationService"); + + cmd.Parameters.Add("VMName", name); + cmd.Parameters.Add("Name", "HeartBeat"); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + var statusString = GetPSObjectProperty(result[0], "PrimaryOperationalStatus"); + + if (statusString != null) + status = (OperationalStatus)Enum.Parse(typeof(OperationalStatus), statusString.ToString()); + } + return status; + } + + public VirtualMachine GetVirtualMachineEx(string vmId) + { + return GetVirtualMachineInternal( vmId, true); + } + + + internal int GetVMProcessors(Runspace runSpace, string name) + { + + int procs = 0; + + Command cmd = new Command("Get-VMProcessor"); + + cmd.Parameters.Add("VMName", name); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + procs = Convert.ToInt32(GetPSObjectProperty(result[0], "Count")); + + } + return procs; + } + + internal MemoryInfo GetVMMemory(Runspace runSpace, string name) + { + MemoryInfo info = new MemoryInfo(); + + Command cmd = new Command("Get-VMMemory"); + + cmd.Parameters.Add("VMName", name); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + info.DynamicMemoryEnabled = Convert.ToBoolean(GetPSObjectProperty(result[0], "DynamicMemoryEnabled")); + info.Startup = Convert.ToInt64(GetPSObjectProperty(result[0], "Startup")); + info.Minimum = Convert.ToInt64(GetPSObjectProperty(result[0], "Minimum")); + info.Maximum = Convert.ToInt64(GetPSObjectProperty(result[0], "Maximum")); + info.Buffer = Convert.ToInt32(GetPSObjectProperty(result[0], "Buffer")); + info.Priority = Convert.ToInt32(GetPSObjectProperty(result[0], "Priority")); + } + return info; + } + + internal BiosInfo GetVMBios(Runspace runSpace, string name) + { + BiosInfo info = new BiosInfo(); + + Command cmd = new Command("Get-VMBios"); + + cmd.Parameters.Add("VMName", name); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + info.NumLockEnabled = Convert.ToBoolean(GetPSObjectProperty(result[0], "NumLockEnabled")); + + List startupOrders = new List(); + + foreach (var item in (IEnumerable)GetPSObjectProperty(result[0], "StartupOrder")) + startupOrders.Add(item.ToString()); + + info.StartupOrder = startupOrders.ToArray(); + } + return info; + } + + internal VirtualHardDiskInfo[] GetVirtualHardDisks(Runspace runSpace, string name) + { + + List disks = new List(); + + Command cmd = new Command("Get-VMHardDiskDrive"); + cmd.Parameters.Add("VMName", name); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + foreach(PSObject d in result) + { + VirtualHardDiskInfo disk = new VirtualHardDiskInfo(); + + disk.SupportPersistentReservations = Convert.ToBoolean(GetPSObjectProperty(d, "SupportPersistentReservations")); + disk.MaximumIOPS = Convert.ToUInt64(GetPSObjectProperty(d, "MaximumIOPS")); + disk.MinimumIOPS = Convert.ToUInt64(GetPSObjectProperty(d, "MinimumIOPS")); + disk.VHDControllerType = (ControllerType)Enum.Parse(typeof(ControllerType), GetPSObjectProperty(d, "ControllerType").ToString()); + disk.ControllerNumber = Convert.ToInt32(GetPSObjectProperty(d, "ControllerNumber")); + disk.ControllerLocation = Convert.ToInt32(GetPSObjectProperty(d, "ControllerLocation")); + disk.Path = GetPSObjectProperty(d, "Path").ToString(); + disk.Name = GetPSObjectProperty(d, "Name").ToString(); + + GetVirtualHardDiskDetail(runSpace, disk.Path, ref disk); + + disks.Add(disk); + } + } + return disks.ToArray(); + } + + internal void GetVirtualHardDiskDetail(Runspace runSpace, string path, ref VirtualHardDiskInfo disk) + { + if (!string.IsNullOrEmpty(path)) + { + Command cmd = new Command("Get-VHD"); + cmd.Parameters.Add("Path", path); + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + disk.DiskFormat = (VirtualHardDiskFormat)Enum.Parse(typeof(VirtualHardDiskFormat), GetPSObjectProperty(result[0], "VhdFormat").ToString()); + disk.DiskType = (VirtualHardDiskType)Enum.Parse(typeof(VirtualHardDiskType), GetPSObjectProperty(result[0], "VhdType").ToString()); + disk.ParentPath = (string)GetPSObjectProperty(result[0], "ParentPath"); + disk.MaxInternalSize = Convert.ToInt64(GetPSObjectProperty(result[0], "Size")) / Size1G; + disk.FileSize = Convert.ToInt64(GetPSObjectProperty(result[0], "FileSize")) / Size1G; + disk.Attached = Convert.ToBoolean(GetPSObjectProperty(result[0], "Attached")); + } + } + } + + + internal VirtualMachineNetworkAdapter[] GetNetworkAdapters(Runspace runSpace, string name) + { + List adapters = new List(); + + Command cmd = new Command("Get-VMNetworkAdapter"); + cmd.Parameters.Add("VMName", name); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + if (result != null && result.Count > 0) + { + foreach(PSObject a in result) + { + VirtualMachineNetworkAdapter adapter = new VirtualMachineNetworkAdapter(); + + adapter.Name = GetPSObjectProperty(a, "Name").ToString(); + adapter.MacAddress = GetPSObjectProperty(a, "MacAddress").ToString(); + + adapters.Add(adapter); + } + } + return adapters.ToArray(); + } + + public List GetVirtualMachines() + { + HostedSolutionLog.LogStart("GetVirtualMachines"); + + Runspace runSpace = null; + List vmachines = new List(); + + try + { + runSpace = OpenRunspace(); + Command cmd = new Command("Get-VM"); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + foreach (PSObject current in result) + { + VirtualMachine vm = new VirtualMachine + { + VirtualMachineId = GetPSObjectProperty(current, "Id").ToString(), + Name = GetPSObjectProperty(current, "Name").ToString(), + State = (VirtualMachineState)Enum.Parse(typeof(VirtualMachineState), GetPSObjectProperty(current, "State").ToString()), + Uptime = GetPSObjectProperty(current, "UpTime").Ticks + }; + vmachines.Add(vm); + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetVirtualMachines", ex); + throw; + } + finally + { + CloseRunspace(runSpace); + } + + HostedSolutionLog.LogEnd("GetVirtualMachines"); + return vmachines; + + } + + public byte[] GetVirtualMachineThumbnailImage(string vmId, ThumbnailSize size) + { + //ManagementBaseObject objSummary = GetVirtualMachineSummaryInformation(vmId, (SummaryInformationRequest)size); + //wmi.Dump(objSummary); + //return GetTumbnailFromSummaryInformation(objSummary, size); + // TODO: + return (byte[]) (new ImageConverter()).ConvertTo(new Bitmap(80, 60), typeof (byte[])); + } + + private byte[] GetTumbnailFromSummaryInformation(ManagementBaseObject objSummary, ThumbnailSize size) + { + int width = 80; + int height = 60; + + if (size == ThumbnailSize.Medium160x120) + { + width = 160; + height = 120; + } + else if (size == ThumbnailSize.Large320x240) + { + width = 320; + height = 240; + } + + byte[] imgData = (byte[])objSummary["ThumbnailImage"]; + + // create new bitmap + Bitmap bmp = new Bitmap(width, height); + + if (imgData != null) + { + // lock bitmap + Rectangle rect = new Rectangle(0, 0, width, height); + BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format16bppRgb565); + + // get address of the first line + IntPtr ptr = bmpData.Scan0; + + // coby thumbnail bytes into bitmap + System.Runtime.InteropServices.Marshal.Copy(imgData, 0, ptr, imgData.Length); + + // unlock image + bmp.UnlockBits(bmpData); + } + else + { + // fill grey rectangle + Graphics g = Graphics.FromImage(bmp); + SolidBrush brush = new SolidBrush(Color.LightGray); + g.FillRectangle(brush, 0, 0, width, height); + } + + MemoryStream stream = new MemoryStream(); + bmp.Save(stream, ImageFormat.Png); + + stream.Flush(); + byte[] buffer = stream.ToArray(); + + bmp.Dispose(); + stream.Dispose(); + + return buffer; + } + + public VirtualMachine CreateVirtualMachine(VirtualMachine vm) + { + // evaluate paths + vm.RootFolderPath = FileUtils.EvaluateSystemVariables(vm.RootFolderPath); + vm.OperatingSystemTemplatePath = FileUtils.EvaluateSystemVariables(vm.OperatingSystemTemplatePath); + vm.VirtualHardDrivePath = FileUtils.EvaluateSystemVariables(vm.VirtualHardDrivePath); + + string vmID = null; + + // request management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // display name + ManagementObject objGlobalSettings = wmi.GetWmiClass("msvm_VirtualSystemGlobalSettingData").CreateInstance(); + objGlobalSettings["ElementName"] = vm.Name; + + // VM folders + objGlobalSettings["ExternalDataRoot"] = vm.RootFolderPath; + objGlobalSettings["SnapshotDataRoot"] = vm.RootFolderPath; + + wmi.Dump(objGlobalSettings); + + // startup/shutdown actions + if (AutomaticStartActionSettings != 100) + { + objGlobalSettings["AutomaticStartupAction"] = AutomaticStartActionSettings; + objGlobalSettings["AutomaticStartupActionDelay"] = String.Format("000000000000{0:d2}.000000:000", AutomaticStartupDelaySettings); + } + + if (AutomaticStopActionSettings != 100) + objGlobalSettings["AutomaticShutdownAction"] = AutomaticStopActionSettings; + + if (AutomaticRecoveryActionSettings != 100) + objGlobalSettings["AutomaticRecoveryAction"] = AutomaticRecoveryActionSettings; + + // create machine + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("DefineVirtualSystem"); + inParams["SystemSettingData"] = objGlobalSettings.GetText(TextFormat.CimDtd20); + inParams["ResourceSettingData"] = new string[] { }; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("DefineVirtualSystem", inParams, null); + ManagementObject objVM = wmi.GetWmiObjectByPath((string)outParams["DefinedSystem"]); + + // job + JobResult job = CreateJobResultFromWmiMethodResults(outParams); ; + + // read VM id + vmID = (string)objVM["Name"]; + + // update general settings + UpdateVirtualMachineGeneralSettings(vmID, objVM, + vm.CpuCores, + vm.RamSize, + vm.BootFromCD, + vm.NumLockEnabled); + + // hard disks + // load IDE 0 controller + ManagementObject objIDE0 = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Emulated IDE Controller'" + + " and InstanceID Like 'Microsoft:{0}%' and Address = 0", vmID); + + // load default hard disk drive + ManagementObject objDefaultHdd = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Synthetic Disk Drive'" + + " and InstanceID like '%Default'"); + ManagementObject objHdd = (ManagementObject)objDefaultHdd.Clone(); + objHdd["Parent"] = objIDE0.Path; + objHdd["Address"] = 0; + + // add HDD to VM resources + ManagementObject objAddedHDD = AddVirtualMachineResources(objVM, objHdd); + + // attach VHD + string fullVhdPath = vm.VirtualHardDrivePath; + ManagementObject objDefaultVHD = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Virtual Hard Disk'" + + " and InstanceID like '%Default'"); + ManagementObject objVhd = (ManagementObject)objDefaultVHD.Clone(); + objVhd["Parent"] = objAddedHDD.Path.Path; + objVhd["Connection"] = new string[] { fullVhdPath }; + + // add VHD to the system + AddVirtualMachineResources(objVM, objVhd); + + // DVD drive + if (vm.DvdDriveInstalled) + { + AddVirtualMachineDvdDrive(vmID, objVM); + } + + // add external adapter + if (vm.ExternalNetworkEnabled && !String.IsNullOrEmpty(vm.ExternalSwitchId)) + AddNetworkAdapter(objVM, vm.ExternalSwitchId, vm.Name, vm.ExternalNicMacAddress, EXTERNAL_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + + // add private adapter + if (vm.PrivateNetworkEnabled && !String.IsNullOrEmpty(vm.PrivateSwitchId)) + AddNetworkAdapter(objVM, vm.PrivateSwitchId, vm.Name, vm.PrivateNicMacAddress, PRIVATE_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + + // add management adapter + if (vm.ManagementNetworkEnabled && !String.IsNullOrEmpty(vm.ManagementSwitchId)) + AddNetworkAdapter(objVM, vm.ManagementSwitchId, vm.Name, vm.ManagementNicMacAddress, MANAGEMENT_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + + vm.VirtualMachineId = vmID; + return vm; + } + + public VirtualMachine UpdateVirtualMachine(VirtualMachine vm) + { + string vmId = vm.VirtualMachineId; + + // get VM object + ManagementObject objVM = GetVirtualMachineObject(vmId); + + // update general settings + UpdateVirtualMachineGeneralSettings(vmId, objVM, + vm.CpuCores, + vm.RamSize, + vm.BootFromCD, + vm.NumLockEnabled); + + // check DVD drive + ManagementObject objDvdDrive = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Synthetic DVD Drive'" + + " and InstanceID like 'Microsoft:{0}%' and Address = 0", vmId); + + if (vm.DvdDriveInstalled && objDvdDrive == null) + AddVirtualMachineDvdDrive(vmId, objVM); + else if (!vm.DvdDriveInstalled && objDvdDrive != null) + RemoveVirtualMachineResources(objVM, objDvdDrive); + + // External NIC + if (!vm.ExternalNetworkEnabled + && !String.IsNullOrEmpty(vm.ExternalNicMacAddress)) + { + // delete adapter + DeleteNetworkAdapter(objVM, vm.ExternalNicMacAddress); + + // reset MAC + vm.ExternalNicMacAddress = null; + } + else if (vm.ExternalNetworkEnabled + && !String.IsNullOrEmpty(vm.ExternalNicMacAddress)) + { + // add external adapter + AddNetworkAdapter(objVM, vm.ExternalSwitchId, vm.Name, vm.ExternalNicMacAddress, EXTERNAL_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + } + + + // Private NIC + if (!vm.PrivateNetworkEnabled + && !String.IsNullOrEmpty(vm.PrivateNicMacAddress)) + { + // delete adapter + DeleteNetworkAdapter(objVM, vm.PrivateNicMacAddress); + + // reset MAC + vm.PrivateNicMacAddress = null; + } + else if (vm.PrivateNetworkEnabled + && !String.IsNullOrEmpty(vm.PrivateNicMacAddress)) + { + // add private adapter + AddNetworkAdapter(objVM, vm.PrivateSwitchId, vm.Name, vm.PrivateNicMacAddress, PRIVATE_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + } + + return vm; + } + + private void UpdateVirtualMachineGeneralSettings(string vmId, ManagementObject objVM, int cpuCores, long ramMB, bool bootFromCD, bool numLockEnabled) + { + // request management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // VM resources + List vmConfig = new List(); + + // get system settings + ManagementObject objSettings = GetVirtualMachineSettingsObject(vmId); + + // BIOS (num lock) + objSettings["BIOSNumLock"] = numLockEnabled; + + // BIOS (boot order) + // BootOrder = 0 - Boot from floppy, 1 - Boot from CD, 2 - Boot from disk, 3 - PXE Boot + objSettings["BootOrder"] = bootFromCD ? new int[] { 1, 2, 3, 0 } : new int[] { 2, 1, 3, 0 }; + + // modify machine settings + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("ModifyVirtualSystem"); + inParams["ComputerSystem"] = objVM; + inParams["SystemSettingData"] = objSettings.GetText(TextFormat.CimDtd20); + ManagementBaseObject outParams = objVmsvc.InvokeMethod("ModifyVirtualSystem", inParams, null); + JobResult job = CreateJobResultFromWmiMethodResults(outParams); + + // setup CPU + ManagementObject objCpu = wmi.GetWmiObject("Msvm_ProcessorSettingData", "InstanceID Like 'Microsoft:{0}%'", vmId); + objCpu["VirtualQuantity"] = cpuCores; + objCpu["Limit"] = Convert.ToInt64(CpuLimitSettings * 1000); + objCpu["Reservation"] = Convert.ToInt64(CpuReserveSettings * 1000); + objCpu["Weight"] = CpuWeightSettings; + vmConfig.Add(objCpu.GetText(TextFormat.CimDtd20)); + + // setup RAM + ManagementObject objRam = wmi.GetWmiObject("Msvm_MemorySettingData", "InstanceID Like 'Microsoft:{0}%'", vmId); + objRam["VirtualQuantity"] = ramMB.ToString(); + objRam["Reservation"] = ramMB.ToString(); + objRam["Limit"] = ramMB.ToString(); + vmConfig.Add(objRam.GetText(TextFormat.CimDtd20)); + + // modify machine resources + inParams = objVmsvc.GetMethodParameters("ModifyVirtualSystemResources"); + inParams["ComputerSystem"] = objVM; + inParams["ResourceSettingData"] = vmConfig.ToArray(); + outParams = objVmsvc.InvokeMethod("ModifyVirtualSystemResources", inParams, null); + job = CreateJobResultFromWmiMethodResults(outParams); + } + + private void AddVirtualMachineDvdDrive(string vmId, ManagementObject objVM) + { + // load IDE 1 controller + ManagementObject objIDE1 = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Emulated IDE Controller'" + + " and InstanceID Like 'Microsoft:{0}%' and Address = 1", vmId); + + // load default hard disk drive + ManagementObject objDefaultDvd = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Synthetic DVD Drive'" + + " and InstanceID like '%Default'"); + ManagementObject objDvd = (ManagementObject)objDefaultDvd.Clone(); + objDvd["Parent"] = objIDE1.Path; + objDvd["Address"] = 0; + + // add DVD drive to VM resources + AddVirtualMachineResources(objVM, objDvd); + } + + private void AddNetworkAdapter(ManagementObject objVm, string switchId, string portName, string macAddress, string adapterName, bool legacyAdapter) + { + string nicClassName = GetNetworkAdapterClassName(legacyAdapter); + + string vmId = (string)objVm["Name"]; + + // check if already exists + ManagementObject objNic = wmi.GetWmiObject( + nicClassName, "InstanceID like 'Microsoft:{0}%' and Address = '{1}'", vmId, macAddress); + + if (objNic != null) + return; // exists - exit + + portName = String.Format("{0} - {1}", + portName, (adapterName == EXTERNAL_NETWORK_ADAPTER_NAME) ? "External" : "Private"); + + // Network service + ManagementObject objNetworkSvc = GetVirtualSwitchManagementService(); + + // default NIC + ManagementObject objDefaultNic = wmi.GetWmiObject(nicClassName, "InstanceID like '%Default'"); + + // find switch + ManagementObject objSwitch = wmi.GetWmiObject("msvm_VirtualSwitch", "Name = '{0}'", switchId); + + // create switch port + ManagementBaseObject inParams = objNetworkSvc.GetMethodParameters("CreateSwitchPort"); + inParams["VirtualSwitch"] = objSwitch; + inParams["Name"] = portName; + inParams["FriendlyName"] = portName; + inParams["ScopeOfResidence"] = ""; + + // invoke method + ManagementBaseObject outParams = objNetworkSvc.InvokeMethod("CreateSwitchPort", inParams, null); + + // process output parameters + ReturnCode code = (ReturnCode)Convert.ToInt32(outParams["ReturnValue"]); + if (code == ReturnCode.OK) + { + // created port + ManagementObject objPort = wmi.GetWmiObjectByPath((string)outParams["CreatedSwitchPort"]); + + // create NIC + ManagementObject objExtNic = (ManagementObject)objDefaultNic.Clone(); + objExtNic["Connection"] = new string[] { objPort.Path.Path }; + + if (!String.IsNullOrEmpty(macAddress)) + { + objExtNic["StaticMacAddress"] = true; + objExtNic["Address"] = macAddress; + } + else + { + objExtNic["StaticMacAddress"] = false; + } + objExtNic["ElementName"] = adapterName; + + if (!legacyAdapter) + objExtNic["VirtualSystemIdentifiers"] = new string[] { Guid.NewGuid().ToString("B") }; + + // add NIC + ManagementObject objCreatedExtNic = AddVirtualMachineResources(objVm, objExtNic); + } + } + + private string GetNetworkAdapterClassName(bool legacy) + { + return legacy ? "Msvm_EmulatedEthernetPortSettingData" : "Msvm_SyntheticEthernetPortSettingData"; + } + + private ManagementObject AddVirtualMachineResources(ManagementObject objVm, ManagementObject resource) + { + if (resource == null) + return resource; + + // request management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // add resources + string txtResource = resource.GetText(TextFormat.CimDtd20); + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("AddVirtualSystemResources"); + inParams["TargetSystem"] = objVm; + inParams["ResourceSettingData"] = new string[] { txtResource }; + ManagementBaseObject outParams = objVmsvc.InvokeMethod("AddVirtualSystemResources", inParams, null); + JobResult result = CreateJobResultFromWmiMethodResults(outParams); + + if (result.ReturnValue == ReturnCode.OK) + { + string[] wmiPaths = (string[])outParams["NewResources"]; + return wmi.GetWmiObjectByPath(wmiPaths[0]); + } + else if (result.ReturnValue == ReturnCode.JobStarted) + { + if (JobCompleted(result.Job)) + { + string[] wmiPaths = (string[])outParams["NewResources"]; + return wmi.GetWmiObjectByPath(wmiPaths[0]); + } + else + { + throw new Exception("Cannot add virtual machine resources"); + } + } + else + { + throw new Exception("Cannot add virtual machine resources: " + txtResource); + } + } + + private JobResult RemoveVirtualMachineResources(ManagementObject objVm, ManagementObject resource) + { + if (resource == null) + return null; + + // request management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // remove resources + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("RemoveVirtualSystemResources"); + inParams["TargetSystem"] = objVm; + inParams["ResourceSettingData"] = new string[] { resource.Path.Path }; + ManagementBaseObject outParams = objVmsvc.InvokeMethod("RemoveVirtualSystemResources", inParams, null); + JobResult result = CreateJobResultFromWmiMethodResults(outParams); + if (result.ReturnValue == ReturnCode.OK) + { + return result; + } + else if (result.ReturnValue == ReturnCode.JobStarted) + { + if (!JobCompleted(result.Job)) + { + throw new Exception("Cannot remove virtual machine resources"); + } + } + else + { + throw new Exception("Cannot remove virtual machine resources: " + resource.Path.Path); + } + + return result; + } + + public JobResult ChangeVirtualMachineState(string vmId, VirtualMachineRequestedState newState) + { + HostedSolutionLog.LogStart("ChangeVirtualMachineState"); + var jobResult = new JobResult(); + + var vm = GetVirtualMachine(vmId); + Runspace runSpace = null; + + try + { + runSpace = OpenRunspace(); + + string cmdTxt; + List paramList = new List(); + + switch (newState) + { + case VirtualMachineRequestedState.Start: + cmdTxt = "Start-VM"; + break; + case VirtualMachineRequestedState.Pause: + cmdTxt = "Suspend-VM"; + break; + case VirtualMachineRequestedState.Reset: + cmdTxt = "Restart-VM"; + break; + case VirtualMachineRequestedState.Resume: + cmdTxt = "Resume-VM"; + break; + case VirtualMachineRequestedState.ShutDown: + cmdTxt = "Stop-VM"; + break; + case VirtualMachineRequestedState.TurnOff: + cmdTxt = "Stop-VM"; + paramList.Add("TurnOff"); + break; + case VirtualMachineRequestedState.Save: + cmdTxt = "Save-VM"; + break; + default: + throw new ArgumentOutOfRangeException("newState"); + } + + Command cmd = new Command(cmdTxt); + + cmd.Parameters.Add("Name", vm.Name); + //cmd.Parameters.Add("AsJob"); + paramList.ForEach(p => cmd.Parameters.Add(p)); + + ExecuteShellCommand(runSpace, cmd, false); + jobResult = CreateSuccessJobResult(); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("ChangeVirtualMachineState", ex); + throw; + } + finally + { + CloseRunspace(runSpace); + } + + HostedSolutionLog.LogEnd("ChangeVirtualMachineState"); + + return jobResult; + } + + public ReturnCode ShutDownVirtualMachine(string vmId, bool force, string reason) + { + HostedSolutionLog.LogStart("ShutDownVirtualMachine"); + ReturnCode returnCode = ReturnCode.OK; + + var vm = GetVirtualMachine(vmId); + Runspace runSpace = null; + + try + { + runSpace = OpenRunspace(); + + Command cmd = new Command("Stop-VM"); + + cmd.Parameters.Add("Name", vm.Name); + if (force) cmd.Parameters.Add("Force"); + + ExecuteShellCommand(runSpace, cmd, false); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("ShutDownVirtualMachine", ex); + throw; + } + finally + { + CloseRunspace(runSpace); + } + + HostedSolutionLog.LogEnd("ShutDownVirtualMachine"); + + return returnCode; + } + + public List GetVirtualMachineJobs(string vmId) + { + List jobs = new List(); + + ManagementBaseObject objSummary = GetVirtualMachineSummaryInformation( + vmId, SummaryInformationRequest.AsynchronousTasks); + ManagementBaseObject[] objJobs = (ManagementBaseObject[])objSummary["AsynchronousTasks"]; + + if (objJobs != null) + { + foreach (ManagementBaseObject objJob in objJobs) + jobs.Add(CreateJobFromWmiObject(objJob)); + } + + return jobs; + } + + public JobResult RenameVirtualMachine(string vmId, string name) + { + // load virtual machine + ManagementObject objVm = GetVirtualMachineObject(vmId); + + // load machine settings + ManagementObject objVmSettings = GetVirtualMachineSettingsObject(vmId); + + // rename machine + objVmSettings["ElementName"] = name; + + // save + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("ModifyVirtualSystem"); + inParams["ComputerSystem"] = objVm.Path.Path; + inParams["SystemSettingData"] = objVmSettings.GetText(TextFormat.CimDtd20); + ManagementBaseObject outParams = objVmsvc.InvokeMethod("ModifyVirtualSystem", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult DeleteVirtualMachine(string vmId) + { + // load virtual machine object + ManagementObject objVm = GetVirtualMachineObject(vmId); + + // check state + VirtualMachine vm = GetVirtualMachine(vmId); + + // The virtual computer system must be in the powered off or saved state prior to calling this method. + if (vm.State == VirtualMachineState.Saved + || vm.State == VirtualMachineState.Off) + { + // delete network adapters and ports + DeleteNetworkAdapters(objVm); + + // destroy machine + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // get method + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("DestroyVirtualSystem"); + inParams["ComputerSystem"] = objVm; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("DestroyVirtualSystem", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + else + { + throw new Exception("The virtual computer system must be in the powered off or saved state prior to calling Destroy method."); + } + } + + private void DeleteNetworkAdapters(ManagementObject objVM) + { + string vmId = (string)objVM["Name"]; + + // delete synthetic adapters + foreach (ManagementObject objNic in wmi.GetWmiObjects("Msvm_SyntheticEthernetPortSettingData", "InstanceID like 'Microsoft:{0}%'", vmId)) + DeleteNetworkAdapter(objVM, objNic); + + // delete legacy adapters + foreach (ManagementObject objNic in wmi.GetWmiObjects("Msvm_EmulatedEthernetPortSettingData", "InstanceID like 'Microsoft:{0}%'", vmId)) + DeleteNetworkAdapter(objVM, objNic); + } + + private void DeleteNetworkAdapter(ManagementObject objVM, string macAddress) + { + // locate network adapter + ManagementObject objNic = wmi.GetWmiObject("CIM_ResourceAllocationSettingData", "Address = '{0}'", macAddress); + + // delete adapter + DeleteNetworkAdapter(objVM, objNic); + } + + private void DeleteNetworkAdapter(ManagementObject objVM, ManagementObject objNic) + { + if (objNic == null) + return; + + // delete corresponding switch port + string[] conn = (string[])objNic["Connection"]; + if (conn != null && conn.Length > 0) + DeleteSwitchPort(conn[0]); + + // delete adapter + RemoveVirtualMachineResources(objVM, objNic); + } + + private void DeleteSwitchPort(string portPath) + { + // Network service + ManagementObject objNetworkSvc = GetVirtualSwitchManagementService(); + + // create switch port + ManagementBaseObject inParams = objNetworkSvc.GetMethodParameters("DeleteSwitchPort"); + inParams["SwitchPort"] = portPath; + + // invoke method + objNetworkSvc.InvokeMethod("DeleteSwitchPort", inParams, null); + } + + public JobResult ExportVirtualMachine(string vmId, string exportPath) + { + // load virtual machine object + ManagementObject objVm = GetVirtualMachineObject(vmId); + + // check state + VirtualMachine vm = GetVirtualMachine(vmId); + + // The virtual computer system must be in the powered off or saved state prior to calling this method. + if (vm.State == VirtualMachineState.Off) + { + // export machine + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // get method + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("ExportVirtualSystem"); + inParams["ComputerSystem"] = objVm; + inParams["CopyVmState"] = true; + inParams["ExportDirectory"] = FileUtils.EvaluateSystemVariables(exportPath); + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("ExportVirtualSystem", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + else + { + throw new Exception("The virtual computer system must be in the powered off or saved state prior to calling Export method."); + } + } + #endregion + + #region Snapshots + public List GetVirtualMachineSnapshots(string vmId) + { + // get all VM setting objects + ManagementObject objVmSettings = GetVirtualMachineSettingsObject(vmId); + VirtualMachineSnapshot runningSnapshot = CreateSnapshotFromWmiObject(objVmSettings); + + // load snapshots + ManagementBaseObject objSummary = GetVirtualMachineSummaryInformation(vmId, SummaryInformationRequest.Snapshots); + ManagementBaseObject[] objSnapshots = (ManagementBaseObject[])objSummary["Snapshots"]; + + List snapshots = new List(); + + if (objSnapshots != null) + { + foreach (ManagementBaseObject objSnapshot in objSnapshots) + { + VirtualMachineSnapshot snapshot = CreateSnapshotFromWmiObject(objSnapshot); + snapshot.IsCurrent = (runningSnapshot.ParentId == snapshot.Id); + snapshots.Add(snapshot); + } + } + + return snapshots; + } + + public VirtualMachineSnapshot GetSnapshot(string snapshotId) + { + // load snapshot + ManagementObject objSnapshot = GetSnapshotObject(snapshotId); + return CreateSnapshotFromWmiObject(objSnapshot); + } + + public JobResult CreateSnapshot(string vmId) + { + // get VM management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // load virtual machine + ManagementObject objVm = GetVirtualMachineObject(vmId); + + // get method params + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("CreateVirtualSystemSnapshot"); + inParams["SourceSystem"] = objVm; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("CreateVirtualSystemSnapshot", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult RenameSnapshot(string vmId, string snapshotId, string name) + { + // load virtual machine + ManagementObject objVm = GetVirtualMachineObject(vmId); + + // load snapshot + ManagementObject objSnapshot = GetSnapshotObject(snapshotId); + + // rename snapshot + objSnapshot["ElementName"] = name; + + // save + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("ModifyVirtualSystem"); + inParams["ComputerSystem"] = objVm.Path.Path; + inParams["SystemSettingData"] = objSnapshot.GetText(TextFormat.CimDtd20); + ManagementBaseObject outParams = objVmsvc.InvokeMethod("ModifyVirtualSystem", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult ApplySnapshot(string vmId, string snapshotId) + { + // get VM management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // load virtual machine + ManagementObject objVm = GetVirtualMachineObject(vmId); + + // load snapshot + ManagementObject objSnapshot = GetSnapshotObject(snapshotId); + + ManagementObjectCollection objRelated = objVm.GetRelated("Msvm_SettingsDefineState"); + + // get method params + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("ApplyVirtualSystemSnapshot"); + inParams["ComputerSystem"] = objVm.Path.Path; + inParams["SnapshotSettingData"] = objSnapshot.Path.Path; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("ApplyVirtualSystemSnapshot", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult DeleteSnapshot(string snapshotId) + { + // get VM management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // load snapshot object + ManagementObject objSnapshot = GetSnapshotObject(snapshotId); + + // get method params + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("RemoveVirtualSystemSnapshot"); + inParams["SnapshotSettingData"] = objSnapshot.Path.Path; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("RemoveVirtualSystemSnapshot", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult DeleteSnapshotSubtree(string snapshotId) + { + // get VM management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // load snapshot object + ManagementObject objSnapshot = GetSnapshotObject(snapshotId); + + // get method params + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("RemoveVirtualSystemSnapshotTree"); + inParams["SnapshotSettingData"] = objSnapshot.Path.Path; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("RemoveVirtualSystemSnapshotTree", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public byte[] GetSnapshotThumbnailImage(string snapshotId, ThumbnailSize size) + { + ManagementBaseObject objSummary = GetSnapshotSummaryInformation(snapshotId, (SummaryInformationRequest)size); + return GetTumbnailFromSummaryInformation(objSummary, size); + } + #endregion + + #region DVD operations + public string GetInsertedDVD(string vmId) + { + // find CD/DVD disk + ManagementObject objDvd = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Virtual CD/DVD Disk'" + + " and InstanceID Like 'Microsoft:{0}%'", vmId); + + if (objDvd == null) + return null; + + string[] path = (string[])objDvd["Connection"]; + if (path != null && path.Length > 0) + return path[0]; + + return null; + } + + public JobResult InsertDVD(string vmId, string isoPath) + { + isoPath = FileUtils.EvaluateSystemVariables(isoPath); + + // find DVD drive + ManagementObject objDvdDrive = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Synthetic DVD Drive'" + + " and InstanceID Like 'Microsoft:{0}%'", vmId); + + // create CD/DVD disk + ManagementObject objDefaultDVD = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Virtual CD/DVD Disk'" + + " and InstanceID like '%Default'"); + ManagementObject objDvd = (ManagementObject)objDefaultDVD.Clone(); + objDvd["Parent"] = objDvdDrive.Path; + objDvd["Connection"] = new string[] { isoPath }; + + // get VM service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // get method + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("AddVirtualSystemResources"); + inParams["TargetSystem"] = GetVirtualMachineObject(vmId); + inParams["ResourceSettingData"] = new string[] { objDvd.GetText(TextFormat.CimDtd20) }; + + // execute method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("AddVirtualSystemResources", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult EjectDVD(string vmId) + { + // find CD/DVD disk + ManagementObject objDvd = wmi.GetWmiObject( + "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Virtual CD/DVD Disk'" + + " and InstanceID Like 'Microsoft:{0}%'", vmId); + + // get VM service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // get method + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("RemoveVirtualSystemResources"); + inParams["TargetSystem"] = GetVirtualMachineObject(vmId); + inParams["ResourceSettingData"] = new object[] { objDvd.Path.Path }; + + // execute method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("RemoveVirtualSystemResources", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + #endregion + + #region Virtual Switches + public List GetSwitches() + { + return GetSwitches(null, null); + } + + public List GetExternalSwitches(string computerName) + { + return GetSwitches(computerName, "External"); + } + + private List GetSwitches(string computerName, string type) + { + HostedSolutionLog.LogStart("GetSwitches"); + HostedSolutionLog.DebugInfo("ComputerName: {0}", computerName); + + Runspace runSpace = null; + List switches = new List(); + + try + { + runSpace = OpenRunspace(); + Command cmd = new Command("Get-VMSwitch"); + + if (!string.IsNullOrEmpty(computerName)) cmd.Parameters.Add("ComputerName", computerName); + if (!string.IsNullOrEmpty(type)) cmd.Parameters.Add("SwitchType", type); + + Collection result = ExecuteShellCommand(runSpace, cmd,false); + foreach (PSObject current in result) + { + VirtualSwitch sw = new VirtualSwitch(); + sw.SwitchId = GetPSObjectProperty(current, "Name").ToString(); + sw.Name = GetPSObjectProperty(current, "Name").ToString(); + sw.SwitchType = GetPSObjectProperty(current, "SwitchType").ToString(); + switches.Add(sw); + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetSwitches", ex); + throw; + } + finally + { + CloseRunspace(runSpace); + } + + HostedSolutionLog.LogEnd("GetSwitches"); + return switches; + + } + + public bool SwitchExists(string switchId) + { + ManagementObject objSwitch = wmi.GetWmiObject("msvm_VirtualSwitch", "Name = '{0}'", switchId); + return (objSwitch != null); + } + + public VirtualSwitch CreateSwitch(string name) + { + // generate ID for new virtual switch + string id = Guid.NewGuid().ToString(); + + // get switch management object + ManagementObject objNetworkSvc = GetVirtualSwitchManagementService(); + + ManagementBaseObject inParams = objNetworkSvc.GetMethodParameters("CreateSwitch"); + inParams["Name"] = id; + inParams["FriendlyName"] = name; + inParams["NumLearnableAddresses"] = SWITCH_PORTS_NUMBER; + + // invoke method + ManagementBaseObject outParams = objNetworkSvc.InvokeMethod("CreateSwitch", inParams, null); + + // process output parameters + ManagementObject objSwitch = wmi.GetWmiObjectByPath((string)outParams["CreatedVirtualSwitch"]); + return CreateSwitchFromWmiObject(objSwitch); + } + + public ReturnCode DeleteSwitch(string switchId) + { + // find requested switch + ManagementObject objSwitch = wmi.GetWmiObject("msvm_VirtualSwitch", "Name = '{0}'", switchId); + + if (objSwitch == null) + throw new Exception("Virtual switch with the specified ID was not found."); + + // get switch management object + ManagementObject objNetworkSvc = GetVirtualSwitchManagementService(); + + // get method params + ManagementBaseObject inParams = objNetworkSvc.GetMethodParameters("DeleteSwitch"); + inParams["VirtualSwitch"] = objSwitch.Path.Path; + + ManagementBaseObject outParams = (ManagementBaseObject)objNetworkSvc.InvokeMethod("DeleteSwitch", inParams, null); + return (ReturnCode)Convert.ToInt32(outParams["ReturnValue"]); + } + #endregion + + #region Library + public LibraryItem[] GetLibraryItems(string path) + { + path = Path.Combine(FileUtils.EvaluateSystemVariables(path), LIBRARY_INDEX_FILE_NAME); + + // convert to UNC if it is a remote computer + path = ConvertToUNC(path); + + if (!File.Exists(path)) + { + HostedSolutionLog.LogWarning("The folder does not contain 'index.xml' file: {0}", path); + return null; + } + + // create list + List items = new List(); + + // load xml + XmlDocument xml = new XmlDocument(); + xml.Load(path); + + XmlNodeList nodeItems = xml.SelectNodes("/items/item"); + + if (nodeItems.Count == 0) + HostedSolutionLog.LogWarning("index.xml found, but contains 0 items: {0}", path); + + foreach (XmlNode nodeItem in nodeItems) + { + LibraryItem item = new LibraryItem(); + item.Path = nodeItem.Attributes["path"].Value; + + // optional attributes + if (nodeItem.Attributes["diskSize"] != null) + item.DiskSize = Int32.Parse(nodeItem.Attributes["diskSize"].Value); + + if (nodeItem.Attributes["legacyNetworkAdapter"] != null) + item.LegacyNetworkAdapter = Boolean.Parse(nodeItem.Attributes["legacyNetworkAdapter"].Value); + + item.ProcessVolume = 0; // process (extend and sysprep) 1st volume by default + if (nodeItem.Attributes["processVolume"] != null) + item.ProcessVolume = Int32.Parse(nodeItem.Attributes["processVolume"].Value); + + if (nodeItem.Attributes["remoteDesktop"] != null) + item.RemoteDesktop = Boolean.Parse(nodeItem.Attributes["remoteDesktop"].Value); + + // inner nodes + item.Name = nodeItem.SelectSingleNode("name").InnerText; + item.Description = nodeItem.SelectSingleNode("description").InnerText; + + // sysprep files + XmlNodeList nodesSyspep = nodeItem.SelectNodes("provisioning/sysprep"); + List sysprepFiles = new List(); + foreach (XmlNode nodeSyspep in nodesSyspep) + { + if (nodeSyspep.Attributes["file"] != null) + sysprepFiles.Add(nodeSyspep.Attributes["file"].Value); + } + item.SysprepFiles = sysprepFiles.ToArray(); + + // vmconfig + XmlNode nodeVmConfig = nodeItem.SelectSingleNode("provisioning/vmconfig"); + if (nodeVmConfig != null) + { + if (nodeVmConfig.Attributes["computerName"] != null) + item.ProvisionComputerName = Boolean.Parse(nodeVmConfig.Attributes["computerName"].Value); + + if (nodeVmConfig.Attributes["administratorPassword"] != null) + item.ProvisionAdministratorPassword = Boolean.Parse(nodeVmConfig.Attributes["administratorPassword"].Value); + + if (nodeVmConfig.Attributes["networkAdapters"] != null) + item.ProvisionNetworkAdapters = Boolean.Parse(nodeVmConfig.Attributes["networkAdapters"].Value); + } + + items.Add(item); + } + + return items.ToArray(); + } + + private string ConvertToUNC(string path) + { + if (String.IsNullOrEmpty(ServerNameSettings) + || path.StartsWith(@"\\")) + return path; + + return String.Format(@"\\{0}\{1}", ServerNameSettings, path.Replace(":", "$")); + } + #endregion + + #region KVP + public List GetKVPItems(string vmId) + { + return GetKVPItems(vmId, "GuestExchangeItems"); + } + + public List GetStandardKVPItems(string vmId) + { + return GetKVPItems(vmId, "GuestIntrinsicExchangeItems"); + } + + private List GetKVPItems(string vmId, string exchangeItemsName) + { + List pairs = new List(); + + // load VM + ManagementObject objVm = GetVirtualMachineObject(vmId); + + ManagementObject objKvpExchange = null; + + try + { + objKvpExchange = wmi.GetRelatedWmiObject(objVm, "msvm_KvpExchangeComponent"); + } + catch + { + // TODO + // add logging... + + return pairs; + } + + // return XML pairs + string[] xmlPairs = (string[])objKvpExchange[exchangeItemsName]; + + if (xmlPairs == null) + return pairs; + + // join all pairs + StringBuilder sb = new StringBuilder(); + sb.Append(""); + foreach (string xmlPair in xmlPairs) + sb.Append(xmlPair); + sb.Append(""); + + // parse pairs + XmlDocument doc = new XmlDocument(); + doc.LoadXml(sb.ToString()); + + foreach (XmlNode nodeName in doc.SelectNodes("/result/INSTANCE/PROPERTY[@NAME='Name']/VALUE")) + { + string name = nodeName.InnerText; + string data = nodeName.ParentNode.ParentNode.SelectSingleNode("PROPERTY[@NAME='Data']/VALUE").InnerText; + pairs.Add(new KvpExchangeDataItem(name, data)); + } + + return pairs; + } + + public JobResult AddKVPItems(string vmId, KvpExchangeDataItem[] items) + { + // get KVP management object + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // create KVP items array + string[] wmiItems = new string[items.Length]; + + for (int i = 0; i < items.Length; i++) + { + ManagementClass clsKvp = wmi.GetWmiClass("Msvm_KvpExchangeDataItem"); + ManagementObject objKvp = clsKvp.CreateInstance(); + objKvp["Name"] = items[i].Name; + objKvp["Data"] = items[i].Data; + objKvp["Source"] = 0; + + // convert to WMI format + wmiItems[i] = objKvp.GetText(TextFormat.CimDtd20); + } + + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("AddKvpItems"); + inParams["TargetSystem"] = GetVirtualMachineObject(vmId); + inParams["DataItems"] = wmiItems; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("AddKvpItems", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult RemoveKVPItems(string vmId, string[] itemNames) + { + // get KVP management object + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // delete items one by one + for (int i = 0; i < itemNames.Length; i++) + { + ManagementClass clsKvp = wmi.GetWmiClass("Msvm_KvpExchangeDataItem"); + ManagementObject objKvp = clsKvp.CreateInstance(); + objKvp["Name"] = itemNames[i]; + objKvp["Data"] = ""; + objKvp["Source"] = 0; + + // convert to WMI format + string wmiItem = objKvp.GetText(TextFormat.CimDtd20); + + // call method + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("RemoveKvpItems"); + inParams["TargetSystem"] = GetVirtualMachineObject(vmId); + inParams["DataItems"] = new string[] { wmiItem }; + + // invoke method + objVmsvc.InvokeMethod("RemoveKvpItems", inParams, null); + } + return null; + } + + public JobResult ModifyKVPItems(string vmId, KvpExchangeDataItem[] items) + { + // get KVP management object + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + // create KVP items array + string[] wmiItems = new string[items.Length]; + + for (int i = 0; i < items.Length; i++) + { + ManagementClass clsKvp = wmi.GetWmiClass("Msvm_KvpExchangeDataItem"); + ManagementObject objKvp = clsKvp.CreateInstance(); + objKvp["Name"] = items[i].Name; + objKvp["Data"] = items[i].Data; + objKvp["Source"] = 0; + + // convert to WMI format + wmiItems[i] = objKvp.GetText(TextFormat.CimDtd20); + } + + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("ModifyKvpItems"); + inParams["TargetSystem"] = GetVirtualMachineObject(vmId); + inParams["DataItems"] = wmiItems; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("ModifyKvpItems", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + #endregion + + #region Storage + public VirtualHardDiskInfo GetVirtualHardDiskInfo(string vhdPath) + { + ManagementObject objImgSvc = GetImageManagementService(); + + // get method params + ManagementBaseObject inParams = objImgSvc.GetMethodParameters("GetVirtualHardDiskInfo"); + inParams["Path"] = FileUtils.EvaluateSystemVariables(vhdPath); + + // execute method + ManagementBaseObject outParams = (ManagementBaseObject)objImgSvc.InvokeMethod("GetVirtualHardDiskInfo", inParams, null); + ReturnCode result = (ReturnCode)Convert.ToInt32(outParams["ReturnValue"]); + if (result == ReturnCode.OK) + { + // create XML + string xml = (string)outParams["Info"]; + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + + // read properties + VirtualHardDiskInfo vhd = new VirtualHardDiskInfo(); + vhd.DiskType = (VirtualHardDiskType)Enum.Parse(typeof(VirtualHardDiskType), GetPropertyValue("Type", doc), true); + vhd.FileSize = Int64.Parse(GetPropertyValue("FileSize", doc)); + vhd.InSavedState = Boolean.Parse(GetPropertyValue("InSavedState", doc)); + vhd.InUse = Boolean.Parse(GetPropertyValue("InUse", doc)); + vhd.MaxInternalSize = Int64.Parse(GetPropertyValue("MaxInternalSize", doc)); + vhd.ParentPath = GetPropertyValue("ParentPath", doc); + return vhd; + } + return null; + } + + private string GetPropertyValue(string propertyName, XmlDocument doc) + { + string xpath = string.Format(@"//PROPERTY[@NAME = '{0}']/VALUE/child::text()", propertyName); + XmlNode node = doc.SelectSingleNode(xpath); + return node != null ? node.Value : null; + } + + public MountedDiskInfo MountVirtualHardDisk(string vhdPath) + { + ManagementObject objImgSvc = GetImageManagementService(); + + // get method params + ManagementBaseObject inParams = objImgSvc.GetMethodParameters("Mount"); + inParams["Path"] = FileUtils.EvaluateSystemVariables(vhdPath); + + ManagementBaseObject outParams = (ManagementBaseObject)objImgSvc.InvokeMethod("Mount", inParams, null); + JobResult result = CreateJobResultFromWmiMethodResults(outParams); + + // load storage job + if (result.ReturnValue != ReturnCode.JobStarted) + throw new Exception("Failed to start Mount job with the following error: " + result.ReturnValue); ; + + ManagementObject objJob = wmi.GetWmiObject("msvm_StorageJob", "InstanceID = '{0}'", result.Job.Id); + + if (!JobCompleted(result.Job)) + throw new Exception("Failed to complete Mount job with the following error: " + result.Job.ErrorDescription); + + try + { + List volumes = new List(); + + // load output data + ManagementObject objImage = wmi.GetRelatedWmiObject(objJob, "Msvm_MountedStorageImage"); + + int pathId = Convert.ToInt32(objImage["PathId"]); + int portNumber = Convert.ToInt32(objImage["PortNumber"]); + int targetId = Convert.ToInt32(objImage["TargetId"]); + int lun = Convert.ToInt32(objImage["Lun"]); + + string diskAddress = String.Format("Port{0}Path{1}Target{2}Lun{3}", portNumber, pathId, targetId, lun); + + HostedSolutionLog.LogInfo("Disk address: " + diskAddress); + + // find mounted disk using VDS + Vds.Advanced.AdvancedDisk advancedDisk = null; + Vds.Pack diskPack = null; + + // first attempt + System.Threading.Thread.Sleep(3000); + HostedSolutionLog.LogInfo("Trying to find mounted disk - first attempt"); + FindVdsDisk(diskAddress, out advancedDisk, out diskPack); + + // second attempt + if (advancedDisk == null) + { + System.Threading.Thread.Sleep(20000); + HostedSolutionLog.LogInfo("Trying to find mounted disk - second attempt"); + FindVdsDisk(diskAddress, out advancedDisk, out diskPack); + } + + if (advancedDisk == null) + throw new Exception("Could not find mounted disk"); + + // check if DiskPart must be used to bring disk online and clear read-only flag + bool useDiskPartToClearReadOnly = false; + if (ConfigurationManager.AppSettings[CONFIG_USE_DISKPART_TO_CLEAR_READONLY_FLAG] != null) + useDiskPartToClearReadOnly = Boolean.Parse(ConfigurationManager.AppSettings[CONFIG_USE_DISKPART_TO_CLEAR_READONLY_FLAG]); + + // determine disk index for DiskPart + Wmi cimv2 = new Wmi(ServerNameSettings, WMI_CIMV2_NAMESPACE); + ManagementObject objDisk = cimv2.GetWmiObject("win32_diskdrive", + "Model='Msft Virtual Disk SCSI Disk Device' and ScsiTargetID={0} and ScsiLogicalUnit={1} and scsiPort={2}", + targetId, lun, portNumber); + + if (useDiskPartToClearReadOnly) + { + // *** Clear Read-Only and bring disk online with DiskPart *** + HostedSolutionLog.LogInfo("Clearing disk Read-only flag and bringing disk online"); + + if (objDisk != null) + { + // disk found + // run DiskPart + string diskPartResult = RunDiskPart(String.Format(@"select disk {0} +attributes disk clear readonly +online disk +exit", Convert.ToInt32(objDisk["Index"]))); + + HostedSolutionLog.LogInfo("DiskPart Result: " + diskPartResult); + } + } + else + { + // *** Clear Read-Only and bring disk online with VDS *** + // clear Read-Only + if ((advancedDisk.Flags & Vds.DiskFlags.ReadOnly) == Vds.DiskFlags.ReadOnly) + { + HostedSolutionLog.LogInfo("Clearing disk Read-only flag"); + advancedDisk.ClearFlags(Vds.DiskFlags.ReadOnly); + while ((advancedDisk.Flags & Vds.DiskFlags.ReadOnly) == Vds.DiskFlags.ReadOnly) + { + System.Threading.Thread.Sleep(100); + advancedDisk.Refresh(); + } + } + + // bring disk ONLINE + if (advancedDisk.Status == Vds.DiskStatus.Offline) + { + HostedSolutionLog.LogInfo("Bringing disk online"); + advancedDisk.Online(); + while (advancedDisk.Status == Vds.DiskStatus.Offline) + { + System.Threading.Thread.Sleep(100); + advancedDisk.Refresh(); + } + } + } + + // small pause after getting disk online + System.Threading.Thread.Sleep(3000); + + // get disk again + FindVdsDisk(diskAddress, out advancedDisk, out diskPack); + + // find volumes using VDS + HostedSolutionLog.LogInfo("Querying disk volumes with VDS"); + foreach (Vds.Volume volume in diskPack.Volumes) + { + string letter = volume.DriveLetter.ToString(); + if (letter != "") + volumes.Add(letter); + } + + // find volumes using WMI + if (volumes.Count == 0 && objDisk != null) + { + HostedSolutionLog.LogInfo("Querying disk volumes with WMI"); + foreach (ManagementObject objPartition in objDisk.GetRelated("Win32_DiskPartition")) + { + foreach (ManagementObject objVolume in objPartition.GetRelated("Win32_LogicalDisk")) + { + volumes.Add(objVolume["Name"].ToString().TrimEnd(':')); + } + } + } + + HostedSolutionLog.LogInfo("Volumes found: " + volumes.Count); + + // info object + MountedDiskInfo info = new MountedDiskInfo(); + info.DiskAddress = diskAddress; + info.DiskVolumes = volumes.ToArray(); + return info; + } + catch (Exception ex) + { + // unmount disk + UnmountVirtualHardDisk(vhdPath); + + // throw error + throw ex; + } + } + + private void FindVdsDisk(string diskAddress, out Vds.Advanced.AdvancedDisk advancedDisk, out Vds.Pack diskPack) + { + advancedDisk = null; + diskPack = null; + + Vds.ServiceLoader serviceLoader = new Vds.ServiceLoader(); + Vds.Service vds = serviceLoader.LoadService(ServerNameSettings); + vds.WaitForServiceReady(); + + foreach (Vds.Disk disk in vds.UnallocatedDisks) + { + if (disk.DiskAddress == diskAddress) + { + advancedDisk = (Vds.Advanced.AdvancedDisk)disk; + break; + } + } + + if (advancedDisk == null) + { + vds.HardwareProvider = false; + vds.SoftwareProvider = true; + + foreach (Vds.SoftwareProvider provider in vds.Providers) + foreach (Vds.Pack pack in provider.Packs) + foreach (Vds.Disk disk in pack.Disks) + if (disk.DiskAddress == diskAddress) + { + diskPack = pack; + advancedDisk = (Vds.Advanced.AdvancedDisk)disk; + break; + } + } + } + + public ReturnCode UnmountVirtualHardDisk(string vhdPath) + { + ManagementObject objImgSvc = GetImageManagementService(); + + // get method params + ManagementBaseObject inParams = objImgSvc.GetMethodParameters("Unmount"); + inParams["Path"] = FileUtils.EvaluateSystemVariables(vhdPath); + + ManagementBaseObject outParams = (ManagementBaseObject)objImgSvc.InvokeMethod("Unmount", inParams, null); + return (ReturnCode)Convert.ToInt32(outParams["ReturnValue"]); + } + + public JobResult ExpandVirtualHardDisk(string vhdPath, UInt64 sizeGB) + { + const UInt64 Size1G = 0x40000000; + + ManagementObject objImgSvc = GetImageManagementService(); + + // get method params + ManagementBaseObject inParams = objImgSvc.GetMethodParameters("ExpandVirtualHardDisk"); + inParams["Path"] = FileUtils.EvaluateSystemVariables(vhdPath); + inParams["MaxInternalSize"] = sizeGB * Size1G; + + ManagementBaseObject outParams = (ManagementBaseObject)objImgSvc.InvokeMethod("ExpandVirtualHardDisk", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public JobResult ConvertVirtualHardDisk(string sourcePath, string destinationPath, VirtualHardDiskType diskType) + { + sourcePath = FileUtils.EvaluateSystemVariables(sourcePath); + destinationPath = FileUtils.EvaluateSystemVariables(destinationPath); + + // check source file + if (!FileExists(sourcePath)) + throw new Exception("Source VHD cannot be found: " + sourcePath); + + // check destination folder + string destFolder = Path.GetDirectoryName(destinationPath); + if (!DirectoryExists(destFolder)) + CreateFolder(destFolder); + + ManagementObject objImgSvc = GetImageManagementService(); + + // get method params + ManagementBaseObject inParams = objImgSvc.GetMethodParameters("ConvertVirtualHardDisk"); + inParams["SourcePath"] = sourcePath; + inParams["DestinationPath"] = destinationPath; + inParams["Type"] = (UInt16)diskType; + + ManagementBaseObject outParams = (ManagementBaseObject)objImgSvc.InvokeMethod("ConvertVirtualHardDisk", inParams, null); + return CreateJobResultFromWmiMethodResults(outParams); + } + + public void DeleteRemoteFile(string path) + { + if (DirectoryExists(path)) + DeleteFolder(path); // WMI way + else if (FileExists(path)) + DeleteFile(path); // WMI way + } + + public void ExpandDiskVolume(string diskAddress, string volumeName) + { + // find mounted disk using VDS + Vds.Advanced.AdvancedDisk advancedDisk = null; + Vds.Pack diskPack = null; + + FindVdsDisk(diskAddress, out advancedDisk, out diskPack); + + if (advancedDisk == null) + throw new Exception("Could not find mounted disk"); + + // find volume + Vds.Volume diskVolume = null; + foreach (Vds.Volume volume in diskPack.Volumes) + { + if (volume.DriveLetter.ToString() == volumeName) + { + diskVolume = volume; + break; + } + } + + if (diskVolume == null) + throw new Exception("Could not find disk volume: " + volumeName); + + // determine maximum available space + ulong oneMegabyte = 1048576; + ulong freeSpace = 0; + foreach (Vds.DiskExtent extent in advancedDisk.Extents) + { + if (extent.Type != Microsoft.Storage.Vds.DiskExtentType.Free) + continue; + + if (extent.Size > oneMegabyte) + freeSpace += extent.Size; + } + + if (freeSpace == 0) + return; + + // input disk + Vds.InputDisk inputDisk = new Vds.InputDisk(); + foreach (Vds.VolumePlex plex in diskVolume.Plexes) + { + inputDisk.DiskId = advancedDisk.Id; + inputDisk.Size = freeSpace; + inputDisk.PlexId = plex.Id; + + foreach (Vds.DiskExtent extent in plex.Extents) + inputDisk.MemberIndex = extent.MemberIndex; + + break; + } + + // extend volume + Vds.Async extendEvent = diskVolume.BeginExtend(new Vds.InputDisk[] { inputDisk }, null, null); + while (!extendEvent.IsCompleted) + System.Threading.Thread.Sleep(100); + diskVolume.EndExtend(extendEvent); + } + + // obsolete and currently is not used + private string RunDiskPart(string script) + { + // create temp script file name + string localPath = Path.Combine(GetTempRemoteFolder(), Guid.NewGuid().ToString("N")); + + // save script to remote temp file + string remotePath = ConvertToUNC(localPath); + File.AppendAllText(remotePath, script); + + // run diskpart + ExecuteRemoteProcess("DiskPart /s " + localPath); + + // delete temp script + try + { + File.Delete(remotePath); + } + catch + { + // TODO + } + + return ""; + } + + public string ReadRemoteFile(string path) + { + // temp file name on "system" drive available through hidden share + string tempPath = Path.Combine(GetTempRemoteFolder(), Guid.NewGuid().ToString("N")); + + HostedSolutionLog.LogInfo("Read remote file: " + path); + HostedSolutionLog.LogInfo("Local file temp path: " + tempPath); + + // copy remote file to temp file (WMI) + if (!CopyFile(path, tempPath)) + return null; + + // read content of temp file + string remoteTempPath = ConvertToUNC(tempPath); + HostedSolutionLog.LogInfo("Remote file temp path: " + remoteTempPath); + + string content = File.ReadAllText(remoteTempPath); + + // delete temp file (WMI) + DeleteFile(tempPath); + + return content; + } + + public void WriteRemoteFile(string path, string content) + { + // temp file name on "system" drive available through hidden share + string tempPath = Path.Combine(GetTempRemoteFolder(), Guid.NewGuid().ToString("N")); + + // write to temp file + string remoteTempPath = ConvertToUNC(tempPath); + File.WriteAllText(remoteTempPath, content); + + // delete file (WMI) + if (FileExists(path)) + DeleteFile(path); + + // copy (WMI) + CopyFile(tempPath, path); + + // delete temp file (WMI) + DeleteFile(tempPath); + } + #endregion + + #region Jobs + public ConcreteJob GetJob(string jobId) + { + HostedSolutionLog.LogStart("GetJob"); + HostedSolutionLog.DebugInfo("jobId: {0}", jobId); + + Runspace runSpace = null; + ConcreteJob job; + + try + { + runSpace = OpenRunspace(); + Command cmd = new Command("Get-Job"); + + if (!string.IsNullOrEmpty(jobId)) cmd.Parameters.Add("Id", jobId); + + Collection result = ExecuteShellCommand(runSpace, cmd, false); + job = CreateJobFromPSObject(result); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetJob", ex); + throw; + } + finally + { + CloseRunspace(runSpace); + } + + HostedSolutionLog.LogEnd("GetJob"); + return job; + } + + public List GetAllJobs() + { + List jobs = new List(); + + ManagementObjectCollection objJobs = wmi.GetWmiObjects("CIM_ConcreteJob"); + foreach (ManagementObject objJob in objJobs) + jobs.Add(CreateJobFromWmiObject(objJob)); + + return jobs; + } + + public ChangeJobStateReturnCode ChangeJobState(string jobId, ConcreteJobRequestedState newState) + { + ManagementObject objJob = GetJobWmiObject(jobId); + + // get method + ManagementBaseObject inParams = objJob.GetMethodParameters("RequestStateChange"); + inParams["RequestedState"] = (Int32)newState; + + // invoke method + ManagementBaseObject outParams = objJob.InvokeMethod("RequestStateChange", inParams, null); + return (ChangeJobStateReturnCode)Convert.ToInt32(outParams["ReturnValue"]); + } + + #endregion + + #region Configuration + public int GetProcessorCoresNumber() + { + Wmi w = new Wmi(ServerNameSettings, @"root\cimv2"); + ManagementObject objCpu = w.GetWmiObject("win32_Processor"); + return Convert.ToInt32(objCpu["NumberOfCores"]); + } + #endregion + + #region IHostingServiceProvier methods + public override string[] Install() + { + List messages = new List(); + + // TODO + + return messages.ToArray(); + } + + public override bool IsInstalled() + { + // check if Hyper-V role is installed and available for management + //Wmi root = new Wmi(ServerNameSettings, "root"); + //ManagementObject objNamespace = root.GetWmiObject("__NAMESPACE", "name = 'virtualization'"); + //return (objNamespace != null); + return true; + } + + public override void ChangeServiceItemsState(ServiceProviderItem[] items, bool enabled) + { + foreach (ServiceProviderItem item in items) + { + if (item is VirtualMachine) + { + // start/stop virtual machine + VirtualMachine vm = item as VirtualMachine; + ChangeVirtualMachineServiceItemState(vm, enabled); + } + } + } + + public override void DeleteServiceItems(ServiceProviderItem[] items) + { + foreach (ServiceProviderItem item in items) + { + if (item is VirtualMachine) + { + // delete virtual machine + VirtualMachine vm = item as VirtualMachine; + DeleteVirtualMachineServiceItem(vm); + } + else if (item is VirtualSwitch) + { + // delete switch + VirtualSwitch vs = item as VirtualSwitch; + DeleteVirtualSwitchServiceItem(vs); + } + } + } + + private void ChangeVirtualMachineServiceItemState(VirtualMachine vm, bool started) + { + try + { + VirtualMachine vps = GetVirtualMachine(vm.VirtualMachineId); + JobResult result = null; + + if (vps == null) + { + HostedSolutionLog.LogWarning(String.Format("Virtual machine '{0}' object with ID '{1}' was not found. Change state operation aborted.", + vm.Name, vm.VirtualMachineId)); + return; + } + + #region Start + if (started && + (vps.State == VirtualMachineState.Off + || vps.State == VirtualMachineState.Paused + || vps.State == VirtualMachineState.Saved)) + { + VirtualMachineRequestedState state = VirtualMachineRequestedState.Start; + if (vps.State == VirtualMachineState.Paused) + state = VirtualMachineRequestedState.Resume; + + result = ChangeVirtualMachineState(vm.VirtualMachineId, state); + + // check result + if (result.ReturnValue != ReturnCode.JobStarted) + { + HostedSolutionLog.LogWarning(String.Format("Cannot {0} '{1}' virtual machine: {2}", + state, vm.Name, result.ReturnValue)); + return; + } + + // wait for completion + if (!JobCompleted(result.Job)) + { + HostedSolutionLog.LogWarning(String.Format("Cannot complete {0} '{1}' of virtual machine: {1}", + state, vm.Name, result.Job.ErrorDescription)); + return; + } + } + #endregion + + #region Stop + else if (!started && + (vps.State == VirtualMachineState.Running + || vps.State == VirtualMachineState.Paused)) + { + if (vps.State == VirtualMachineState.Running) + { + // try to shutdown the system + ReturnCode code = ShutDownVirtualMachine(vm.VirtualMachineId, true, "Virtual Machine has been suspended from WebsitePanel"); + if (code == ReturnCode.OK) + return; + } + + // turn off + VirtualMachineRequestedState state = VirtualMachineRequestedState.TurnOff; + result = ChangeVirtualMachineState(vm.VirtualMachineId, state); + + // check result + if (result.ReturnValue != ReturnCode.JobStarted) + { + HostedSolutionLog.LogWarning(String.Format("Cannot {0} '{1}' virtual machine: {2}", + state, vm.Name, result.ReturnValue)); + return; + } + + // wait for completion + if (!JobCompleted(result.Job)) + { + HostedSolutionLog.LogWarning(String.Format("Cannot complete {0} '{1}' of virtual machine: {1}", + state, vm.Name, result.Job.ErrorDescription)); + return; + } + } + #endregion + } + catch (Exception ex) + { + HostedSolutionLog.LogError(String.Format("Error {0} Virtual Machine '{1}'", + started ? "starting" : "turning off", + vm.Name), ex); + } + } + + private void DeleteVirtualMachineServiceItem(VirtualMachine vm) + { + try + { + JobResult result = null; + VirtualMachine vps = GetVirtualMachine(vm.VirtualMachineId); + + if (vps == null) + { + HostedSolutionLog.LogWarning(String.Format("Virtual machine '{0}' object with ID '{1}' was not found. Delete operation aborted.", + vm.Name, vm.VirtualMachineId)); + return; + } + + #region Turn off (if required) + if (vps.State != VirtualMachineState.Off) + { + result = ChangeVirtualMachineState(vm.VirtualMachineId, VirtualMachineRequestedState.TurnOff); + // check result + if (result.ReturnValue != ReturnCode.JobStarted) + { + HostedSolutionLog.LogWarning(String.Format("Cannot Turn off '{0}' virtual machine before deletion: {1}", + vm.Name, result.ReturnValue)); + return; + } + + // wait for completion + if (!JobCompleted(result.Job)) + { + HostedSolutionLog.LogWarning(String.Format("Cannot complete Turn off '{0}' of virtual machine before deletion: {1}", + vm.Name, result.Job.ErrorDescription)); + return; + } + } + #endregion + + #region Delete virtual machine + result = DeleteVirtualMachine(vm.VirtualMachineId); + + // check result + if (result.ReturnValue != ReturnCode.JobStarted) + { + HostedSolutionLog.LogWarning(String.Format("Cannot delete '{0}' virtual machine: {1}", + vm.Name, result.ReturnValue)); + return; + } + + // wait for completion + if (!JobCompleted(result.Job)) + { + HostedSolutionLog.LogWarning(String.Format("Cannot complete deletion of '{0}' virtual machine: {1}", + vm.Name, result.Job.ErrorDescription)); + return; + } + #endregion + + #region Delete virtual machine + try + { + DeleteFile(vm.RootFolderPath); + } + catch (Exception ex) + { + HostedSolutionLog.LogError(String.Format("Cannot delete virtual machine folder '{0}'", + vm.RootFolderPath), ex); + } + #endregion + + } + catch (Exception ex) + { + HostedSolutionLog.LogError(String.Format("Error deleting Virtual Machine '{0}'", vm.Name), ex); + } + } + + private void DeleteVirtualSwitchServiceItem(VirtualSwitch vs) + { + try + { + // delete virtual switch + DeleteSwitch(vs.SwitchId); + } + catch (Exception ex) + { + HostedSolutionLog.LogError(String.Format("Error deleting Virtual Switch '{0}'", vs.Name), ex); + } + } + #endregion + + #region Private Methods + protected JobResult CreateSuccessJobResult() + { + JobResult result = new JobResult(); + + result.Job = new ConcreteJob(){JobState = ConcreteJobState.Completed}; + result.ReturnValue = ReturnCode.OK; + + return result; + } + protected JobResult CreateJobResultFromPSResults(Collection objJob) + { + if (objJob == null || objJob.Count == 0) + return null; + + JobResult result = new JobResult(); + + result.Job = CreateJobFromPSObject(objJob); + + result.ReturnValue = ReturnCode.JobStarted; + switch (result.Job.JobState) + { + case ConcreteJobState.Failed: + result.ReturnValue = ReturnCode.Failed; + break; + } + + return result; + } + + protected JobResult CreateJobResultFromWmiMethodResults(ManagementBaseObject outParams) + { + JobResult result = new JobResult(); + + // return value + result.ReturnValue = (ReturnCode)Convert.ToInt32(outParams["ReturnValue"]); + + // try getting job details job + try + { + ManagementBaseObject objJob = wmi.GetWmiObjectByPath((string)outParams["Job"]); + if (objJob != null && objJob.Properties.Count > 0) + { + result.Job = CreateJobFromWmiObject(objJob); + } + } + catch { /* dumb */ } + + return result; + } + + private ManagementObject GetJobWmiObject(string id) + { + return wmi.GetWmiObject("msvm_ConcreteJob", "InstanceID = '{0}'", id); + } + + private ManagementObject GetVirtualSystemManagementService() + { + return wmi.GetWmiObject("msvm_VirtualSystemManagementService"); + } + + private ManagementObject GetVirtualSwitchManagementService() + { + return wmi.GetWmiObject("msvm_VirtualSwitchManagementService"); + } + + protected ManagementObject GetImageManagementService() + { + return wmi.GetWmiObject("msvm_ImageManagementService"); + } + + private ManagementObject GetVirtualMachineObject(string vmId) + { + return wmi.GetWmiObject("msvm_ComputerSystem", "Name = '{0}'", vmId); + } + + private ManagementObject GetSnapshotObject(string snapshotId) + { + return wmi.GetWmiObject("Msvm_VirtualSystemSettingData", "InstanceID = '{0}'", snapshotId); + } + + + private VirtualMachineSnapshot CreateSnapshotFromWmiObject(ManagementBaseObject objSnapshot) + { + if (objSnapshot == null || objSnapshot.Properties.Count == 0) + return null; + + VirtualMachineSnapshot snapshot = new VirtualMachineSnapshot(); + snapshot.Id = (string)objSnapshot["InstanceID"]; + snapshot.Name = (string)objSnapshot["ElementName"]; + + string parentId = (string)objSnapshot["Parent"]; + if (!String.IsNullOrEmpty(parentId)) + { + int idx = parentId.IndexOf("Microsoft:"); + snapshot.ParentId = parentId.Substring(idx, parentId.Length - idx - 1); + } + snapshot.Created = wmi.ToDateTime((string)objSnapshot["CreationTime"]); + + return snapshot; + } + + private VirtualSwitch CreateSwitchFromWmiObject(ManagementObject objSwitch) + { + if (objSwitch == null || objSwitch.Properties.Count == 0) + return null; + + VirtualSwitch sw = new VirtualSwitch(); + sw.SwitchId = (string)objSwitch["Name"]; + sw.Name = (string)objSwitch["ElementName"]; + return sw; + } + + private ConcreteJob CreateJobFromPSObject(Collection objJob) + { + if (objJob == null || objJob.Count == 0) + return null; + + ConcreteJob job = new ConcreteJob(); + job.Id = GetPSObjectProperty(objJob[0], "Id").ToString(); + job.JobState = GetPSObjectPropertyEnum(objJob[0], "JobStateInfo"); + job.Caption = GetPSObjectProperty(objJob[0], "Name"); + job.Description = GetPSObjectProperty(objJob[0], "Command"); + job.StartTime = GetPSObjectProperty(objJob[0], "PSBeginTime"); + job.ElapsedTime = GetPSObjectProperty(objJob[0], "PSEndTime") ?? DateTime.Now; + + // PercentComplete + job.PercentComplete = 0; + var progress = (PSDataCollection)GetPSObjectProperty(objJob[0], "Progress"); + if (progress != null && progress.Count > 0) + job.PercentComplete = progress[0].PercentComplete; + + // Errors + var errors = (PSDataCollection)GetPSObjectProperty(objJob[0], "Error"); + if (errors != null && errors.Count > 0) + { + job.ErrorDescription = errors[0].ErrorDetails.Message + ". " + errors[0].ErrorDetails.RecommendedAction; + job.ErrorCode = errors[0].Exception != null ? -1 : 0; + } + + return job; + } + + private ConcreteJob CreateJobFromWmiObject(ManagementBaseObject objJob) + { + if (objJob == null || objJob.Properties.Count == 0) + return null; + + ConcreteJob job = new ConcreteJob(); + job.Id = (string)objJob["InstanceID"]; + job.JobState = (ConcreteJobState)Convert.ToInt32(objJob["JobState"]); + job.Caption = (string)objJob["Caption"]; + job.Description = (string)objJob["Description"]; + job.StartTime = wmi.ToDateTime((string)objJob["StartTime"]); + // TODO proper parsing of WMI time spans, e.g. 00000000000001.325247:000 + job.ElapsedTime = DateTime.Now; //wmi.ToDateTime((string)objJob["ElapsedTime"]); + job.ErrorCode = Convert.ToInt32(objJob["ErrorCode"]); + job.ErrorDescription = (string)objJob["ErrorDescription"]; + job.PercentComplete = Convert.ToInt32(objJob["PercentComplete"]); + return job; + } + + private ManagementBaseObject GetSnapshotSummaryInformation( + string snapshotId, + SummaryInformationRequest requestedInformation) + { + // find VM settings object + ManagementObject objVmSetting = GetSnapshotObject(snapshotId); + + // get summary + return GetSummaryInformation(objVmSetting, requestedInformation); + } + + private ManagementBaseObject GetVirtualMachineSummaryInformation( + string vmId, + params SummaryInformationRequest[] requestedInformation) + { + // find VM settings object + ManagementObject objVmSetting = GetVirtualMachineSettingsObject(vmId); + + // get summary + return GetSummaryInformation(objVmSetting, requestedInformation); + } + + private ManagementBaseObject GetSummaryInformation( + ManagementObject objVmSetting, params SummaryInformationRequest[] requestedInformation) + { + if (requestedInformation == null || requestedInformation.Length == 0) + throw new ArgumentNullException("requestedInformation"); + + // get management service + ManagementObject objVmsvc = GetVirtualSystemManagementService(); + + uint[] reqif = new uint[requestedInformation.Length]; + for (int i = 0; i < requestedInformation.Length; i++) + reqif[i] = (uint)requestedInformation[i]; + + // get method params + ManagementBaseObject inParams = objVmsvc.GetMethodParameters("GetSummaryInformation"); + inParams["SettingData"] = new ManagementObject[] { objVmSetting }; + inParams["RequestedInformation"] = reqif; + + // invoke method + ManagementBaseObject outParams = objVmsvc.InvokeMethod("GetSummaryInformation", inParams, null); + return ((ManagementBaseObject[])outParams["SummaryInformation"])[0]; + } + + private ManagementObject GetVirtualMachineSettingsObject(string vmId) + { + return wmi.GetWmiObject("msvm_VirtualSystemSettingData", "InstanceID Like 'Microsoft:{0}%'", vmId); + } + + private bool JobCompleted(ConcreteJob job) + { + bool jobCompleted = true; + + while (job.JobState == ConcreteJobState.Starting || + job.JobState == ConcreteJobState.Running) + { + System.Threading.Thread.Sleep(200); + job = GetJob(job.Id); + } + + if (job.JobState != ConcreteJobState.Completed) + { + jobCompleted = false; + } + + return jobCompleted; + } + #endregion + + #region Remote File Methods + public bool FileExists(string path) + { + HostedSolutionLog.LogInfo("Check remote file exists: " + path); + + if (path.StartsWith(@"\\")) // network share + return File.Exists(path); + else + { + Wmi cimv2 = new Wmi(ServerNameSettings, WMI_CIMV2_NAMESPACE); + ManagementObject objFile = cimv2.GetWmiObject("CIM_Datafile", "Name='{0}'", path.Replace("\\", "\\\\")); + return (objFile != null); + } + } + + public bool DirectoryExists(string path) + { + if (path.StartsWith(@"\\")) // network share + return Directory.Exists(path); + else + { + Wmi cimv2 = new Wmi(ServerNameSettings, WMI_CIMV2_NAMESPACE); + ManagementObject objDir = cimv2.GetWmiObject("Win32_Directory", "Name='{0}'", path.Replace("\\", "\\\\")); + return (objDir != null); + } + } + + public bool CopyFile(string sourceFileName, string destinationFileName) + { + HostedSolutionLog.LogInfo("Copy file - source: " + sourceFileName); + HostedSolutionLog.LogInfo("Copy file - destination: " + destinationFileName); + + if (sourceFileName.StartsWith(@"\\")) // network share + { + if (!File.Exists(sourceFileName)) + return false; + + File.Copy(sourceFileName, destinationFileName); + } + else + { + if (!FileExists(sourceFileName)) + return false; + + // copy using WMI + Wmi cimv2 = new Wmi(ServerNameSettings, WMI_CIMV2_NAMESPACE); + ManagementObject objFile = cimv2.GetWmiObject("CIM_Datafile", "Name='{0}'", sourceFileName.Replace("\\", "\\\\")); + if (objFile == null) + throw new Exception("Source file does not exists: " + sourceFileName); + + objFile.InvokeMethod("Copy", new object[] { destinationFileName }); + } + return true; + } + + public void DeleteFile(string path) + { + if (path.StartsWith(@"\\")) + { + // network share + File.Delete(path); + } + else + { + // delete file using WMI + Wmi cimv2 = new Wmi(ServerNameSettings, "root\\cimv2"); + ManagementObject objFile = cimv2.GetWmiObject("CIM_Datafile", "Name='{0}'", path.Replace("\\", "\\\\")); + objFile.InvokeMethod("Delete", null); + } + } + + public void DeleteFolder(string path) + { + if (path.StartsWith(@"\\")) + { + // network share + try + { + FileUtils.DeleteFile(path); + } + catch { /* just skip */ } + FileUtils.DeleteFile(path); + } + else + { + // local folder + // delete sub folders first + ManagementObjectCollection objSubFolders = GetSubFolders(path); + foreach (ManagementObject objSubFolder in objSubFolders) + DeleteFolder(objSubFolder["Name"].ToString()); + + // delete this folder itself + Wmi cimv2 = new Wmi(ServerNameSettings, "root\\cimv2"); + ManagementObject objFolder = cimv2.GetWmiObject("Win32_Directory", "Name='{0}'", path.Replace("\\", "\\\\")); + objFolder.InvokeMethod("Delete", null); + } + } + + private ManagementObjectCollection GetSubFolders(string path) + { + if (path.EndsWith("\\")) + path = path.Substring(0, path.Length - 1); + + Wmi cimv2 = new Wmi(ServerNameSettings, "root\\cimv2"); + + return cimv2.ExecuteWmiQuery("Associators of {Win32_Directory.Name='" + + path + "'} " + + "Where AssocClass = Win32_Subdirectory " + + "ResultRole = PartComponent"); + } + + public void CreateFolder(string path) + { + ExecuteRemoteProcess(String.Format("cmd.exe /c md \"{0}\"", path)); + } + + public void ExecuteRemoteProcess(string command) + { + Wmi cimv2 = new Wmi(ServerNameSettings, "root\\cimv2"); + ManagementClass objProcess = cimv2.GetWmiClass("Win32_Process"); + + // run process + object[] methodArgs = { command, null, null, 0 }; + objProcess.InvokeMethod("Create", methodArgs); + + // process ID + int processId = Convert.ToInt32(methodArgs[3]); + + // wait until finished + // Create event query to be notified within 1 second of + // a change in a service + WqlEventQuery query = + new WqlEventQuery("__InstanceDeletionEvent", + new TimeSpan(0, 0, 1), + "TargetInstance isa \"Win32_Process\""); + + // Initialize an event watcher and subscribe to events + // that match this query + ManagementEventWatcher watcher = new ManagementEventWatcher(cimv2.GetScope(), query); + // times out watcher.WaitForNextEvent in 20 seconds + watcher.Options.Timeout = new TimeSpan(0, 0, 20); + + // Block until the next event occurs + // Note: this can be done in a loop if waiting for + // more than one occurrence + while (true) + { + ManagementBaseObject e = null; + + try + { + // wait untill next process finish + e = watcher.WaitForNextEvent(); + } + catch + { + // nothing has been finished in timeout period + return; // exit + } + + // check process id + int pid = Convert.ToInt32(((ManagementBaseObject)e["TargetInstance"])["ProcessID"]); + if (pid == processId) + { + //Cancel the subscription + watcher.Stop(); + + // exit + return; + } + } + } + + public string GetTempRemoteFolder() + { + Wmi cimv2 = new Wmi(ServerNameSettings, "root\\cimv2"); + ManagementObject objOS = cimv2.GetWmiObject("win32_OperatingSystem"); + string sysPath = (string)objOS["SystemDirectory"]; + + // remove trailing slash + if (sysPath.EndsWith("\\")) + sysPath = sysPath.Substring(0, sysPath.Length - 1); + + sysPath = sysPath.Substring(0, sysPath.LastIndexOf("\\") + 1) + "Temp"; + + return sysPath; + } + #endregion + + #region Hyper-V Cloud + public bool CheckServerState(string connString) + { + return !String.IsNullOrEmpty(connString); + } + #endregion Hyper-V Cloud + + #region PowerShell integration + private static InitialSessionState session = null; + + internal virtual Runspace OpenRunspace() + { + HostedSolutionLog.LogStart("OpenRunspace"); + + if (session == null) + { + session = InitialSessionState.CreateDefault(); + session.ImportPSModule(new string[] { "Hyper-V" }); + } + Runspace runSpace = RunspaceFactory.CreateRunspace(session); + // + runSpace.Open(); + // + runSpace.SessionStateProxy.SetVariable("ConfirmPreference", "none"); + HostedSolutionLog.LogEnd("OpenRunspace"); + return runSpace; + } + + internal void CloseRunspace(Runspace runspace) + { + try + { + if (runspace != null && runspace.RunspaceStateInfo.State == RunspaceState.Opened) + { + runspace.Close(); + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("Runspace error", ex); + } + } + + internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd) + { + return ExecuteShellCommand(runSpace, cmd, true); + } + + internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, bool useDomainController) + { + object[] errors; + return ExecuteShellCommand(runSpace, cmd, useDomainController, out errors); + } + + internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, out object[] errors) + { + return ExecuteShellCommand(runSpace, cmd, true, out errors); + } + + internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, bool useDomainController, out object[] errors) + { + HostedSolutionLog.LogStart("ExecuteShellCommand"); + List errorList = new List(); + + HostedSolutionLog.DebugCommand(cmd); + Collection results = null; + // Create a pipeline + Pipeline pipeLine = runSpace.CreatePipeline(); + using (pipeLine) + { + // Add the command + pipeLine.Commands.Add(cmd); + // Execute the pipeline and save the objects returned. + results = pipeLine.Invoke(); + + // Log out any errors in the pipeline execution + // NOTE: These errors are NOT thrown as exceptions! + // Be sure to check this to ensure that no errors + // happened while executing the command. + if (pipeLine.Error != null && pipeLine.Error.Count > 0) + { + foreach (object item in pipeLine.Error.ReadToEnd()) + { + errorList.Add(item); + string errorMessage = string.Format("Invoke error: {0}", item); + HostedSolutionLog.LogWarning(errorMessage); + } + } + } + pipeLine = null; + errors = errorList.ToArray(); + HostedSolutionLog.LogEnd("ExecuteShellCommand"); + return results; + } + + internal object GetPSObjectProperty(PSObject obj, string name) + { + return obj.Members[name].Value; + } + internal T GetPSObjectProperty(PSObject obj, string name) + { + return (T)obj.Members[name].Value; + } + internal T GetPSObjectPropertyEnum(PSObject obj, string name) where T : struct + { + return (T) Enum.Parse(typeof (T), GetPSObjectProperty(obj, name).ToString()); + } + + /// + /// Returns the identity of the object from the shell execution result + /// + /// + /// + internal string GetResultObjectIdentity(Collection result) + { + HostedSolutionLog.LogStart("GetResultObjectIdentity"); + if (result == null) + throw new ArgumentNullException("result", "Execution result is not specified"); + + if (result.Count < 1) + throw new ArgumentException("Execution result is empty", "result"); + + if (result.Count > 1) + throw new ArgumentException("Execution result contains more than one object", "result"); + + PSMemberInfo info = result[0].Members["Identity"]; + if (info == null) + throw new ArgumentException("Execution result does not contain Identity property", "result"); + + string ret = info.Value.ToString(); + HostedSolutionLog.LogEnd("GetResultObjectIdentity"); + return ret; + } + + internal string GetResultObjectDN(Collection result) + { + HostedSolutionLog.LogStart("GetResultObjectDN"); + if (result == null) + throw new ArgumentNullException("result", "Execution result is not specified"); + + if (result.Count < 1) + throw new ArgumentException("Execution result does not contain any object"); + + if (result.Count > 1) + throw new ArgumentException("Execution result contains more than one object"); + + PSMemberInfo info = result[0].Members["DistinguishedName"]; + if (info == null) + throw new ArgumentException("Execution result does not contain DistinguishedName property", "result"); + + string ret = info.Value.ToString(); + HostedSolutionLog.LogEnd("GetResultObjectDN"); + return ret; + } + #endregion + + + internal int ConvertNullableToInt32(object value) + { + return value == null ? 0 : Convert.ToInt32(value); + } + + internal long ConvertNullableToInt64(object value) + { + return value == null ? 0 : Convert.ToInt64(value); + } + + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Properties/AssemblyInfo.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..cb7f4edb --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Properties/AssemblyInfo.cs @@ -0,0 +1,21 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WebsitePanel.Providers.Virtualization.HyperV2012R2")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("WebsitePanel.Providers.Virtualization.HyperV2012R2")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("959c2614-2d69-4e4f-9e77-bd868e5afd4b")] diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj new file mode 100644 index 00000000..31e9533c --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj @@ -0,0 +1,82 @@ + + + + + Debug + AnyCPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC} + Library + Properties + WebsitePanel.Providers.Virtualization.HyperV2012R2 + WebsitePanel.Providers.Virtualization.HyperV2012R2 + v4.5 + 512 + + + + true + full + false + ..\WebsitePanel.Server\bin\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + ..\..\Lib\References\Microsoft\Microsoft.Storage.Vds.dll + + + + + + + + + + False + ..\..\Lib\References\Microsoft\Windows2012\System.Management.Automation.dll + + + + + + VersionInfo.cs + + + + + + + + {684c932a-6c75-46ac-a327-f3689d89eb42} + WebsitePanel.Providers.Base + + + {a06de5e4-4331-47e1-8f46-7b846146b559} + WebsitePanel.Providers.HostedSolution + + + {e91e52f3-9555-4d00-b577-2b1dbdd87ca7} + WebsitePanel.Server.Utils + + + + + \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Wmi.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Wmi.cs new file mode 100644 index 00000000..8ab33885 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Wmi.cs @@ -0,0 +1,295 @@ +// Copyright (c) 2014, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.Collections.Generic; +using System.Text; +using System.Management; +using System.Diagnostics; + +namespace WebsitePanel.Providers.Virtualization +{ + internal class Wmi + { + string nameSpace = null; + string computerName = null; + ManagementScope scope = null; + + public Wmi(string nameSpace) : this(nameSpace, null) + { + } + + public Wmi(string computerName, string nameSpace) + { + this.nameSpace = nameSpace; + this.computerName = computerName; + } + + internal ManagementObjectCollection ExecuteWmiQuery(string query, params object[] args) + { + if (args != null && args.Length > 0) + query = String.Format(query, args); + + ManagementObjectSearcher searcher = new ManagementObjectSearcher(GetScope(), + new ObjectQuery(query)); + return searcher.Get(); + } + + internal ManagementObject GetWmiObject(string className, string filter, params object[] args) + { + ManagementObjectCollection col = GetWmiObjects(className, filter, args); + ManagementObjectCollection.ManagementObjectEnumerator enumerator = col.GetEnumerator(); + return enumerator.MoveNext() ? (ManagementObject)enumerator.Current : null; + } + + internal ManagementObject GetWmiObject(string className) + { + return GetWmiObject(className, null); + } + + internal ManagementObjectCollection GetWmiObjects(string className, string filter, params object[] args) + { + string query = "select * from " + className; + if (!String.IsNullOrEmpty(filter)) + query += " where " + filter; + return ExecuteWmiQuery(query, args); + } + + internal ManagementObjectCollection GetWmiObjects(string className) + { + return GetWmiObjects(className, null); + } + + internal ManagementObject GetWmiObjectByPath(string path) + { + return new ManagementObject(GetScope(), new ManagementPath(path), null); + } + + internal ManagementClass GetWmiClass(string className) + { + return new ManagementClass(GetScope(), new ManagementPath(className), null); + } + + internal ManagementObject GetRelatedWmiObject(ManagementObject obj, string className) + { + ManagementObjectCollection col = obj.GetRelated(className); + ManagementObjectCollection.ManagementObjectEnumerator enumerator = col.GetEnumerator(); + enumerator.MoveNext(); + return (ManagementObject)enumerator.Current; + } + + internal void Dump(ManagementBaseObject obj) + { +#if DEBUG + foreach (PropertyData prop in obj.Properties) + { + string typeName = prop.Value == null ? "null" : prop.Value.GetType().ToString(); + Debug.WriteLine(prop.Name + ": " + prop.Value + " (" + typeName + ")"); + } +#endif + } + + // Converts a given datetime in DMTF format to System.DateTime object. + internal System.DateTime ToDateTime(string dmtfDate) + { + System.DateTime initializer = System.DateTime.MinValue; + int year = initializer.Year; + int month = initializer.Month; + int day = initializer.Day; + int hour = initializer.Hour; + int minute = initializer.Minute; + int second = initializer.Second; + long ticks = 0; + string dmtf = dmtfDate; + System.DateTime datetime = System.DateTime.MinValue; + string tempString = string.Empty; + if (String.IsNullOrEmpty(dmtf)) + { + return DateTime.MinValue; + } + else if ((dmtf.Length != 25)) + { + throw new System.ArgumentOutOfRangeException(); + } + try + { + tempString = dmtf.Substring(0, 4); + if (("****" != tempString)) + { + year = int.Parse(tempString); + } + tempString = dmtf.Substring(4, 2); + if (("**" != tempString)) + { + month = int.Parse(tempString); + } + tempString = dmtf.Substring(6, 2); + if (("**" != tempString)) + { + day = int.Parse(tempString); + } + tempString = dmtf.Substring(8, 2); + if (("**" != tempString)) + { + hour = int.Parse(tempString); + } + tempString = dmtf.Substring(10, 2); + if (("**" != tempString)) + { + minute = int.Parse(tempString); + } + tempString = dmtf.Substring(12, 2); + if (("**" != tempString)) + { + second = int.Parse(tempString); + } + tempString = dmtf.Substring(15, 6); + if (("******" != tempString)) + { + ticks = (long.Parse(tempString) * ((long)((System.TimeSpan.TicksPerMillisecond / 1000)))); + } + if (((((((((year < 0) + || (month < 0)) + || (day < 0)) + || (hour < 0)) + || (minute < 0)) + || (minute < 0)) + || (second < 0)) + || (ticks < 0))) + { + throw new System.ArgumentOutOfRangeException(); + } + } + catch (System.Exception e) + { + throw new System.ArgumentOutOfRangeException(null, e.Message); + } + + if (year == 0 + && month == 0 + && day == 0 + && hour == 0 + && minute == 0 + && second == 0 + && ticks == 0) + return DateTime.MinValue; + + datetime = new System.DateTime(year, month, day, hour, minute, second, 0); + datetime = datetime.AddTicks(ticks); + System.TimeSpan tickOffset = System.TimeZone.CurrentTimeZone.GetUtcOffset(datetime); + int UTCOffset = 0; + int OffsetToBeAdjusted = 0; + long OffsetMins = ((long)((tickOffset.Ticks / System.TimeSpan.TicksPerMinute))); + tempString = dmtf.Substring(22, 3); + if ((tempString != "******")) + { + tempString = dmtf.Substring(21, 4); + try + { + UTCOffset = int.Parse(tempString); + } + catch (System.Exception e) + { + throw new System.ArgumentOutOfRangeException(null, e.Message); + } + OffsetToBeAdjusted = ((int)((OffsetMins - UTCOffset))); + datetime = datetime.AddMinutes(((double)(OffsetToBeAdjusted))); + } + return datetime; + } + + // Converts a given System.DateTime object to DMTF datetime format. + internal string ToDmtfDateTime(System.DateTime date) + { + string utcString = string.Empty; + System.TimeSpan tickOffset = System.TimeZone.CurrentTimeZone.GetUtcOffset(date); + long OffsetMins = ((long)((tickOffset.Ticks / System.TimeSpan.TicksPerMinute))); + if ((System.Math.Abs(OffsetMins) > 999)) + { + date = date.ToUniversalTime(); + utcString = "+000"; + } + else + { + if ((tickOffset.Ticks >= 0)) + { + utcString = string.Concat("+", ((System.Int64)((tickOffset.Ticks / System.TimeSpan.TicksPerMinute))).ToString().PadLeft(3, '0')); + } + else + { + string strTemp = ((System.Int64)(OffsetMins)).ToString(); + utcString = string.Concat("-", strTemp.Substring(1, (strTemp.Length - 1)).PadLeft(3, '0')); + } + } + string dmtfDateTime = ((System.Int32)(date.Year)).ToString().PadLeft(4, '0'); + dmtfDateTime = string.Concat(dmtfDateTime, ((System.Int32)(date.Month)).ToString().PadLeft(2, '0')); + dmtfDateTime = string.Concat(dmtfDateTime, ((System.Int32)(date.Day)).ToString().PadLeft(2, '0')); + dmtfDateTime = string.Concat(dmtfDateTime, ((System.Int32)(date.Hour)).ToString().PadLeft(2, '0')); + dmtfDateTime = string.Concat(dmtfDateTime, ((System.Int32)(date.Minute)).ToString().PadLeft(2, '0')); + dmtfDateTime = string.Concat(dmtfDateTime, ((System.Int32)(date.Second)).ToString().PadLeft(2, '0')); + dmtfDateTime = string.Concat(dmtfDateTime, "."); + System.DateTime dtTemp = new System.DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, 0); + long microsec = ((long)((((date.Ticks - dtTemp.Ticks) + * 1000) + / System.TimeSpan.TicksPerMillisecond))); + string strMicrosec = ((System.Int64)(microsec)).ToString(); + if ((strMicrosec.Length > 6)) + { + strMicrosec = strMicrosec.Substring(0, 6); + } + dmtfDateTime = string.Concat(dmtfDateTime, strMicrosec.PadLeft(6, '0')); + dmtfDateTime = string.Concat(dmtfDateTime, utcString); + return dmtfDateTime; + } + + public ManagementScope GetScope() + { + if (scope != null) + return scope; + + // create new scope + if (String.IsNullOrEmpty(computerName)) + { + // local + scope = new ManagementScope(nameSpace); + } + else + { + // remote + ConnectionOptions options = new ConnectionOptions(); + + string path = String.Format(@"\\{0}\{1}", computerName, nameSpace); + scope = new ManagementScope(path, options); + } + + // connect + scope.Connect(); + return scope; + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV/HyperV.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV/HyperV.cs index d035f0d9..2658df64 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV/HyperV.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV/HyperV.cs @@ -488,7 +488,7 @@ namespace WebsitePanel.Providers.Virtualization return vm; } - private void UpdateVirtualMachineGeneralSettings(string vmId, ManagementObject objVM, int cpuCores, int ramMB, bool bootFromCD, bool numLockEnabled) + private void UpdateVirtualMachineGeneralSettings(string vmId, ManagementObject objVM, int cpuCores, long ramMB, bool bootFromCD, bool numLockEnabled) { // request management service ManagementObject objVmsvc = GetVirtualSystemManagementService(); @@ -1989,10 +1989,10 @@ exit", Convert.ToInt32(objDisk["Index"]))); #region Stop else if (!started && - (vps.State == VirtualMachineState.Started + (vps.State == VirtualMachineState.Running || vps.State == VirtualMachineState.Paused)) { - if (vps.State == VirtualMachineState.Started) + if (vps.State == VirtualMachineState.Running) { // try to shutdown the system ReturnCode code = ShutDownVirtualMachine(vm.VirtualMachineId, true, "Virtual Machine has been suspended from WebsitePanel"); diff --git a/WebsitePanel/Sources/WebsitePanel.Server.sln b/WebsitePanel/Sources/WebsitePanel.Server.sln index 8f1323d3..2b573470 100644 --- a/WebsitePanel/Sources/WebsitePanel.Server.sln +++ b/WebsitePanel/Sources/WebsitePanel.Server.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.30723.0 +VisualStudioVersion = 12.0.21005.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Caching Application Block", "Caching Application Block", "{C8E6F2E4-A5B8-486A-A56E-92D864524682}" ProjectSection(SolutionItems) = preProject @@ -154,6 +154,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebsitePanel.Providers.Host EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebsitePanel.Providers.Mail.IceWarp", "WebsitePanel.Providers.Mail.IceWarp\WebsitePanel.Providers.Mail.IceWarp.csproj", "{95EA2D6E-278C-4A74-97DB-946362C4DEEA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebsitePanel.Providers.Virtualization.HyperV2012R2", "WebsitePanel.Providers.Virtualization.HyperV-2012R2\WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj", "{EE40516B-93DF-4CAB-80C4-64DCE0B751CC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -774,6 +776,16 @@ Global {95EA2D6E-278C-4A74-97DB-946362C4DEEA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {95EA2D6E-278C-4A74-97DB-946362C4DEEA}.Release|Mixed Platforms.Build.0 = Release|Any CPU {95EA2D6E-278C-4A74-97DB-946362C4DEEA}.Release|x86.ActiveCfg = Release|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Debug|x86.ActiveCfg = Debug|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Release|Any CPU.Build.0 = Release|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {EE40516B-93DF-4CAB-80C4-64DCE0B751CC}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/UserControls/Gauge.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/UserControls/Gauge.ascx.cs index 6d80d876..9a7d4440 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/UserControls/Gauge.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/UserControls/Gauge.ascx.cs @@ -56,15 +56,15 @@ namespace WebsitePanel.Portal set { ViewState["DisplayText"] = value; } } - public int Progress + public long Progress { - get { return (ViewState["Progress"] != null) ? (int)ViewState["Progress"] : 0; } + get { return (ViewState["Progress"] != null) ? (long)ViewState["Progress"] : 0; } set { ViewState["Progress"] = value; } } - public int Total + public long Total { - get { return (ViewState["Total"] != null) ? (int)ViewState["Total"] : 0; } + get { return (ViewState["Total"] != null) ? (long)ViewState["Total"] : 0; } set { ViewState["Total"] = value; } } @@ -101,7 +101,7 @@ namespace WebsitePanel.Portal string bkgSrc = Page.ResolveUrl(PortalUtils.GetThemedImage("gauge_bkg.gif")); // calculate the width of the gauge - int fTotal = Total; + long fTotal = Total; int percent = (fTotal > 0) ? Convert.ToInt32(Math.Round((double)Progress / (double)fTotal * 100)) : 0; double fFilledWidth = (fTotal > 0) ? ((double)Progress / (double)fTotal * Width) : 0; diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/UserControls/QuotaViewer.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/UserControls/QuotaViewer.ascx.cs index 2ee49ac3..83bdf399 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/UserControls/QuotaViewer.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/UserControls/QuotaViewer.ascx.cs @@ -92,7 +92,7 @@ namespace WebsitePanel.Portal private void UpdateControl() { - int total = gauge.Total; + long total = gauge.Total; if (QuotaTypeId == 1) { litValue.Text = (total == 0) ? GetLocalizedString("Text.Disabled") : GetLocalizedString("Text.Enabled"); diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/VPS/App_LocalResources/VpsDetailsGeneral.ascx.resx b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/VPS/App_LocalResources/VpsDetailsGeneral.ascx.resx index aa3c2195..5d662ca5 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/VPS/App_LocalResources/VpsDetailsGeneral.ascx.resx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/VPS/App_LocalResources/VpsDetailsGeneral.ascx.resx @@ -213,6 +213,9 @@ OK + + Paused + Change VPS Host Name @@ -273,6 +276,9 @@ Starting + + Running + Stopping diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/VPS/VpsDetailsGeneral.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/VPS/VpsDetailsGeneral.ascx.cs index 5345cc52..5366c59d 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/VPS/VpsDetailsGeneral.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/VPS/VpsDetailsGeneral.ascx.cs @@ -98,9 +98,9 @@ namespace WebsitePanel.Portal.VPS TimeSpan uptime = TimeSpan.FromMilliseconds(vm.Uptime); uptime = uptime.Subtract(TimeSpan.FromMilliseconds(uptime.Milliseconds)); litUptime.Text = uptime.ToString(); - litStatus.Text = GetLocalizedString("State." + vm.State.ToString()); + litStatus.Text = GetLocalizedString("State." + vm.State); litCreated.Text = vm.CreatedDate.ToString(); - litHeartbeat.Text = GetLocalizedString("Heartbeat." + vm.Heartbeat.ToString()); + litHeartbeat.Text = GetLocalizedString("Heartbeat." + vm.Heartbeat); // CPU cpuGauge.Progress = vm.CpuUsage; @@ -155,7 +155,7 @@ namespace WebsitePanel.Portal.VPS || vm.State == VirtualMachineState.Saved)) buttons.Add(CreateActionButton("Start", "start.png")); - if (vm.State == VirtualMachineState.Started) + if (vm.State == VirtualMachineState.Running) { if(vmi.RebootAllowed) buttons.Add(CreateActionButton("Reboot", "reboot.png")); @@ -165,12 +165,12 @@ namespace WebsitePanel.Portal.VPS } if (vmi.StartTurnOffAllowed - && (vm.State == VirtualMachineState.Started + && (vm.State == VirtualMachineState.Running || vm.State == VirtualMachineState.Paused)) buttons.Add(CreateActionButton("TurnOff", "turnoff.png")); if (vmi.PauseResumeAllowed - && vm.State == VirtualMachineState.Started) + && vm.State == VirtualMachineState.Running) buttons.Add(CreateActionButton("Pause", "pause.png")); if (vmi.PauseResumeAllowed @@ -178,7 +178,7 @@ namespace WebsitePanel.Portal.VPS buttons.Add(CreateActionButton("Resume", "start2.png")); if (vmi.ResetAllowed - && (vm.State == VirtualMachineState.Started + && (vm.State == VirtualMachineState.Running || vm.State == VirtualMachineState.Paused)) buttons.Add(CreateActionButton("Reset", "reset2.png")); From fe0aee862c896f4d6196b7d45b835d823f488d07 Mon Sep 17 00:00:00 2001 From: me Date: Wed, 4 Mar 2015 20:08:45 +0400 Subject: [PATCH 03/12] =?UTF-8?q?wsp-10323=20=D0=A1onvert=20the=20VSP=20pr?= =?UTF-8?q?ovider=20into=20one=20utilizing=20PowerShell=20-=20Step=201.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebsitePanel/Sources/.nuget/NuGet.targets | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WebsitePanel/Sources/.nuget/NuGet.targets b/WebsitePanel/Sources/.nuget/NuGet.targets index b2066df9..428c8dad 100644 --- a/WebsitePanel/Sources/.nuget/NuGet.targets +++ b/WebsitePanel/Sources/.nuget/NuGet.targets @@ -97,9 +97,9 @@ - + Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" /> From 5b09a543f8e3bdd7157fcea40da94994aa86b8fc Mon Sep 17 00:00:00 2001 From: AlexanderTr Date: Fri, 6 Mar 2015 15:03:07 +0300 Subject: [PATCH 04/12] wsp-10323 Convert the VSP provider into one utilizing PowerShell. Step 2 --- .../Virtualization/DvdDriveInfo.cs | 43 ++ .../VirtualMachineNetworkAdapter.cs | 1 + .../WebsitePanel.Providers.Base.csproj | 1 + .../Extensions/PSObjectExtension.cs | 37 ++ .../Helpers/DvdDriveHelper.cs | 83 +++ .../Helpers/NetworkAdapterHelper.cs | 104 ++++ .../Helpers/VirtualMachineHelper.cs | 195 ++++++ .../HyperV2012R2.cs | 564 +++--------------- .../PowerShellManager.cs | 159 +++++ ...oviders.Virtualization.HyperV2012R2.csproj | 5 + 10 files changed, 699 insertions(+), 493 deletions(-) create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/DvdDriveInfo.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Extensions/PSObjectExtension.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/DvdDriveHelper.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/NetworkAdapterHelper.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VirtualMachineHelper.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/PowerShellManager.cs diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/DvdDriveInfo.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/DvdDriveInfo.cs new file mode 100644 index 00000000..df548b0b --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/DvdDriveInfo.cs @@ -0,0 +1,43 @@ +// Copyright (c) 2014, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace WebsitePanel.Providers.Virtualization +{ + public class DvdDriveInfo + { + public ControllerType ControllerType { get; set; } + public int ControllerNumber { get; set; } + public int ControllerLocation { get; set; } + public string Name { get; set; } + public string Id { get; set; } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineNetworkAdapter.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineNetworkAdapter.cs index fe2646d3..d1209d10 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineNetworkAdapter.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineNetworkAdapter.cs @@ -36,5 +36,6 @@ namespace WebsitePanel.Providers.Virtualization { public string Name { get; set; } public string MacAddress { get; set; } + public string SwitchName { get; set; } } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj index c52d1cdb..f60340ee 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj @@ -309,6 +309,7 @@ + diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Extensions/PSObjectExtension.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Extensions/PSObjectExtension.cs new file mode 100644 index 00000000..e1a50bfa --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Extensions/PSObjectExtension.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + static class PSObjectExtension + { + public static object GetProperty(this PSObject obj, string name) + { + return obj.Members[name].Value; + } + public static T GetProperty(this PSObject obj, string name) + { + return (T)obj.Members[name].Value; + } + public static T GetEnum(this PSObject obj, string name) where T : struct + { + return (T)Enum.Parse(typeof(T), GetProperty(obj, name).ToString()); + } + public static int GetInt(this PSObject obj, string name) + { + return Convert.ToInt32(obj.Members[name].Value); + } + public static long GetLong(this PSObject obj, string name) + { + return Convert.ToInt64(obj.Members[name].Value); + } + public static string GetString(this PSObject obj, string name) + { + return obj.Members[name].Value == null ? "" : obj.Members[name].Value.ToString(); + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/DvdDriveHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/DvdDriveHelper.cs new file mode 100644 index 00000000..871e8fa3 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/DvdDriveHelper.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class DvdDriveHelper + { + public static DvdDriveInfo Get(PowerShellManager powerShell, string vmName) + { + DvdDriveInfo info = new DvdDriveInfo(); + + Command cmd = new Command("Get-VMDvdDrive"); + + cmd.Parameters.Add("VMName", vmName); + + Collection result = powerShell.Execute(cmd, false); + + if (result != null && result.Count > 0) + { + info.Id = result[0].GetString("Id"); + info.Name = result[0].GetString("Name"); + info.ControllerType = result[0].GetEnum("ControllerType"); + info.ControllerNumber = result[0].GetInt("ControllerNumber"); + info.ControllerLocation = result[0].GetInt("ControllerLocation"); + } + return info; + } + + public static void Set(PowerShellManager powerShell, string vmName, string path) + { + var dvd = Get(powerShell, vmName); + + Command cmd = new Command("Set-VMDvdDrive"); + + cmd.Parameters.Add("VMName", vmName); + cmd.Parameters.Add("Path", path); + cmd.Parameters.Add("ControllerNumber", dvd.ControllerNumber); + cmd.Parameters.Add("ControllerLocation", dvd.ControllerLocation); + + powerShell.Execute(cmd, false); + } + + public static void Update(PowerShellManager powerShell, VirtualMachine vm, bool dvdDriveShouldBeInstalled) + { + if (!vm.DvdDriveInstalled && dvdDriveShouldBeInstalled) + Add(powerShell, vm.Name); + else if (vm.DvdDriveInstalled && !dvdDriveShouldBeInstalled) + Remove(powerShell, vm.Name); + } + + public static void Add(PowerShellManager powerShell, string vmName) + { + var dvd = Get(powerShell, vmName); + + Command cmd = new Command("Add-VMDvdDrive"); + + cmd.Parameters.Add("VMName", vmName); + cmd.Parameters.Add("ControllerNumber", dvd.ControllerNumber); + cmd.Parameters.Add("ControllerLocation", dvd.ControllerLocation); + + powerShell.Execute(cmd, false); + } + + public static void Remove(PowerShellManager powerShell, string vmName) + { + var dvd = Get(powerShell, vmName); + + Command cmd = new Command("Remove-VMDvdDrive"); + + cmd.Parameters.Add("VMName", vmName); + cmd.Parameters.Add("ControllerNumber", dvd.ControllerNumber); + cmd.Parameters.Add("ControllerLocation", dvd.ControllerLocation); + + powerShell.Execute(cmd, false); + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/NetworkAdapterHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/NetworkAdapterHelper.cs new file mode 100644 index 00000000..c00ff956 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/NetworkAdapterHelper.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class NetworkAdapterHelper + { + #region Constants + + private const string EXTERNAL_NETWORK_ADAPTER_NAME = "External Network Adapter"; + private const string PRIVATE_NETWORK_ADAPTER_NAME = "Private Network Adapter"; + private const string MANAGEMENT_NETWORK_ADAPTER_NAME = "Management Network Adapter"; + + #endregion + + public static VirtualMachineNetworkAdapter[] Get(PowerShellManager powerShell, string vmName) + { + List adapters = new List(); + + Command cmd = new Command("Get-VMNetworkAdapter"); + if (!string.IsNullOrEmpty(vmName)) cmd.Parameters.Add("VMName", vmName); + + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + foreach (PSObject psAdapter in result) + { + VirtualMachineNetworkAdapter adapter = new VirtualMachineNetworkAdapter(); + + adapter.Name = psAdapter.GetString("Name"); + adapter.MacAddress = psAdapter.GetString("MacAddress"); + adapter.SwitchName = psAdapter.GetString("SwitchName"); + + adapters.Add(adapter); + } + } + return adapters.ToArray(); + } + + public static VirtualMachineNetworkAdapter Get(PowerShellManager powerShell, string vmName, string macAddress) + { + var adapters = Get(powerShell, vmName); + return adapters.FirstOrDefault(a => a.MacAddress == macAddress); + } + + public static void Update(PowerShellManager powerShell, VirtualMachine vm, string switchId, string portName, string macAddress, string adapterName, bool legacyAdapter) + { + // External NIC + if (!vm.ExternalNetworkEnabled && !String.IsNullOrEmpty(vm.ExternalNicMacAddress)) + { + // delete adapter + Delete(powerShell, vm.Name, vm.ExternalNicMacAddress); + vm.ExternalNicMacAddress = null; // reset MAC + } + else if (vm.ExternalNetworkEnabled && !String.IsNullOrEmpty(vm.ExternalNicMacAddress)) + { + // add external adapter + Add(powerShell, vm.Name, vm.ExternalSwitchId, vm.ExternalNicMacAddress, EXTERNAL_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + } + + // Private NIC + if (!vm.PrivateNetworkEnabled && !String.IsNullOrEmpty(vm.PrivateNicMacAddress)) + { + Delete(powerShell, vm.Name, vm.PrivateNicMacAddress); + vm.PrivateNicMacAddress = null; // reset MAC + } + else if (vm.PrivateNetworkEnabled && !String.IsNullOrEmpty(vm.PrivateNicMacAddress)) + { + Add(powerShell, vm.Name, vm.ExternalSwitchId, vm.ExternalNicMacAddress, PRIVATE_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + } + } + + public static void Add(PowerShellManager powerShell, string vmName, string switchId, string macAddress, string adapterName, bool legacyAdapter) + { + //var dvd = Get(powerShell, vmName); + + //Command cmd = new Command("Add-VMDvdDrive"); + + //cmd.Parameters.Add("VMName", vmName); + //cmd.Parameters.Add("ControllerNumber", dvd.ControllerNumber); + //cmd.Parameters.Add("ControllerLocation", dvd.ControllerLocation); + + //powerShell.Execute(cmd, false); + } + public static void Delete(PowerShellManager powerShell, string vmName, string macAddress) + { + //var dvd = Get(powerShell, vmName); + + //Command cmd = new Command("Add-VMDvdDrive"); + + //cmd.Parameters.Add("VMName", vmName); + //cmd.Parameters.Add("ControllerNumber", dvd.ControllerNumber); + //cmd.Parameters.Add("ControllerLocation", dvd.ControllerLocation); + + //powerShell.Execute(cmd, false); + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VirtualMachineHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VirtualMachineHelper.cs new file mode 100644 index 00000000..66958337 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VirtualMachineHelper.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class VirtualMachineHelper + { + #region Constants + + private const Int64 Size1G = 0x40000000; + private const Int64 Size1M = 0x100000; + + #endregion + + public static OperationalStatus GetVMHeartBeatStatus(PowerShellManager powerShell, string name) + { + + OperationalStatus status = OperationalStatus.None; + + Command cmd = new Command("Get-VMIntegrationService"); + + cmd.Parameters.Add("VMName", name); + cmd.Parameters.Add("Name", "HeartBeat"); + + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + var statusString = result[0].GetProperty("PrimaryOperationalStatus"); + + if (statusString != null) + status = (OperationalStatus)Enum.Parse(typeof(OperationalStatus), statusString.ToString()); + } + return status; + } + + + public static int GetVMProcessors(PowerShellManager powerShell, string name) + { + + int procs = 0; + + Command cmd = new Command("Get-VMProcessor"); + + cmd.Parameters.Add("VMName", name); + + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + procs = Convert.ToInt32(result[0].GetProperty("Count")); + + } + return procs; + } + + public static MemoryInfo GetVMMemory(PowerShellManager powerShell, string name) + { + MemoryInfo info = new MemoryInfo(); + + Command cmd = new Command("Get-VMMemory"); + + cmd.Parameters.Add("VMName", name); + + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + info.DynamicMemoryEnabled = Convert.ToBoolean(result[0].GetProperty("DynamicMemoryEnabled")); + info.Startup = Convert.ToInt64(result[0].GetProperty("Startup")); + info.Minimum = Convert.ToInt64(result[0].GetProperty("Minimum")); + info.Maximum = Convert.ToInt64(result[0].GetProperty("Maximum")); + info.Buffer = Convert.ToInt32(result[0].GetProperty("Buffer")); + info.Priority = Convert.ToInt32(result[0].GetProperty("Priority")); + } + return info; + } + + public static BiosInfo GetVMBios(PowerShellManager powerShell, string name) + { + BiosInfo info = new BiosInfo(); + + Command cmd = new Command("Get-VMBios"); + + cmd.Parameters.Add("VMName", name); + + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + info.NumLockEnabled = Convert.ToBoolean(result[0].GetProperty("NumLockEnabled")); + + List startupOrders = new List(); + + foreach (var item in (IEnumerable)result[0].GetProperty("StartupOrder")) + startupOrders.Add(item.ToString()); + + info.StartupOrder = startupOrders.ToArray(); + } + return info; + } + + public static VirtualHardDiskInfo[] GetVirtualHardDisks(PowerShellManager powerShell, string name) + { + + List disks = new List(); + + Command cmd = new Command("Get-VMHardDiskDrive"); + cmd.Parameters.Add("VMName", name); + + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + foreach (PSObject d in result) + { + VirtualHardDiskInfo disk = new VirtualHardDiskInfo(); + + disk.SupportPersistentReservations = Convert.ToBoolean(d.GetProperty("SupportPersistentReservations")); + disk.MaximumIOPS = Convert.ToUInt64(d.GetProperty("MaximumIOPS")); + disk.MinimumIOPS = Convert.ToUInt64(d.GetProperty("MinimumIOPS")); + disk.VHDControllerType = d.GetEnum("ControllerType"); + disk.ControllerNumber = Convert.ToInt32(d.GetProperty("ControllerNumber")); + disk.ControllerLocation = Convert.ToInt32(d.GetProperty("ControllerLocation")); + disk.Path = d.GetProperty("Path").ToString(); + disk.Name = d.GetProperty("Name").ToString(); + + GetVirtualHardDiskDetail(powerShell, disk.Path, ref disk); + + disks.Add(disk); + } + } + return disks.ToArray(); + } + + public static void GetVirtualHardDiskDetail(PowerShellManager powerShell, string path, ref VirtualHardDiskInfo disk) + { + if (!string.IsNullOrEmpty(path)) + { + Command cmd = new Command("Get-VHD"); + cmd.Parameters.Add("Path", path); + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + disk.DiskFormat = result[0].GetEnum("VhdFormat"); + disk.DiskType = result[0].GetEnum("VhdType"); + disk.ParentPath = result[0].GetProperty("ParentPath"); + disk.MaxInternalSize = Convert.ToInt64(result[0].GetProperty("Size")) / Size1G; + disk.FileSize = Convert.ToInt64(result[0].GetProperty("FileSize")) / Size1G; + disk.Attached = Convert.ToBoolean(result[0].GetProperty("Attached")); + } + } + } + + + + + public static void UpdateBios(PowerShellManager powerShell, VirtualMachine vm, bool bootFromCD, bool numLockEnabled) + { + Command cmd = new Command("Set-VMBios"); + + cmd.Parameters.Add("VMName", vm.Name); + cmd.Parameters.Add(numLockEnabled ? "EnableNumLock" : "DisableNumLock"); + var bootOrder = bootFromCD + ? new[] { "CD", "IDE", "LegacyNetworkAdapter", "Floppy" } + : new[] { "IDE", "CD", "LegacyNetworkAdapter", "Floppy" }; + cmd.Parameters.Add("StartupOrder", bootOrder); + + powerShell.Execute(cmd, false); + } + public static void UpdateProcessors(PowerShellManager powerShell, VirtualMachine vm, int cpuCores, int cpuLimitSettings, int cpuReserveSettings, int cpuWeightSettings) + { + Command cmd = new Command("Set-VMProcessor"); + + cmd.Parameters.Add("VMName", vm.Name); + cmd.Parameters.Add("Count", cpuCores); + cmd.Parameters.Add("Maximum", Convert.ToInt64(cpuLimitSettings * 1000)); + cmd.Parameters.Add("Reserve", Convert.ToInt64(cpuReserveSettings * 1000)); + cmd.Parameters.Add("RelativeWeight", cpuWeightSettings); + + powerShell.Execute(cmd, false); + } + public static void UpdateMemory(PowerShellManager powerShell, VirtualMachine vm, long ramMB) + { + Command cmd = new Command("Set-VMMemory"); + + cmd.Parameters.Add("VMName", vm.Name); + cmd.Parameters.Add("StartupBytes", ramMB); + + powerShell.Execute(cmd, false); + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs index 71b11f28..2247adf7 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs @@ -70,8 +70,6 @@ namespace WebsitePanel.Providers.Virtualization private const string KVP_RAM_SUMMARY_KEY = "VM-RAM-Summary"; private const string KVP_HDD_SUMMARY_KEY = "VM-HDD-Summary"; - private const Int64 Size1G = 0x40000000; - private const Int64 Size1M = 0x100000; #endregion @@ -141,49 +139,51 @@ namespace WebsitePanel.Providers.Virtualization public VirtualMachine GetVirtualMachine(string vmId) { - return GetVirtualMachineInternal( vmId, false); + return GetVirtualMachineInternal(vmId, false); } - public VirtualMachine GetVirtualMachineInternal(string vmId, bool extendedInfo) + public VirtualMachine GetVirtualMachineEx(string vmId) { + return GetVirtualMachineInternal(vmId, true); + } + protected VirtualMachine GetVirtualMachineInternal(string vmId, bool extendedInfo) + { HostedSolutionLog.LogStart("GetVirtualMachine"); HostedSolutionLog.DebugInfo("Virtual Machine: {0}", vmId); - Runspace runSpace = null; VirtualMachine vm = new VirtualMachine(); try { - runSpace = OpenRunspace(); Command cmd = new Command("Get-VM"); cmd.Parameters.Add("Id", vmId); - - Collection result = ExecuteShellCommand(runSpace, cmd, false); + + Collection result = PowerShell.Execute(cmd, false); if (result != null && result.Count > 0) { - vm.Name = GetPSObjectProperty(result[0], "Name").ToString(); - vm.State = (VirtualMachineState)Enum.Parse(typeof(VirtualMachineState), GetPSObjectProperty(result[0], "State").ToString()); - vm.CpuUsage = ConvertNullableToInt32(GetPSObjectProperty(result[0], "CpuUsage")); - vm.RamUsage = ConvertNullableToInt64(GetPSObjectProperty(result[0], "MemoryAssigned")); - vm.Uptime = TimeSpan.Parse(GetPSObjectProperty(result[0], "Uptime").ToString()).Ticks; - vm.Status = GetPSObjectProperty(result[0], "Status").ToString(); - vm.ReplicationState = GetPSObjectProperty(result[0], "ReplicationState").ToString(); - - vm.Heartbeat = GetVMHeartBeatStatus(runSpace, vm.Name); + vm.Name = result[0].GetProperty("Name").ToString(); + vm.State = result[0].GetEnum("State"); + vm.CpuUsage = ConvertNullableToInt32(result[0].GetProperty("CpuUsage")); + vm.RamUsage = ConvertNullableToInt64(result[0].GetProperty("MemoryAssigned")); + vm.Uptime = Convert.ToInt64(result[0].GetProperty("UpTime").TotalMilliseconds); + vm.Status = result[0].GetProperty("Status").ToString(); + vm.ReplicationState = result[0].GetProperty("ReplicationState").ToString(); + + vm.Heartbeat = VirtualMachineHelper.GetVMHeartBeatStatus(PowerShell, vm.Name); vm.CreatedDate = DateTime.Now; if (extendedInfo) { - vm.CpuCores = GetVMProcessors(runSpace, vm.Name); + vm.CpuCores = VirtualMachineHelper.GetVMProcessors(PowerShell, vm.Name); - MemoryInfo memoryInfo = GetVMMemory(runSpace, vm.Name); + MemoryInfo memoryInfo = VirtualMachineHelper.GetVMMemory(PowerShell, vm.Name); vm.RamSize = memoryInfo.Startup; // BIOS - BiosInfo biosInfo = GetVMBios(runSpace, vm.Name); + BiosInfo biosInfo = VirtualMachineHelper.GetVMBios(PowerShell, vm.Name); vm.NumLockEnabled = biosInfo.NumLockEnabled; vm.BootFromCD = false; @@ -191,27 +191,21 @@ namespace WebsitePanel.Providers.Virtualization vm.BootFromCD = (biosInfo.StartupOrder[0] == "CD"); // DVD drive - cmd = new Command("Get-VMDvdDrive"); - cmd.Parameters.Add("VMName", vm.Name); - - result = ExecuteShellCommand(runSpace, cmd, false); - vm.DvdDriveInstalled = (result != null && result.Count > 0); + var dvdInfo = DvdDriveHelper.Get(PowerShell, vm.Name); + vm.DvdDriveInstalled = dvdInfo != null; // HDD - vm.Disks = GetVirtualHardDisks(runSpace, vm.Name); + vm.Disks = VirtualMachineHelper.GetVirtualHardDisks(PowerShell, vm.Name); - if ((vm.Disks != null) & (vm.Disks.GetLength(0) > 0)) + if (vm.Disks != null && vm.Disks.GetLength(0) > 0) { vm.VirtualHardDrivePath = vm.Disks[0].Path; vm.HddSize = Convert.ToInt32(vm.Disks[0].FileSize); } // network adapters - vm.Adapters = GetNetworkAdapters(runSpace, vm.Name); - return vm; - + vm.Adapters = NetworkAdapterHelper.Get(PowerShell, vm.Name); } - } } catch (Exception ex) @@ -219,201 +213,30 @@ namespace WebsitePanel.Providers.Virtualization HostedSolutionLog.LogError("GetVirtualMachine", ex); throw; } - finally - { - CloseRunspace(runSpace); - } HostedSolutionLog.LogEnd("GetVirtualMachine"); return vm; } - - internal OperationalStatus GetVMHeartBeatStatus(Runspace runSpace, string name) - { - - OperationalStatus status = OperationalStatus.None; - - Command cmd = new Command("Get-VMIntegrationService"); - - cmd.Parameters.Add("VMName", name); - cmd.Parameters.Add("Name", "HeartBeat"); - - Collection result = ExecuteShellCommand(runSpace, cmd, false); - if (result != null && result.Count > 0) - { - var statusString = GetPSObjectProperty(result[0], "PrimaryOperationalStatus"); - - if (statusString != null) - status = (OperationalStatus)Enum.Parse(typeof(OperationalStatus), statusString.ToString()); - } - return status; - } - - public VirtualMachine GetVirtualMachineEx(string vmId) - { - return GetVirtualMachineInternal( vmId, true); - } - - - internal int GetVMProcessors(Runspace runSpace, string name) - { - - int procs = 0; - - Command cmd = new Command("Get-VMProcessor"); - - cmd.Parameters.Add("VMName", name); - - Collection result = ExecuteShellCommand(runSpace, cmd, false); - if (result != null && result.Count > 0) - { - procs = Convert.ToInt32(GetPSObjectProperty(result[0], "Count")); - - } - return procs; - } - - internal MemoryInfo GetVMMemory(Runspace runSpace, string name) - { - MemoryInfo info = new MemoryInfo(); - - Command cmd = new Command("Get-VMMemory"); - - cmd.Parameters.Add("VMName", name); - - Collection result = ExecuteShellCommand(runSpace, cmd, false); - if (result != null && result.Count > 0) - { - info.DynamicMemoryEnabled = Convert.ToBoolean(GetPSObjectProperty(result[0], "DynamicMemoryEnabled")); - info.Startup = Convert.ToInt64(GetPSObjectProperty(result[0], "Startup")); - info.Minimum = Convert.ToInt64(GetPSObjectProperty(result[0], "Minimum")); - info.Maximum = Convert.ToInt64(GetPSObjectProperty(result[0], "Maximum")); - info.Buffer = Convert.ToInt32(GetPSObjectProperty(result[0], "Buffer")); - info.Priority = Convert.ToInt32(GetPSObjectProperty(result[0], "Priority")); - } - return info; - } - - internal BiosInfo GetVMBios(Runspace runSpace, string name) - { - BiosInfo info = new BiosInfo(); - - Command cmd = new Command("Get-VMBios"); - - cmd.Parameters.Add("VMName", name); - - Collection result = ExecuteShellCommand(runSpace, cmd, false); - if (result != null && result.Count > 0) - { - info.NumLockEnabled = Convert.ToBoolean(GetPSObjectProperty(result[0], "NumLockEnabled")); - - List startupOrders = new List(); - - foreach (var item in (IEnumerable)GetPSObjectProperty(result[0], "StartupOrder")) - startupOrders.Add(item.ToString()); - - info.StartupOrder = startupOrders.ToArray(); - } - return info; - } - - internal VirtualHardDiskInfo[] GetVirtualHardDisks(Runspace runSpace, string name) - { - - List disks = new List(); - - Command cmd = new Command("Get-VMHardDiskDrive"); - cmd.Parameters.Add("VMName", name); - - Collection result = ExecuteShellCommand(runSpace, cmd, false); - if (result != null && result.Count > 0) - { - foreach(PSObject d in result) - { - VirtualHardDiskInfo disk = new VirtualHardDiskInfo(); - - disk.SupportPersistentReservations = Convert.ToBoolean(GetPSObjectProperty(d, "SupportPersistentReservations")); - disk.MaximumIOPS = Convert.ToUInt64(GetPSObjectProperty(d, "MaximumIOPS")); - disk.MinimumIOPS = Convert.ToUInt64(GetPSObjectProperty(d, "MinimumIOPS")); - disk.VHDControllerType = (ControllerType)Enum.Parse(typeof(ControllerType), GetPSObjectProperty(d, "ControllerType").ToString()); - disk.ControllerNumber = Convert.ToInt32(GetPSObjectProperty(d, "ControllerNumber")); - disk.ControllerLocation = Convert.ToInt32(GetPSObjectProperty(d, "ControllerLocation")); - disk.Path = GetPSObjectProperty(d, "Path").ToString(); - disk.Name = GetPSObjectProperty(d, "Name").ToString(); - - GetVirtualHardDiskDetail(runSpace, disk.Path, ref disk); - - disks.Add(disk); - } - } - return disks.ToArray(); - } - - internal void GetVirtualHardDiskDetail(Runspace runSpace, string path, ref VirtualHardDiskInfo disk) - { - if (!string.IsNullOrEmpty(path)) - { - Command cmd = new Command("Get-VHD"); - cmd.Parameters.Add("Path", path); - Collection result = ExecuteShellCommand(runSpace, cmd, false); - if (result != null && result.Count > 0) - { - disk.DiskFormat = (VirtualHardDiskFormat)Enum.Parse(typeof(VirtualHardDiskFormat), GetPSObjectProperty(result[0], "VhdFormat").ToString()); - disk.DiskType = (VirtualHardDiskType)Enum.Parse(typeof(VirtualHardDiskType), GetPSObjectProperty(result[0], "VhdType").ToString()); - disk.ParentPath = (string)GetPSObjectProperty(result[0], "ParentPath"); - disk.MaxInternalSize = Convert.ToInt64(GetPSObjectProperty(result[0], "Size")) / Size1G; - disk.FileSize = Convert.ToInt64(GetPSObjectProperty(result[0], "FileSize")) / Size1G; - disk.Attached = Convert.ToBoolean(GetPSObjectProperty(result[0], "Attached")); - } - } - } - - - internal VirtualMachineNetworkAdapter[] GetNetworkAdapters(Runspace runSpace, string name) - { - List adapters = new List(); - - Command cmd = new Command("Get-VMNetworkAdapter"); - cmd.Parameters.Add("VMName", name); - - Collection result = ExecuteShellCommand(runSpace, cmd, false); - if (result != null && result.Count > 0) - { - foreach(PSObject a in result) - { - VirtualMachineNetworkAdapter adapter = new VirtualMachineNetworkAdapter(); - - adapter.Name = GetPSObjectProperty(a, "Name").ToString(); - adapter.MacAddress = GetPSObjectProperty(a, "MacAddress").ToString(); - - adapters.Add(adapter); - } - } - return adapters.ToArray(); - } - public List GetVirtualMachines() { HostedSolutionLog.LogStart("GetVirtualMachines"); - Runspace runSpace = null; List vmachines = new List(); try { - runSpace = OpenRunspace(); Command cmd = new Command("Get-VM"); - Collection result = ExecuteShellCommand(runSpace, cmd, false); + Collection result = PowerShell.Execute(cmd, false); foreach (PSObject current in result) { VirtualMachine vm = new VirtualMachine { - VirtualMachineId = GetPSObjectProperty(current, "Id").ToString(), - Name = GetPSObjectProperty(current, "Name").ToString(), - State = (VirtualMachineState)Enum.Parse(typeof(VirtualMachineState), GetPSObjectProperty(current, "State").ToString()), - Uptime = GetPSObjectProperty(current, "UpTime").Ticks + VirtualMachineId = current.GetProperty("Id").ToString(), + Name = current.GetProperty("Name").ToString(), + State = (VirtualMachineState)Enum.Parse(typeof(VirtualMachineState), current.GetProperty("State").ToString()), + Uptime = Convert.ToInt64(current.GetProperty("UpTime").TotalMilliseconds) }; vmachines.Add(vm); } @@ -423,10 +246,6 @@ namespace WebsitePanel.Providers.Virtualization HostedSolutionLog.LogError("GetVirtualMachines", ex); throw; } - finally - { - CloseRunspace(runSpace); - } HostedSolutionLog.LogEnd("GetVirtualMachines"); return vmachines; @@ -549,11 +368,11 @@ namespace WebsitePanel.Providers.Virtualization vmID = (string)objVM["Name"]; // update general settings - UpdateVirtualMachineGeneralSettings(vmID, objVM, - vm.CpuCores, - vm.RamSize, - vm.BootFromCD, - vm.NumLockEnabled); + //UpdateVirtualMachineGeneralSettings(vmID, objVM, + // vm.CpuCores, + // vm.RamSize, + // vm.BootFromCD, + // vm.NumLockEnabled); // hard disks // load IDE 0 controller @@ -608,113 +427,32 @@ namespace WebsitePanel.Providers.Virtualization public VirtualMachine UpdateVirtualMachine(VirtualMachine vm) { - string vmId = vm.VirtualMachineId; + HostedSolutionLog.LogStart("UpdateVirtualMachine"); + HostedSolutionLog.DebugInfo("Virtual Machine: {0}", vm.VirtualMachineId); - // get VM object - ManagementObject objVM = GetVirtualMachineObject(vmId); + Runspace runSpace = null; - // update general settings - UpdateVirtualMachineGeneralSettings(vmId, objVM, - vm.CpuCores, - vm.RamSize, - vm.BootFromCD, - vm.NumLockEnabled); - - // check DVD drive - ManagementObject objDvdDrive = wmi.GetWmiObject( - "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Synthetic DVD Drive'" - + " and InstanceID like 'Microsoft:{0}%' and Address = 0", vmId); - - if (vm.DvdDriveInstalled && objDvdDrive == null) - AddVirtualMachineDvdDrive(vmId, objVM); - else if (!vm.DvdDriveInstalled && objDvdDrive != null) - RemoveVirtualMachineResources(objVM, objDvdDrive); - - // External NIC - if (!vm.ExternalNetworkEnabled - && !String.IsNullOrEmpty(vm.ExternalNicMacAddress)) + try { - // delete adapter - DeleteNetworkAdapter(objVM, vm.ExternalNicMacAddress); + var realVm = GetVirtualMachine(vm.VirtualMachineId); + + VirtualMachineHelper.UpdateBios(PowerShell, realVm, vm.BootFromCD, vm.NumLockEnabled); + VirtualMachineHelper.UpdateProcessors(PowerShell, realVm, vm.CpuCores, CpuLimitSettings, CpuReserveSettings, CpuWeightSettings); + VirtualMachineHelper.UpdateMemory(PowerShell, realVm, vm.RamSize); + DvdDriveHelper.Update(PowerShell, realVm, vm.DvdDriveInstalled); - // reset MAC - vm.ExternalNicMacAddress = null; } - else if (vm.ExternalNetworkEnabled - && !String.IsNullOrEmpty(vm.ExternalNicMacAddress)) + catch (Exception ex) { - // add external adapter - AddNetworkAdapter(objVM, vm.ExternalSwitchId, vm.Name, vm.ExternalNicMacAddress, EXTERNAL_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); - } - - - // Private NIC - if (!vm.PrivateNetworkEnabled - && !String.IsNullOrEmpty(vm.PrivateNicMacAddress)) - { - // delete adapter - DeleteNetworkAdapter(objVM, vm.PrivateNicMacAddress); - - // reset MAC - vm.PrivateNicMacAddress = null; - } - else if (vm.PrivateNetworkEnabled - && !String.IsNullOrEmpty(vm.PrivateNicMacAddress)) - { - // add private adapter - AddNetworkAdapter(objVM, vm.PrivateSwitchId, vm.Name, vm.PrivateNicMacAddress, PRIVATE_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + HostedSolutionLog.LogError("UpdateVirtualMachine", ex); + throw; } + HostedSolutionLog.LogEnd("UpdateVirtualMachine"); + return vm; } - private void UpdateVirtualMachineGeneralSettings(string vmId, ManagementObject objVM, int cpuCores, long ramMB, bool bootFromCD, bool numLockEnabled) - { - // request management service - ManagementObject objVmsvc = GetVirtualSystemManagementService(); - - // VM resources - List vmConfig = new List(); - - // get system settings - ManagementObject objSettings = GetVirtualMachineSettingsObject(vmId); - - // BIOS (num lock) - objSettings["BIOSNumLock"] = numLockEnabled; - - // BIOS (boot order) - // BootOrder = 0 - Boot from floppy, 1 - Boot from CD, 2 - Boot from disk, 3 - PXE Boot - objSettings["BootOrder"] = bootFromCD ? new int[] { 1, 2, 3, 0 } : new int[] { 2, 1, 3, 0 }; - - // modify machine settings - ManagementBaseObject inParams = objVmsvc.GetMethodParameters("ModifyVirtualSystem"); - inParams["ComputerSystem"] = objVM; - inParams["SystemSettingData"] = objSettings.GetText(TextFormat.CimDtd20); - ManagementBaseObject outParams = objVmsvc.InvokeMethod("ModifyVirtualSystem", inParams, null); - JobResult job = CreateJobResultFromWmiMethodResults(outParams); - - // setup CPU - ManagementObject objCpu = wmi.GetWmiObject("Msvm_ProcessorSettingData", "InstanceID Like 'Microsoft:{0}%'", vmId); - objCpu["VirtualQuantity"] = cpuCores; - objCpu["Limit"] = Convert.ToInt64(CpuLimitSettings * 1000); - objCpu["Reservation"] = Convert.ToInt64(CpuReserveSettings * 1000); - objCpu["Weight"] = CpuWeightSettings; - vmConfig.Add(objCpu.GetText(TextFormat.CimDtd20)); - - // setup RAM - ManagementObject objRam = wmi.GetWmiObject("Msvm_MemorySettingData", "InstanceID Like 'Microsoft:{0}%'", vmId); - objRam["VirtualQuantity"] = ramMB.ToString(); - objRam["Reservation"] = ramMB.ToString(); - objRam["Limit"] = ramMB.ToString(); - vmConfig.Add(objRam.GetText(TextFormat.CimDtd20)); - - // modify machine resources - inParams = objVmsvc.GetMethodParameters("ModifyVirtualSystemResources"); - inParams["ComputerSystem"] = objVM; - inParams["ResourceSettingData"] = vmConfig.ToArray(); - outParams = objVmsvc.InvokeMethod("ModifyVirtualSystemResources", inParams, null); - job = CreateJobResultFromWmiMethodResults(outParams); - } private void AddVirtualMachineDvdDrive(string vmId, ManagementObject objVM) { @@ -883,12 +621,9 @@ namespace WebsitePanel.Providers.Virtualization var jobResult = new JobResult(); var vm = GetVirtualMachine(vmId); - Runspace runSpace = null; try { - runSpace = OpenRunspace(); - string cmdTxt; List paramList = new List(); @@ -926,7 +661,7 @@ namespace WebsitePanel.Providers.Virtualization //cmd.Parameters.Add("AsJob"); paramList.ForEach(p => cmd.Parameters.Add(p)); - ExecuteShellCommand(runSpace, cmd, false); + PowerShell.Execute(cmd, false); jobResult = CreateSuccessJobResult(); } catch (Exception ex) @@ -934,10 +669,6 @@ namespace WebsitePanel.Providers.Virtualization HostedSolutionLog.LogError("ChangeVirtualMachineState", ex); throw; } - finally - { - CloseRunspace(runSpace); - } HostedSolutionLog.LogEnd("ChangeVirtualMachineState"); @@ -950,28 +681,22 @@ namespace WebsitePanel.Providers.Virtualization ReturnCode returnCode = ReturnCode.OK; var vm = GetVirtualMachine(vmId); - Runspace runSpace = null; try { - runSpace = OpenRunspace(); - Command cmd = new Command("Stop-VM"); cmd.Parameters.Add("Name", vm.Name); if (force) cmd.Parameters.Add("Force"); + //if (!string.IsNullOrEmpty(reason)) cmd.Parameters.Add("Reason", reason); - ExecuteShellCommand(runSpace, cmd, false); + PowerShell.Execute(cmd, false); } catch (Exception ex) { HostedSolutionLog.LogError("ShutDownVirtualMachine", ex); throw; } - finally - { - CloseRunspace(runSpace); - } HostedSolutionLog.LogEnd("ShutDownVirtualMachine"); @@ -1351,19 +1076,19 @@ namespace WebsitePanel.Providers.Virtualization try { - runSpace = OpenRunspace(); + Command cmd = new Command("Get-VMSwitch"); if (!string.IsNullOrEmpty(computerName)) cmd.Parameters.Add("ComputerName", computerName); if (!string.IsNullOrEmpty(type)) cmd.Parameters.Add("SwitchType", type); - Collection result = ExecuteShellCommand(runSpace, cmd,false); + Collection result = PowerShell.Execute(cmd,false); foreach (PSObject current in result) { VirtualSwitch sw = new VirtualSwitch(); - sw.SwitchId = GetPSObjectProperty(current, "Name").ToString(); - sw.Name = GetPSObjectProperty(current, "Name").ToString(); - sw.SwitchType = GetPSObjectProperty(current, "SwitchType").ToString(); + sw.SwitchId = current.GetProperty("Name").ToString(); + sw.Name = current.GetProperty("Name").ToString(); + sw.SwitchType = current.GetProperty("SwitchType").ToString(); switches.Add(sw); } } @@ -1372,10 +1097,6 @@ namespace WebsitePanel.Providers.Virtualization HostedSolutionLog.LogError("GetSwitches", ex); throw; } - finally - { - CloseRunspace(runSpace); - } HostedSolutionLog.LogEnd("GetSwitches"); return switches; @@ -2100,12 +1821,12 @@ exit", Convert.ToInt32(objDisk["Index"]))); try { - runSpace = OpenRunspace(); + Command cmd = new Command("Get-Job"); if (!string.IsNullOrEmpty(jobId)) cmd.Parameters.Add("Id", jobId); - Collection result = ExecuteShellCommand(runSpace, cmd, false); + Collection result = PowerShell.Execute( cmd, false); job = CreateJobFromPSObject(result); } catch (Exception ex) @@ -2113,10 +1834,6 @@ exit", Convert.ToInt32(objDisk["Index"]))); HostedSolutionLog.LogError("GetJob", ex); throw; } - finally - { - CloseRunspace(runSpace); - } HostedSolutionLog.LogEnd("GetJob"); return job; @@ -2503,21 +2220,21 @@ exit", Convert.ToInt32(objDisk["Index"]))); return null; ConcreteJob job = new ConcreteJob(); - job.Id = GetPSObjectProperty(objJob[0], "Id").ToString(); - job.JobState = GetPSObjectPropertyEnum(objJob[0], "JobStateInfo"); - job.Caption = GetPSObjectProperty(objJob[0], "Name"); - job.Description = GetPSObjectProperty(objJob[0], "Command"); - job.StartTime = GetPSObjectProperty(objJob[0], "PSBeginTime"); - job.ElapsedTime = GetPSObjectProperty(objJob[0], "PSEndTime") ?? DateTime.Now; + job.Id = objJob[0].GetProperty("Id").ToString(); + job.JobState = objJob[0].GetEnum("JobStateInfo"); + job.Caption = objJob[0].GetProperty("Name"); + job.Description = objJob[0].GetProperty("Command"); + job.StartTime = objJob[0].GetProperty("PSBeginTime"); + job.ElapsedTime = objJob[0].GetProperty("PSEndTime") ?? DateTime.Now; // PercentComplete job.PercentComplete = 0; - var progress = (PSDataCollection)GetPSObjectProperty(objJob[0], "Progress"); + var progress = (PSDataCollection)objJob[0].GetProperty("Progress"); if (progress != null && progress.Count > 0) job.PercentComplete = progress[0].PercentComplete; // Errors - var errors = (PSDataCollection)GetPSObjectProperty(objJob[0], "Error"); + var errors = (PSDataCollection)objJob[0].GetProperty("Error"); if (errors != null && errors.Count > 0) { job.ErrorDescription = errors[0].ErrorDetails.Message + ". " + errors[0].ErrorDetails.RecommendedAction; @@ -2813,152 +2530,13 @@ exit", Convert.ToInt32(objDisk["Index"]))); #endregion Hyper-V Cloud #region PowerShell integration - private static InitialSessionState session = null; - internal virtual Runspace OpenRunspace() + private PowerShellManager _powerShell; + protected PowerShellManager PowerShell { - HostedSolutionLog.LogStart("OpenRunspace"); - - if (session == null) - { - session = InitialSessionState.CreateDefault(); - session.ImportPSModule(new string[] { "Hyper-V" }); - } - Runspace runSpace = RunspaceFactory.CreateRunspace(session); - // - runSpace.Open(); - // - runSpace.SessionStateProxy.SetVariable("ConfirmPreference", "none"); - HostedSolutionLog.LogEnd("OpenRunspace"); - return runSpace; + get { return _powerShell ?? (_powerShell = new PowerShellManager()); } } - internal void CloseRunspace(Runspace runspace) - { - try - { - if (runspace != null && runspace.RunspaceStateInfo.State == RunspaceState.Opened) - { - runspace.Close(); - } - } - catch (Exception ex) - { - HostedSolutionLog.LogError("Runspace error", ex); - } - } - - internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd) - { - return ExecuteShellCommand(runSpace, cmd, true); - } - - internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, bool useDomainController) - { - object[] errors; - return ExecuteShellCommand(runSpace, cmd, useDomainController, out errors); - } - - internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, out object[] errors) - { - return ExecuteShellCommand(runSpace, cmd, true, out errors); - } - - internal Collection ExecuteShellCommand(Runspace runSpace, Command cmd, bool useDomainController, out object[] errors) - { - HostedSolutionLog.LogStart("ExecuteShellCommand"); - List errorList = new List(); - - HostedSolutionLog.DebugCommand(cmd); - Collection results = null; - // Create a pipeline - Pipeline pipeLine = runSpace.CreatePipeline(); - using (pipeLine) - { - // Add the command - pipeLine.Commands.Add(cmd); - // Execute the pipeline and save the objects returned. - results = pipeLine.Invoke(); - - // Log out any errors in the pipeline execution - // NOTE: These errors are NOT thrown as exceptions! - // Be sure to check this to ensure that no errors - // happened while executing the command. - if (pipeLine.Error != null && pipeLine.Error.Count > 0) - { - foreach (object item in pipeLine.Error.ReadToEnd()) - { - errorList.Add(item); - string errorMessage = string.Format("Invoke error: {0}", item); - HostedSolutionLog.LogWarning(errorMessage); - } - } - } - pipeLine = null; - errors = errorList.ToArray(); - HostedSolutionLog.LogEnd("ExecuteShellCommand"); - return results; - } - - internal object GetPSObjectProperty(PSObject obj, string name) - { - return obj.Members[name].Value; - } - internal T GetPSObjectProperty(PSObject obj, string name) - { - return (T)obj.Members[name].Value; - } - internal T GetPSObjectPropertyEnum(PSObject obj, string name) where T : struct - { - return (T) Enum.Parse(typeof (T), GetPSObjectProperty(obj, name).ToString()); - } - - /// - /// Returns the identity of the object from the shell execution result - /// - /// - /// - internal string GetResultObjectIdentity(Collection result) - { - HostedSolutionLog.LogStart("GetResultObjectIdentity"); - if (result == null) - throw new ArgumentNullException("result", "Execution result is not specified"); - - if (result.Count < 1) - throw new ArgumentException("Execution result is empty", "result"); - - if (result.Count > 1) - throw new ArgumentException("Execution result contains more than one object", "result"); - - PSMemberInfo info = result[0].Members["Identity"]; - if (info == null) - throw new ArgumentException("Execution result does not contain Identity property", "result"); - - string ret = info.Value.ToString(); - HostedSolutionLog.LogEnd("GetResultObjectIdentity"); - return ret; - } - - internal string GetResultObjectDN(Collection result) - { - HostedSolutionLog.LogStart("GetResultObjectDN"); - if (result == null) - throw new ArgumentNullException("result", "Execution result is not specified"); - - if (result.Count < 1) - throw new ArgumentException("Execution result does not contain any object"); - - if (result.Count > 1) - throw new ArgumentException("Execution result contains more than one object"); - - PSMemberInfo info = result[0].Members["DistinguishedName"]; - if (info == null) - throw new ArgumentException("Execution result does not contain DistinguishedName property", "result"); - - string ret = info.Value.ToString(); - HostedSolutionLog.LogEnd("GetResultObjectDN"); - return ret; - } #endregion diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/PowerShellManager.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/PowerShellManager.cs new file mode 100644 index 00000000..949b08bd --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/PowerShellManager.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; +using WebsitePanel.Providers.HostedSolution; + +namespace WebsitePanel.Providers.Virtualization +{ + public class PowerShellManager : IDisposable + { + protected static InitialSessionState session = null; + + protected Runspace RunSpace { get; set; } + + public PowerShellManager() + { + OpenRunspace(); + } + + protected void OpenRunspace() + { + HostedSolutionLog.LogStart("OpenRunspace"); + + if (session == null) + { + session = InitialSessionState.CreateDefault(); + session.ImportPSModule(new[] {"Hyper-V"}); + } + + Runspace runSpace = RunspaceFactory.CreateRunspace(session); + runSpace.Open(); + runSpace.SessionStateProxy.SetVariable("ConfirmPreference", "none"); + + RunSpace = runSpace; + + HostedSolutionLog.LogEnd("OpenRunspace"); + } + + public void Dispose() + { + try + { + if (RunSpace != null && RunSpace.RunspaceStateInfo.State == RunspaceState.Opened) + { + RunSpace.Close(); + RunSpace = null; + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("Runspace error", ex); + } + } + + public Collection Execute(Command cmd) + { + return Execute(cmd, true); + } + + public Collection Execute(Command cmd, bool useDomainController) + { + object[] errors; + return Execute(cmd, useDomainController, out errors); + } + + public Collection Execute(Command cmd, out object[] errors) + { + return Execute(cmd, true, out errors); + } + + public Collection Execute(Command cmd, bool useDomainController, out object[] errors) + { + HostedSolutionLog.LogStart("Execute"); + List errorList = new List(); + + HostedSolutionLog.DebugCommand(cmd); + Collection results = null; + // Create a pipeline + Pipeline pipeLine = RunSpace.CreatePipeline(); + using (pipeLine) + { + // Add the command + pipeLine.Commands.Add(cmd); + // Execute the pipeline and save the objects returned. + results = pipeLine.Invoke(); + + // Log out any errors in the pipeline execution + // NOTE: These errors are NOT thrown as exceptions! + // Be sure to check this to ensure that no errors + // happened while executing the command. + if (pipeLine.Error != null && pipeLine.Error.Count > 0) + { + foreach (object item in pipeLine.Error.ReadToEnd()) + { + errorList.Add(item); + string errorMessage = string.Format("Invoke error: {0}", item); + HostedSolutionLog.LogWarning(errorMessage); + } + } + } + pipeLine = null; + errors = errorList.ToArray(); + HostedSolutionLog.LogEnd("Execute"); + return results; + } + + + /// + /// Returns the identity of the object from the shell execution result + /// + /// + /// + public static string GetResultObjectIdentity(Collection result) + { + HostedSolutionLog.LogStart("GetResultObjectIdentity"); + if (result == null) + throw new ArgumentNullException("result", "Execution result is not specified"); + + if (result.Count < 1) + throw new ArgumentException("Execution result is empty", "result"); + + if (result.Count > 1) + throw new ArgumentException("Execution result contains more than one object", "result"); + + PSMemberInfo info = result[0].Members["Identity"]; + if (info == null) + throw new ArgumentException("Execution result does not contain Identity property", "result"); + + string ret = info.Value.ToString(); + HostedSolutionLog.LogEnd("GetResultObjectIdentity"); + return ret; + } + + public static string GetResultObjectDN(Collection result) + { + HostedSolutionLog.LogStart("GetResultObjectDN"); + if (result == null) + throw new ArgumentNullException("result", "Execution result is not specified"); + + if (result.Count < 1) + throw new ArgumentException("Execution result does not contain any object"); + + if (result.Count > 1) + throw new ArgumentException("Execution result contains more than one object"); + + PSMemberInfo info = result[0].Members["DistinguishedName"]; + if (info == null) + throw new ArgumentException("Execution result does not contain DistinguishedName property", "result"); + + string ret = info.Value.ToString(); + HostedSolutionLog.LogEnd("GetResultObjectDN"); + return ret; + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj index 31e9533c..2198fbaa 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj @@ -53,8 +53,13 @@ VersionInfo.cs + + + + + From 6a1efab8d5c395137e00c9e5a37ff68aad73ebe1 Mon Sep 17 00:00:00 2001 From: AlexanderTr Date: Sun, 8 Mar 2015 19:26:47 +0300 Subject: [PATCH 05/12] wsp-10323 Convert the VSP provider into one utilizing PowerShell. Step 3 --- .../Virtualization/BiosInfo.cs | 1 + .../Virtualization/DvdDriveInfo.cs | 1 + .../Helpers/BiosHelper.cs | 109 +++++++++ .../Helpers/DvdDriveHelper.cs | 32 ++- .../Helpers/NetworkAdapterHelper.cs | 41 ++-- .../Helpers/VirtualMachineHelper.cs | 58 ++--- .../HyperV2012R2.cs | 212 +++++++++--------- ...oviders.Virtualization.HyperV2012R2.csproj | 1 + 8 files changed, 281 insertions(+), 174 deletions(-) create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/BiosHelper.cs diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/BiosInfo.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/BiosInfo.cs index f7fd4a59..796876fc 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/BiosInfo.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/BiosInfo.cs @@ -36,5 +36,6 @@ namespace WebsitePanel.Providers.Virtualization { public bool NumLockEnabled { get; set; } public string[] StartupOrder { get; set; } + public bool BootFromCD { get; set; } } } \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/DvdDriveInfo.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/DvdDriveInfo.cs index df548b0b..34a1100d 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/DvdDriveInfo.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/DvdDriveInfo.cs @@ -39,5 +39,6 @@ namespace WebsitePanel.Providers.Virtualization public int ControllerLocation { get; set; } public string Name { get; set; } public string Id { get; set; } + public string Path { get; set; } } } \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/BiosHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/BiosHelper.cs new file mode 100644 index 00000000..25fcfe3b --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/BiosHelper.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class BiosHelper + { + public static BiosInfo GetVMBios(PowerShellManager powerShell, string name, int generation) + { + BiosInfo info = new BiosInfo(); + + // for Win2012R2+ and Win8.1+ + if (generation == 2) + { + Command cmd = new Command("Get-VMFirmware"); + + cmd.Parameters.Add("VMName", name); + + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + info.NumLockEnabled = true; + + List startupOrders = new List(); + info.BootFromCD = false; + + foreach (dynamic item in (IEnumerable)result[0].GetProperty("BootOrder")) + { + string bootType = item.BootType.ToString(); + + // bootFromCD + if (!startupOrders.Any() && bootType == "Drive") + { + var device = item.Device; + info.BootFromCD = device.GetType().Name == "DvdDrive"; + } + + // startupOrders + startupOrders.Add(bootType); + } + + info.StartupOrder = startupOrders.ToArray(); + } + } + // for others win and linux + else + { + Command cmd = new Command("Get-VMBios"); + + cmd.Parameters.Add("VMName", name); + + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + info.NumLockEnabled = Convert.ToBoolean(result[0].GetProperty("NumLockEnabled")); + + List startupOrders = new List(); + + foreach (var item in (IEnumerable)result[0].GetProperty("StartupOrder")) + startupOrders.Add(item.ToString()); + + info.StartupOrder = startupOrders.ToArray(); + info.BootFromCD = false; + if (info.StartupOrder != null && info.StartupOrder.Length > 0) + info.BootFromCD = info.StartupOrder[0] == "CD"; + } + } + + return info; + } + + public static void UpdateBios(PowerShellManager powerShell, VirtualMachine vm, bool bootFromCD, bool numLockEnabled) + { + // for Win2012R2+ and Win8.1+ + if (vm.Generation == 2) + { + Command cmd = new Command("Set-VMFirmware"); + + cmd.Parameters.Add("VMName", vm.Name); + if (bootFromCD) + cmd.Parameters.Add("FirstBootDevice", DvdDriveHelper.GetPS(powerShell, vm.Name)); + else + cmd.Parameters.Add("FirstBootDevice", VirtualMachineHelper.GetVirtualHardDisksPS(powerShell, vm.Name).FirstOrDefault()); + + powerShell.Execute(cmd, false); + } + // for others win and linux + else + { + Command cmd = new Command("Set-VMBios"); + + cmd.Parameters.Add("VMName", vm.Name); + var bootOrder = bootFromCD + ? new[] { "CD", "IDE", "LegacyNetworkAdapter", "Floppy" } + : new[] { "IDE", "CD", "LegacyNetworkAdapter", "Floppy" }; + cmd.Parameters.Add("StartupOrder", bootOrder); + + powerShell.Execute(cmd, false); + } + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/DvdDriveHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/DvdDriveHelper.cs index 871e8fa3..012232ee 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/DvdDriveHelper.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/DvdDriveHelper.cs @@ -13,8 +13,25 @@ namespace WebsitePanel.Providers.Virtualization { public static DvdDriveInfo Get(PowerShellManager powerShell, string vmName) { - DvdDriveInfo info = new DvdDriveInfo(); + DvdDriveInfo info = null; + PSObject result = GetPS(powerShell, vmName); + + if (result != null) + { + info = new DvdDriveInfo(); + info.Id = result.GetString("Id"); + info.Name = result.GetString("Name"); + info.ControllerType = result.GetEnum("ControllerType"); + info.ControllerNumber = result.GetInt("ControllerNumber"); + info.ControllerLocation = result.GetInt("ControllerLocation"); + info.Path = result.GetString("Path"); + } + return info; + } + + public static PSObject GetPS(PowerShellManager powerShell, string vmName) + { Command cmd = new Command("Get-VMDvdDrive"); cmd.Parameters.Add("VMName", vmName); @@ -23,13 +40,10 @@ namespace WebsitePanel.Providers.Virtualization if (result != null && result.Count > 0) { - info.Id = result[0].GetString("Id"); - info.Name = result[0].GetString("Name"); - info.ControllerType = result[0].GetEnum("ControllerType"); - info.ControllerNumber = result[0].GetInt("ControllerNumber"); - info.ControllerLocation = result[0].GetInt("ControllerLocation"); + return result[0]; } - return info; + + return null; } public static void Set(PowerShellManager powerShell, string vmName, string path) @@ -56,13 +70,9 @@ namespace WebsitePanel.Providers.Virtualization public static void Add(PowerShellManager powerShell, string vmName) { - var dvd = Get(powerShell, vmName); - Command cmd = new Command("Add-VMDvdDrive"); cmd.Parameters.Add("VMName", vmName); - cmd.Parameters.Add("ControllerNumber", dvd.ControllerNumber); - cmd.Parameters.Add("ControllerLocation", dvd.ControllerLocation); powerShell.Execute(cmd, false); } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/NetworkAdapterHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/NetworkAdapterHelper.cs index c00ff956..bd6f9b37 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/NetworkAdapterHelper.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/NetworkAdapterHelper.cs @@ -49,18 +49,17 @@ namespace WebsitePanel.Providers.Virtualization return adapters.FirstOrDefault(a => a.MacAddress == macAddress); } - public static void Update(PowerShellManager powerShell, VirtualMachine vm, string switchId, string portName, string macAddress, string adapterName, bool legacyAdapter) + public static void Update(PowerShellManager powerShell, VirtualMachine vm) { // External NIC if (!vm.ExternalNetworkEnabled && !String.IsNullOrEmpty(vm.ExternalNicMacAddress)) { - // delete adapter Delete(powerShell, vm.Name, vm.ExternalNicMacAddress); vm.ExternalNicMacAddress = null; // reset MAC } - else if (vm.ExternalNetworkEnabled && !String.IsNullOrEmpty(vm.ExternalNicMacAddress)) + else if (vm.ExternalNetworkEnabled && !String.IsNullOrEmpty(vm.ExternalNicMacAddress) + && Get(powerShell,vm.Name,vm.ExternalNicMacAddress) == null) { - // add external adapter Add(powerShell, vm.Name, vm.ExternalSwitchId, vm.ExternalNicMacAddress, EXTERNAL_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); } @@ -70,35 +69,41 @@ namespace WebsitePanel.Providers.Virtualization Delete(powerShell, vm.Name, vm.PrivateNicMacAddress); vm.PrivateNicMacAddress = null; // reset MAC } - else if (vm.PrivateNetworkEnabled && !String.IsNullOrEmpty(vm.PrivateNicMacAddress)) + else if (vm.PrivateNetworkEnabled && !String.IsNullOrEmpty(vm.PrivateNicMacAddress) + && Get(powerShell, vm.Name, vm.PrivateNicMacAddress) == null) { - Add(powerShell, vm.Name, vm.ExternalSwitchId, vm.ExternalNicMacAddress, PRIVATE_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + Add(powerShell, vm.Name, vm.PrivateSwitchId, vm.PrivateNicMacAddress, PRIVATE_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); } } public static void Add(PowerShellManager powerShell, string vmName, string switchId, string macAddress, string adapterName, bool legacyAdapter) { - //var dvd = Get(powerShell, vmName); + Command cmd = new Command("Add-VMNetworkAdapter"); - //Command cmd = new Command("Add-VMDvdDrive"); + cmd.Parameters.Add("VMName", vmName); + cmd.Parameters.Add("Name", adapterName); + cmd.Parameters.Add("SwitchName", switchId); - //cmd.Parameters.Add("VMName", vmName); - //cmd.Parameters.Add("ControllerNumber", dvd.ControllerNumber); - //cmd.Parameters.Add("ControllerLocation", dvd.ControllerLocation); + if (String.IsNullOrEmpty(macAddress)) + cmd.Parameters.Add("DynamicMacAddress"); + else + cmd.Parameters.Add("StaticMacAddress", macAddress); - //powerShell.Execute(cmd, false); + powerShell.Execute(cmd, false); } public static void Delete(PowerShellManager powerShell, string vmName, string macAddress) { - //var dvd = Get(powerShell, vmName); + var networkAdapter = Get(powerShell, vmName, macAddress); - //Command cmd = new Command("Add-VMDvdDrive"); + if (networkAdapter == null) + return; - //cmd.Parameters.Add("VMName", vmName); - //cmd.Parameters.Add("ControllerNumber", dvd.ControllerNumber); - //cmd.Parameters.Add("ControllerLocation", dvd.ControllerLocation); + Command cmd = new Command("Remove-VMNetworkAdapter"); - //powerShell.Execute(cmd, false); + cmd.Parameters.Add("VMName", vmName); + cmd.Parameters.Add("Name", networkAdapter.Name); + + powerShell.Execute(cmd, false); } } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VirtualMachineHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VirtualMachineHelper.cs index 66958337..0d24c149 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VirtualMachineHelper.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VirtualMachineHelper.cs @@ -71,47 +71,22 @@ namespace WebsitePanel.Providers.Virtualization if (result != null && result.Count > 0) { info.DynamicMemoryEnabled = Convert.ToBoolean(result[0].GetProperty("DynamicMemoryEnabled")); - info.Startup = Convert.ToInt64(result[0].GetProperty("Startup")); - info.Minimum = Convert.ToInt64(result[0].GetProperty("Minimum")); - info.Maximum = Convert.ToInt64(result[0].GetProperty("Maximum")); + info.Startup = Convert.ToInt64(result[0].GetProperty("Startup")) / Size1M; + info.Minimum = Convert.ToInt64(result[0].GetProperty("Minimum")) / Size1M; + info.Maximum = Convert.ToInt64(result[0].GetProperty("Maximum")) / Size1M; info.Buffer = Convert.ToInt32(result[0].GetProperty("Buffer")); info.Priority = Convert.ToInt32(result[0].GetProperty("Priority")); } return info; } - public static BiosInfo GetVMBios(PowerShellManager powerShell, string name) - { - BiosInfo info = new BiosInfo(); - - Command cmd = new Command("Get-VMBios"); - - cmd.Parameters.Add("VMName", name); - - Collection result = powerShell.Execute(cmd, false); - if (result != null && result.Count > 0) - { - info.NumLockEnabled = Convert.ToBoolean(result[0].GetProperty("NumLockEnabled")); - - List startupOrders = new List(); - - foreach (var item in (IEnumerable)result[0].GetProperty("StartupOrder")) - startupOrders.Add(item.ToString()); - - info.StartupOrder = startupOrders.ToArray(); - } - return info; - } public static VirtualHardDiskInfo[] GetVirtualHardDisks(PowerShellManager powerShell, string name) { - List disks = new List(); - Command cmd = new Command("Get-VMHardDiskDrive"); - cmd.Parameters.Add("VMName", name); + Collection result = GetVirtualHardDisksPS(powerShell, name); - Collection result = powerShell.Execute(cmd, false); if (result != null && result.Count > 0) { foreach (PSObject d in result) @@ -135,6 +110,14 @@ namespace WebsitePanel.Providers.Virtualization return disks.ToArray(); } + public static Collection GetVirtualHardDisksPS(PowerShellManager powerShell, string name) + { + Command cmd = new Command("Get-VMHardDiskDrive"); + cmd.Parameters.Add("VMName", name); + + return powerShell.Execute(cmd, false); + } + public static void GetVirtualHardDiskDetail(PowerShellManager powerShell, string path, ref VirtualHardDiskInfo disk) { if (!string.IsNullOrEmpty(path)) @@ -155,21 +138,6 @@ namespace WebsitePanel.Providers.Virtualization } - - - public static void UpdateBios(PowerShellManager powerShell, VirtualMachine vm, bool bootFromCD, bool numLockEnabled) - { - Command cmd = new Command("Set-VMBios"); - - cmd.Parameters.Add("VMName", vm.Name); - cmd.Parameters.Add(numLockEnabled ? "EnableNumLock" : "DisableNumLock"); - var bootOrder = bootFromCD - ? new[] { "CD", "IDE", "LegacyNetworkAdapter", "Floppy" } - : new[] { "IDE", "CD", "LegacyNetworkAdapter", "Floppy" }; - cmd.Parameters.Add("StartupOrder", bootOrder); - - powerShell.Execute(cmd, false); - } public static void UpdateProcessors(PowerShellManager powerShell, VirtualMachine vm, int cpuCores, int cpuLimitSettings, int cpuReserveSettings, int cpuWeightSettings) { Command cmd = new Command("Set-VMProcessor"); @@ -187,7 +155,7 @@ namespace WebsitePanel.Providers.Virtualization Command cmd = new Command("Set-VMMemory"); cmd.Parameters.Add("VMName", vm.Name); - cmd.Parameters.Add("StartupBytes", ramMB); + cmd.Parameters.Add("StartupBytes", ramMB * Size1M); powerShell.Execute(cmd, false); } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs index 2247adf7..e14db44b 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs @@ -71,6 +71,9 @@ namespace WebsitePanel.Providers.Virtualization private const string KVP_RAM_SUMMARY_KEY = "VM-RAM-Summary"; private const string KVP_HDD_SUMMARY_KEY = "VM-HDD-Summary"; + private const Int64 Size1G = 0x40000000; + private const Int64 Size1M = 0x100000; + #endregion #region Provider Settings @@ -166,10 +169,11 @@ namespace WebsitePanel.Providers.Virtualization vm.Name = result[0].GetProperty("Name").ToString(); vm.State = result[0].GetEnum("State"); vm.CpuUsage = ConvertNullableToInt32(result[0].GetProperty("CpuUsage")); - vm.RamUsage = ConvertNullableToInt64(result[0].GetProperty("MemoryAssigned")); + vm.RamUsage = ConvertNullableToInt64(result[0].GetProperty("MemoryAssigned")) / Size1M; vm.Uptime = Convert.ToInt64(result[0].GetProperty("UpTime").TotalMilliseconds); vm.Status = result[0].GetProperty("Status").ToString(); vm.ReplicationState = result[0].GetProperty("ReplicationState").ToString(); + vm.Generation = result[0].GetInt("Generation"); vm.Heartbeat = VirtualMachineHelper.GetVMHeartBeatStatus(PowerShell, vm.Name); @@ -183,12 +187,9 @@ namespace WebsitePanel.Providers.Virtualization vm.RamSize = memoryInfo.Startup; // BIOS - BiosInfo biosInfo = VirtualMachineHelper.GetVMBios(PowerShell, vm.Name); + BiosInfo biosInfo = BiosHelper.GetVMBios(PowerShell, vm.Name, vm.Generation); vm.NumLockEnabled = biosInfo.NumLockEnabled; - - vm.BootFromCD = false; - if ((biosInfo.StartupOrder != null) && (biosInfo.StartupOrder.Length > 0)) - vm.BootFromCD = (biosInfo.StartupOrder[0] == "CD"); + vm.BootFromCD = biosInfo.BootFromCD; // DVD drive var dvdInfo = DvdDriveHelper.Get(PowerShell, vm.Name); @@ -430,17 +431,15 @@ namespace WebsitePanel.Providers.Virtualization HostedSolutionLog.LogStart("UpdateVirtualMachine"); HostedSolutionLog.DebugInfo("Virtual Machine: {0}", vm.VirtualMachineId); - Runspace runSpace = null; - try { - var realVm = GetVirtualMachine(vm.VirtualMachineId); + var realVm = GetVirtualMachineEx(vm.VirtualMachineId); - VirtualMachineHelper.UpdateBios(PowerShell, realVm, vm.BootFromCD, vm.NumLockEnabled); + DvdDriveHelper.Update(PowerShell, realVm, vm.DvdDriveInstalled); // Dvd should be before bios because bios sets boot order + BiosHelper.UpdateBios(PowerShell, realVm, vm.BootFromCD, vm.NumLockEnabled); VirtualMachineHelper.UpdateProcessors(PowerShell, realVm, vm.CpuCores, CpuLimitSettings, CpuReserveSettings, CpuWeightSettings); VirtualMachineHelper.UpdateMemory(PowerShell, realVm, vm.RamSize); - DvdDriveHelper.Update(PowerShell, realVm, vm.DvdDriveInstalled); - + NetworkAdapterHelper.Update(PowerShell, vm); } catch (Exception ex) { @@ -989,69 +988,68 @@ namespace WebsitePanel.Providers.Virtualization #region DVD operations public string GetInsertedDVD(string vmId) { - // find CD/DVD disk - ManagementObject objDvd = wmi.GetWmiObject( - "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Virtual CD/DVD Disk'" - + " and InstanceID Like 'Microsoft:{0}%'", vmId); + HostedSolutionLog.LogStart("GetInsertedDVD"); + HostedSolutionLog.DebugInfo("Virtual Machine: {0}", vmId); - if (objDvd == null) + DvdDriveInfo dvdInfo; + + try + { + var vm = GetVirtualMachineEx(vmId); + dvdInfo = DvdDriveHelper.Get(PowerShell, vm.Name); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetInsertedDVD", ex); + throw; + } + + if (dvdInfo == null) return null; - string[] path = (string[])objDvd["Connection"]; - if (path != null && path.Length > 0) - return path[0]; - - return null; + HostedSolutionLog.LogEnd("GetInsertedDVD"); + return dvdInfo.Path; } public JobResult InsertDVD(string vmId, string isoPath) { - isoPath = FileUtils.EvaluateSystemVariables(isoPath); + HostedSolutionLog.LogStart("InsertDVD"); + HostedSolutionLog.DebugInfo("Virtual Machine: {0}", vmId); + HostedSolutionLog.DebugInfo("Path: {0}", isoPath); - // find DVD drive - ManagementObject objDvdDrive = wmi.GetWmiObject( - "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Synthetic DVD Drive'" - + " and InstanceID Like 'Microsoft:{0}%'", vmId); + try + { + var vm = GetVirtualMachineEx(vmId); + DvdDriveHelper.Set(PowerShell, vm.Name, isoPath); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("InsertDVD", ex); + throw; + } - // create CD/DVD disk - ManagementObject objDefaultDVD = wmi.GetWmiObject( - "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Virtual CD/DVD Disk'" - + " and InstanceID like '%Default'"); - ManagementObject objDvd = (ManagementObject)objDefaultDVD.Clone(); - objDvd["Parent"] = objDvdDrive.Path; - objDvd["Connection"] = new string[] { isoPath }; - - // get VM service - ManagementObject objVmsvc = GetVirtualSystemManagementService(); - - // get method - ManagementBaseObject inParams = objVmsvc.GetMethodParameters("AddVirtualSystemResources"); - inParams["TargetSystem"] = GetVirtualMachineObject(vmId); - inParams["ResourceSettingData"] = new string[] { objDvd.GetText(TextFormat.CimDtd20) }; - - // execute method - ManagementBaseObject outParams = objVmsvc.InvokeMethod("AddVirtualSystemResources", inParams, null); - return CreateJobResultFromWmiMethodResults(outParams); + HostedSolutionLog.LogEnd("InsertDVD"); + return CreateSuccessJobResult(); } public JobResult EjectDVD(string vmId) { - // find CD/DVD disk - ManagementObject objDvd = wmi.GetWmiObject( - "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Virtual CD/DVD Disk'" - + " and InstanceID Like 'Microsoft:{0}%'", vmId); + HostedSolutionLog.LogStart("InsertDVD"); + HostedSolutionLog.DebugInfo("Virtual Machine: {0}", vmId); - // get VM service - ManagementObject objVmsvc = GetVirtualSystemManagementService(); + try + { + var vm = GetVirtualMachineEx(vmId); + DvdDriveHelper.Set(PowerShell, vm.Name, null); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("InsertDVD", ex); + throw; + } - // get method - ManagementBaseObject inParams = objVmsvc.GetMethodParameters("RemoveVirtualSystemResources"); - inParams["TargetSystem"] = GetVirtualMachineObject(vmId); - inParams["ResourceSettingData"] = new object[] { objDvd.Path.Path }; - - // execute method - ManagementBaseObject outParams = objVmsvc.InvokeMethod("RemoveVirtualSystemResources", inParams, null); - return CreateJobResultFromWmiMethodResults(outParams); + HostedSolutionLog.LogEnd("InsertDVD"); + return CreateSuccessJobResult(); } #endregion @@ -1071,7 +1069,6 @@ namespace WebsitePanel.Providers.Virtualization HostedSolutionLog.LogStart("GetSwitches"); HostedSolutionLog.DebugInfo("ComputerName: {0}", computerName); - Runspace runSpace = null; List switches = new List(); try @@ -1100,53 +1097,67 @@ namespace WebsitePanel.Providers.Virtualization HostedSolutionLog.LogEnd("GetSwitches"); return switches; - } public bool SwitchExists(string switchId) { - ManagementObject objSwitch = wmi.GetWmiObject("msvm_VirtualSwitch", "Name = '{0}'", switchId); - return (objSwitch != null); + return GetSwitches().Any(s => s.Name == switchId); } public VirtualSwitch CreateSwitch(string name) { - // generate ID for new virtual switch - string id = Guid.NewGuid().ToString(); + // Create private switch - // get switch management object - ManagementObject objNetworkSvc = GetVirtualSwitchManagementService(); + HostedSolutionLog.LogStart("CreateSwitch"); + HostedSolutionLog.DebugInfo("Name: {0}", name); - ManagementBaseObject inParams = objNetworkSvc.GetMethodParameters("CreateSwitch"); - inParams["Name"] = id; - inParams["FriendlyName"] = name; - inParams["NumLearnableAddresses"] = SWITCH_PORTS_NUMBER; + VirtualSwitch virtualSwitch = null; - // invoke method - ManagementBaseObject outParams = objNetworkSvc.InvokeMethod("CreateSwitch", inParams, null); + try + { + Command cmd = new Command("New-VMSwitch"); - // process output parameters - ManagementObject objSwitch = wmi.GetWmiObjectByPath((string)outParams["CreatedVirtualSwitch"]); - return CreateSwitchFromWmiObject(objSwitch); + cmd.Parameters.Add("SwitchType", "Private"); + cmd.Parameters.Add("Name", name); + + Collection result = PowerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + virtualSwitch = new VirtualSwitch(); + virtualSwitch.SwitchId = result[0].GetString("Name"); + virtualSwitch.Name = result[0].GetString("Name"); + virtualSwitch.SwitchType = result[0].GetString("SwitchType"); + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("CreateSwitch", ex); + throw; + } + + HostedSolutionLog.LogEnd("CreateSwitch"); + return virtualSwitch; } public ReturnCode DeleteSwitch(string switchId) { - // find requested switch - ManagementObject objSwitch = wmi.GetWmiObject("msvm_VirtualSwitch", "Name = '{0}'", switchId); + HostedSolutionLog.LogStart("DeleteSwitch"); + HostedSolutionLog.DebugInfo("switchId: {0}", switchId); - if (objSwitch == null) - throw new Exception("Virtual switch with the specified ID was not found."); + try + { + Command cmd = new Command("Remove-VMSwitch"); + cmd.Parameters.Add("Name", switchId); + PowerShell.Execute(cmd, false); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("DeleteSwitch", ex); + throw; + } - // get switch management object - ManagementObject objNetworkSvc = GetVirtualSwitchManagementService(); - - // get method params - ManagementBaseObject inParams = objNetworkSvc.GetMethodParameters("DeleteSwitch"); - inParams["VirtualSwitch"] = objSwitch.Path.Path; - - ManagementBaseObject outParams = (ManagementBaseObject)objNetworkSvc.InvokeMethod("DeleteSwitch", inParams, null); - return (ReturnCode)Convert.ToInt32(outParams["ReturnValue"]); + HostedSolutionLog.LogEnd("DeleteSwitch"); + return ReturnCode.OK; } #endregion @@ -2102,6 +2113,17 @@ exit", Convert.ToInt32(objDisk["Index"]))); #endregion #region Private Methods + + internal int ConvertNullableToInt32(object value) + { + return value == null ? 0 : Convert.ToInt32(value); + } + + internal long ConvertNullableToInt64(object value) + { + return value == null ? 0 : Convert.ToInt64(value); + } + protected JobResult CreateSuccessJobResult() { JobResult result = new JobResult(); @@ -2540,15 +2562,5 @@ exit", Convert.ToInt32(objDisk["Index"]))); #endregion - internal int ConvertNullableToInt32(object value) - { - return value == null ? 0 : Convert.ToInt32(value); - } - - internal long ConvertNullableToInt64(object value) - { - return value == null ? 0 : Convert.ToInt64(value); - } - } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj index 2198fbaa..d033344b 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj @@ -54,6 +54,7 @@ VersionInfo.cs + From 8f6b035e890cbdf08105c681a466c70ac79c4470 Mon Sep 17 00:00:00 2001 From: AlexanderTr Date: Fri, 13 Mar 2015 08:02:31 +0300 Subject: [PATCH 06/12] wsp-10323 Convert the VSP provider into one utilizing PowerShell. Step 4 Snapshots --- .../Virtualization/VirtualMachine.cs | 6 + .../Virtualization/VirtualMachineSnapshot.cs | 1 + .../Extensions/PSObjectExtension.cs | 25 ++ .../Helpers/BiosHelper.cs | 4 +- .../Helpers/JobHelper.cs | 74 ++++ .../Helpers/SnapshotHelper.cs | 72 ++++ .../HyperV2012R2.cs | 374 +++++++++--------- ...oviders.Virtualization.HyperV2012R2.csproj | 2 + 8 files changed, 377 insertions(+), 181 deletions(-) create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/JobHelper.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/SnapshotHelper.cs diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachine.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachine.cs index 5f5847a8..11da6676 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachine.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachine.cs @@ -136,5 +136,11 @@ namespace WebsitePanel.Providers.Virtualization [Persistent] public int Generation { get; set; } + [Persistent] + public int ProcessorCount { get; set; } + + [Persistent] + public string ParentSnapshotId { get; set; } + } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineSnapshot.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineSnapshot.cs index f8f496ce..a20ae5f0 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineSnapshot.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineSnapshot.cs @@ -37,6 +37,7 @@ namespace WebsitePanel.Providers.Virtualization public string Id { get; set; } public string CheckPointId { get; set; } public string Name { get; set; } + public string VMName { get; set; } public string ParentId { get; set; } public DateTime Created { get; set; } public bool IsCurrent { get; set; } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Extensions/PSObjectExtension.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Extensions/PSObjectExtension.cs index e1a50bfa..33426fe2 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Extensions/PSObjectExtension.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Extensions/PSObjectExtension.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Management; using System.Management.Automation; using System.Text; using System.Threading.Tasks; @@ -9,6 +10,8 @@ namespace WebsitePanel.Providers.Virtualization { static class PSObjectExtension { + #region Properties + public static object GetProperty(this PSObject obj, string name) { return obj.Members[name].Value; @@ -33,5 +36,27 @@ namespace WebsitePanel.Providers.Virtualization { return obj.Members[name].Value == null ? "" : obj.Members[name].Value.ToString(); } + + #endregion + + + #region Methods + + public static ManagementObject Invoke(this PSObject obj, string name, object argument) + { + return obj.Invoke(name, new[] {argument}); + } + public static ManagementObject Invoke(this PSObject obj, string name, params object[] arguments) + { + var results = (ManagementObjectCollection)obj.Methods[name].Invoke(arguments); + + foreach (var result in results) + { + return (ManagementObject) result; + } + return null; + } + + #endregion } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/BiosHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/BiosHelper.cs index 25fcfe3b..ebfc9694 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/BiosHelper.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/BiosHelper.cs @@ -12,7 +12,7 @@ namespace WebsitePanel.Providers.Virtualization { public static class BiosHelper { - public static BiosInfo GetVMBios(PowerShellManager powerShell, string name, int generation) + public static BiosInfo Get(PowerShellManager powerShell, string name, int generation) { BiosInfo info = new BiosInfo(); @@ -76,7 +76,7 @@ namespace WebsitePanel.Providers.Virtualization return info; } - public static void UpdateBios(PowerShellManager powerShell, VirtualMachine vm, bool bootFromCD, bool numLockEnabled) + public static void Update(PowerShellManager powerShell, VirtualMachine vm, bool bootFromCD, bool numLockEnabled) { // for Win2012R2+ and Win8.1+ if (vm.Generation == 2) diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/JobHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/JobHelper.cs new file mode 100644 index 00000000..92d1e95a --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/JobHelper.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class JobHelper + { + public static JobResult CreateSuccessResult(ReturnCode returnCode = ReturnCode.OK) + { + return new JobResult + { + Job = new ConcreteJob {JobState = ConcreteJobState.Completed}, + ReturnValue = returnCode + }; + } + + public static JobResult CreateResultFromPSResults(Collection objJob) + { + if (objJob == null || objJob.Count == 0) + return null; + + JobResult result = new JobResult(); + + result.Job = CreateFromPSObject(objJob); + result.ReturnValue = ReturnCode.JobStarted; + + switch (result.Job.JobState) + { + case ConcreteJobState.Failed: + result.ReturnValue = ReturnCode.Failed; + break; + } + + return result; + } + + public static ConcreteJob CreateFromPSObject(Collection objJob) + { + if (objJob == null || objJob.Count == 0) + return null; + + ConcreteJob job = new ConcreteJob(); + job.Id = objJob[0].GetProperty("Id").ToString(); + job.JobState = objJob[0].GetEnum("JobStateInfo"); + job.Caption = objJob[0].GetProperty("Name"); + job.Description = objJob[0].GetProperty("Command"); + job.StartTime = objJob[0].GetProperty("PSBeginTime"); + job.ElapsedTime = objJob[0].GetProperty("PSEndTime") ?? DateTime.Now; + + // PercentComplete + job.PercentComplete = 0; + var progress = (PSDataCollection)objJob[0].GetProperty("Progress"); + if (progress != null && progress.Count > 0) + job.PercentComplete = progress[0].PercentComplete; + + // Errors + var errors = (PSDataCollection)objJob[0].GetProperty("Error"); + if (errors != null && errors.Count > 0) + { + job.ErrorDescription = errors[0].ErrorDetails.Message + ". " + errors[0].ErrorDetails.RecommendedAction; + job.ErrorCode = errors[0].Exception != null ? -1 : 0; + } + + return job; + } + + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/SnapshotHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/SnapshotHelper.cs new file mode 100644 index 00000000..b5dee5c2 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/SnapshotHelper.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class SnapshotHelper + { + public static VirtualMachineSnapshot GetFromPS(PSObject psObject, string runningSnapshotId = null) + { + var snapshot = new VirtualMachineSnapshot + { + Id = psObject.GetString("Id"), + Name = psObject.GetString("Name"), + VMName = psObject.GetString("VMName"), + ParentId = psObject.GetString("ParentSnapshotId"), + Created = psObject.GetProperty("CreationTime") + }; + + if (string.IsNullOrEmpty(snapshot.ParentId)) + snapshot.ParentId = null; // for capability + + if (!String.IsNullOrEmpty(runningSnapshotId)) + snapshot.IsCurrent = snapshot.Id == runningSnapshotId; + + return snapshot; + } + + public static VirtualMachineSnapshot GetFromWmi(ManagementBaseObject objSnapshot) + { + if (objSnapshot == null || objSnapshot.Properties.Count == 0) + return null; + + VirtualMachineSnapshot snapshot = new VirtualMachineSnapshot(); + snapshot.Id = (string)objSnapshot["InstanceID"]; + snapshot.Name = (string)objSnapshot["ElementName"]; + + string parentId = (string)objSnapshot["Parent"]; + if (!String.IsNullOrEmpty(parentId)) + { + int idx = parentId.IndexOf("Microsoft:"); + snapshot.ParentId = parentId.Substring(idx, parentId.Length - idx - 1); + snapshot.ParentId = snapshot.ParentId.ToLower().Replace("microsoft:", ""); + } + if (!String.IsNullOrEmpty(snapshot.Id)) + { + snapshot.Id = snapshot.Id.ToLower().Replace("microsoft:", ""); + } + snapshot.Created = Wmi.ToDateTime((string)objSnapshot["CreationTime"]); + + if (string.IsNullOrEmpty(snapshot.ParentId)) + snapshot.ParentId = null; // for capability + + return snapshot; + } + + public static void Delete(PowerShellManager powerShell, VirtualMachineSnapshot snapshot, bool includeChilds) + { + Command cmd = new Command("Remove-VMSnapshot"); + cmd.Parameters.Add("VMName", snapshot.VMName); + cmd.Parameters.Add("Name", snapshot.Name); + if (includeChilds) cmd.Parameters.Add("IncludeAllChildSnapshots", true); + + powerShell.Execute(cmd, false); + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs index e14db44b..095eba14 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs @@ -59,7 +59,7 @@ namespace WebsitePanel.Providers.Virtualization { #region Constants private const string CONFIG_USE_DISKPART_TO_CLEAR_READONLY_FLAG = "WebsitePanel.HyperV.UseDiskPartClearReadOnlyFlag"; - private const string WMI_VIRTUALIZATION_NAMESPACE = @"root\virtualization"; + private const string WMI_VIRTUALIZATION_NAMESPACE = @"root\virtualization\v2"; private const string WMI_CIMV2_NAMESPACE = @"root\cimv2"; private const int SWITCH_PORTS_NUMBER = 1024; @@ -174,6 +174,8 @@ namespace WebsitePanel.Providers.Virtualization vm.Status = result[0].GetProperty("Status").ToString(); vm.ReplicationState = result[0].GetProperty("ReplicationState").ToString(); vm.Generation = result[0].GetInt("Generation"); + vm.ProcessorCount = result[0].GetInt("ProcessorCount"); + vm.ParentSnapshotId = result[0].GetString("ParentSnapshotId"); vm.Heartbeat = VirtualMachineHelper.GetVMHeartBeatStatus(PowerShell, vm.Name); @@ -187,7 +189,7 @@ namespace WebsitePanel.Providers.Virtualization vm.RamSize = memoryInfo.Startup; // BIOS - BiosInfo biosInfo = BiosHelper.GetVMBios(PowerShell, vm.Name, vm.Generation); + BiosInfo biosInfo = BiosHelper.Get(PowerShell, vm.Name, vm.Generation); vm.NumLockEnabled = biosInfo.NumLockEnabled; vm.BootFromCD = biosInfo.BootFromCD; @@ -219,6 +221,7 @@ namespace WebsitePanel.Providers.Virtualization return vm; } + public List GetVirtualMachines() { HostedSolutionLog.LogStart("GetVirtualMachines"); @@ -255,11 +258,10 @@ namespace WebsitePanel.Providers.Virtualization public byte[] GetVirtualMachineThumbnailImage(string vmId, ThumbnailSize size) { - //ManagementBaseObject objSummary = GetVirtualMachineSummaryInformation(vmId, (SummaryInformationRequest)size); - //wmi.Dump(objSummary); - //return GetTumbnailFromSummaryInformation(objSummary, size); - // TODO: - return (byte[]) (new ImageConverter()).ConvertTo(new Bitmap(80, 60), typeof (byte[])); + ManagementBaseObject objSummary = GetVirtualMachineSummaryInformation(vmId, (SummaryInformationRequest)size); + wmi.Dump(objSummary); + return GetTumbnailFromSummaryInformation(objSummary, size); + //return (byte[]) (new ImageConverter()).ConvertTo(new Bitmap(80, 60), typeof (byte[])); } private byte[] GetTumbnailFromSummaryInformation(ManagementBaseObject objSummary, ThumbnailSize size) @@ -436,7 +438,7 @@ namespace WebsitePanel.Providers.Virtualization var realVm = GetVirtualMachineEx(vm.VirtualMachineId); DvdDriveHelper.Update(PowerShell, realVm, vm.DvdDriveInstalled); // Dvd should be before bios because bios sets boot order - BiosHelper.UpdateBios(PowerShell, realVm, vm.BootFromCD, vm.NumLockEnabled); + BiosHelper.Update(PowerShell, realVm, vm.BootFromCD, vm.NumLockEnabled); VirtualMachineHelper.UpdateProcessors(PowerShell, realVm, vm.CpuCores, CpuLimitSettings, CpuReserveSettings, CpuWeightSettings); VirtualMachineHelper.UpdateMemory(PowerShell, realVm, vm.RamSize); NetworkAdapterHelper.Update(PowerShell, vm); @@ -661,7 +663,7 @@ namespace WebsitePanel.Providers.Virtualization paramList.ForEach(p => cmd.Parameters.Add(p)); PowerShell.Execute(cmd, false); - jobResult = CreateSuccessJobResult(); + jobResult = JobHelper.CreateSuccessResult(); } catch (Exception ex) { @@ -852,130 +854,148 @@ namespace WebsitePanel.Providers.Virtualization #endregion #region Snapshots + public List GetVirtualMachineSnapshots(string vmId) { - // get all VM setting objects - ManagementObject objVmSettings = GetVirtualMachineSettingsObject(vmId); - VirtualMachineSnapshot runningSnapshot = CreateSnapshotFromWmiObject(objVmSettings); - - // load snapshots - ManagementBaseObject objSummary = GetVirtualMachineSummaryInformation(vmId, SummaryInformationRequest.Snapshots); - ManagementBaseObject[] objSnapshots = (ManagementBaseObject[])objSummary["Snapshots"]; - List snapshots = new List(); - if (objSnapshots != null) + try { - foreach (ManagementBaseObject objSnapshot in objSnapshots) + var vm = GetVirtualMachine(vmId); + + Command cmd = new Command("Get-VMSnapshot"); + cmd.Parameters.Add("VMName", vm.Name); + + Collection result = PowerShell.Execute(cmd, false); + if (result != null && result.Count > 0) { - VirtualMachineSnapshot snapshot = CreateSnapshotFromWmiObject(objSnapshot); - snapshot.IsCurrent = (runningSnapshot.ParentId == snapshot.Id); - snapshots.Add(snapshot); + foreach (PSObject psSnapshot in result) + { + snapshots.Add(SnapshotHelper.GetFromPS(psSnapshot, vm.ParentSnapshotId)); + } } } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetVirtualMachineSnapshots", ex); + throw; + } return snapshots; } public VirtualMachineSnapshot GetSnapshot(string snapshotId) { - // load snapshot - ManagementObject objSnapshot = GetSnapshotObject(snapshotId); - return CreateSnapshotFromWmiObject(objSnapshot); + try + { + Command cmd = new Command("Get-VMSnapshot"); + cmd.Parameters.Add("Id", snapshotId); + + Collection result = PowerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + return SnapshotHelper.GetFromPS(result[0]); + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetSnapshot", ex); + throw; + } + + return null; } public JobResult CreateSnapshot(string vmId) { - // get VM management service - ManagementObject objVmsvc = GetVirtualSystemManagementService(); + try + { + var vm = GetVirtualMachine(vmId); - // load virtual machine - ManagementObject objVm = GetVirtualMachineObject(vmId); + Command cmd = new Command("Checkpoint-VM"); + cmd.Parameters.Add("Name", vm.Name); - // get method params - ManagementBaseObject inParams = objVmsvc.GetMethodParameters("CreateVirtualSystemSnapshot"); - inParams["SourceSystem"] = objVm; - - // invoke method - ManagementBaseObject outParams = objVmsvc.InvokeMethod("CreateVirtualSystemSnapshot", inParams, null); - return CreateJobResultFromWmiMethodResults(outParams); + PowerShell.Execute(cmd, false); + return JobHelper.CreateSuccessResult(ReturnCode.JobStarted); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("CreateSnapshot", ex); + throw; + } } public JobResult RenameSnapshot(string vmId, string snapshotId, string name) { - // load virtual machine - ManagementObject objVm = GetVirtualMachineObject(vmId); + try + { + var vm = GetVirtualMachine(vmId); + var snapshot = GetSnapshot(snapshotId); - // load snapshot - ManagementObject objSnapshot = GetSnapshotObject(snapshotId); + Command cmd = new Command("Rename-VMSnapshot"); + cmd.Parameters.Add("VMName", vm.Name); + cmd.Parameters.Add("Name", snapshot.Name); + cmd.Parameters.Add("NewName", name); - // rename snapshot - objSnapshot["ElementName"] = name; - - // save - ManagementObject objVmsvc = GetVirtualSystemManagementService(); - ManagementBaseObject inParams = objVmsvc.GetMethodParameters("ModifyVirtualSystem"); - inParams["ComputerSystem"] = objVm.Path.Path; - inParams["SystemSettingData"] = objSnapshot.GetText(TextFormat.CimDtd20); - ManagementBaseObject outParams = objVmsvc.InvokeMethod("ModifyVirtualSystem", inParams, null); - return CreateJobResultFromWmiMethodResults(outParams); + PowerShell.Execute(cmd, false); + return JobHelper.CreateSuccessResult(); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("RenameSnapshot", ex); + throw; + } } public JobResult ApplySnapshot(string vmId, string snapshotId) { - // get VM management service - ManagementObject objVmsvc = GetVirtualSystemManagementService(); + try + { + var vm = GetVirtualMachine(vmId); + var snapshot = GetSnapshot(snapshotId); - // load virtual machine - ManagementObject objVm = GetVirtualMachineObject(vmId); + Command cmd = new Command("Restore-VMSnapshot"); + cmd.Parameters.Add("VMName", vm.Name); + cmd.Parameters.Add("Name", snapshot.Name); - // load snapshot - ManagementObject objSnapshot = GetSnapshotObject(snapshotId); - - ManagementObjectCollection objRelated = objVm.GetRelated("Msvm_SettingsDefineState"); - - // get method params - ManagementBaseObject inParams = objVmsvc.GetMethodParameters("ApplyVirtualSystemSnapshot"); - inParams["ComputerSystem"] = objVm.Path.Path; - inParams["SnapshotSettingData"] = objSnapshot.Path.Path; - - // invoke method - ManagementBaseObject outParams = objVmsvc.InvokeMethod("ApplyVirtualSystemSnapshot", inParams, null); - return CreateJobResultFromWmiMethodResults(outParams); + PowerShell.Execute(cmd, false); + return JobHelper.CreateSuccessResult(); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("ApplySnapshot", ex); + throw; + } } public JobResult DeleteSnapshot(string snapshotId) { - // get VM management service - ManagementObject objVmsvc = GetVirtualSystemManagementService(); - - // load snapshot object - ManagementObject objSnapshot = GetSnapshotObject(snapshotId); - - // get method params - ManagementBaseObject inParams = objVmsvc.GetMethodParameters("RemoveVirtualSystemSnapshot"); - inParams["SnapshotSettingData"] = objSnapshot.Path.Path; - - // invoke method - ManagementBaseObject outParams = objVmsvc.InvokeMethod("RemoveVirtualSystemSnapshot", inParams, null); - return CreateJobResultFromWmiMethodResults(outParams); + try + { + var snapshot = GetSnapshot(snapshotId); + SnapshotHelper.Delete(PowerShell, snapshot, false); + return JobHelper.CreateSuccessResult(ReturnCode.JobStarted); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("DeleteSnapshot", ex); + throw; + } } public JobResult DeleteSnapshotSubtree(string snapshotId) { - // get VM management service - ManagementObject objVmsvc = GetVirtualSystemManagementService(); - - // load snapshot object - ManagementObject objSnapshot = GetSnapshotObject(snapshotId); - - // get method params - ManagementBaseObject inParams = objVmsvc.GetMethodParameters("RemoveVirtualSystemSnapshotTree"); - inParams["SnapshotSettingData"] = objSnapshot.Path.Path; - - // invoke method - ManagementBaseObject outParams = objVmsvc.InvokeMethod("RemoveVirtualSystemSnapshotTree", inParams, null); - return CreateJobResultFromWmiMethodResults(outParams); + try + { + var snapshot = GetSnapshot(snapshotId); + SnapshotHelper.Delete(PowerShell, snapshot, true); + return JobHelper.CreateSuccessResult(ReturnCode.JobStarted); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("DeleteSnapshot", ex); + throw; + } } public byte[] GetSnapshotThumbnailImage(string snapshotId, ThumbnailSize size) @@ -983,6 +1003,7 @@ namespace WebsitePanel.Providers.Virtualization ManagementBaseObject objSummary = GetSnapshotSummaryInformation(snapshotId, (SummaryInformationRequest)size); return GetTumbnailFromSummaryInformation(objSummary, size); } + #endregion #region DVD operations @@ -1029,7 +1050,7 @@ namespace WebsitePanel.Providers.Virtualization } HostedSolutionLog.LogEnd("InsertDVD"); - return CreateSuccessJobResult(); + return JobHelper.CreateSuccessResult(); } public JobResult EjectDVD(string vmId) @@ -1049,7 +1070,7 @@ namespace WebsitePanel.Providers.Virtualization } HostedSolutionLog.LogEnd("InsertDVD"); - return CreateSuccessJobResult(); + return JobHelper.CreateSuccessResult(); } #endregion @@ -1276,8 +1297,7 @@ namespace WebsitePanel.Providers.Virtualization } catch { - // TODO - // add logging... + HostedSolutionLog.LogError("GetKVPItems", new Exception("msvm_KvpExchangeComponent")); return pairs; } @@ -1306,7 +1326,71 @@ namespace WebsitePanel.Providers.Virtualization pairs.Add(new KvpExchangeDataItem(name, data)); } - return pairs; + return pairs; + + //HostedSolutionLog.LogStart("GetKVPItems"); + //HostedSolutionLog.DebugInfo("Virtual Machine: {0}", vmId); + //HostedSolutionLog.DebugInfo("exchangeItemsName: {0}", exchangeItemsName); + + //List pairs = new List(); + + //try + //{ + // var vm = GetVirtualMachine(vmId); + + // Command cmdGetVm = new Command("Get-WmiObject"); + + // cmdGetVm.Parameters.Add("Namespace", WMI_VIRTUALIZATION_NAMESPACE); + // cmdGetVm.Parameters.Add("Class", "Msvm_ComputerSystem"); + // cmdGetVm.Parameters.Add("Filter", "ElementName = '" + vm.Name + "'"); + + // Collection result = PowerShell.Execute(cmdGetVm, false); + + // if (result != null && result.Count > 0) + // { + // dynamic resultDynamic = result[0];//.Invoke(); + // var kvp = resultDynamic.GetRelated("Msvm_KvpExchangeComponent"); + + // // return XML pairs + // string[] xmlPairs = null; + + // foreach (dynamic a in kvp) + // { + // xmlPairs = a[exchangeItemsName]; + // break; + // } + + // if (xmlPairs == null) + // return pairs; + + // // join all pairs + // StringBuilder sb = new StringBuilder(); + // sb.Append(""); + // foreach (string xmlPair in xmlPairs) + // sb.Append(xmlPair); + // sb.Append(""); + + // // parse pairs + // XmlDocument doc = new XmlDocument(); + // doc.LoadXml(sb.ToString()); + + // foreach (XmlNode nodeName in doc.SelectNodes("/result/INSTANCE/PROPERTY[@NAME='Name']/VALUE")) + // { + // string name = nodeName.InnerText; + // string data = nodeName.ParentNode.ParentNode.SelectSingleNode("PROPERTY[@NAME='Data']/VALUE").InnerText; + // pairs.Add(new KvpExchangeDataItem(name, data)); + // } + // } + //} + //catch (Exception ex) + //{ + // HostedSolutionLog.LogError("GetKVPItems", ex); + // throw; + //} + + //HostedSolutionLog.LogEnd("GetKVPItems"); + + //return pairs; } public JobResult AddKVPItems(string vmId, KvpExchangeDataItem[] items) @@ -1832,13 +1916,12 @@ exit", Convert.ToInt32(objDisk["Index"]))); try { - Command cmd = new Command("Get-Job"); if (!string.IsNullOrEmpty(jobId)) cmd.Parameters.Add("Id", jobId); Collection result = PowerShell.Execute( cmd, false); - job = CreateJobFromPSObject(result); + job = JobHelper.CreateFromPSObject(result); } catch (Exception ex) { @@ -2123,35 +2206,15 @@ exit", Convert.ToInt32(objDisk["Index"]))); { return value == null ? 0 : Convert.ToInt64(value); } + + //protected VirtualMachineSnapshot GetSnapshotById(string id) + //{ + // var vms = GetVirtualMachines(); + // var allSnapshots = vms.SelectMany(vm => GetVirtualMachineSnapshots(vm.Id.ToString())); + + // return allSnapshots.FirstOrDefault(s => s.Id == id); + //} - protected JobResult CreateSuccessJobResult() - { - JobResult result = new JobResult(); - - result.Job = new ConcreteJob(){JobState = ConcreteJobState.Completed}; - result.ReturnValue = ReturnCode.OK; - - return result; - } - protected JobResult CreateJobResultFromPSResults(Collection objJob) - { - if (objJob == null || objJob.Count == 0) - return null; - - JobResult result = new JobResult(); - - result.Job = CreateJobFromPSObject(objJob); - - result.ReturnValue = ReturnCode.JobStarted; - switch (result.Job.JobState) - { - case ConcreteJobState.Failed: - result.ReturnValue = ReturnCode.Failed; - break; - } - - return result; - } protected JobResult CreateJobResultFromWmiMethodResults(ManagementBaseObject outParams) { @@ -2201,29 +2264,12 @@ exit", Convert.ToInt32(objDisk["Index"]))); private ManagementObject GetSnapshotObject(string snapshotId) { - return wmi.GetWmiObject("Msvm_VirtualSystemSettingData", "InstanceID = '{0}'", snapshotId); + return wmi.GetWmiObject("Msvm_VirtualSystemSettingData", "InstanceID = '{0}'", snapshotId) ?? + wmi.GetWmiObject("Msvm_VirtualSystemSettingData", "InstanceID = '{0}'", "Microsoft:" + snapshotId); } - private VirtualMachineSnapshot CreateSnapshotFromWmiObject(ManagementBaseObject objSnapshot) - { - if (objSnapshot == null || objSnapshot.Properties.Count == 0) - return null; - - VirtualMachineSnapshot snapshot = new VirtualMachineSnapshot(); - snapshot.Id = (string)objSnapshot["InstanceID"]; - snapshot.Name = (string)objSnapshot["ElementName"]; - - string parentId = (string)objSnapshot["Parent"]; - if (!String.IsNullOrEmpty(parentId)) - { - int idx = parentId.IndexOf("Microsoft:"); - snapshot.ParentId = parentId.Substring(idx, parentId.Length - idx - 1); - } - snapshot.Created = wmi.ToDateTime((string)objSnapshot["CreationTime"]); - - return snapshot; - } + private VirtualSwitch CreateSwitchFromWmiObject(ManagementObject objSwitch) { @@ -2236,36 +2282,6 @@ exit", Convert.ToInt32(objDisk["Index"]))); return sw; } - private ConcreteJob CreateJobFromPSObject(Collection objJob) - { - if (objJob == null || objJob.Count == 0) - return null; - - ConcreteJob job = new ConcreteJob(); - job.Id = objJob[0].GetProperty("Id").ToString(); - job.JobState = objJob[0].GetEnum("JobStateInfo"); - job.Caption = objJob[0].GetProperty("Name"); - job.Description = objJob[0].GetProperty("Command"); - job.StartTime = objJob[0].GetProperty("PSBeginTime"); - job.ElapsedTime = objJob[0].GetProperty("PSEndTime") ?? DateTime.Now; - - // PercentComplete - job.PercentComplete = 0; - var progress = (PSDataCollection)objJob[0].GetProperty("Progress"); - if (progress != null && progress.Count > 0) - job.PercentComplete = progress[0].PercentComplete; - - // Errors - var errors = (PSDataCollection)objJob[0].GetProperty("Error"); - if (errors != null && errors.Count > 0) - { - job.ErrorDescription = errors[0].ErrorDetails.Message + ". " + errors[0].ErrorDetails.RecommendedAction; - job.ErrorCode = errors[0].Exception != null ? -1 : 0; - } - - return job; - } - private ConcreteJob CreateJobFromWmiObject(ManagementBaseObject objJob) { if (objJob == null || objJob.Properties.Count == 0) @@ -2276,7 +2292,7 @@ exit", Convert.ToInt32(objDisk["Index"]))); job.JobState = (ConcreteJobState)Convert.ToInt32(objJob["JobState"]); job.Caption = (string)objJob["Caption"]; job.Description = (string)objJob["Description"]; - job.StartTime = wmi.ToDateTime((string)objJob["StartTime"]); + job.StartTime = Wmi.ToDateTime((string)objJob["StartTime"]); // TODO proper parsing of WMI time spans, e.g. 00000000000001.325247:000 job.ElapsedTime = DateTime.Now; //wmi.ToDateTime((string)objJob["ElapsedTime"]); job.ErrorCode = Convert.ToInt32(objJob["ErrorCode"]); diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj index d033344b..c18926b0 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj @@ -57,6 +57,8 @@ + + From 04bb21447e2d4a2c5a5470942166f5a74e673df8 Mon Sep 17 00:00:00 2001 From: AlexanderTr Date: Fri, 13 Mar 2015 08:02:51 +0300 Subject: [PATCH 07/12] wsp-10323 Convert the VSP provider into one utilizing PowerShell. Step 4 Snapshots --- .../WebsitePanel.Providers.Virtualization.HyperV-2012R2/Wmi.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Wmi.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Wmi.cs index 8ab33885..6a7140a2 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Wmi.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Wmi.cs @@ -115,7 +115,7 @@ namespace WebsitePanel.Providers.Virtualization } // Converts a given datetime in DMTF format to System.DateTime object. - internal System.DateTime ToDateTime(string dmtfDate) + internal static System.DateTime ToDateTime(string dmtfDate) { System.DateTime initializer = System.DateTime.MinValue; int year = initializer.Year; From d61b1613d9adea82cfb4f23567584a06522a1d74 Mon Sep 17 00:00:00 2001 From: me Date: Fri, 13 Mar 2015 09:08:01 +0400 Subject: [PATCH 08/12] wsp-10323 Convert the VSP provider into one utilizing PowerShell. Step 4 Snapshots --- .../WebsitePanel/UserControls/SpaceServiceItems.ascx.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/UserControls/SpaceServiceItems.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/UserControls/SpaceServiceItems.ascx.cs index c3914eee..a6275f7e 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/UserControls/SpaceServiceItems.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/UserControls/SpaceServiceItems.ascx.cs @@ -211,18 +211,24 @@ namespace WebsitePanel.Portal.UserControls private void ShowActionList() { + var checkboxColumn = gvItems.Columns[0]; websiteActions.Visible = false; mailActions.Visible = false; + checkboxColumn.Visible = false; switch (QuotaName) { case "Web.Sites": websiteActions.Visible = true; + checkboxColumn.Visible = true; break; case "Mail.Accounts": ProviderInfo provider = ES.Services.Servers.GetPackageServiceProvider(PanelSecurity.PackageId, "Mail"); if (provider.EditorControl == "SmarterMail100") + { mailActions.Visible = true; + checkboxColumn.Visible = true; + } break; } } From b07a57c1d32f394d96ffc8572f1f54f2cd067ed6 Mon Sep 17 00:00:00 2001 From: me Date: Fri, 13 Mar 2015 15:57:02 +0400 Subject: [PATCH 09/12] wsp-10323 Compatibility with old HyperV --- .../Virtualization/VirtualMachineState.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineState.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineState.cs index d7db3390..c33396f7 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineState.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineState.cs @@ -49,18 +49,21 @@ namespace WebsitePanel.Providers.Virtualization Deleted = 32775, Pausing = 32776 */ + Snapshotting = 32771, + Migrating = 32772, + Deleted = 32775, Unknown = 0, Other = 1, Running = 2, Off = 3, - Stopping = 4, - Saved = 6, - Paused = 9, - Starting = 10, - Reset = 11, - Saving = 32773, - Pausing = 32776, + Stopping = 32774, // new 4 + Saved = 32769, // new 6 + Paused = 32768, // new 9 + Starting = 32770, // new 10 + Reset = 10, // new 11 + Saving = 32773, // new 32773 + Pausing = 32776, // new 32776 Resuming = 32777, FastSaved = 32779, FastSaving = 32780, From 8ba7c518494fe447838889009a1662b770b8495c Mon Sep 17 00:00:00 2001 From: me Date: Sat, 14 Mar 2015 20:19:22 +0400 Subject: [PATCH 10/12] wsp-10323 Compatibility with old HyperV --- .../Virtualization/VirtualHardDiskFormat.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs index 8787c82d..6d6a9fb0 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs @@ -34,7 +34,7 @@ namespace WebsitePanel.Providers.Virtualization { public enum VirtualHardDiskFormat { - VHD = 1, - VHDX = 2 + VHD = 0, + VHDX = 1 } } From 13ae446aa1cdbcba2523260d3fa34dc49b081f9a Mon Sep 17 00:00:00 2001 From: AlexanderTr Date: Mon, 16 Mar 2015 06:40:31 +0300 Subject: [PATCH 11/12] wsp-10323 Convert the VSP provider into one utilizing PowerShell. Step 5 Storage --- .../Virtualization/AutomaticStartAction.cs | 15 ++ .../Virtualization/AutomaticStopAction.cs | 15 ++ .../Virtualization/VirtualHardDiskFormat.cs | 4 +- .../WebsitePanel.Providers.Base.csproj | 2 + .../Constants.cs | 14 ++ .../Helpers/BiosHelper.cs | 2 +- .../Helpers/HardDriveHelper.cs | 89 ++++++++ .../Helpers/VirtualMachineHelper.cs | 76 +------ .../HyperV2012R2.cs | 200 ++++++------------ ...oviders.Virtualization.HyperV2012R2.csproj | 4 +- .../Sources/WebsitePanel.Server/Web.config | 124 +++++------ 11 files changed, 274 insertions(+), 271 deletions(-) create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/AutomaticStartAction.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/AutomaticStopAction.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Constants.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/HardDriveHelper.cs diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/AutomaticStartAction.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/AutomaticStartAction.cs new file mode 100644 index 00000000..881a8a27 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/AutomaticStartAction.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WebsitePanel.Providers.Virtualization +{ + public enum AutomaticStartAction + { + Undefined = 100, + Nothing = 0, + StartIfRunning = 1, + Start = 2, + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/AutomaticStopAction.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/AutomaticStopAction.cs new file mode 100644 index 00000000..3386e0d8 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/AutomaticStopAction.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WebsitePanel.Providers.Virtualization +{ + public enum AutomaticStopAction + { + Undefined = 100, + TurnOff = 0, + Save = 1, + ShutDown = 2, + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs index 8787c82d..6d6a9fb0 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualHardDiskFormat.cs @@ -34,7 +34,7 @@ namespace WebsitePanel.Providers.Virtualization { public enum VirtualHardDiskFormat { - VHD = 1, - VHDX = 2 + VHD = 0, + VHDX = 1 } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj index 5a7ebe5b..d0bf17c3 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj @@ -288,6 +288,8 @@ + + diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Constants.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Constants.cs new file mode 100644 index 00000000..7821a6fd --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Constants.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class Constants + { + public const Int64 Size1G = 0x40000000; + public const Int64 Size1M = 0x100000; + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/BiosHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/BiosHelper.cs index ebfc9694..21dafb52 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/BiosHelper.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/BiosHelper.cs @@ -87,7 +87,7 @@ namespace WebsitePanel.Providers.Virtualization if (bootFromCD) cmd.Parameters.Add("FirstBootDevice", DvdDriveHelper.GetPS(powerShell, vm.Name)); else - cmd.Parameters.Add("FirstBootDevice", VirtualMachineHelper.GetVirtualHardDisksPS(powerShell, vm.Name).FirstOrDefault()); + cmd.Parameters.Add("FirstBootDevice", HardDriveHelper.GetPS(powerShell, vm.Name).FirstOrDefault()); powerShell.Execute(cmd, false); } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/HardDriveHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/HardDriveHelper.cs new file mode 100644 index 00000000..c8a951d6 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/HardDriveHelper.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class HardDriveHelper + { + public static VirtualHardDiskInfo[] Get(PowerShellManager powerShell, string vmname) + { + List disks = new List(); + + Collection result = GetPS(powerShell, vmname); + + if (result != null && result.Count > 0) + { + foreach (PSObject d in result) + { + VirtualHardDiskInfo disk = new VirtualHardDiskInfo(); + + disk.SupportPersistentReservations = Convert.ToBoolean(d.GetProperty("SupportPersistentReservations")); + disk.MaximumIOPS = Convert.ToUInt64(d.GetProperty("MaximumIOPS")); + disk.MinimumIOPS = Convert.ToUInt64(d.GetProperty("MinimumIOPS")); + disk.VHDControllerType = d.GetEnum("ControllerType"); + disk.ControllerNumber = Convert.ToInt32(d.GetProperty("ControllerNumber")); + disk.ControllerLocation = Convert.ToInt32(d.GetProperty("ControllerLocation")); + disk.Path = d.GetProperty("Path").ToString(); + disk.Name = d.GetProperty("Name").ToString(); + + GetVirtualHardDiskDetail(powerShell, disk.Path, ref disk); + + disks.Add(disk); + } + } + return disks.ToArray(); + } + + //public static VirtualHardDiskInfo GetByPath(PowerShellManager powerShell, string vhdPath) + //{ + // VirtualHardDiskInfo info = null; + // var vmNames = new List(); + + // Command cmd = new Command("Get-VM"); + + // Collection result = powerShell.Execute(cmd, false); + + // if (result == null || result.Count == 0) + // return null; + + // vmNames = result.Select(r => r.GetString("Name")).ToList(); + // var drives = vmNames.SelectMany(n => Get(powerShell, n)); + + // return drives.FirstOrDefault(d=>d.Path == vhdPath); + //} + + public static Collection GetPS(PowerShellManager powerShell, string vmname) + { + Command cmd = new Command("Get-VMHardDiskDrive"); + cmd.Parameters.Add("VMName", vmname); + + return powerShell.Execute(cmd, false); + } + + public static void GetVirtualHardDiskDetail(PowerShellManager powerShell, string path, ref VirtualHardDiskInfo disk) + { + if (!string.IsNullOrEmpty(path)) + { + Command cmd = new Command("Get-VHD"); + cmd.Parameters.Add("Path", path); + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + disk.DiskFormat = result[0].GetEnum("VhdFormat"); + disk.DiskType = result[0].GetEnum("VhdType"); + disk.ParentPath = result[0].GetProperty("ParentPath"); + disk.MaxInternalSize = Convert.ToInt64(result[0].GetProperty("Size")); + disk.FileSize = Convert.ToInt64(result[0].GetProperty("FileSize")); + disk.Attached = disk.InUse = Convert.ToBoolean(result[0].GetProperty("Attached")); + } + } + } + + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VirtualMachineHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VirtualMachineHelper.cs index 0d24c149..e7d05288 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VirtualMachineHelper.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VirtualMachineHelper.cs @@ -12,16 +12,8 @@ namespace WebsitePanel.Providers.Virtualization { public static class VirtualMachineHelper { - #region Constants - - private const Int64 Size1G = 0x40000000; - private const Int64 Size1M = 0x100000; - - #endregion - public static OperationalStatus GetVMHeartBeatStatus(PowerShellManager powerShell, string name) { - OperationalStatus status = OperationalStatus.None; Command cmd = new Command("Get-VMIntegrationService"); @@ -40,7 +32,6 @@ namespace WebsitePanel.Providers.Virtualization return status; } - public static int GetVMProcessors(PowerShellManager powerShell, string name) { @@ -71,73 +62,15 @@ namespace WebsitePanel.Providers.Virtualization if (result != null && result.Count > 0) { info.DynamicMemoryEnabled = Convert.ToBoolean(result[0].GetProperty("DynamicMemoryEnabled")); - info.Startup = Convert.ToInt64(result[0].GetProperty("Startup")) / Size1M; - info.Minimum = Convert.ToInt64(result[0].GetProperty("Minimum")) / Size1M; - info.Maximum = Convert.ToInt64(result[0].GetProperty("Maximum")) / Size1M; + info.Startup = Convert.ToInt64(result[0].GetProperty("Startup")) / Constants.Size1M; + info.Minimum = Convert.ToInt64(result[0].GetProperty("Minimum")) / Constants.Size1M; + info.Maximum = Convert.ToInt64(result[0].GetProperty("Maximum")) / Constants.Size1M; info.Buffer = Convert.ToInt32(result[0].GetProperty("Buffer")); info.Priority = Convert.ToInt32(result[0].GetProperty("Priority")); } return info; } - - public static VirtualHardDiskInfo[] GetVirtualHardDisks(PowerShellManager powerShell, string name) - { - List disks = new List(); - - Collection result = GetVirtualHardDisksPS(powerShell, name); - - if (result != null && result.Count > 0) - { - foreach (PSObject d in result) - { - VirtualHardDiskInfo disk = new VirtualHardDiskInfo(); - - disk.SupportPersistentReservations = Convert.ToBoolean(d.GetProperty("SupportPersistentReservations")); - disk.MaximumIOPS = Convert.ToUInt64(d.GetProperty("MaximumIOPS")); - disk.MinimumIOPS = Convert.ToUInt64(d.GetProperty("MinimumIOPS")); - disk.VHDControllerType = d.GetEnum("ControllerType"); - disk.ControllerNumber = Convert.ToInt32(d.GetProperty("ControllerNumber")); - disk.ControllerLocation = Convert.ToInt32(d.GetProperty("ControllerLocation")); - disk.Path = d.GetProperty("Path").ToString(); - disk.Name = d.GetProperty("Name").ToString(); - - GetVirtualHardDiskDetail(powerShell, disk.Path, ref disk); - - disks.Add(disk); - } - } - return disks.ToArray(); - } - - public static Collection GetVirtualHardDisksPS(PowerShellManager powerShell, string name) - { - Command cmd = new Command("Get-VMHardDiskDrive"); - cmd.Parameters.Add("VMName", name); - - return powerShell.Execute(cmd, false); - } - - public static void GetVirtualHardDiskDetail(PowerShellManager powerShell, string path, ref VirtualHardDiskInfo disk) - { - if (!string.IsNullOrEmpty(path)) - { - Command cmd = new Command("Get-VHD"); - cmd.Parameters.Add("Path", path); - Collection result = powerShell.Execute(cmd, false); - if (result != null && result.Count > 0) - { - disk.DiskFormat = result[0].GetEnum("VhdFormat"); - disk.DiskType = result[0].GetEnum("VhdType"); - disk.ParentPath = result[0].GetProperty("ParentPath"); - disk.MaxInternalSize = Convert.ToInt64(result[0].GetProperty("Size")) / Size1G; - disk.FileSize = Convert.ToInt64(result[0].GetProperty("FileSize")) / Size1G; - disk.Attached = Convert.ToBoolean(result[0].GetProperty("Attached")); - } - } - } - - public static void UpdateProcessors(PowerShellManager powerShell, VirtualMachine vm, int cpuCores, int cpuLimitSettings, int cpuReserveSettings, int cpuWeightSettings) { Command cmd = new Command("Set-VMProcessor"); @@ -150,12 +83,13 @@ namespace WebsitePanel.Providers.Virtualization powerShell.Execute(cmd, false); } + public static void UpdateMemory(PowerShellManager powerShell, VirtualMachine vm, long ramMB) { Command cmd = new Command("Set-VMMemory"); cmd.Parameters.Add("VMName", vm.Name); - cmd.Parameters.Add("StartupBytes", ramMB * Size1M); + cmd.Parameters.Add("StartupBytes", ramMB * Constants.Size1M); powerShell.Execute(cmd, false); } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs index 095eba14..7b1801ea 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs @@ -198,12 +198,12 @@ namespace WebsitePanel.Providers.Virtualization vm.DvdDriveInstalled = dvdInfo != null; // HDD - vm.Disks = VirtualMachineHelper.GetVirtualHardDisks(PowerShell, vm.Name); + vm.Disks = HardDriveHelper.Get(PowerShell, vm.Name); if (vm.Disks != null && vm.Disks.GetLength(0) > 0) { vm.VirtualHardDrivePath = vm.Disks[0].Path; - vm.HddSize = Convert.ToInt32(vm.Disks[0].FileSize); + vm.HddSize = Convert.ToInt32(vm.Disks[0].FileSize / Size1G); } // network adapters @@ -327,104 +327,46 @@ namespace WebsitePanel.Providers.Virtualization vm.OperatingSystemTemplatePath = FileUtils.EvaluateSystemVariables(vm.OperatingSystemTemplatePath); vm.VirtualHardDrivePath = FileUtils.EvaluateSystemVariables(vm.VirtualHardDrivePath); - string vmID = null; - - // request management service - ManagementObject objVmsvc = GetVirtualSystemManagementService(); - - // display name - ManagementObject objGlobalSettings = wmi.GetWmiClass("msvm_VirtualSystemGlobalSettingData").CreateInstance(); - objGlobalSettings["ElementName"] = vm.Name; - - // VM folders - objGlobalSettings["ExternalDataRoot"] = vm.RootFolderPath; - objGlobalSettings["SnapshotDataRoot"] = vm.RootFolderPath; - - wmi.Dump(objGlobalSettings); - - // startup/shutdown actions - if (AutomaticStartActionSettings != 100) + try { - objGlobalSettings["AutomaticStartupAction"] = AutomaticStartActionSettings; - objGlobalSettings["AutomaticStartupActionDelay"] = String.Format("000000000000{0:d2}.000000:000", AutomaticStartupDelaySettings); + // Add new VM + Command cmdNew = new Command("New-VM"); + cmdNew.Parameters.Add("Name", vm.Name); + cmdNew.Parameters.Add("VHDPath", vm.VirtualHardDrivePath); + PowerShell.Execute(cmdNew, false); + + // Set VM + Command cmdSet = new Command("Set-VM"); + cmdSet.Parameters.Add("Name", vm.Name); + cmdSet.Parameters.Add("SmartPagingFilePath", vm.RootFolderPath); + cmdSet.Parameters.Add("SnapshotFileLocation", vm.RootFolderPath); + // startup/shutdown actions + var autoStartAction = (AutomaticStartAction) AutomaticStartActionSettings; + var autoStopAction = (AutomaticStopAction) AutomaticStartActionSettings; + if (autoStartAction != AutomaticStartAction.Undefined) + { + cmdSet.Parameters.Add("AutomaticStartAction", autoStartAction.ToString()); + cmdSet.Parameters.Add("AutomaticStartDelay", AutomaticStartupDelaySettings); + } + if (autoStopAction != AutomaticStopAction.Undefined) + cmdSet.Parameters.Add("AutomaticStopAction", autoStopAction.ToString()); + PowerShell.Execute(cmdSet, false); + + // Get created machine Id + var createdMachine = GetVirtualMachines().FirstOrDefault(m => m.Name == vm.Name); + if (createdMachine == null) + throw new Exception("Can't find created machine"); + vm.VirtualMachineId = createdMachine.VirtualMachineId; + + // Update common settings + UpdateVirtualMachine(vm); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("CreateVirtualMachine", ex); + throw; } - if (AutomaticStopActionSettings != 100) - objGlobalSettings["AutomaticShutdownAction"] = AutomaticStopActionSettings; - - if (AutomaticRecoveryActionSettings != 100) - objGlobalSettings["AutomaticRecoveryAction"] = AutomaticRecoveryActionSettings; - - // create machine - ManagementBaseObject inParams = objVmsvc.GetMethodParameters("DefineVirtualSystem"); - inParams["SystemSettingData"] = objGlobalSettings.GetText(TextFormat.CimDtd20); - inParams["ResourceSettingData"] = new string[] { }; - - // invoke method - ManagementBaseObject outParams = objVmsvc.InvokeMethod("DefineVirtualSystem", inParams, null); - ManagementObject objVM = wmi.GetWmiObjectByPath((string)outParams["DefinedSystem"]); - - // job - JobResult job = CreateJobResultFromWmiMethodResults(outParams); ; - - // read VM id - vmID = (string)objVM["Name"]; - - // update general settings - //UpdateVirtualMachineGeneralSettings(vmID, objVM, - // vm.CpuCores, - // vm.RamSize, - // vm.BootFromCD, - // vm.NumLockEnabled); - - // hard disks - // load IDE 0 controller - ManagementObject objIDE0 = wmi.GetWmiObject( - "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Emulated IDE Controller'" - + " and InstanceID Like 'Microsoft:{0}%' and Address = 0", vmID); - - // load default hard disk drive - ManagementObject objDefaultHdd = wmi.GetWmiObject( - "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Synthetic Disk Drive'" - + " and InstanceID like '%Default'"); - ManagementObject objHdd = (ManagementObject)objDefaultHdd.Clone(); - objHdd["Parent"] = objIDE0.Path; - objHdd["Address"] = 0; - - // add HDD to VM resources - ManagementObject objAddedHDD = AddVirtualMachineResources(objVM, objHdd); - - // attach VHD - string fullVhdPath = vm.VirtualHardDrivePath; - ManagementObject objDefaultVHD = wmi.GetWmiObject( - "Msvm_ResourceAllocationSettingData", "ResourceSubType = 'Microsoft Virtual Hard Disk'" - + " and InstanceID like '%Default'"); - ManagementObject objVhd = (ManagementObject)objDefaultVHD.Clone(); - objVhd["Parent"] = objAddedHDD.Path.Path; - objVhd["Connection"] = new string[] { fullVhdPath }; - - // add VHD to the system - AddVirtualMachineResources(objVM, objVhd); - - // DVD drive - if (vm.DvdDriveInstalled) - { - AddVirtualMachineDvdDrive(vmID, objVM); - } - - // add external adapter - if (vm.ExternalNetworkEnabled && !String.IsNullOrEmpty(vm.ExternalSwitchId)) - AddNetworkAdapter(objVM, vm.ExternalSwitchId, vm.Name, vm.ExternalNicMacAddress, EXTERNAL_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); - - // add private adapter - if (vm.PrivateNetworkEnabled && !String.IsNullOrEmpty(vm.PrivateSwitchId)) - AddNetworkAdapter(objVM, vm.PrivateSwitchId, vm.Name, vm.PrivateNicMacAddress, PRIVATE_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); - - // add management adapter - if (vm.ManagementNetworkEnabled && !String.IsNullOrEmpty(vm.ManagementSwitchId)) - AddNetworkAdapter(objVM, vm.ManagementSwitchId, vm.Name, vm.ManagementNicMacAddress, MANAGEMENT_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); - - vm.VirtualMachineId = vmID; return vm; } @@ -1483,33 +1425,17 @@ namespace WebsitePanel.Providers.Virtualization #region Storage public VirtualHardDiskInfo GetVirtualHardDiskInfo(string vhdPath) { - ManagementObject objImgSvc = GetImageManagementService(); - - // get method params - ManagementBaseObject inParams = objImgSvc.GetMethodParameters("GetVirtualHardDiskInfo"); - inParams["Path"] = FileUtils.EvaluateSystemVariables(vhdPath); - - // execute method - ManagementBaseObject outParams = (ManagementBaseObject)objImgSvc.InvokeMethod("GetVirtualHardDiskInfo", inParams, null); - ReturnCode result = (ReturnCode)Convert.ToInt32(outParams["ReturnValue"]); - if (result == ReturnCode.OK) + try { - // create XML - string xml = (string)outParams["Info"]; - XmlDocument doc = new XmlDocument(); - doc.LoadXml(xml); - - // read properties - VirtualHardDiskInfo vhd = new VirtualHardDiskInfo(); - vhd.DiskType = (VirtualHardDiskType)Enum.Parse(typeof(VirtualHardDiskType), GetPropertyValue("Type", doc), true); - vhd.FileSize = Int64.Parse(GetPropertyValue("FileSize", doc)); - vhd.InSavedState = Boolean.Parse(GetPropertyValue("InSavedState", doc)); - vhd.InUse = Boolean.Parse(GetPropertyValue("InUse", doc)); - vhd.MaxInternalSize = Int64.Parse(GetPropertyValue("MaxInternalSize", doc)); - vhd.ParentPath = GetPropertyValue("ParentPath", doc); - return vhd; + VirtualHardDiskInfo hardDiskInfo = new VirtualHardDiskInfo(); + HardDriveHelper.GetVirtualHardDiskDetail(PowerShell, vhdPath, ref hardDiskInfo); + return hardDiskInfo; + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetVirtualHardDiskInfo", ex); + throw; } - return null; } private string GetPropertyValue(string propertyName, XmlDocument doc) @@ -1741,9 +1667,6 @@ exit", Convert.ToInt32(objDisk["Index"]))); public JobResult ConvertVirtualHardDisk(string sourcePath, string destinationPath, VirtualHardDiskType diskType) { - sourcePath = FileUtils.EvaluateSystemVariables(sourcePath); - destinationPath = FileUtils.EvaluateSystemVariables(destinationPath); - // check source file if (!FileExists(sourcePath)) throw new Exception("Source VHD cannot be found: " + sourcePath); @@ -1752,17 +1675,26 @@ exit", Convert.ToInt32(objDisk["Index"]))); string destFolder = Path.GetDirectoryName(destinationPath); if (!DirectoryExists(destFolder)) CreateFolder(destFolder); + + sourcePath = FileUtils.EvaluateSystemVariables(sourcePath); + destinationPath = FileUtils.EvaluateSystemVariables(destinationPath); + + try + { + Command cmd = new Command("Convert-VHD"); - ManagementObject objImgSvc = GetImageManagementService(); + cmd.Parameters.Add("Path", sourcePath); + cmd.Parameters.Add("DestinationPath", destinationPath); + cmd.Parameters.Add("VHDType", diskType.ToString()); - // get method params - ManagementBaseObject inParams = objImgSvc.GetMethodParameters("ConvertVirtualHardDisk"); - inParams["SourcePath"] = sourcePath; - inParams["DestinationPath"] = destinationPath; - inParams["Type"] = (UInt16)diskType; - - ManagementBaseObject outParams = (ManagementBaseObject)objImgSvc.InvokeMethod("ConvertVirtualHardDisk", inParams, null); - return CreateJobResultFromWmiMethodResults(outParams); + PowerShell.Execute(cmd, false); + return JobHelper.CreateSuccessResult(ReturnCode.JobStarted); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("ConvertVirtualHardDisk", ex); + throw; + } } public void DeleteRemoteFile(string path) diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj index c18926b0..635edce5 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj @@ -17,7 +17,7 @@ true full false - ..\WebsitePanel.Server\bin\ + ..\WebsitePanel.Server\bin\HyperV2012R2\ DEBUG;TRACE prompt 4 @@ -53,8 +53,10 @@ VersionInfo.cs + + diff --git a/WebsitePanel/Sources/WebsitePanel.Server/Web.config b/WebsitePanel/Sources/WebsitePanel.Server/Web.config index 0dcd0c7d..8074f342 100644 --- a/WebsitePanel/Sources/WebsitePanel.Server/Web.config +++ b/WebsitePanel/Sources/WebsitePanel.Server/Web.config @@ -1,4 +1,4 @@ - + @@ -7,34 +7,34 @@
- - - - - + + + + + - + - + - + - + - + @@ -42,85 +42,85 @@ - + - + - + - + - + - - - + + + - + - - - - + + + + - - + + - - - - + + + + - + - + - - + + - + - + - + - - + + - - + + - - + + - - + + @@ -129,40 +129,40 @@ - - + + - + - - + + - - - - - - - + + + + + + + - - + + - - + + - + \ No newline at end of file From 2b920e47ea562bfb11c8ad0e15dc7163ce50d0b3 Mon Sep 17 00:00:00 2001 From: SweetSoft Date: Wed, 18 Mar 2015 14:30:26 +0100 Subject: [PATCH 12/12] EditWebSite form marge changes --- .../App_Themes/Default/Styles/Skin.css | 4 +- .../WebsitePanel/WebSitesEditSite.ascx | 3 +- .../WebsitePanel/WebSitesEditSite.ascx.cs | 14 ++++++- .../WebSitesEditSite.ascx.designer.cs | 39 +++++-------------- 4 files changed, 26 insertions(+), 34 deletions(-) diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/App_Themes/Default/Styles/Skin.css b/WebsitePanel/Sources/WebsitePanel.WebPortal/App_Themes/Default/Styles/Skin.css index 4d50d626..bb2b9fb5 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/App_Themes/Default/Styles/Skin.css +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/App_Themes/Default/Styles/Skin.css @@ -54,9 +54,9 @@ legend {font-weight:700; color:#428bca; padding:0 4px;} legend span.NormalBold {display:inline;} .Tabs {width:100%;} .Separator {display:none;} -.Tabs span {display:table; table-layout:fixed; width:100%; table-layout:fixed;} +.Tabs span {display:table; table-layout:fixed; min-width:100%; table-layout:fixed;} .Tabs span span {display:table-cell; width:15%; text-align:center; padding:0;} -.Tabs a {display:block; padding:10px 0; text-decoration:none; border-bottom:1px solid #ddd;} +.Tabs a {display:block; padding:10px 5px; text-decoration:none; border-bottom:1px solid #ddd;} .Tabs a:Hover {background:#eee; text-decoration:none;} .Tabs a.ActiveTab {border:1px solid #ddd; border-bottom:none; color:#555; margin-bottom:-1px;} .Tabs a.ActiveTab:hover {background:none;} diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx index a8fd47ff..4b72149d 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx @@ -66,7 +66,7 @@ - +
@@ -120,6 +120,7 @@ +     diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.cs index 777e9c62..eb8ddab0 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.cs @@ -69,7 +69,7 @@ namespace WebsitePanel.Portal new Tab { Id = "webman", ResourceKey = "Tab.WebManagement", Quota = Quotas.WEB_REMOTEMANAGEMENT, ViewId = "tabWebManagement" }, new Tab { Id = "SSL", ResourceKey = "Tab.SSL", Quota = Quotas.WEB_SSL, ViewId = "SSL" }, }; - + protected string SharedIdAddres { get; set; } private int PackageId { get { return (int)ViewState["PackageId"]; } @@ -198,6 +198,16 @@ namespace WebsitePanel.Portal { litIPAddress.Text = site.SiteIPAddress; } + else + { + IPAddressInfo[] ipsGeneral = ES.Services.Servers.GetIPAddresses(IPAddressPool.General, PanelRequest.ServerId); + bool generalIPExists = ipsGeneral.Any() && !string.IsNullOrEmpty(ipsGeneral[0].ExternalIP); + if (generalIPExists) + { + lblSharedIP.Text = string.Format("({0})", ipsGeneral[0].ExternalIP); + } + lblSharedIP.Visible = generalIPExists; + } dedicatedIP.Visible = site.IsDedicatedIP; sharedIP.Visible = !site.IsDedicatedIP; @@ -312,7 +322,7 @@ namespace WebsitePanel.Portal // AppPool AppPoolState appPoolState = ES.Services.WebServers.GetAppPoolState(PanelRequest.ItemID); BindAppPoolState(appPoolState); - + // bind pointers BindPointers(); diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.designer.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.designer.cs index f786b81f..735941ab 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.designer.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.designer.cs @@ -1,37 +1,9 @@ -// Copyright (c) 2015, Outercurve Foundation. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// - Neither the name of the Outercurve Foundation nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - //------------------------------------------------------------------------------ // // This code was generated by a tool. // // Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -238,6 +210,15 @@ namespace WebsitePanel.Portal { /// protected global::System.Web.UI.WebControls.Localize locSharedIPAddress; + /// + /// lblSharedIP control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblSharedIP; + /// /// cmdSwitchToDedicatedIP control. ///