// 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 Parameters = new Dictionary(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 volumes = new List(); 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 [-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."); } } }