websitepanel/WebsitePanel/Sources/WebsitePanel.Providers.Virtualization.HyperV-2012R2/Helpers/VdsHelper.cs
2015-03-25 20:46:35 +04:00

294 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Management;
using System.Threading;
using Microsoft.Storage.Vds;
using Microsoft.Storage.Vds.Advanced;
using WebsitePanel.Providers.HostedSolution;
using WebsitePanel.Providers.Virtualization.Extensions;
using Path = System.IO.Path;
namespace WebsitePanel.Providers.Virtualization
{
public static class VdsHelper
{
public static MountedDiskInfo GetMountedDiskInfo(string serverName, int driveNumber)
{
MountedDiskInfo diskInfo = new MountedDiskInfo { DiskNumber = driveNumber };
// find mounted disk using VDS
AdvancedDisk advancedDisk = null;
Pack diskPack = null;
// first attempt
Thread.Sleep(3000);
HostedSolutionLog.LogInfo("Trying to find mounted disk - first attempt");
FindVdsDisk(serverName, diskInfo.DiskNumber, out advancedDisk, out diskPack);
// second attempt
if (advancedDisk == null)
{
Thread.Sleep(20000);
HostedSolutionLog.LogInfo("Trying to find mounted disk - second attempt");
FindVdsDisk(serverName, diskInfo.DiskNumber, out advancedDisk, out diskPack);
}
if (advancedDisk == null)
throw new Exception("Could not find mounted disk");
// Set disk address
diskInfo.DiskAddress = advancedDisk.DiskAddress;
var addressParts = diskInfo.DiskAddress.ParseExact("Port{0}Path{1}Target{2}Lun{3}");
var portNumber = addressParts[0];
var targetId = addressParts[2];
var lun = addressParts[3];
// check if DiskPart must be used to bring disk online and clear read-only flag
bool useDiskPartToClearReadOnly = false;
if (ConfigurationManager.AppSettings[Constants.CONFIG_USE_DISKPART_TO_CLEAR_READONLY_FLAG] != null)
useDiskPartToClearReadOnly = Boolean.Parse(ConfigurationManager.AppSettings[Constants.CONFIG_USE_DISKPART_TO_CLEAR_READONLY_FLAG]);
// determine disk index for DiskPart
Wmi cimv2 = new Wmi(serverName, Constants.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(serverName, 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 & DiskFlags.ReadOnly) == DiskFlags.ReadOnly)
{
HostedSolutionLog.LogInfo("Clearing disk Read-only flag");
advancedDisk.ClearFlags(DiskFlags.ReadOnly);
while ((advancedDisk.Flags & DiskFlags.ReadOnly) == DiskFlags.ReadOnly)
{
Thread.Sleep(100);
advancedDisk.Refresh();
}
}
// bring disk ONLINE
if (advancedDisk.Status == DiskStatus.Offline)
{
HostedSolutionLog.LogInfo("Bringing disk online");
advancedDisk.Online();
while (advancedDisk.Status == DiskStatus.Offline)
{
Thread.Sleep(100);
advancedDisk.Refresh();
}
}
}
// small pause after getting disk online
Thread.Sleep(3000);
// get disk again
FindVdsDisk(serverName, diskInfo.DiskNumber, out advancedDisk, out diskPack);
// find volumes using VDS
List<string> volumes = new List<string>();
HostedSolutionLog.LogInfo("Querying disk volumes with VDS");
foreach (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);
// Set volumes
diskInfo.DiskVolumes = volumes.ToArray();
return diskInfo;
}
public static void FindVdsDisk(string serverName, int driveNumber, out AdvancedDisk advancedDisk, out Pack diskPack)
{
Func<Disk, bool> compareFunc = disk => disk.Name.EndsWith("PhysicalDrive" + driveNumber);
FindVdsDisk(serverName, compareFunc, out advancedDisk, out diskPack);
}
public static void FindVdsDisk(string serverName, string diskAddress, out AdvancedDisk advancedDisk, out Pack diskPack)
{
Func<Disk, bool> compareFunc = disk => disk.DiskAddress == diskAddress;
FindVdsDisk(serverName, compareFunc, out advancedDisk, out diskPack);
}
private static void FindVdsDisk(string serverName, Func<Disk, bool> compareFunc, out AdvancedDisk advancedDisk, out Pack diskPack)
{
advancedDisk = null;
diskPack = null;
ServiceLoader serviceLoader = new ServiceLoader();
Service vds = serviceLoader.LoadService(serverName);
vds.WaitForServiceReady();
foreach (Disk disk in vds.UnallocatedDisks)
{
if (compareFunc(disk))
{
advancedDisk = (AdvancedDisk) disk;
break;
}
}
if (advancedDisk == null)
{
vds.HardwareProvider = false;
vds.SoftwareProvider = true;
foreach (SoftwareProvider provider in vds.Providers)
foreach (Pack pack in provider.Packs)
foreach (Disk disk in pack.Disks)
if (compareFunc(disk))
{
diskPack = pack;
advancedDisk = (AdvancedDisk) disk;
break;
}
}
}
// obsolete and currently is not used
private static string RunDiskPart(string serverName, string script)
{
// create temp script file name
string localPath = Path.Combine(GetTempRemoteFolder(serverName), Guid.NewGuid().ToString("N"));
// save script to remote temp file
string remotePath = ConvertToUNC(serverName, localPath);
File.AppendAllText(remotePath, script);
// run diskpart
ExecuteRemoteProcess(serverName, "DiskPart /s " + localPath);
// delete temp script
try
{
File.Delete(remotePath);
}
catch
{
// TODO
}
return "";
}
public static string ConvertToUNC(string serverName, string path)
{
if (String.IsNullOrEmpty(serverName)
|| path.StartsWith(@"\\"))
return path;
return String.Format(@"\\{0}\{1}", serverName, path.Replace(":", "$"));
}
public static string GetTempRemoteFolder(string serverName)
{
Wmi cimv2 = new Wmi(serverName, "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;
}
public static void ExecuteRemoteProcess(string serverName, string command)
{
Wmi cimv2 = new Wmi(serverName, "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;
}
}
}
}
}