From 3604f87c8d4a55a11d103b3b040bb3c36af33657 Mon Sep 17 00:00:00 2001 From: Devdeep Singh Date: Tue, 17 Dec 2013 22:53:12 +0530 Subject: [PATCH] CLOUDSTACK-5217: Attach volume fails with NPE. Made changes to make sure the hypervisor product version is reported on startup. Also made changes to fix attach and detach volume on scsi controller. --- .../HypervResource/CloudStackTypes.cs | 4 + .../HypervResourceController.cs | 53 +++++-- .../HypervResource/IWmiCallsV2.cs | 3 +- .../HypervResource/WmiCallsV2.cs | 137 ++++++++++++++---- 4 files changed, 158 insertions(+), 39 deletions(-) diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs index f4d25ecfb6c..eb20d7fd082 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs @@ -406,7 +406,9 @@ namespace HypervResource public class DiskTO { public string type; + public string diskSequence = null; public TemplateObjectTO templateObjectTO = null; + public VolumeObjectTO volumeObjectTO = null; public static DiskTO ParseJson(dynamic json) { @@ -416,7 +418,9 @@ namespace HypervResource result = new DiskTO() { templateObjectTO = TemplateObjectTO.ParseJson(json.data), + volumeObjectTO = VolumeObjectTO.ParseJson(json.data), type = (string)json.type, + diskSequence = json.diskSeq }; } diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs index b283d976c51..51a27bfdcd2 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs @@ -215,18 +215,29 @@ namespace HypervResource { string vmName = (string)cmd.vmName; DiskTO disk = DiskTO.ParseJson(cmd.disk); - TemplateObjectTO dataStore = disk.templateObjectTO; - if (dataStore.nfsDataStoreTO != null) + if (disk.type.Equals("ISO")) { + TemplateObjectTO dataStore = disk.templateObjectTO; NFSTO share = dataStore.nfsDataStoreTO; Utils.ConnectToRemote(share.UncPath, share.Domain, share.User, share.Password); - - // The share is mapped, now attach the iso - string isoPath = Utils.NormalizePath(Path.Combine(share.UncPath, dataStore.path)); - wmiCallsV2.AttachIso(vmName, isoPath); + string diskPath = Utils.NormalizePath(Path.Combine(share.UncPath, dataStore.path)); + wmiCallsV2.AttachIso(vmName, diskPath); result = true; } + else if (disk.type.Equals("DATADISK")) + { + VolumeObjectTO volume = disk.volumeObjectTO; + PrimaryDataStoreTO primary = volume.primaryDataStore; + Utils.ConnectToRemote(primary.UncPath, primary.Domain, primary.User, primary.Password); + string diskPath = Utils.NormalizePath(volume.FullFileName); + wmiCallsV2.AttachDisk(vmName, diskPath, disk.diskSequence); + result = true; + } + else + { + details = "Invalid disk type to be attached to vm " + vmName; + } } catch (Exception sysEx) { @@ -238,6 +249,7 @@ namespace HypervResource { result = result, details = details, + disk = cmd.disk, contextMap = contextMap }; @@ -261,16 +273,27 @@ namespace HypervResource { string vmName = (string)cmd.vmName; DiskTO disk = DiskTO.ParseJson(cmd.disk); - TemplateObjectTO dataStore = disk.templateObjectTO; - if (dataStore.nfsDataStoreTO != null) + if (disk.type.Equals("ISO")) { + TemplateObjectTO dataStore = disk.templateObjectTO; NFSTO share = dataStore.nfsDataStoreTO; - // The share is mapped, now attach the iso - string isoPath = Utils.NormalizePath(Path.Combine(share.UncPath, dataStore.path)); - wmiCallsV2.DetachDisk(vmName, isoPath); + string diskPath = Utils.NormalizePath(Path.Combine(share.UncPath, dataStore.path)); + wmiCallsV2.DetachDisk(vmName, diskPath); result = true; } + else if (disk.type.Equals("DATADISK")) + { + VolumeObjectTO volume = disk.volumeObjectTO; + PrimaryDataStoreTO primary = volume.primaryDataStore; + string diskPath = Utils.NormalizePath(volume.FullFileName); + wmiCallsV2.DetachDisk(vmName, diskPath); + result = true; + } + else + { + details = "Invalid disk type to be dettached from vm " + vmName; + } } catch (Exception sysEx) { @@ -1649,6 +1672,14 @@ namespace HypervResource strtRouteCmd.hypervisorVersion = System.Environment.OSVersion.Version.ToString(); strtRouteCmd.caps = "hvm"; + dynamic details = strtRouteCmd.hostDetails; + if (details != null) + { + string productVersion = System.Environment.OSVersion.Version.Major.ToString() + "." + + System.Environment.OSVersion.Version.Minor.ToString(); + details.Add("product_version", productVersion); + } + // Detect CPUs, speed, memory uint cores; uint mhz; diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs index 99ce35276b7..35cdec00373 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs @@ -27,9 +27,10 @@ namespace HypervResource { public interface IWmiCallsV2 { - System.Management.ManagementPath AddDiskDriveToVm(ComputerSystem vm, string vhdfile, string cntrllerAddr, string driveResourceType); + System.Management.ManagementPath AddDiskDriveToIdeController(ComputerSystem vm, string vhdfile, string cntrllerAddr, string driveResourceType); ComputerSystem AddUserData(ComputerSystem vm, string userData); void AttachIso(string displayName, string iso); + void AttachDisk(string vmName, string diskPath, string addressOnController); void CreateDynamicVirtualHardDisk(ulong MaxInternalSize, string Path); SyntheticEthernetPortSettingData CreateNICforVm(ComputerSystem vm, string mac); ComputerSystem CreateVM(string name, long memory_mb, int vcpus); diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs index c89f837a7a6..b7ae6656bca 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs @@ -207,14 +207,14 @@ namespace HypervResource return new SyntheticEthernetPortSettingData(newResourcePaths[0]); } - public const string IDE_HARDDISK_CONTROLLER = "Microsoft:Hyper-V:Emulated IDE Controller"; + public const string IDE_CONTROLLER = "Microsoft:Hyper-V:Emulated IDE Controller"; public const string SCSI_CONTROLLER = "Microsoft:Hyper-V:Synthetic SCSI Controller"; - public const string IDE_HARDDISK_DRIVE = "Microsoft:Hyper-V:Synthetic Disk Drive"; - public const string IDE_ISO_DRIVE = "Microsoft:Hyper-V:Synthetic DVD Drive"; + public const string HARDDISK_DRIVE = "Microsoft:Hyper-V:Synthetic Disk Drive"; + public const string ISO_DRIVE = "Microsoft:Hyper-V:Synthetic DVD Drive"; // TODO: names harvested from Msvm_ResourcePool, not clear how to create new instances - public const string IDE_ISO_DISK = "Microsoft:Hyper-V:Virtual CD/DVD Disk"; // For IDE_ISO_DRIVE - public const string IDE_HARDDISK_DISK = "Microsoft:Hyper-V:Virtual Hard Disk"; // For IDE_HARDDISK_DRIVE + public const string ISO_DISK = "Microsoft:Hyper-V:Virtual CD/DVD Disk"; // For IDE_ISO_DRIVE + public const string HARDDISK_DISK = "Microsoft:Hyper-V:Virtual Hard Disk"; // For IDE_HARDDISK_DRIVE /// /// Create new VM. By default we start it. @@ -280,7 +280,7 @@ namespace HypervResource var newVm = CreateVM(vmName, memSize, vcpus); // Add a SCSI controller for attaching/detaching data volumes. - AddScsiControllerToVm(newVm); + AddScsiController(newVm); foreach (var diskDrive in diskDrives) { @@ -345,11 +345,11 @@ namespace HypervResource switch (driveType) { case "ROOT": ideCtrllr = "0"; - driveResourceType = IDE_HARDDISK_DRIVE; + driveResourceType = HARDDISK_DRIVE; break; case "ISO": ideCtrllr = "1"; - driveResourceType = IDE_ISO_DRIVE; + driveResourceType = ISO_DRIVE; break; default: // TODO: double check exception type @@ -362,7 +362,7 @@ namespace HypervResource } logger.DebugFormat("Create disk type {1} (Named: {0}), on vm {2} {3}", diskName, driveResourceType, vmName, string.IsNullOrEmpty(vhdFile) ? " no disk to insert" : ", inserting disk" +vhdFile ); - AddDiskDriveToVm(newVm, vhdFile, ideCtrllr, driveResourceType); + AddDiskDriveToIdeController(newVm, vhdFile, ideCtrllr, driveResourceType); if (isoPath != null) { AttachIso(vmName, isoPath); @@ -566,16 +566,32 @@ namespace HypervResource public void patchSystemVmIso(String vmName, String systemVmIso) { ComputerSystem vmObject = GetComputerSystem(vmName); - AddDiskDriveToVm(vmObject, "", "1", IDE_ISO_DRIVE); + AddDiskDriveToIdeController(vmObject, "", "1", ISO_DRIVE); AttachIso(vmName, systemVmIso); } + public void AttachDisk(string vmName, string diskPath, string addressOnController) + { + logger.DebugFormat("Got request to attach disk {0} to vm {1}", diskPath, vmName); + + ComputerSystem vm = GetComputerSystem(vmName); + if (vm == null) + { + logger.DebugFormat("VM {0} not found", vmName); + return; + } + else + { + ManagementPath newDrivePath = AttachDiskDriveToScsiController(vm, addressOnController); + InsertDiskImage(vm, diskPath, HARDDISK_DISK, newDrivePath); + } + } /// /// /// /// IDE_HARDDISK_DRIVE or IDE_ISO_DRIVE - public ManagementPath AddDiskDriveToVm(ComputerSystem vm, string vhdfile, string cntrllerAddr, string driveResourceType) + public ManagementPath AddDiskDriveToIdeController(ComputerSystem vm, string vhdfile, string cntrllerAddr, string driveResourceType) { logger.DebugFormat("Creating DISK for VM {0} (GUID {1}) by attaching {2}", vm.ElementName, @@ -585,11 +601,11 @@ namespace HypervResource // Determine disk type for drive and assert drive type valid string diskResourceSubType = null; switch(driveResourceType) { - case IDE_HARDDISK_DRIVE: - diskResourceSubType = IDE_HARDDISK_DISK; + case HARDDISK_DRIVE: + diskResourceSubType = HARDDISK_DISK; break; - case IDE_ISO_DRIVE: - diskResourceSubType = IDE_ISO_DISK; + case ISO_DRIVE: + diskResourceSubType = ISO_DISK; break; default: var errMsg = string.Format( @@ -602,7 +618,7 @@ namespace HypervResource throw ex; } - ManagementPath newDrivePath = AttachNewDriveToVm(vm, cntrllerAddr, driveResourceType); + ManagementPath newDrivePath = AttachNewDrive(vm, cntrllerAddr, driveResourceType); // If there's not disk to insert, we are done. if (String.IsNullOrEmpty(vhdfile)) @@ -629,7 +645,7 @@ namespace HypervResource } else { - RemoveStorageImageFromVm(vm, diskFileName); + RemoveStorageImage(vm, diskFileName); } } @@ -638,7 +654,7 @@ namespace HypervResource /// /// /// - private void RemoveStorageImageFromVm(ComputerSystem vm, string diskFileName) + private void RemoveStorageImage(ComputerSystem vm, string diskFileName) { // Obtain StorageAllocationSettingData for disk StorageAllocationSettingData.StorageAllocationSettingDataCollection storageSettingsObjs = StorageAllocationSettingData.GetInstances(); @@ -674,13 +690,13 @@ namespace HypervResource RemoveStorageResource(imageToRemove.Path, vm); - logger.InfoFormat("REmoved disk image {0} from VM {1} (GUID {2}): the disk image is not attached.", + logger.InfoFormat("Removed disk image {0} from VM {1} (GUID {2}): the disk image is not attached.", diskFileName, vm.ElementName, vm.Name); } - private ManagementPath AttachNewDriveToVm(ComputerSystem vm, string cntrllerAddr, string driveType) + private ManagementPath AttachNewDrive(ComputerSystem vm, string cntrllerAddr, string driveType) { // Disk drives are attached to a 'Parent' IDE controller. We IDE Controller's settings for the 'Path', which our new Disk drive will use to reference it. VirtualSystemSettingData vmSettings = GetVmSettings(vm); @@ -722,7 +738,7 @@ namespace HypervResource return newDrivePaths[0]; } - private ManagementPath AddScsiControllerToVm(ComputerSystem vm) + private ManagementPath AddScsiController(ComputerSystem vm) { // A description of the controller is created by modifying a clone of the default ResourceAllocationSettingData for scsi controller string scsiQuery = String.Format("ResourceSubType LIKE \"{0}\" AND InstanceID LIKE \"%Default\"", SCSI_CONTROLLER); @@ -754,6 +770,55 @@ namespace HypervResource return newResourcePaths[0]; } + private ManagementPath GetDiskDriveToScsiController(ComputerSystem vm, string addrOnController) + { + VirtualSystemSettingData vmSettings = GetVmSettings(vm); + var ctrller = GetScsiControllerSettings(vmSettings); + return null; + } + + private ManagementPath AttachDiskDriveToScsiController(ComputerSystem vm, string addrOnController) + { + // Disk drives are attached to a 'Parent' Scsi controller. + VirtualSystemSettingData vmSettings = GetVmSettings(vm); + var ctrller = GetScsiControllerSettings(vmSettings); + + // A description of the drive is created by modifying a clone of the default ResourceAllocationSettingData for that drive type + string defaultDriveQuery = String.Format("ResourceSubType LIKE \"{0}\" AND InstanceID LIKE \"%Default\"", HARDDISK_DRIVE); + var newDiskDriveSettings = CloneResourceAllocationSetting(defaultDriveQuery); + + // Set IDE controller and address on the controller for the new drive + newDiskDriveSettings.LateBoundObject["Parent"] = ctrller.Path.ToString(); + newDiskDriveSettings.LateBoundObject["AddressOnParent"] = addrOnController; + newDiskDriveSettings.CommitObject(); + + // Add this new disk drive to the VM + logger.DebugFormat("Creating disk drive type {0}, parent IDE controller is {1} and address on controller is {2}", + newDiskDriveSettings.ResourceSubType, + newDiskDriveSettings.Parent, + newDiskDriveSettings.AddressOnParent); + string[] newDriveResource = new string[] { newDiskDriveSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) }; + ManagementPath[] newDrivePaths = AddVirtualResource(newDriveResource, vm); + + // assert + if (newDrivePaths.Length != 1) + { + var errMsg = string.Format( + "Failed to add disk drive type {3} to VM {0} (GUID {1}): number of resource created {2}", + vm.ElementName, + vm.Name, + newDrivePaths.Length, + HARDDISK_DRIVE); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + logger.DebugFormat("New disk drive type {0} WMI path is {1}s", + newDiskDriveSettings.ResourceSubType, + newDrivePaths[0].Path); + return newDrivePaths[0]; + } + private void InsertDiskImage(ComputerSystem vm, string diskImagePath, string diskResourceSubType, ManagementPath drivePath) { @@ -795,16 +860,14 @@ namespace HypervResource /// Create Msvm_StorageAllocationSettingData corresponding to the ISO image, and /// associate this with the VM's DVD drive. /// - private void AttachIsoToVm(ComputerSystem vm, string isoPath) + private void AttachIso(ComputerSystem vm, string isoPath) { // Disk drives are attached to a 'Parent' IDE controller. We IDE Controller's settings for the 'Path', which our new Disk drive will use to reference it. VirtualSystemSettingData vmSettings = GetVmSettings(vm); var driveWmiObj = GetDvdDriveSettings(vmSettings); - InsertDiskImage(vm, isoPath, IDE_ISO_DISK, driveWmiObj.Path); + InsertDiskImage(vm, isoPath, ISO_DISK, driveWmiObj.Path); } - - private static ResourceAllocationSettingData CloneResourceAllocationSetting(string wmiQuery) { var defaultDiskDriveSettingsObjs = ResourceAllocationSettingData.GetInstances(wmiQuery); @@ -834,7 +897,7 @@ namespace HypervResource } else { - AttachIsoToVm(vm, iso); + AttachIso(vm, iso); } } @@ -1827,7 +1890,7 @@ namespace HypervResource foreach (ResourceAllocationSettingData wmiObj in wmiObjCollection) { - if (wmiObj.ResourceSubType == IDE_HARDDISK_CONTROLLER && wmiObj.Address == cntrllerAddr) + if (wmiObj.ResourceSubType == IDE_CONTROLLER && wmiObj.Address == cntrllerAddr) { return wmiObj; } @@ -1842,6 +1905,26 @@ namespace HypervResource throw ex; } + public ResourceAllocationSettingData GetScsiControllerSettings(VirtualSystemSettingData vmSettings) + { + var wmiObjCollection = GetResourceAllocationSettings(vmSettings); + + foreach (ResourceAllocationSettingData wmiObj in wmiObjCollection) + { + if (wmiObj.ResourceSubType == SCSI_CONTROLLER) + { + return wmiObj; + } + } + + var errMsg = string.Format( + "Cannot find the Microsoft Synthetic SCSI Controller in VirtualSystemSettingData {1}", + vmSettings.Path.Path); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + /// /// VM resources, typically hardware a described by a generic MSVM_ResourceAllocationSettingData object. The hardware type being /// described is identified in two ways: in general terms using an enum in the ResourceType field, and in terms of the implementation