From d9d41827f69589625a23211e3a32c1db3139e54b Mon Sep 17 00:00:00 2001 From: me Date: Wed, 4 Mar 2015 20:04:18 +0400 Subject: [PATCH] =?UTF-8?q?wsp-10323=20=D0=A1onvert=20the=20VSP=20provider?= =?UTF-8?q?=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"));