diff --git a/WebsitePanel/Database/update_db.sql b/WebsitePanel/Database/update_db.sql index 11b65b96..802718bc 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 @@ -8860,6 +8860,12 @@ AND ((@GroupName IS NULL) OR (@GroupName IS NOT NULL AND RG.GroupName = @GroupNa 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 --ES OWA Editing IF NOT EXISTS (SELECT * FROM SYS.TABLES WHERE name = 'EnterpriseFoldersOwaPermissions') 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/AutomaticStartAction.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/AutomaticStartAction.cs new file mode 100644 index 00000000..881a8a27 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/AutomaticStartAction.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WebsitePanel.Providers.Virtualization +{ + public enum AutomaticStartAction + { + Undefined = 100, + Nothing = 0, + StartIfRunning = 1, + Start = 2, + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/AutomaticStopAction.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/AutomaticStopAction.cs new file mode 100644 index 00000000..3386e0d8 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/AutomaticStopAction.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WebsitePanel.Providers.Virtualization +{ + public enum AutomaticStopAction + { + Undefined = 100, + TurnOff = 0, + Save = 1, + ShutDown = 2, + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/BiosInfo.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/BiosInfo.cs new file mode 100644 index 00000000..796876fc --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/BiosInfo.cs @@ -0,0 +1,41 @@ +// 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; } + public bool BootFromCD { 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/DvdDriveInfo.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/DvdDriveInfo.cs new file mode 100644 index 00000000..34a1100d --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/DvdDriveInfo.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 DvdDriveInfo + { + public ControllerType ControllerType { get; set; } + public int ControllerNumber { get; set; } + public int ControllerLocation { get; set; } + public string Name { get; set; } + public string Id { get; set; } + public string Path { get; set; } + } +} \ No newline at end of file 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..6d6a9fb0 --- /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 = 0, + VHDX = 1 + } +} 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..11da6676 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,24 @@ 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; } + + [Persistent] + public int ProcessorCount { get; set; } + + [Persistent] + public string ParentSnapshotId { get; set; } + } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineNetworkAdapter.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineNetworkAdapter.cs index fe2646d3..d1209d10 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineNetworkAdapter.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineNetworkAdapter.cs @@ -36,5 +36,6 @@ namespace WebsitePanel.Providers.Virtualization { public string Name { get; set; } public string MacAddress { get; set; } + public string SwitchName { get; set; } } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineSnapshot.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineSnapshot.cs index f8f496ce..a20ae5f0 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineSnapshot.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineSnapshot.cs @@ -37,6 +37,7 @@ namespace WebsitePanel.Providers.Virtualization public string Id { get; set; } public string CheckPointId { get; set; } public string Name { get; set; } + public string VMName { get; set; } public string ParentId { get; set; } public DateTime Created { get; set; } public bool IsCurrent { get; set; } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineState.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Virtualization/VirtualMachineState.cs index 86ba3354..c33396f7 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,36 @@ namespace WebsitePanel.Providers.Virtualization Stopping = 32774, Deleted = 32775, Pausing = 32776 + */ + Snapshotting = 32771, + Migrating = 32772, + Deleted = 32775, + + Unknown = 0, + Other = 1, + Running = 2, + Off = 3, + Stopping = 32774, // new 4 + Saved = 32769, // new 6 + Paused = 32768, // new 9 + Starting = 32770, // new 10 + Reset = 10, // new 11 + Saving = 32773, // new 32773 + Pausing = 32776, // new 32776 + Resuming = 32777, + FastSaved = 32779, + FastSaving = 32780, + 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 c0f01014..d0bf17c3 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj @@ -288,6 +288,9 @@ + + + Code @@ -298,6 +301,7 @@ Code + Code @@ -308,6 +312,8 @@ + + @@ -322,6 +328,7 @@ Code + diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Constants.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Constants.cs new file mode 100644 index 00000000..7821a6fd --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Constants.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class Constants + { + public const Int64 Size1G = 0x40000000; + public const Int64 Size1M = 0x100000; + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Extensions/PSObjectExtension.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Extensions/PSObjectExtension.cs new file mode 100644 index 00000000..33426fe2 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Extensions/PSObjectExtension.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management; +using System.Management.Automation; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + static class PSObjectExtension + { + #region Properties + + public static object GetProperty(this PSObject obj, string name) + { + return obj.Members[name].Value; + } + public static T GetProperty(this PSObject obj, string name) + { + return (T)obj.Members[name].Value; + } + public static T GetEnum(this PSObject obj, string name) where T : struct + { + return (T)Enum.Parse(typeof(T), GetProperty(obj, name).ToString()); + } + public static int GetInt(this PSObject obj, string name) + { + return Convert.ToInt32(obj.Members[name].Value); + } + public static long GetLong(this PSObject obj, string name) + { + return Convert.ToInt64(obj.Members[name].Value); + } + public static string GetString(this PSObject obj, string name) + { + return obj.Members[name].Value == null ? "" : obj.Members[name].Value.ToString(); + } + + #endregion + + + #region Methods + + public static ManagementObject Invoke(this PSObject obj, string name, object argument) + { + return obj.Invoke(name, new[] {argument}); + } + public static ManagementObject Invoke(this PSObject obj, string name, params object[] arguments) + { + var results = (ManagementObjectCollection)obj.Methods[name].Invoke(arguments); + + foreach (var result in results) + { + return (ManagementObject) result; + } + return null; + } + + #endregion + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/BiosHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/BiosHelper.cs new file mode 100644 index 00000000..21dafb52 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/BiosHelper.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class BiosHelper + { + public static BiosInfo Get(PowerShellManager powerShell, string name, int generation) + { + BiosInfo info = new BiosInfo(); + + // for Win2012R2+ and Win8.1+ + if (generation == 2) + { + Command cmd = new Command("Get-VMFirmware"); + + cmd.Parameters.Add("VMName", name); + + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + info.NumLockEnabled = true; + + List startupOrders = new List(); + info.BootFromCD = false; + + foreach (dynamic item in (IEnumerable)result[0].GetProperty("BootOrder")) + { + string bootType = item.BootType.ToString(); + + // bootFromCD + if (!startupOrders.Any() && bootType == "Drive") + { + var device = item.Device; + info.BootFromCD = device.GetType().Name == "DvdDrive"; + } + + // startupOrders + startupOrders.Add(bootType); + } + + info.StartupOrder = startupOrders.ToArray(); + } + } + // for others win and linux + else + { + Command cmd = new Command("Get-VMBios"); + + cmd.Parameters.Add("VMName", name); + + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + info.NumLockEnabled = Convert.ToBoolean(result[0].GetProperty("NumLockEnabled")); + + List startupOrders = new List(); + + foreach (var item in (IEnumerable)result[0].GetProperty("StartupOrder")) + startupOrders.Add(item.ToString()); + + info.StartupOrder = startupOrders.ToArray(); + info.BootFromCD = false; + if (info.StartupOrder != null && info.StartupOrder.Length > 0) + info.BootFromCD = info.StartupOrder[0] == "CD"; + } + } + + return info; + } + + public static void Update(PowerShellManager powerShell, VirtualMachine vm, bool bootFromCD, bool numLockEnabled) + { + // for Win2012R2+ and Win8.1+ + if (vm.Generation == 2) + { + Command cmd = new Command("Set-VMFirmware"); + + cmd.Parameters.Add("VMName", vm.Name); + if (bootFromCD) + cmd.Parameters.Add("FirstBootDevice", DvdDriveHelper.GetPS(powerShell, vm.Name)); + else + cmd.Parameters.Add("FirstBootDevice", HardDriveHelper.GetPS(powerShell, vm.Name).FirstOrDefault()); + + powerShell.Execute(cmd, false); + } + // for others win and linux + else + { + Command cmd = new Command("Set-VMBios"); + + cmd.Parameters.Add("VMName", vm.Name); + var bootOrder = bootFromCD + ? new[] { "CD", "IDE", "LegacyNetworkAdapter", "Floppy" } + : new[] { "IDE", "CD", "LegacyNetworkAdapter", "Floppy" }; + cmd.Parameters.Add("StartupOrder", bootOrder); + + powerShell.Execute(cmd, false); + } + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/DvdDriveHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/DvdDriveHelper.cs new file mode 100644 index 00000000..012232ee --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/DvdDriveHelper.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class DvdDriveHelper + { + public static DvdDriveInfo Get(PowerShellManager powerShell, string vmName) + { + DvdDriveInfo info = null; + + PSObject result = GetPS(powerShell, vmName); + + if (result != null) + { + info = new DvdDriveInfo(); + info.Id = result.GetString("Id"); + info.Name = result.GetString("Name"); + info.ControllerType = result.GetEnum("ControllerType"); + info.ControllerNumber = result.GetInt("ControllerNumber"); + info.ControllerLocation = result.GetInt("ControllerLocation"); + info.Path = result.GetString("Path"); + } + return info; + } + + public static PSObject GetPS(PowerShellManager powerShell, string vmName) + { + Command cmd = new Command("Get-VMDvdDrive"); + + cmd.Parameters.Add("VMName", vmName); + + Collection result = powerShell.Execute(cmd, false); + + if (result != null && result.Count > 0) + { + return result[0]; + } + + return null; + } + + public static void Set(PowerShellManager powerShell, string vmName, string path) + { + var dvd = Get(powerShell, vmName); + + Command cmd = new Command("Set-VMDvdDrive"); + + cmd.Parameters.Add("VMName", vmName); + cmd.Parameters.Add("Path", path); + cmd.Parameters.Add("ControllerNumber", dvd.ControllerNumber); + cmd.Parameters.Add("ControllerLocation", dvd.ControllerLocation); + + powerShell.Execute(cmd, false); + } + + public static void Update(PowerShellManager powerShell, VirtualMachine vm, bool dvdDriveShouldBeInstalled) + { + if (!vm.DvdDriveInstalled && dvdDriveShouldBeInstalled) + Add(powerShell, vm.Name); + else if (vm.DvdDriveInstalled && !dvdDriveShouldBeInstalled) + Remove(powerShell, vm.Name); + } + + public static void Add(PowerShellManager powerShell, string vmName) + { + Command cmd = new Command("Add-VMDvdDrive"); + + cmd.Parameters.Add("VMName", vmName); + + powerShell.Execute(cmd, false); + } + + public static void Remove(PowerShellManager powerShell, string vmName) + { + var dvd = Get(powerShell, vmName); + + Command cmd = new Command("Remove-VMDvdDrive"); + + cmd.Parameters.Add("VMName", vmName); + cmd.Parameters.Add("ControllerNumber", dvd.ControllerNumber); + cmd.Parameters.Add("ControllerLocation", dvd.ControllerLocation); + + powerShell.Execute(cmd, false); + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/HardDriveHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/HardDriveHelper.cs new file mode 100644 index 00000000..c8a951d6 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/HardDriveHelper.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class HardDriveHelper + { + public static VirtualHardDiskInfo[] Get(PowerShellManager powerShell, string vmname) + { + List disks = new List(); + + Collection result = GetPS(powerShell, vmname); + + if (result != null && result.Count > 0) + { + foreach (PSObject d in result) + { + VirtualHardDiskInfo disk = new VirtualHardDiskInfo(); + + disk.SupportPersistentReservations = Convert.ToBoolean(d.GetProperty("SupportPersistentReservations")); + disk.MaximumIOPS = Convert.ToUInt64(d.GetProperty("MaximumIOPS")); + disk.MinimumIOPS = Convert.ToUInt64(d.GetProperty("MinimumIOPS")); + disk.VHDControllerType = d.GetEnum("ControllerType"); + disk.ControllerNumber = Convert.ToInt32(d.GetProperty("ControllerNumber")); + disk.ControllerLocation = Convert.ToInt32(d.GetProperty("ControllerLocation")); + disk.Path = d.GetProperty("Path").ToString(); + disk.Name = d.GetProperty("Name").ToString(); + + GetVirtualHardDiskDetail(powerShell, disk.Path, ref disk); + + disks.Add(disk); + } + } + return disks.ToArray(); + } + + //public static VirtualHardDiskInfo GetByPath(PowerShellManager powerShell, string vhdPath) + //{ + // VirtualHardDiskInfo info = null; + // var vmNames = new List(); + + // Command cmd = new Command("Get-VM"); + + // Collection result = powerShell.Execute(cmd, false); + + // if (result == null || result.Count == 0) + // return null; + + // vmNames = result.Select(r => r.GetString("Name")).ToList(); + // var drives = vmNames.SelectMany(n => Get(powerShell, n)); + + // return drives.FirstOrDefault(d=>d.Path == vhdPath); + //} + + public static Collection GetPS(PowerShellManager powerShell, string vmname) + { + Command cmd = new Command("Get-VMHardDiskDrive"); + cmd.Parameters.Add("VMName", vmname); + + return powerShell.Execute(cmd, false); + } + + public static void GetVirtualHardDiskDetail(PowerShellManager powerShell, string path, ref VirtualHardDiskInfo disk) + { + if (!string.IsNullOrEmpty(path)) + { + Command cmd = new Command("Get-VHD"); + cmd.Parameters.Add("Path", path); + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + disk.DiskFormat = result[0].GetEnum("VhdFormat"); + disk.DiskType = result[0].GetEnum("VhdType"); + disk.ParentPath = result[0].GetProperty("ParentPath"); + disk.MaxInternalSize = Convert.ToInt64(result[0].GetProperty("Size")); + disk.FileSize = Convert.ToInt64(result[0].GetProperty("FileSize")); + disk.Attached = disk.InUse = Convert.ToBoolean(result[0].GetProperty("Attached")); + } + } + } + + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/JobHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/JobHelper.cs new file mode 100644 index 00000000..92d1e95a --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/JobHelper.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class JobHelper + { + public static JobResult CreateSuccessResult(ReturnCode returnCode = ReturnCode.OK) + { + return new JobResult + { + Job = new ConcreteJob {JobState = ConcreteJobState.Completed}, + ReturnValue = returnCode + }; + } + + public static JobResult CreateResultFromPSResults(Collection objJob) + { + if (objJob == null || objJob.Count == 0) + return null; + + JobResult result = new JobResult(); + + result.Job = CreateFromPSObject(objJob); + result.ReturnValue = ReturnCode.JobStarted; + + switch (result.Job.JobState) + { + case ConcreteJobState.Failed: + result.ReturnValue = ReturnCode.Failed; + break; + } + + return result; + } + + public static ConcreteJob CreateFromPSObject(Collection objJob) + { + if (objJob == null || objJob.Count == 0) + return null; + + ConcreteJob job = new ConcreteJob(); + job.Id = objJob[0].GetProperty("Id").ToString(); + job.JobState = objJob[0].GetEnum("JobStateInfo"); + job.Caption = objJob[0].GetProperty("Name"); + job.Description = objJob[0].GetProperty("Command"); + job.StartTime = objJob[0].GetProperty("PSBeginTime"); + job.ElapsedTime = objJob[0].GetProperty("PSEndTime") ?? DateTime.Now; + + // PercentComplete + job.PercentComplete = 0; + var progress = (PSDataCollection)objJob[0].GetProperty("Progress"); + if (progress != null && progress.Count > 0) + job.PercentComplete = progress[0].PercentComplete; + + // Errors + var errors = (PSDataCollection)objJob[0].GetProperty("Error"); + if (errors != null && errors.Count > 0) + { + job.ErrorDescription = errors[0].ErrorDetails.Message + ". " + errors[0].ErrorDetails.RecommendedAction; + job.ErrorCode = errors[0].Exception != null ? -1 : 0; + } + + return job; + } + + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/NetworkAdapterHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/NetworkAdapterHelper.cs new file mode 100644 index 00000000..bd6f9b37 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/NetworkAdapterHelper.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class NetworkAdapterHelper + { + #region Constants + + private const string EXTERNAL_NETWORK_ADAPTER_NAME = "External Network Adapter"; + private const string PRIVATE_NETWORK_ADAPTER_NAME = "Private Network Adapter"; + private const string MANAGEMENT_NETWORK_ADAPTER_NAME = "Management Network Adapter"; + + #endregion + + public static VirtualMachineNetworkAdapter[] Get(PowerShellManager powerShell, string vmName) + { + List adapters = new List(); + + Command cmd = new Command("Get-VMNetworkAdapter"); + if (!string.IsNullOrEmpty(vmName)) cmd.Parameters.Add("VMName", vmName); + + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + foreach (PSObject psAdapter in result) + { + VirtualMachineNetworkAdapter adapter = new VirtualMachineNetworkAdapter(); + + adapter.Name = psAdapter.GetString("Name"); + adapter.MacAddress = psAdapter.GetString("MacAddress"); + adapter.SwitchName = psAdapter.GetString("SwitchName"); + + adapters.Add(adapter); + } + } + return adapters.ToArray(); + } + + public static VirtualMachineNetworkAdapter Get(PowerShellManager powerShell, string vmName, string macAddress) + { + var adapters = Get(powerShell, vmName); + return adapters.FirstOrDefault(a => a.MacAddress == macAddress); + } + + public static void Update(PowerShellManager powerShell, VirtualMachine vm) + { + // External NIC + if (!vm.ExternalNetworkEnabled && !String.IsNullOrEmpty(vm.ExternalNicMacAddress)) + { + Delete(powerShell, vm.Name, vm.ExternalNicMacAddress); + vm.ExternalNicMacAddress = null; // reset MAC + } + else if (vm.ExternalNetworkEnabled && !String.IsNullOrEmpty(vm.ExternalNicMacAddress) + && Get(powerShell,vm.Name,vm.ExternalNicMacAddress) == null) + { + Add(powerShell, vm.Name, vm.ExternalSwitchId, vm.ExternalNicMacAddress, EXTERNAL_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + } + + // Private NIC + if (!vm.PrivateNetworkEnabled && !String.IsNullOrEmpty(vm.PrivateNicMacAddress)) + { + Delete(powerShell, vm.Name, vm.PrivateNicMacAddress); + vm.PrivateNicMacAddress = null; // reset MAC + } + else if (vm.PrivateNetworkEnabled && !String.IsNullOrEmpty(vm.PrivateNicMacAddress) + && Get(powerShell, vm.Name, vm.PrivateNicMacAddress) == null) + { + Add(powerShell, vm.Name, vm.PrivateSwitchId, vm.PrivateNicMacAddress, PRIVATE_NETWORK_ADAPTER_NAME, vm.LegacyNetworkAdapter); + } + } + + public static void Add(PowerShellManager powerShell, string vmName, string switchId, string macAddress, string adapterName, bool legacyAdapter) + { + Command cmd = new Command("Add-VMNetworkAdapter"); + + cmd.Parameters.Add("VMName", vmName); + cmd.Parameters.Add("Name", adapterName); + cmd.Parameters.Add("SwitchName", switchId); + + if (String.IsNullOrEmpty(macAddress)) + cmd.Parameters.Add("DynamicMacAddress"); + else + cmd.Parameters.Add("StaticMacAddress", macAddress); + + powerShell.Execute(cmd, false); + } + public static void Delete(PowerShellManager powerShell, string vmName, string macAddress) + { + var networkAdapter = Get(powerShell, vmName, macAddress); + + if (networkAdapter == null) + return; + + Command cmd = new Command("Remove-VMNetworkAdapter"); + + cmd.Parameters.Add("VMName", vmName); + cmd.Parameters.Add("Name", networkAdapter.Name); + + powerShell.Execute(cmd, false); + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/SnapshotHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/SnapshotHelper.cs new file mode 100644 index 00000000..b5dee5c2 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/SnapshotHelper.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class SnapshotHelper + { + public static VirtualMachineSnapshot GetFromPS(PSObject psObject, string runningSnapshotId = null) + { + var snapshot = new VirtualMachineSnapshot + { + Id = psObject.GetString("Id"), + Name = psObject.GetString("Name"), + VMName = psObject.GetString("VMName"), + ParentId = psObject.GetString("ParentSnapshotId"), + Created = psObject.GetProperty("CreationTime") + }; + + if (string.IsNullOrEmpty(snapshot.ParentId)) + snapshot.ParentId = null; // for capability + + if (!String.IsNullOrEmpty(runningSnapshotId)) + snapshot.IsCurrent = snapshot.Id == runningSnapshotId; + + return snapshot; + } + + public static VirtualMachineSnapshot GetFromWmi(ManagementBaseObject objSnapshot) + { + if (objSnapshot == null || objSnapshot.Properties.Count == 0) + return null; + + VirtualMachineSnapshot snapshot = new VirtualMachineSnapshot(); + snapshot.Id = (string)objSnapshot["InstanceID"]; + snapshot.Name = (string)objSnapshot["ElementName"]; + + string parentId = (string)objSnapshot["Parent"]; + if (!String.IsNullOrEmpty(parentId)) + { + int idx = parentId.IndexOf("Microsoft:"); + snapshot.ParentId = parentId.Substring(idx, parentId.Length - idx - 1); + snapshot.ParentId = snapshot.ParentId.ToLower().Replace("microsoft:", ""); + } + if (!String.IsNullOrEmpty(snapshot.Id)) + { + snapshot.Id = snapshot.Id.ToLower().Replace("microsoft:", ""); + } + snapshot.Created = Wmi.ToDateTime((string)objSnapshot["CreationTime"]); + + if (string.IsNullOrEmpty(snapshot.ParentId)) + snapshot.ParentId = null; // for capability + + return snapshot; + } + + public static void Delete(PowerShellManager powerShell, VirtualMachineSnapshot snapshot, bool includeChilds) + { + Command cmd = new Command("Remove-VMSnapshot"); + cmd.Parameters.Add("VMName", snapshot.VMName); + cmd.Parameters.Add("Name", snapshot.Name); + if (includeChilds) cmd.Parameters.Add("IncludeAllChildSnapshots", true); + + powerShell.Execute(cmd, false); + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VirtualMachineHelper.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VirtualMachineHelper.cs new file mode 100644 index 00000000..e7d05288 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VirtualMachineHelper.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; + +namespace WebsitePanel.Providers.Virtualization +{ + public static class VirtualMachineHelper + { + public static OperationalStatus GetVMHeartBeatStatus(PowerShellManager powerShell, string name) + { + OperationalStatus status = OperationalStatus.None; + + Command cmd = new Command("Get-VMIntegrationService"); + + cmd.Parameters.Add("VMName", name); + cmd.Parameters.Add("Name", "HeartBeat"); + + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + var statusString = result[0].GetProperty("PrimaryOperationalStatus"); + + if (statusString != null) + status = (OperationalStatus)Enum.Parse(typeof(OperationalStatus), statusString.ToString()); + } + return status; + } + + public static int GetVMProcessors(PowerShellManager powerShell, string name) + { + + int procs = 0; + + Command cmd = new Command("Get-VMProcessor"); + + cmd.Parameters.Add("VMName", name); + + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + procs = Convert.ToInt32(result[0].GetProperty("Count")); + + } + return procs; + } + + public static MemoryInfo GetVMMemory(PowerShellManager powerShell, string name) + { + MemoryInfo info = new MemoryInfo(); + + Command cmd = new Command("Get-VMMemory"); + + cmd.Parameters.Add("VMName", name); + + Collection result = powerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + info.DynamicMemoryEnabled = Convert.ToBoolean(result[0].GetProperty("DynamicMemoryEnabled")); + info.Startup = Convert.ToInt64(result[0].GetProperty("Startup")) / Constants.Size1M; + info.Minimum = Convert.ToInt64(result[0].GetProperty("Minimum")) / Constants.Size1M; + info.Maximum = Convert.ToInt64(result[0].GetProperty("Maximum")) / Constants.Size1M; + info.Buffer = Convert.ToInt32(result[0].GetProperty("Buffer")); + info.Priority = Convert.ToInt32(result[0].GetProperty("Priority")); + } + return info; + } + + public static void UpdateProcessors(PowerShellManager powerShell, VirtualMachine vm, int cpuCores, int cpuLimitSettings, int cpuReserveSettings, int cpuWeightSettings) + { + Command cmd = new Command("Set-VMProcessor"); + + cmd.Parameters.Add("VMName", vm.Name); + cmd.Parameters.Add("Count", cpuCores); + cmd.Parameters.Add("Maximum", Convert.ToInt64(cpuLimitSettings * 1000)); + cmd.Parameters.Add("Reserve", Convert.ToInt64(cpuReserveSettings * 1000)); + cmd.Parameters.Add("RelativeWeight", cpuWeightSettings); + + powerShell.Execute(cmd, false); + } + + public static void UpdateMemory(PowerShellManager powerShell, VirtualMachine vm, long ramMB) + { + Command cmd = new Command("Set-VMMemory"); + + cmd.Parameters.Add("VMName", vm.Name); + cmd.Parameters.Add("StartupBytes", ramMB * Constants.Size1M); + + powerShell.Execute(cmd, false); + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs new file mode 100644 index 00000000..7b1801ea --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/HyperV2012R2.cs @@ -0,0 +1,2514 @@ +// 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\v2"; + 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 GetVirtualMachineEx(string vmId) + { + return GetVirtualMachineInternal(vmId, true); + } + + protected VirtualMachine GetVirtualMachineInternal(string vmId, bool extendedInfo) + { + HostedSolutionLog.LogStart("GetVirtualMachine"); + HostedSolutionLog.DebugInfo("Virtual Machine: {0}", vmId); + + VirtualMachine vm = new VirtualMachine(); + + try + { + Command cmd = new Command("Get-VM"); + + cmd.Parameters.Add("Id", vmId); + + Collection result = PowerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + vm.Name = result[0].GetProperty("Name").ToString(); + vm.State = result[0].GetEnum("State"); + vm.CpuUsage = ConvertNullableToInt32(result[0].GetProperty("CpuUsage")); + vm.RamUsage = ConvertNullableToInt64(result[0].GetProperty("MemoryAssigned")) / Size1M; + vm.Uptime = Convert.ToInt64(result[0].GetProperty("UpTime").TotalMilliseconds); + vm.Status = result[0].GetProperty("Status").ToString(); + vm.ReplicationState = result[0].GetProperty("ReplicationState").ToString(); + vm.Generation = result[0].GetInt("Generation"); + vm.ProcessorCount = result[0].GetInt("ProcessorCount"); + vm.ParentSnapshotId = result[0].GetString("ParentSnapshotId"); + + vm.Heartbeat = VirtualMachineHelper.GetVMHeartBeatStatus(PowerShell, vm.Name); + + vm.CreatedDate = DateTime.Now; + + if (extendedInfo) + { + vm.CpuCores = VirtualMachineHelper.GetVMProcessors(PowerShell, vm.Name); + + MemoryInfo memoryInfo = VirtualMachineHelper.GetVMMemory(PowerShell, vm.Name); + vm.RamSize = memoryInfo.Startup; + + // BIOS + BiosInfo biosInfo = BiosHelper.Get(PowerShell, vm.Name, vm.Generation); + vm.NumLockEnabled = biosInfo.NumLockEnabled; + vm.BootFromCD = biosInfo.BootFromCD; + + // DVD drive + var dvdInfo = DvdDriveHelper.Get(PowerShell, vm.Name); + vm.DvdDriveInstalled = dvdInfo != null; + + // HDD + vm.Disks = HardDriveHelper.Get(PowerShell, vm.Name); + + if (vm.Disks != null && vm.Disks.GetLength(0) > 0) + { + vm.VirtualHardDrivePath = vm.Disks[0].Path; + vm.HddSize = Convert.ToInt32(vm.Disks[0].FileSize / Size1G); + } + + // network adapters + vm.Adapters = NetworkAdapterHelper.Get(PowerShell, vm.Name); + } + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetVirtualMachine", ex); + throw; + } + + HostedSolutionLog.LogEnd("GetVirtualMachine"); + return vm; + + } + + public List GetVirtualMachines() + { + HostedSolutionLog.LogStart("GetVirtualMachines"); + + List vmachines = new List(); + + try + { + Command cmd = new Command("Get-VM"); + + Collection result = PowerShell.Execute(cmd, false); + foreach (PSObject current in result) + { + VirtualMachine vm = new VirtualMachine + { + VirtualMachineId = current.GetProperty("Id").ToString(), + Name = current.GetProperty("Name").ToString(), + State = (VirtualMachineState)Enum.Parse(typeof(VirtualMachineState), current.GetProperty("State").ToString()), + Uptime = Convert.ToInt64(current.GetProperty("UpTime").TotalMilliseconds) + }; + vmachines.Add(vm); + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetVirtualMachines", ex); + throw; + } + + 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); + //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); + + try + { + // Add new VM + Command cmdNew = new Command("New-VM"); + cmdNew.Parameters.Add("Name", vm.Name); + cmdNew.Parameters.Add("VHDPath", vm.VirtualHardDrivePath); + PowerShell.Execute(cmdNew, false); + + // Set VM + Command cmdSet = new Command("Set-VM"); + cmdSet.Parameters.Add("Name", vm.Name); + cmdSet.Parameters.Add("SmartPagingFilePath", vm.RootFolderPath); + cmdSet.Parameters.Add("SnapshotFileLocation", vm.RootFolderPath); + // startup/shutdown actions + var autoStartAction = (AutomaticStartAction) AutomaticStartActionSettings; + var autoStopAction = (AutomaticStopAction) AutomaticStartActionSettings; + if (autoStartAction != AutomaticStartAction.Undefined) + { + cmdSet.Parameters.Add("AutomaticStartAction", autoStartAction.ToString()); + cmdSet.Parameters.Add("AutomaticStartDelay", AutomaticStartupDelaySettings); + } + if (autoStopAction != AutomaticStopAction.Undefined) + cmdSet.Parameters.Add("AutomaticStopAction", autoStopAction.ToString()); + PowerShell.Execute(cmdSet, false); + + // Get created machine Id + var createdMachine = GetVirtualMachines().FirstOrDefault(m => m.Name == vm.Name); + if (createdMachine == null) + throw new Exception("Can't find created machine"); + vm.VirtualMachineId = createdMachine.VirtualMachineId; + + // Update common settings + UpdateVirtualMachine(vm); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("CreateVirtualMachine", ex); + throw; + } + + return vm; + } + + public VirtualMachine UpdateVirtualMachine(VirtualMachine vm) + { + HostedSolutionLog.LogStart("UpdateVirtualMachine"); + HostedSolutionLog.DebugInfo("Virtual Machine: {0}", vm.VirtualMachineId); + + try + { + var realVm = GetVirtualMachineEx(vm.VirtualMachineId); + + DvdDriveHelper.Update(PowerShell, realVm, vm.DvdDriveInstalled); // Dvd should be before bios because bios sets boot order + BiosHelper.Update(PowerShell, realVm, vm.BootFromCD, vm.NumLockEnabled); + VirtualMachineHelper.UpdateProcessors(PowerShell, realVm, vm.CpuCores, CpuLimitSettings, CpuReserveSettings, CpuWeightSettings); + VirtualMachineHelper.UpdateMemory(PowerShell, realVm, vm.RamSize); + NetworkAdapterHelper.Update(PowerShell, vm); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("UpdateVirtualMachine", ex); + throw; + } + + HostedSolutionLog.LogEnd("UpdateVirtualMachine"); + + return vm; + } + + + 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); + + try + { + 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)); + + PowerShell.Execute(cmd, false); + jobResult = JobHelper.CreateSuccessResult(); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("ChangeVirtualMachineState", ex); + throw; + } + + 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); + + try + { + Command cmd = new Command("Stop-VM"); + + cmd.Parameters.Add("Name", vm.Name); + if (force) cmd.Parameters.Add("Force"); + //if (!string.IsNullOrEmpty(reason)) cmd.Parameters.Add("Reason", reason); + + PowerShell.Execute(cmd, false); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("ShutDownVirtualMachine", ex); + throw; + } + + 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) + { + List snapshots = new List(); + + try + { + var vm = GetVirtualMachine(vmId); + + Command cmd = new Command("Get-VMSnapshot"); + cmd.Parameters.Add("VMName", vm.Name); + + Collection result = PowerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + foreach (PSObject psSnapshot in result) + { + snapshots.Add(SnapshotHelper.GetFromPS(psSnapshot, vm.ParentSnapshotId)); + } + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetVirtualMachineSnapshots", ex); + throw; + } + + return snapshots; + } + + public VirtualMachineSnapshot GetSnapshot(string snapshotId) + { + try + { + Command cmd = new Command("Get-VMSnapshot"); + cmd.Parameters.Add("Id", snapshotId); + + Collection result = PowerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + return SnapshotHelper.GetFromPS(result[0]); + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetSnapshot", ex); + throw; + } + + return null; + } + + public JobResult CreateSnapshot(string vmId) + { + try + { + var vm = GetVirtualMachine(vmId); + + Command cmd = new Command("Checkpoint-VM"); + cmd.Parameters.Add("Name", vm.Name); + + PowerShell.Execute(cmd, false); + return JobHelper.CreateSuccessResult(ReturnCode.JobStarted); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("CreateSnapshot", ex); + throw; + } + } + + public JobResult RenameSnapshot(string vmId, string snapshotId, string name) + { + try + { + var vm = GetVirtualMachine(vmId); + var snapshot = GetSnapshot(snapshotId); + + Command cmd = new Command("Rename-VMSnapshot"); + cmd.Parameters.Add("VMName", vm.Name); + cmd.Parameters.Add("Name", snapshot.Name); + cmd.Parameters.Add("NewName", name); + + PowerShell.Execute(cmd, false); + return JobHelper.CreateSuccessResult(); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("RenameSnapshot", ex); + throw; + } + } + + public JobResult ApplySnapshot(string vmId, string snapshotId) + { + try + { + var vm = GetVirtualMachine(vmId); + var snapshot = GetSnapshot(snapshotId); + + Command cmd = new Command("Restore-VMSnapshot"); + cmd.Parameters.Add("VMName", vm.Name); + cmd.Parameters.Add("Name", snapshot.Name); + + PowerShell.Execute(cmd, false); + return JobHelper.CreateSuccessResult(); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("ApplySnapshot", ex); + throw; + } + } + + public JobResult DeleteSnapshot(string snapshotId) + { + try + { + var snapshot = GetSnapshot(snapshotId); + SnapshotHelper.Delete(PowerShell, snapshot, false); + return JobHelper.CreateSuccessResult(ReturnCode.JobStarted); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("DeleteSnapshot", ex); + throw; + } + } + + public JobResult DeleteSnapshotSubtree(string snapshotId) + { + try + { + var snapshot = GetSnapshot(snapshotId); + SnapshotHelper.Delete(PowerShell, snapshot, true); + return JobHelper.CreateSuccessResult(ReturnCode.JobStarted); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("DeleteSnapshot", ex); + throw; + } + } + + public byte[] GetSnapshotThumbnailImage(string snapshotId, ThumbnailSize size) + { + ManagementBaseObject objSummary = GetSnapshotSummaryInformation(snapshotId, (SummaryInformationRequest)size); + return GetTumbnailFromSummaryInformation(objSummary, size); + } + + #endregion + + #region DVD operations + public string GetInsertedDVD(string vmId) + { + HostedSolutionLog.LogStart("GetInsertedDVD"); + HostedSolutionLog.DebugInfo("Virtual Machine: {0}", vmId); + + DvdDriveInfo dvdInfo; + + try + { + var vm = GetVirtualMachineEx(vmId); + dvdInfo = DvdDriveHelper.Get(PowerShell, vm.Name); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetInsertedDVD", ex); + throw; + } + + if (dvdInfo == null) + return null; + + HostedSolutionLog.LogEnd("GetInsertedDVD"); + return dvdInfo.Path; + } + + public JobResult InsertDVD(string vmId, string isoPath) + { + HostedSolutionLog.LogStart("InsertDVD"); + HostedSolutionLog.DebugInfo("Virtual Machine: {0}", vmId); + HostedSolutionLog.DebugInfo("Path: {0}", isoPath); + + try + { + var vm = GetVirtualMachineEx(vmId); + DvdDriveHelper.Set(PowerShell, vm.Name, isoPath); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("InsertDVD", ex); + throw; + } + + HostedSolutionLog.LogEnd("InsertDVD"); + return JobHelper.CreateSuccessResult(); + } + + public JobResult EjectDVD(string vmId) + { + HostedSolutionLog.LogStart("InsertDVD"); + HostedSolutionLog.DebugInfo("Virtual Machine: {0}", vmId); + + try + { + var vm = GetVirtualMachineEx(vmId); + DvdDriveHelper.Set(PowerShell, vm.Name, null); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("InsertDVD", ex); + throw; + } + + HostedSolutionLog.LogEnd("InsertDVD"); + return JobHelper.CreateSuccessResult(); + } + #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); + + List switches = new List(); + + try + { + + 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 = PowerShell.Execute(cmd,false); + foreach (PSObject current in result) + { + VirtualSwitch sw = new VirtualSwitch(); + sw.SwitchId = current.GetProperty("Name").ToString(); + sw.Name = current.GetProperty("Name").ToString(); + sw.SwitchType = current.GetProperty("SwitchType").ToString(); + switches.Add(sw); + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetSwitches", ex); + throw; + } + + HostedSolutionLog.LogEnd("GetSwitches"); + return switches; + } + + public bool SwitchExists(string switchId) + { + return GetSwitches().Any(s => s.Name == switchId); + } + + public VirtualSwitch CreateSwitch(string name) + { + // Create private switch + + HostedSolutionLog.LogStart("CreateSwitch"); + HostedSolutionLog.DebugInfo("Name: {0}", name); + + VirtualSwitch virtualSwitch = null; + + try + { + Command cmd = new Command("New-VMSwitch"); + + cmd.Parameters.Add("SwitchType", "Private"); + cmd.Parameters.Add("Name", name); + + Collection result = PowerShell.Execute(cmd, false); + if (result != null && result.Count > 0) + { + virtualSwitch = new VirtualSwitch(); + virtualSwitch.SwitchId = result[0].GetString("Name"); + virtualSwitch.Name = result[0].GetString("Name"); + virtualSwitch.SwitchType = result[0].GetString("SwitchType"); + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("CreateSwitch", ex); + throw; + } + + HostedSolutionLog.LogEnd("CreateSwitch"); + return virtualSwitch; + } + + public ReturnCode DeleteSwitch(string switchId) + { + HostedSolutionLog.LogStart("DeleteSwitch"); + HostedSolutionLog.DebugInfo("switchId: {0}", switchId); + + try + { + Command cmd = new Command("Remove-VMSwitch"); + cmd.Parameters.Add("Name", switchId); + PowerShell.Execute(cmd, false); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("DeleteSwitch", ex); + throw; + } + + HostedSolutionLog.LogEnd("DeleteSwitch"); + return ReturnCode.OK; + } + #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 + { + HostedSolutionLog.LogError("GetKVPItems", new Exception("msvm_KvpExchangeComponent")); + + 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; + + //HostedSolutionLog.LogStart("GetKVPItems"); + //HostedSolutionLog.DebugInfo("Virtual Machine: {0}", vmId); + //HostedSolutionLog.DebugInfo("exchangeItemsName: {0}", exchangeItemsName); + + //List pairs = new List(); + + //try + //{ + // var vm = GetVirtualMachine(vmId); + + // Command cmdGetVm = new Command("Get-WmiObject"); + + // cmdGetVm.Parameters.Add("Namespace", WMI_VIRTUALIZATION_NAMESPACE); + // cmdGetVm.Parameters.Add("Class", "Msvm_ComputerSystem"); + // cmdGetVm.Parameters.Add("Filter", "ElementName = '" + vm.Name + "'"); + + // Collection result = PowerShell.Execute(cmdGetVm, false); + + // if (result != null && result.Count > 0) + // { + // dynamic resultDynamic = result[0];//.Invoke(); + // var kvp = resultDynamic.GetRelated("Msvm_KvpExchangeComponent"); + + // // return XML pairs + // string[] xmlPairs = null; + + // foreach (dynamic a in kvp) + // { + // xmlPairs = a[exchangeItemsName]; + // break; + // } + + // if (xmlPairs == null) + // return pairs; + + // // join all pairs + // StringBuilder sb = new StringBuilder(); + // sb.Append(""); + // foreach (string xmlPair in xmlPairs) + // sb.Append(xmlPair); + // sb.Append(""); + + // // parse pairs + // XmlDocument doc = new XmlDocument(); + // doc.LoadXml(sb.ToString()); + + // foreach (XmlNode nodeName in doc.SelectNodes("/result/INSTANCE/PROPERTY[@NAME='Name']/VALUE")) + // { + // string name = nodeName.InnerText; + // string data = nodeName.ParentNode.ParentNode.SelectSingleNode("PROPERTY[@NAME='Data']/VALUE").InnerText; + // pairs.Add(new KvpExchangeDataItem(name, data)); + // } + // } + //} + //catch (Exception ex) + //{ + // HostedSolutionLog.LogError("GetKVPItems", ex); + // throw; + //} + + //HostedSolutionLog.LogEnd("GetKVPItems"); + + //return pairs; + } + + public JobResult AddKVPItems(string vmId, KvpExchangeDataItem[] items) + { + // 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) + { + try + { + VirtualHardDiskInfo hardDiskInfo = new VirtualHardDiskInfo(); + HardDriveHelper.GetVirtualHardDiskDetail(PowerShell, vhdPath, ref hardDiskInfo); + return hardDiskInfo; + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetVirtualHardDiskInfo", ex); + throw; + } + } + + 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) + { + // 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); + + sourcePath = FileUtils.EvaluateSystemVariables(sourcePath); + destinationPath = FileUtils.EvaluateSystemVariables(destinationPath); + + try + { + Command cmd = new Command("Convert-VHD"); + + cmd.Parameters.Add("Path", sourcePath); + cmd.Parameters.Add("DestinationPath", destinationPath); + cmd.Parameters.Add("VHDType", diskType.ToString()); + + PowerShell.Execute(cmd, false); + return JobHelper.CreateSuccessResult(ReturnCode.JobStarted); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("ConvertVirtualHardDisk", ex); + throw; + } + } + + 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 + { + Command cmd = new Command("Get-Job"); + + if (!string.IsNullOrEmpty(jobId)) cmd.Parameters.Add("Id", jobId); + + Collection result = PowerShell.Execute( cmd, false); + job = JobHelper.CreateFromPSObject(result); + } + catch (Exception ex) + { + HostedSolutionLog.LogError("GetJob", ex); + throw; + } + + 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 + + internal int ConvertNullableToInt32(object value) + { + return value == null ? 0 : Convert.ToInt32(value); + } + + internal long ConvertNullableToInt64(object value) + { + return value == null ? 0 : Convert.ToInt64(value); + } + + //protected VirtualMachineSnapshot GetSnapshotById(string id) + //{ + // var vms = GetVirtualMachines(); + // var allSnapshots = vms.SelectMany(vm => GetVirtualMachineSnapshots(vm.Id.ToString())); + + // return allSnapshots.FirstOrDefault(s => s.Id == id); + //} + + + protected JobResult 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) ?? + wmi.GetWmiObject("Msvm_VirtualSystemSettingData", "InstanceID = '{0}'", "Microsoft:" + snapshotId); + } + + + + + 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 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 PowerShellManager _powerShell; + protected PowerShellManager PowerShell + { + get { return _powerShell ?? (_powerShell = new PowerShellManager()); } + } + + #endregion + + + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/PowerShellManager.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/PowerShellManager.cs new file mode 100644 index 00000000..949b08bd --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/PowerShellManager.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Text; +using System.Threading.Tasks; +using WebsitePanel.Providers.HostedSolution; + +namespace WebsitePanel.Providers.Virtualization +{ + public class PowerShellManager : IDisposable + { + protected static InitialSessionState session = null; + + protected Runspace RunSpace { get; set; } + + public PowerShellManager() + { + OpenRunspace(); + } + + protected void OpenRunspace() + { + HostedSolutionLog.LogStart("OpenRunspace"); + + if (session == null) + { + session = InitialSessionState.CreateDefault(); + session.ImportPSModule(new[] {"Hyper-V"}); + } + + Runspace runSpace = RunspaceFactory.CreateRunspace(session); + runSpace.Open(); + runSpace.SessionStateProxy.SetVariable("ConfirmPreference", "none"); + + RunSpace = runSpace; + + HostedSolutionLog.LogEnd("OpenRunspace"); + } + + public void Dispose() + { + try + { + if (RunSpace != null && RunSpace.RunspaceStateInfo.State == RunspaceState.Opened) + { + RunSpace.Close(); + RunSpace = null; + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("Runspace error", ex); + } + } + + public Collection Execute(Command cmd) + { + return Execute(cmd, true); + } + + public Collection Execute(Command cmd, bool useDomainController) + { + object[] errors; + return Execute(cmd, useDomainController, out errors); + } + + public Collection Execute(Command cmd, out object[] errors) + { + return Execute(cmd, true, out errors); + } + + public Collection Execute(Command cmd, bool useDomainController, out object[] errors) + { + HostedSolutionLog.LogStart("Execute"); + List errorList = new List(); + + HostedSolutionLog.DebugCommand(cmd); + Collection results = null; + // Create a pipeline + Pipeline pipeLine = RunSpace.CreatePipeline(); + using (pipeLine) + { + // Add the command + pipeLine.Commands.Add(cmd); + // Execute the pipeline and save the objects returned. + results = pipeLine.Invoke(); + + // Log out any errors in the pipeline execution + // NOTE: These errors are NOT thrown as exceptions! + // Be sure to check this to ensure that no errors + // happened while executing the command. + if (pipeLine.Error != null && pipeLine.Error.Count > 0) + { + foreach (object item in pipeLine.Error.ReadToEnd()) + { + errorList.Add(item); + string errorMessage = string.Format("Invoke error: {0}", item); + HostedSolutionLog.LogWarning(errorMessage); + } + } + } + pipeLine = null; + errors = errorList.ToArray(); + HostedSolutionLog.LogEnd("Execute"); + return results; + } + + + /// + /// Returns the identity of the object from the shell execution result + /// + /// + /// + public static string GetResultObjectIdentity(Collection result) + { + HostedSolutionLog.LogStart("GetResultObjectIdentity"); + if (result == null) + throw new ArgumentNullException("result", "Execution result is not specified"); + + if (result.Count < 1) + throw new ArgumentException("Execution result is empty", "result"); + + if (result.Count > 1) + throw new ArgumentException("Execution result contains more than one object", "result"); + + PSMemberInfo info = result[0].Members["Identity"]; + if (info == null) + throw new ArgumentException("Execution result does not contain Identity property", "result"); + + string ret = info.Value.ToString(); + HostedSolutionLog.LogEnd("GetResultObjectIdentity"); + return ret; + } + + public static string GetResultObjectDN(Collection result) + { + HostedSolutionLog.LogStart("GetResultObjectDN"); + if (result == null) + throw new ArgumentNullException("result", "Execution result is not specified"); + + if (result.Count < 1) + throw new ArgumentException("Execution result does not contain any object"); + + if (result.Count > 1) + throw new ArgumentException("Execution result contains more than one object"); + + PSMemberInfo info = result[0].Members["DistinguishedName"]; + if (info == null) + throw new ArgumentException("Execution result does not contain DistinguishedName property", "result"); + + string ret = info.Value.ToString(); + HostedSolutionLog.LogEnd("GetResultObjectDN"); + return ret; + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/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..635edce5 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/WebsitePanel.Providers.Virtualization.HyperV2012R2.csproj @@ -0,0 +1,92 @@ + + + + + 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\HyperV2012R2\ + 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..6a7140a2 --- /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 static 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 8aa90dcb..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.40121.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,7 +154,7 @@ 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.HostedSolution.Crm2015", "WebsitePanel.Providers.HostedSolution.Crm2015\WebsitePanel.Providers.HostedSolution.Crm2015.csproj", "{96EC3A56-5598-4B2D-A1F2-2E0DB6BA2AB6}" +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 @@ -776,16 +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 - {96EC3A56-5598-4B2D-A1F2-2E0DB6BA2AB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {96EC3A56-5598-4B2D-A1F2-2E0DB6BA2AB6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {96EC3A56-5598-4B2D-A1F2-2E0DB6BA2AB6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {96EC3A56-5598-4B2D-A1F2-2E0DB6BA2AB6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {96EC3A56-5598-4B2D-A1F2-2E0DB6BA2AB6}.Debug|x86.ActiveCfg = Debug|Any CPU - {96EC3A56-5598-4B2D-A1F2-2E0DB6BA2AB6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {96EC3A56-5598-4B2D-A1F2-2E0DB6BA2AB6}.Release|Any CPU.Build.0 = Release|Any CPU - {96EC3A56-5598-4B2D-A1F2-2E0DB6BA2AB6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {96EC3A56-5598-4B2D-A1F2-2E0DB6BA2AB6}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {96EC3A56-5598-4B2D-A1F2-2E0DB6BA2AB6}.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.Server/Web.config b/WebsitePanel/Sources/WebsitePanel.Server/Web.config index f1dd4e98..8074f342 100644 --- a/WebsitePanel/Sources/WebsitePanel.Server/Web.config +++ b/WebsitePanel/Sources/WebsitePanel.Server/Web.config @@ -1,4 +1,4 @@ - + @@ -7,34 +7,34 @@
- - - - - + + + + + - + - + - + - + - + @@ -42,85 +42,85 @@ - + - + - + - + - + - - - + + + - + - - - - + + + + - - + + - - - - + + + + - + - + - - + + - + - + - + - - + + - - + + - - + + - - + + @@ -129,40 +129,40 @@ - - + + - + - - + + - - - - - - - + + + + + + + - - + + - - + + - + \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/App_Themes/Default/Styles/Skin.css b/WebsitePanel/Sources/WebsitePanel.WebPortal/App_Themes/Default/Styles/Skin.css index 4d50d626..bb2b9fb5 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/App_Themes/Default/Styles/Skin.css +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/App_Themes/Default/Styles/Skin.css @@ -54,9 +54,9 @@ legend {font-weight:700; color:#428bca; padding:0 4px;} legend span.NormalBold {display:inline;} .Tabs {width:100%;} .Separator {display:none;} -.Tabs span {display:table; table-layout:fixed; width:100%; table-layout:fixed;} +.Tabs span {display:table; table-layout:fixed; min-width:100%; table-layout:fixed;} .Tabs span span {display:table-cell; width:15%; text-align:center; padding:0;} -.Tabs a {display:block; padding:10px 0; text-decoration:none; border-bottom:1px solid #ddd;} +.Tabs a {display:block; padding:10px 5px; text-decoration:none; border-bottom:1px solid #ddd;} .Tabs a:Hover {background:#eee; text-decoration:none;} .Tabs a.ActiveTab {border:1px solid #ddd; border-bottom:none; color:#555; margin-bottom:-1px;} .Tabs a.ActiveTab:hover {background:none;} diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/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/UserControls/SpaceServiceItems.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/UserControls/SpaceServiceItems.ascx.cs index c3914eee..a6275f7e 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/UserControls/SpaceServiceItems.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/UserControls/SpaceServiceItems.ascx.cs @@ -211,18 +211,24 @@ namespace WebsitePanel.Portal.UserControls private void ShowActionList() { + var checkboxColumn = gvItems.Columns[0]; websiteActions.Visible = false; mailActions.Visible = false; + checkboxColumn.Visible = false; switch (QuotaName) { case "Web.Sites": websiteActions.Visible = true; + checkboxColumn.Visible = true; break; case "Mail.Accounts": ProviderInfo provider = ES.Services.Servers.GetPackageServiceProvider(PanelSecurity.PackageId, "Mail"); if (provider.EditorControl == "SmarterMail100") + { mailActions.Visible = true; + checkboxColumn.Visible = true; + } break; } } 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")); diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx index a8fd47ff..4b72149d 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx @@ -66,7 +66,7 @@ - +
@@ -120,6 +120,7 @@ +     diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.cs index 777e9c62..eb8ddab0 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.cs @@ -69,7 +69,7 @@ namespace WebsitePanel.Portal new Tab { Id = "webman", ResourceKey = "Tab.WebManagement", Quota = Quotas.WEB_REMOTEMANAGEMENT, ViewId = "tabWebManagement" }, new Tab { Id = "SSL", ResourceKey = "Tab.SSL", Quota = Quotas.WEB_SSL, ViewId = "SSL" }, }; - + protected string SharedIdAddres { get; set; } private int PackageId { get { return (int)ViewState["PackageId"]; } @@ -198,6 +198,16 @@ namespace WebsitePanel.Portal { litIPAddress.Text = site.SiteIPAddress; } + else + { + IPAddressInfo[] ipsGeneral = ES.Services.Servers.GetIPAddresses(IPAddressPool.General, PanelRequest.ServerId); + bool generalIPExists = ipsGeneral.Any() && !string.IsNullOrEmpty(ipsGeneral[0].ExternalIP); + if (generalIPExists) + { + lblSharedIP.Text = string.Format("({0})", ipsGeneral[0].ExternalIP); + } + lblSharedIP.Visible = generalIPExists; + } dedicatedIP.Visible = site.IsDedicatedIP; sharedIP.Visible = !site.IsDedicatedIP; @@ -312,7 +322,7 @@ namespace WebsitePanel.Portal // AppPool AppPoolState appPoolState = ES.Services.WebServers.GetAppPoolState(PanelRequest.ItemID); BindAppPoolState(appPoolState); - + // bind pointers BindPointers(); diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.designer.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.designer.cs index f786b81f..735941ab 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.designer.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.designer.cs @@ -1,37 +1,9 @@ -// Copyright (c) 2015, Outercurve Foundation. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// - Neither the name of the Outercurve Foundation nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - //------------------------------------------------------------------------------ // // This code was generated by a tool. // // Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -238,6 +210,15 @@ namespace WebsitePanel.Portal { /// protected global::System.Web.UI.WebControls.Localize locSharedIPAddress; + /// + /// lblSharedIP control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblSharedIP; + /// /// cmdSwitchToDedicatedIP control. ///