websitepanel/WebsitePanel.HyperV.Utils/Sources/WebsitePanel.HyperV.Utils/Program.cs
2011-07-13 16:07:32 -07:00

445 lines
17 KiB
C#

// Copyright (c) 2011, 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.Reflection;
using System.Management;
using Vds = Microsoft.Storage.Vds;
using System.Diagnostics;
namespace WebsitePanel.HyperV.Utils
{
class Program
{
// command line parameters
private const string ns = @"root\virtualization";
private static Wmi wmi = null;
private static string computer = null;
private static string command = null;
private static Dictionary<string, string> Parameters = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
static void Main(string[] args)
{
// display welcome screen
DisplayWelcome();
// parse parameters
if (!ParseParameters(args))
{
DisplayUsage();
return;
}
// connect WMI
wmi = new Wmi(computer, ns);
try
{
// run command
if (String.Equals(command, "MountVHD", StringComparison.CurrentCultureIgnoreCase))
MountVHD();
else if (String.Equals(command, "UnmountVHD", StringComparison.CurrentCultureIgnoreCase))
UnmountVHD();
else
{
Console.WriteLine("Unknown command: " + command);
DisplayUsage();
}
}
catch (Exception ex)
{
Console.WriteLine("\nError: " + ex.Message);
if (ex.InnerException != null)
Console.WriteLine("System message: " + ex.InnerException.Message);
}
}
private static void MountVHD()
{
// check parameters
AssertParameter("path");
string vhdPath = Parameters["path"];
ManagementObject objImgSvc = GetImageManagementService();
Console.WriteLine("Mount VHD: " + vhdPath);
// get method params
ManagementBaseObject inParams = objImgSvc.GetMethodParameters("Mount");
inParams["Path"] = vhdPath;
ManagementBaseObject outParams = (ManagementBaseObject)objImgSvc.InvokeMethod("Mount", inParams, null);
JobResult result = CreateJobResultFromWmiMethodResults(outParams);
// load storage job
if (result.ReturnValue != ReturnCode.JobStarted
|| result.Job.JobState == ConcreteJobState.Exception)
{
throw new Exception("Mount job failed to start with the following message: " + result.Job.ErrorDescription);
}
ManagementObject objJob = wmi.GetWmiObject("msvm_StorageJob", "InstanceID = '{0}'", result.Job.Id);
if (JobCompleted(result.Job))
{
// 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);
// find mounted disk using VDS
Vds.Advanced.AdvancedDisk advancedDisk = null;
Vds.Pack diskPack = null;
// first attempt
System.Threading.Thread.Sleep(3000);
Console.WriteLine("Querying mounted disk...");
FindVdsDisk(diskAddress, out advancedDisk, out diskPack);
// second attempt
if (advancedDisk == null)
{
System.Threading.Thread.Sleep(20000);
Console.WriteLine("Querying mounted disk - second attempt...");
FindVdsDisk(diskAddress, out advancedDisk, out diskPack);
}
if (advancedDisk == null)
throw new Exception("Could not find mounted disk");
List<string> volumes = new List<string>();
Console.WriteLine("Disk flags: " + advancedDisk.Flags);
// clear READONLY
if ((advancedDisk.Flags & Vds.DiskFlags.ReadOnly) == Vds.DiskFlags.ReadOnly)
{
Console.Write("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();
}
Console.WriteLine("Done");
}
// bring disk ONLINE
if (advancedDisk.Status == Vds.DiskStatus.Offline)
{
Console.Write("Bringing disk online...");
advancedDisk.Online();
while (advancedDisk.Status == Vds.DiskStatus.Offline)
{
System.Threading.Thread.Sleep(100);
advancedDisk.Refresh();
}
Console.WriteLine("Done");
}
// determine disk index for DiskPart
Wmi cimv2 = new Wmi(computer, "root\\CIMV2");
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 (objDisk != null)
{
Console.WriteLine("DiskPart disk index: " + Convert.ToInt32(objDisk["Index"]));
}
// find volume
diskPack.Refresh();
foreach (Vds.Volume volume in diskPack.Volumes)
{
volumes.Add(volume.DriveLetter.ToString());
}
if (volumes.Count == 0 && objDisk != null)
{
// find volumes using WMI
foreach (ManagementObject objPartition in objDisk.GetRelated("Win32_DiskPartition"))
{
foreach (ManagementObject objVolume in objPartition.GetRelated("Win32_LogicalDisk"))
{
volumes.Add(objVolume["Name"].ToString().TrimEnd(':'));
}
}
}
foreach (string volume in volumes)
{
Console.WriteLine("Volume found: " + volume);
}
//// find disk index
//Wmi win32 = new Wmi(computer, @"root\cimv2");
//System.Threading.Thread.Sleep(1000); // small pause
//ManagementObject objDisk = win32.GetWmiObject("win32_DiskDrive",
// "Model='Msft Virtual Disk SCSI Disk Device' and ScsiTargetID={0} and ScsiLogicalUnit={1} and ScsiPort={2}",
// targetId, lun, portNumber);
//int diskIndex = Convert.ToInt32(objDisk["Index"]);
Console.WriteLine("\nDisk has been mounted.\n");
//Console.WriteLine("Disk index: " + advancedDisk.);
}
}
private static 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(computer);
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;
}
}
}
private static void UnmountVHD()
{
// check parameters
AssertParameter("path");
string vhdPath = Parameters["path"];
Console.WriteLine("Unmount VHD: " + vhdPath);
ManagementObject objImgSvc = GetImageManagementService();
// get method params
ManagementBaseObject inParams = objImgSvc.GetMethodParameters("Unmount");
inParams["Path"] = vhdPath;
ManagementBaseObject outParams = (ManagementBaseObject)objImgSvc.InvokeMethod("Unmount", inParams, null);
ReturnCode result = (ReturnCode)Convert.ToInt32(outParams["ReturnValue"]);
if (result != ReturnCode.OK)
{
throw new Exception("Unmount task failed with the \"" + result + "\" code");
}
Console.WriteLine("\nDisk has been unmounted.");
}
#region Jobs
private static 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;
}
public static ConcreteJob GetJob(string jobId)
{
ManagementObject objJob = GetJobWmiObject(jobId);
return CreateJobFromWmiObject(objJob);
}
private static ConcreteJob CreateJobFromWmiMethodResults(ManagementBaseObject outParams)
{
ManagementBaseObject objJob = wmi.GetWmiObjectByPath((string)outParams["Job"]);
if (objJob == null || objJob.Properties.Count == 0)
return null;
return CreateJobFromWmiObject(objJob);
}
private static JobResult CreateJobResultFromWmiMethodResults(ManagementBaseObject outParams)
{
JobResult result = new JobResult();
// return value
result.ReturnValue = (ReturnCode)Convert.ToInt32(outParams["ReturnValue"]);
// job
ManagementBaseObject objJob = wmi.GetWmiObjectByPath((string)outParams["Job"]);
if (objJob != null && objJob.Properties.Count > 0)
{
result.Job = CreateJobFromWmiObject(objJob);
}
return result;
}
private static ManagementObject GetJobWmiObject(string id)
{
return wmi.GetWmiObject("CIM_ConcreteJob", "InstanceID = '{0}'", id);
}
private static 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.ElapsedTime = wmi.ToDateTime((string)objJob["ElapsedTime"]);
job.StartTime = wmi.ToDateTime((string)objJob["StartTime"]);
job.ErrorCode = Convert.ToInt32(objJob["ErrorCode"]);
job.ErrorDescription = (string)objJob["ErrorDescription"];
job.PercentComplete = Convert.ToInt32(objJob["PercentComplete"]);
return job;
}
#endregion
#region Managers
private static ManagementObject GetVirtualSystemManagementService()
{
return wmi.GetWmiObject("msvm_VirtualSystemManagementService");
}
private static ManagementObject GetVirtualSwitchManagementService()
{
return wmi.GetWmiObject("msvm_VirtualSwitchManagementService");
}
private static ManagementObject GetImageManagementService()
{
return wmi.GetWmiObject("msvm_ImageManagementService");
}
#endregion
private static void DisplayWelcome()
{
string ver = Assembly.GetExecutingAssembly().GetName().Version.ToString(2);
Console.WriteLine("VmUtils - utility to work with Hyper-V virtual resources. Version " + ver);
Console.WriteLine("Copyright (C) 2011 by Outercurve Foundation. All rights reserved.\n");
}
private static bool ParseParameters(string[] args)
{
if (args == null || args.Length == 0)
return false;
// command
command = args[0];
for (int i = 1; i < args.Length; i++)
{
if (!args[i].StartsWith("-"))
return false; // wrong parameter format
string name = args[i].Substring(1);
if (i == (args.Length - 1))
return false; // no parameter value
string val = args[i + 1];
i++;
// add parameter to the hash
Parameters.Add(name, val);
if (String.Equals(name, "computer", StringComparison.CurrentCultureIgnoreCase))
computer = val;
}
return true;
}
private static void AssertParameter(string name)
{
if (!Parameters.ContainsKey(name))
{
throw new Exception(String.Format("Command \"{0}\" expect \"{1}\" parameter which was not supplied.", command, name));
}
}
private static void DisplayUsage()
{
Console.WriteLine("\nUSAGE:");
Console.WriteLine(" vmutils <command> [-parameter1 value1 -parameter2 value2 ...]\n");
Console.WriteLine("EXAMPLE:");
Console.WriteLine(" vmtuils MountVHD -path \"c:\\templates\\Windows 2008.vhd\" -computer HYPERV01\n");
Console.WriteLine("SUPPORTED COMMANDS:");
Console.WriteLine(" MountVHD - mounts VHD.");
Console.WriteLine(" Parameters:");
Console.WriteLine(" -path - path to VHD file.");
Console.WriteLine(" -computer - (optional) remote computer with Hyper-V role installed.");
Console.WriteLine("");
Console.WriteLine(" UnmountVHD - unmounts VHD.");
Console.WriteLine(" Parameters:");
Console.WriteLine(" -path - path to VHD file.");
Console.WriteLine(" -computer - (optional) remote computer with Hyper-V role installed.");
}
}
}