From 8fb89cdc8e2dff60d49fecd3e51a9bf997061035 Mon Sep 17 00:00:00 2001 From: Anshul Gangwar Date: Thu, 24 Apr 2014 22:30:12 -0700 Subject: [PATCH] CLOUDSTACK-6504: removed warnings coming in building hyper-v agent code (cherry picked from commit 66f8e0e1b5c81cbfde926c0c65c4d5969767cab9) Conflicts: plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs --- .../HypervResource/CloudStackTypes.cs | 8 +- .../HypervResourceController.cs | 3424 ++++++++--------- .../ServerResource/HypervResource/Utils.cs | 4 +- .../HypervResource/WmiCallsV2.cs | 1141 ++++-- 4 files changed, 2502 insertions(+), 2075 deletions(-) diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs index ef24c79bf80..4516191c014 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs @@ -302,7 +302,7 @@ namespace HypervResource path = Utils.NormalizePath(path); if (Directory.Exists(path)) { - string[] choices = choices = Directory.GetFiles(path, volInfo.uuid + ".vhd*"); + string[] choices = Directory.GetFiles(path, volInfo.uuid + ".vhd*"); if (choices.Length != 1) { String errMsg = "Tried to guess file extension, but cannot find file corresponding to " + @@ -609,6 +609,7 @@ namespace HypervResource public struct VolumeInfo { +#pragma warning disable 0414 public long id; public string type; public string storagePoolType; @@ -618,6 +619,7 @@ namespace HypervResource public string path; long size; string chainInfo; +#pragma warning restore 0414 public VolumeInfo(long id, string type, string poolType, String poolUuid, String name, String mountPoint, String path, long size, String chainInfo) { @@ -635,10 +637,12 @@ namespace HypervResource public class VmState { +#pragma warning disable 0414 [JsonProperty("state")] public String state; [JsonProperty("host")] String host; +#pragma warning restore 0414 public VmState() { } public VmState(String vmState, String host) { @@ -649,6 +653,7 @@ namespace HypervResource public struct StoragePoolInfo { +#pragma warning disable 0414 [JsonProperty("uuid")] public String uuid; [JsonProperty("host")] @@ -667,6 +672,7 @@ namespace HypervResource long availableBytes; [JsonProperty("details")] Dictionary details; +#pragma warning restore 0414 public StoragePoolInfo(String uuid, String host, String hostPath, String localPath, string poolType, long capacityBytes, diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs index 1a46b501f97..41a3a8aa71e 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs @@ -189,149 +189,154 @@ namespace HypervResource using (log4net.NDC.Push(Guid.NewGuid().ToString())) { logger.Info(CloudStackTypes.SetupCommand + Utils.CleanString(cmd.ToString())); - - string details = null; - bool result = false; - - try - { - result = true; - } - catch (Exception sysEx) - { - details = CloudStackTypes.SetupCommand + " failed due to " + sysEx.Message; - logger.Error(details, sysEx); - } - - object ansContent = new - { - result = result, - details = "success - NOP", - _reconnect = false, - contextMap = contextMap - }; - - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.SetupAnswer); - } - } - - // POST api/HypervResource/AttachCommand - [HttpPost] - [ActionName(CloudStackTypes.AttachCommand)] - public JContainer AttachCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.AttachCommand + Utils.CleanString(cmd.ToString())); - - string details = null; - bool result = false; - - try - { - string vmName = (string)cmd.vmName; - DiskTO disk = DiskTO.ParseJson(cmd.disk); - - if (disk.type.Equals("ISO")) - { - TemplateObjectTO dataStore = disk.templateObjectTO; - NFSTO share = dataStore.nfsDataStoreTO; - 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; - 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) - { - details = CloudStackTypes.AttachCommand + " failed due to " + sysEx.Message; - logger.Error(details, sysEx); - } - - object ansContent = new - { - result = result, - details = details, - disk = cmd.disk, - contextMap = contextMap - }; - - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.AttachAnswer); - } - } - - // POST api/HypervResource/DetachCommand - [HttpPost] - [ActionName(CloudStackTypes.DettachCommand)] - public JContainer DetachCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.DettachCommand + Utils.CleanString(cmd.ToString())); - - string details = null; - bool result = false; - - try - { - string vmName = (string)cmd.vmName; - DiskTO disk = DiskTO.ParseJson(cmd.disk); - - if (disk.type.Equals("ISO")) - { - TemplateObjectTO dataStore = disk.templateObjectTO; - NFSTO share = dataStore.nfsDataStoreTO; - 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) - { - details = CloudStackTypes.DettachCommand + " failed due to " + sysEx.Message; - logger.Error(details, sysEx); - } - - object ansContent = new - { - result = result, - details = details, - contextMap = contextMap - }; - - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.DettachAnswer); - } - } - - // POST api/HypervResource/RebootCommand - [HttpPost] - [ActionName(CloudStackTypes.RebootCommand)] - public JContainer RebootCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { + + string details = null; + bool result = false; + + try + { + result = true; + } + catch (Exception sysEx) + { + details = CloudStackTypes.SetupCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + details = "success - NOP", + _reconnect = false, + contextMap = contextMap + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.SetupAnswer); + } + } + + // POST api/HypervResource/AttachCommand + [HttpPost] + [ActionName(CloudStackTypes.AttachCommand)] + public JContainer AttachCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.AttachCommand + Utils.CleanString(cmd.ToString())); + + string details = null; + bool result = false; + + try + { + string vmName = (string)cmd.vmName; + DiskTO disk = DiskTO.ParseJson(cmd.disk); + + if (disk.type.Equals("ISO")) + { + TemplateObjectTO dataStore = disk.templateObjectTO; + NFSTO share = dataStore.nfsDataStoreTO; + Utils.ConnectToRemote(share.UncPath, share.Domain, share.User, share.Password); + 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; + if (!primary.isLocal) + { + 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) + { + details = CloudStackTypes.AttachCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + details = details, + disk = cmd.disk, + contextMap = contextMap + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.AttachAnswer); + } + } + + // POST api/HypervResource/DetachCommand + [HttpPost] + [ActionName(CloudStackTypes.DettachCommand)] + public JContainer DetachCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.DettachCommand + Utils.CleanString(cmd.ToString())); + + string details = null; + bool result = false; + + try + { + string vmName = (string)cmd.vmName; + DiskTO disk = DiskTO.ParseJson(cmd.disk); + + if (disk.type.Equals("ISO")) + { + TemplateObjectTO dataStore = disk.templateObjectTO; + NFSTO share = dataStore.nfsDataStoreTO; + 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; + 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) + { + details = CloudStackTypes.DettachCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + details = details, + contextMap = contextMap + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.DettachAnswer); + } + } + + // POST api/HypervResource/RebootCommand + [HttpPost] + [ActionName(CloudStackTypes.RebootCommand)] + public JContainer RebootCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { logger.Info(CloudStackTypes.RebootCommand + Utils.CleanString(cmd.ToString())); string details = null; @@ -448,1609 +453,344 @@ namespace HypervResource { // Assert String errMsg = "No 'volume' details in " + CloudStackTypes.DestroyCommand + " " + Utils.CleanString(cmd.ToString()); - VolumeObjectTO destVolumeObjectTO = VolumeObjectTO.ParseJson(cmd.data); - if (destVolumeObjectTO.name == null) - { - logger.Error(errMsg); - throw new ArgumentException(errMsg); - } - - String path = destVolumeObjectTO.FullFileName; - if (!File.Exists(path)) - { - logger.Info(CloudStackTypes.DestroyCommand + ", but volume at pass already deleted " + path); - } - - string vmName = (string)cmd.vmName; - if (!string.IsNullOrEmpty(vmName) && File.Exists(path)) - { - // Make sure that this resource is removed from the VM - wmiCallsV2.DetachDisk(vmName, path); - } - - File.Delete(path); - result = true; - } - catch (Exception sysEx) - { - details = CloudStackTypes.DestroyCommand + " failed due to " + sysEx.Message; - logger.Error(details, sysEx); - } - - object ansContent = new - { - result = result, - details = details, - contextMap = contextMap - }; - - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer); - } - } - - - private static JArray ReturnCloudStackTypedJArray(object ansContent, string ansType) - { - JObject ansObj = Utils.CreateCloudStackObject(ansType, ansContent); - JArray answer = new JArray(ansObj); - logger.Info(Utils.CleanString(ansObj.ToString())); - return answer; - } - - // POST api/HypervResource/CreateCommand - [HttpPost] - [ActionName(CloudStackTypes.CreateCommand)] - public JContainer CreateCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.CreateCommand + Utils.CleanString(cmd.ToString())); - - string details = null; - bool result = false; - VolumeInfo volume = new VolumeInfo(); - - try - { - string diskType = cmd.diskCharacteristics.type; - ulong disksize = cmd.diskCharacteristics.size; - string templateUri = cmd.templateUrl; - - // assert: valid storagepool? - string poolTypeStr = cmd.pool.type; - string poolLocalPath = cmd.pool.path; - string poolUuid = cmd.pool.uuid; - string newVolPath = null; - long volId = cmd.volId; - string newVolName = null; - - if (ValidStoragePool(poolTypeStr, poolLocalPath, poolUuid, ref details)) - { - // No template URI? Its a blank disk. - if (string.IsNullOrEmpty(templateUri)) - { - // assert - VolumeType volType; - if (!Enum.TryParse(diskType, out volType) && volType != VolumeType.DATADISK) - { - details = "Cannot create volumes of type " + (string.IsNullOrEmpty(diskType) ? "NULL" : diskType); - } - else - { - newVolName = cmd.diskCharacteristics.name; - newVolPath = Path.Combine(poolLocalPath, newVolName, diskType.ToLower()); - // TODO: make volume format and block size configurable - wmiCallsV2.CreateDynamicVirtualHardDisk(disksize, newVolPath); - if (File.Exists(newVolPath)) - { - result = true; - } - else - { - details = "Failed to create DATADISK with name " + newVolName; - } - } - } - else - { - // TODO: Does this always work, or do I need to download template at times? - if (templateUri.Contains("/") || templateUri.Contains("\\")) - { - details = "Problem with templateURL " + templateUri + - " the URL should be volume UUID in primary storage created by previous PrimaryStorageDownloadCommand"; - logger.Error(details); - } - else - { - logger.Debug("Template's name in primary store should be " + templateUri); - // HypervPhysicalDisk BaseVol = primaryPool.getPhysicalDisk(tmplturl); - FileInfo srcFileInfo = new FileInfo(templateUri); - newVolName = Guid.NewGuid() + srcFileInfo.Extension; - newVolPath = Path.Combine(poolLocalPath, newVolName); - logger.Debug("New volume will be at " + newVolPath); - string oldVolPath = Path.Combine(poolLocalPath, templateUri); - File.Copy(oldVolPath, newVolPath); - if (File.Exists(newVolPath)) - { - result = true; - } - else - { - details = "Failed to create DATADISK with name " + newVolName; - } - } - volume = new VolumeInfo( - volId, diskType, - poolTypeStr, poolUuid, newVolName, - newVolPath, newVolPath, (long)disksize, null); - } - } - } - catch (Exception sysEx) - { - // TODO: consider this as model for error processing in all commands - details = CloudStackTypes.CreateCommand + " failed due to " + sysEx.Message; - logger.Error(details, sysEx); - } - - object ansContent = new - { - result = result, - details = details, - volume = volume, - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CreateAnswer); - } - } - - // POST api/HypervResource/PrimaryStorageDownloadCommand - [HttpPost] - [ActionName(CloudStackTypes.PrimaryStorageDownloadCommand)] - public JContainer PrimaryStorageDownloadCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.PrimaryStorageDownloadCommand + Utils.CleanString(cmd.ToString())); - string details = null; - bool result = false; - long size = 0; - string newCopyFileName = null; - - string poolLocalPath = cmd.localPath; - string poolUuid = cmd.poolUuid; - if (!Directory.Exists(poolLocalPath)) - { - details = "None existent local path " + poolLocalPath; - } - else - { - // Compose name for downloaded file. - string sourceUrl = cmd.url; - if (sourceUrl.ToLower().EndsWith(".vhd")) - { - newCopyFileName = Guid.NewGuid() + ".vhd"; - } - if (sourceUrl.ToLower().EndsWith(".vhdx")) - { - newCopyFileName = Guid.NewGuid() + ".vhdx"; - } - - // assert - if (newCopyFileName == null) - { - details = CloudStackTypes.PrimaryStorageDownloadCommand + " Invalid file extension for hypervisor type in source URL " + sourceUrl; - logger.Error(details); - } - else - { - try - { - FileInfo newFile; - if (CopyURI(sourceUrl, newCopyFileName, poolLocalPath, out newFile, ref details)) - { - size = newFile.Length; - result = true; - } - } - catch (System.Exception ex) - { - details = CloudStackTypes.PrimaryStorageDownloadCommand + " Cannot download source URL " + sourceUrl + " due to " + ex.Message; - logger.Error(details, ex); - } - } - } - - object ansContent = new - { - result = result, - details = details, - templateSize = size, - installPath = newCopyFileName, - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.PrimaryStorageDownloadAnswer); - } - } - - private static bool ValidStoragePool(string poolTypeStr, string poolLocalPath, string poolUuid, ref string details) - { - StoragePoolType poolType; - if (!Enum.TryParse(poolTypeStr, out poolType) || poolType != StoragePoolType.Filesystem) - { - details = "Primary storage pool " + poolUuid + " type " + poolType + " local path " + poolLocalPath + " has invalid StoragePoolType"; - logger.Error(details); - return false; - } - else if (!Directory.Exists(poolLocalPath)) - { - details = "Primary storage pool " + poolUuid + " type " + poolType + " local path " + poolLocalPath + " has invalid local path"; - logger.Error(details); - return false; - } - return true; - } - - /// - /// Exceptions to watch out for: - /// Exceptions related to URI creation - /// System.SystemException - /// +-System.ArgumentNullException - /// +-System.FormatException - /// +-System.UriFormatException - /// - /// Exceptions related to NFS URIs - /// System.SystemException - /// +-System.NotSupportedException - /// +-System.ArgumentException - /// +-System.ArgumentNullException - /// +-System.Security.SecurityException; - /// +-System.UnauthorizedAccessException - /// +-System.IO.IOException - /// +-System.IO.PathTooLongException - /// - /// Exceptions related to HTTP URIs - /// System.SystemException - /// +-System.InvalidOperationException - /// +-System.Net.WebException - /// +-System.NotSupportedException - /// +-System.ArgumentNullException - /// - /// - /// - /// - /// - private bool CopyURI(string sourceUri, string newCopyFileName, string poolLocalPath, out FileInfo newFile, ref string details) - { - Uri source = new Uri(sourceUri); - String destFilePath = Path.Combine(poolLocalPath, newCopyFileName); - string[] pathSegments = source.Segments; - String templateUUIDandExtension = pathSegments[pathSegments.Length - 1]; - newFile = new FileInfo(destFilePath); - - // NFS URI assumed to already be mounted locally. Mount location given by settings. - if (source.Scheme.ToLower().Equals("nfs")) - { - String srcDiskPath = Path.Combine(HypervResourceController.config.LocalSecondaryStoragePath, templateUUIDandExtension); - String taskMsg = "Copy NFS url in " + sourceUri + " at " + srcDiskPath + " to pool " + poolLocalPath; - logger.Debug(taskMsg); - File.Copy(srcDiskPath, destFilePath); - } - else if (source.Scheme.ToLower().Equals("http") || source.Scheme.ToLower().Equals("https")) - { - System.Net.WebClient webclient = new WebClient(); - webclient.DownloadFile(source, destFilePath); - } - else - { - details = "Unsupported URI scheme " + source.Scheme.ToLower() + " in source URI " + sourceUri; - logger.Error(details); - return false; - } - - if (!File.Exists(destFilePath)) - { - details = "Filed to copy " + sourceUri + " to primary pool destination " + destFilePath; - logger.Error(details); - return false; - } - return true; - } - - // POST api/HypervResource/CheckHealthCommand - [HttpPost] - [ActionName(CloudStackTypes.CheckHealthCommand)] - public JContainer CheckHealthCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { + VolumeObjectTO destVolumeObjectTO = VolumeObjectTO.ParseJson(cmd.data); + + if (destVolumeObjectTO.name == null) + { + logger.Error(errMsg); + throw new ArgumentException(errMsg); + } + + String path = destVolumeObjectTO.FullFileName; + if (!File.Exists(path)) + { + logger.Info(CloudStackTypes.DestroyCommand + ", but volume at pass already deleted " + path); + } + + string vmName = (string)cmd.vmName; + if (!string.IsNullOrEmpty(vmName) && File.Exists(path)) + { + // Make sure that this resource is removed from the VM + wmiCallsV2.DetachDisk(vmName, path); + } + + File.Delete(path); + result = true; + } + catch (Exception sysEx) + { + details = CloudStackTypes.DestroyCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + details = details, + contextMap = contextMap + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer); + } + } + + + private static JArray ReturnCloudStackTypedJArray(object ansContent, string ansType) + { + JObject ansObj = Utils.CreateCloudStackObject(ansType, ansContent); + JArray answer = new JArray(ansObj); + logger.Info(Utils.CleanString(ansObj.ToString())); + return answer; + } + + // POST api/HypervResource/CreateCommand + [HttpPost] + [ActionName(CloudStackTypes.CreateCommand)] + public JContainer CreateCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.CreateCommand + Utils.CleanString(cmd.ToString())); + + string details = null; + bool result = false; + VolumeInfo volume = new VolumeInfo(); + + try + { + string diskType = cmd.diskCharacteristics.type; + ulong disksize = cmd.diskCharacteristics.size; + string templateUri = cmd.templateUrl; + + // assert: valid storagepool? + string poolTypeStr = cmd.pool.type; + string poolLocalPath = cmd.pool.path; + string poolUuid = cmd.pool.uuid; + string newVolPath = null; + long volId = cmd.volId; + string newVolName = null; + + if (ValidStoragePool(poolTypeStr, poolLocalPath, poolUuid, ref details)) + { + // No template URI? Its a blank disk. + if (string.IsNullOrEmpty(templateUri)) + { + // assert + VolumeType volType; + if (!Enum.TryParse(diskType, out volType) && volType != VolumeType.DATADISK) + { + details = "Cannot create volumes of type " + (string.IsNullOrEmpty(diskType) ? "NULL" : diskType); + } + else + { + newVolName = cmd.diskCharacteristics.name; + newVolPath = Path.Combine(poolLocalPath, newVolName, diskType.ToLower()); + // TODO: make volume format and block size configurable + wmiCallsV2.CreateDynamicVirtualHardDisk(disksize, newVolPath); + if (File.Exists(newVolPath)) + { + result = true; + } + else + { + details = "Failed to create DATADISK with name " + newVolName; + } + } + } + else + { + // TODO: Does this always work, or do I need to download template at times? + if (templateUri.Contains("/") || templateUri.Contains("\\")) + { + details = "Problem with templateURL " + templateUri + + " the URL should be volume UUID in primary storage created by previous PrimaryStorageDownloadCommand"; + logger.Error(details); + } + else + { + logger.Debug("Template's name in primary store should be " + templateUri); + // HypervPhysicalDisk BaseVol = primaryPool.getPhysicalDisk(tmplturl); + FileInfo srcFileInfo = new FileInfo(templateUri); + newVolName = Guid.NewGuid() + srcFileInfo.Extension; + newVolPath = Path.Combine(poolLocalPath, newVolName); + logger.Debug("New volume will be at " + newVolPath); + string oldVolPath = Path.Combine(poolLocalPath, templateUri); + File.Copy(oldVolPath, newVolPath); + if (File.Exists(newVolPath)) + { + result = true; + } + else + { + details = "Failed to create DATADISK with name " + newVolName; + } + } + volume = new VolumeInfo( + volId, diskType, + poolTypeStr, poolUuid, newVolName, + newVolPath, newVolPath, (long)disksize, null); + } + } + } + catch (Exception sysEx) + { + // TODO: consider this as model for error processing in all commands + details = CloudStackTypes.CreateCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + details = details, + volume = volume, + contextMap = contextMap + }; + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CreateAnswer); + } + } + + // POST api/HypervResource/PrimaryStorageDownloadCommand + [HttpPost] + [ActionName(CloudStackTypes.PrimaryStorageDownloadCommand)] + public JContainer PrimaryStorageDownloadCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.PrimaryStorageDownloadCommand + Utils.CleanString(cmd.ToString())); + string details = null; + bool result = false; + long size = 0; + string newCopyFileName = null; + + string poolLocalPath = cmd.localPath; + if (!Directory.Exists(poolLocalPath)) + { + details = "None existent local path " + poolLocalPath; + } + else + { + // Compose name for downloaded file. + string sourceUrl = cmd.url; + if (sourceUrl.ToLower().EndsWith(".vhd")) + { + newCopyFileName = Guid.NewGuid() + ".vhd"; + } + if (sourceUrl.ToLower().EndsWith(".vhdx")) + { + newCopyFileName = Guid.NewGuid() + ".vhdx"; + } + + // assert + if (newCopyFileName == null) + { + details = CloudStackTypes.PrimaryStorageDownloadCommand + " Invalid file extension for hypervisor type in source URL " + sourceUrl; + logger.Error(details); + } + else + { + try + { + FileInfo newFile; + if (CopyURI(sourceUrl, newCopyFileName, poolLocalPath, out newFile, ref details)) + { + size = newFile.Length; + result = true; + } + } + catch (System.Exception ex) + { + details = CloudStackTypes.PrimaryStorageDownloadCommand + " Cannot download source URL " + sourceUrl + " due to " + ex.Message; + logger.Error(details, ex); + } + } + } + + object ansContent = new + { + result = result, + details = details, + templateSize = size, + installPath = newCopyFileName, + contextMap = contextMap + }; + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.PrimaryStorageDownloadAnswer); + } + } + + private static bool ValidStoragePool(string poolTypeStr, string poolLocalPath, string poolUuid, ref string details) + { + StoragePoolType poolType; + if (!Enum.TryParse(poolTypeStr, out poolType) || poolType != StoragePoolType.Filesystem) + { + details = "Primary storage pool " + poolUuid + " type " + poolType + " local path " + poolLocalPath + " has invalid StoragePoolType"; + logger.Error(details); + return false; + } + else if (!Directory.Exists(poolLocalPath)) + { + details = "Primary storage pool " + poolUuid + " type " + poolType + " local path " + poolLocalPath + " has invalid local path"; + logger.Error(details); + return false; + } + return true; + } + + /// + /// Exceptions to watch out for: + /// Exceptions related to URI creation + /// System.SystemException + /// +-System.ArgumentNullException + /// +-System.FormatException + /// +-System.UriFormatException + /// + /// Exceptions related to NFS URIs + /// System.SystemException + /// +-System.NotSupportedException + /// +-System.ArgumentException + /// +-System.ArgumentNullException + /// +-System.Security.SecurityException; + /// +-System.UnauthorizedAccessException + /// +-System.IO.IOException + /// +-System.IO.PathTooLongException + /// + /// Exceptions related to HTTP URIs + /// System.SystemException + /// +-System.InvalidOperationException + /// +-System.Net.WebException + /// +-System.NotSupportedException + /// +-System.ArgumentNullException + /// + /// + /// + /// + /// + private bool CopyURI(string sourceUri, string newCopyFileName, string poolLocalPath, out FileInfo newFile, ref string details) + { + Uri source = new Uri(sourceUri); + String destFilePath = Path.Combine(poolLocalPath, newCopyFileName); + string[] pathSegments = source.Segments; + String templateUUIDandExtension = pathSegments[pathSegments.Length - 1]; + newFile = new FileInfo(destFilePath); + + // NFS URI assumed to already be mounted locally. Mount location given by settings. + if (source.Scheme.ToLower().Equals("nfs")) + { + String srcDiskPath = Path.Combine(HypervResourceController.config.LocalSecondaryStoragePath, templateUUIDandExtension); + String taskMsg = "Copy NFS url in " + sourceUri + " at " + srcDiskPath + " to pool " + poolLocalPath; + logger.Debug(taskMsg); + File.Copy(srcDiskPath, destFilePath); + } + else if (source.Scheme.ToLower().Equals("http") || source.Scheme.ToLower().Equals("https")) + { + System.Net.WebClient webclient = new WebClient(); + webclient.DownloadFile(source, destFilePath); + } + else + { + details = "Unsupported URI scheme " + source.Scheme.ToLower() + " in source URI " + sourceUri; + logger.Error(details); + return false; + } + + if (!File.Exists(destFilePath)) + { + details = "Filed to copy " + sourceUri + " to primary pool destination " + destFilePath; + logger.Error(details); + return false; + } + return true; + } + + // POST api/HypervResource/CheckHealthCommand + [HttpPost] + [ActionName(CloudStackTypes.CheckHealthCommand)] + public JContainer CheckHealthCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { logger.Info(CloudStackTypes.CheckHealthCommand + Utils.CleanString(cmd.ToString())); - object ansContent = new - { - result = true, - details = "resource is alive", - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CheckHealthAnswer); - } - } - - // POST api/HypervResource/CheckOnHostCommand - [HttpPost] - [ActionName(CloudStackTypes.CheckOnHostCommand)] - public JContainer CheckOnHostCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { + object ansContent = new + { + result = true, + details = "resource is alive", + contextMap = contextMap + }; + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CheckHealthAnswer); + } + } + + // POST api/HypervResource/CheckOnHostCommand + [HttpPost] + [ActionName(CloudStackTypes.CheckOnHostCommand)] + public JContainer CheckOnHostCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { logger.Info(CloudStackTypes.CheckOnHostCommand + Utils.CleanString(cmd.ToString())); string details = "host is not alive"; bool result = true; - try - { - foreach (string poolPath in config.getAllPrimaryStorages()) - { - if (IsHostAlive(poolPath, (string)cmd.host.privateNetwork.ip)) - { - result = false; - details = "host is alive"; - break; - } - } - } - catch (Exception e) - { - logger.Error("Error Occurred in " + CloudStackTypes.CheckOnHostCommand + " : " + e.Message); - } - - object ansContent = new - { - result = result, - details = details, - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CheckOnHostAnswer); - } - } - - private bool IsHostAlive(string poolPath, string privateIp) - { - bool hostAlive = false; - try - { - string hbFile = Path.Combine(poolPath, "hb-" + privateIp); - FileInfo file = new FileInfo(hbFile); - using (StreamReader sr = file.OpenText()) - { - string epoch = sr.ReadLine(); - string[] dateTime = epoch.Split('@'); - string[] date = dateTime[0].Split('-'); - string[] time = dateTime[1].Split(':'); - DateTime epochTime = new DateTime(Convert.ToInt32(date[0]), Convert.ToInt32(date[1]), Convert.ToInt32(date[2]), Convert.ToInt32(time[0]), - Convert.ToInt32(time[1]), Convert.ToInt32(time[2]), DateTimeKind.Utc); - DateTime currentTime = DateTime.UtcNow; - DateTime ThreeMinuteLaterEpoch = epochTime.AddMinutes(3); - if (currentTime.CompareTo(ThreeMinuteLaterEpoch) < 0) - { - hostAlive = true; - } - sr.Close(); - } - } - catch (Exception e) - { - logger.Info("Exception occurred in verifying host " + e.Message); - } - - return hostAlive; - } - - // POST api/HypervResource/CheckSshCommand - // TODO: create test - [HttpPost] - [ActionName(CloudStackTypes.CheckSshCommand)] - public JContainer CheckSshCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.CheckSshCommand + Utils.CleanString(cmd.ToString())); - object ansContent = new - { - result = true, - details = "NOP, TODO: implement properly", - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CheckSshAnswer); - } - } - - // POST api/HypervResource/CheckVirtualMachineCommand - [HttpPost] - [ActionName(CloudStackTypes.CheckVirtualMachineCommand)] - public JContainer CheckVirtualMachineCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.CheckVirtualMachineCommand + Utils.CleanString(cmd.ToString())); - string details = null; - bool result = false; - string vmName = cmd.vmName; - string state = null; - - // TODO: Look up the VM, convert Hyper-V state to CloudStack version. - var sys = wmiCallsV2.GetComputerSystem(vmName); - if (sys == null) - { - details = CloudStackTypes.CheckVirtualMachineCommand + " requested unknown VM " + vmName; - logger.Error(details); - } - else - { - state = EnabledState.ToCloudStackState(sys.EnabledState); // TODO: V2 changes? - result = true; - } - - object ansContent = new - { - result = result, - details = details, - state = state, - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CheckVirtualMachineAnswer); - } - } - - // POST api/HypervResource/DeleteStoragePoolCommand - [HttpPost] - [ActionName(CloudStackTypes.DeleteStoragePoolCommand)] - public JContainer DeleteStoragePoolCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.DeleteStoragePoolCommand + Utils.CleanString(cmd.ToString())); - object ansContent = new - { - result = true, - details = "Current implementation does not delete local path corresponding to storage pool!", - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer); - } - } - - /// - /// NOP - legacy command - - /// POST api/HypervResource/CreateStoragePoolCommand - /// - /// - /// - [HttpPost] - [ActionName(CloudStackTypes.CreateStoragePoolCommand)] - public JContainer CreateStoragePoolCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.CreateStoragePoolCommand + Utils.CleanString(cmd.ToString())); - object ansContent = new - { - result = true, - details = "success - NOP", - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer); - } - } - - // POST api/HypervResource/ModifyStoragePoolCommand - [HttpPost] - [ActionName(CloudStackTypes.ModifyStoragePoolCommand)] - public JContainer ModifyStoragePoolCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.ModifyStoragePoolCommand + Utils.CleanString(cmd.ToString())); - string details = null; - string localPath; - StoragePoolType poolType; - object ansContent; - - bool result = ValidateStoragePoolCommand(cmd, out localPath, out poolType, ref details); - if (!result) - { - ansContent = new - { - result = result, - details = details, - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer); - } - - var tInfo = new Dictionary(); - long capacityBytes = 0; - long availableBytes = 0; - string hostPath = null; - if (poolType == StoragePoolType.Filesystem) - { - GetCapacityForLocalPath(localPath, out capacityBytes, out availableBytes); - hostPath = localPath; - } - else if (poolType == StoragePoolType.NetworkFilesystem || - poolType == StoragePoolType.SMB) - { - NFSTO share = new NFSTO(); - String uriStr = "cifs://" + (string)cmd.pool.host + (string)cmd.pool.path; - share.uri = new Uri(uriStr); - hostPath = Utils.NormalizePath(share.UncPath); - - // Check access to share. - Utils.GetShareDetails(share.UncPath, out capacityBytes, out availableBytes); - config.setPrimaryStorage((string)cmd.pool.uuid, hostPath); - } - else - { - result = false; - } - - String uuid = null; - var poolInfo = new - { - uuid = uuid, - host = cmd.pool.host, - hostPath = cmd.pool.path, - localPath = hostPath, - poolType = cmd.pool.type, - capacityBytes = capacityBytes, - availableBytes = availableBytes - }; - - ansContent = new - { - result = result, - details = details, - localPath = hostPath, - templateInfo = tInfo, - poolInfo = poolInfo, - contextMap = contextMap - }; - - if (result) - { - try - { - if ((bool)cmd.add) - { - logger.Info("Adding HeartBeat Task to task scheduler for pool " + (string)cmd.pool.uuid); - Utils.AddHeartBeatTask((string)cmd.pool.uuid, hostPath, config.PrivateIpAddress); - } - else - { - logger.Info("Deleting HeartBeat Task from task scheduler for pool " + (string)cmd.pool.uuid); - Utils.RemoveHeartBeatTask(cmd.pool.uuid); - } - } - catch (Exception e) - { - logger.Error("Error occurred in adding/delete HeartBeat Task to/from Task Scheduler : " + e.Message); - } - } - - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.ModifyStoragePoolAnswer); - } - } - - private bool ValidateStoragePoolCommand(dynamic cmd, out string localPath, out StoragePoolType poolType, ref string details) - { - dynamic pool = cmd.pool; - string poolTypeStr = pool.type; - localPath = cmd.localPath; - if (!Enum.TryParse(poolTypeStr, out poolType)) - { - details = "Request to create / modify unsupported pool type: " + (poolTypeStr == null ? "NULL" : poolTypeStr) + "in cmd " + JsonConvert.SerializeObject(cmd); - logger.Error(details); - return false; - } - - if (poolType != StoragePoolType.Filesystem && - poolType != StoragePoolType.NetworkFilesystem && - poolType != StoragePoolType.SMB) - { - details = "Request to create / modify unsupported pool type: " + (poolTypeStr == null ? "NULL" : poolTypeStr) + "in cmd " + JsonConvert.SerializeObject(cmd); - logger.Error(details); - return false; - } - - return true; - } - - // POST api/HypervResource/PlugNicCommand - [HttpPost] - [ActionName(CloudStackTypes.PlugNicCommand)] - public JContainer PlugNicCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.PlugNicCommand + Utils.CleanString(cmd.ToString())); - object ansContent = new - { - result = true, - details = "Hot Nic plug not supported, change any empty virtual network adapter network settings", - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.PlugNicAnswer); - } - } - - - // POST api/HypervResource/CleanupNetworkRulesCmd - [HttpPost] - [ActionName(CloudStackTypes.CleanupNetworkRulesCmd)] - public JContainer CleanupNetworkRulesCmd([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.CleanupNetworkRulesCmd + Utils.CleanString(cmd.ToString())); - object ansContent = new - { - result = false, - details = "nothing to cleanup in our current implementation", - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer); - } - } - - // POST api/HypervResource/CheckNetworkCommand - [HttpPost] - [ActionName(CloudStackTypes.CheckNetworkCommand)] - public JContainer CheckNetworkCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.CheckNetworkCommand + Utils.CleanString(cmd.ToString())); - object ansContent = new - { - result = true, - details = (string)null, - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CheckNetworkAnswer); - } - } - - // POST api/HypervResource/ReadyCommand - [HttpPost] - [ActionName(CloudStackTypes.ReadyCommand)] - public JContainer ReadyCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.ReadyCommand + Utils.CleanString(cmd.ToString())); - object ansContent = new - { - result = true, - details = (string)null, - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.ReadyAnswer); - } - - } - - // POST api/HypervResource/StartCommand - [HttpPost] - [ActionName(CloudStackTypes.StartCommand)] - public JContainer StartCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.StartCommand + Utils.CleanString(cmd.ToString())); - string details = null; - bool result = false; - - try - { - string systemVmIsoPath = systemVmIso; - lock (systemVmIso) - { - systemVmIsoPath = systemVmIso; - String uriStr = (String)cmd.secondaryStorage; - if (!String.IsNullOrEmpty(uriStr)) - { - if (String.IsNullOrEmpty(systemVmIsoPath) || !File.Exists(systemVmIsoPath)) - { - NFSTO share = new NFSTO(); - share.uri = new Uri(uriStr); - string defaultDataPath = wmiCallsV2.GetDefaultDataRoot(); - - string secondaryPath = Utils.NormalizePath(Path.Combine(share.UncPath, "systemvm")); - string[] choices = Directory.GetFiles(secondaryPath, "systemvm*.iso"); - if (choices.Length != 1) - { - String errMsg = "Couldn't locate the systemvm iso on " + secondaryPath; - logger.Debug(errMsg); - } - else - { - systemVmIsoPath = Utils.NormalizePath(Path.Combine(defaultDataPath, Path.GetFileName(choices[0]))); - if (!File.Exists(systemVmIsoPath)) - { - Utils.DownloadCifsFileToLocalFile(choices[0], share, systemVmIsoPath); - } - systemVmIso = systemVmIsoPath; - } - } - } - } - - wmiCallsV2.DeployVirtualMachine(cmd, systemVmIsoPath); - result = true; - } - catch (Exception wmiEx) - { - details = CloudStackTypes.StartCommand + " fail on exception" + wmiEx.Message; - logger.Error(details, wmiEx); - } - - object ansContent = new - { - result = result, - details = details, - vm = cmd.vm, - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.StartAnswer); - } - } - - // POST api/HypervResource/StopCommand - [HttpPost] - [ActionName(CloudStackTypes.StopCommand)] - public JContainer StopCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.StopCommand + Utils.CleanString(cmd.ToString())); - string details = null; - bool result = false; - bool checkBeforeCleanup = cmd.checkBeforeCleanup; - String vmName = cmd.vmName; - - if (checkBeforeCleanup == true) - { - ComputerSystem vm = wmiCallsV2.GetComputerSystem(vmName); - if (vm == null || vm.EnabledState == 2) - { - // VM is not available or vm in running state - return ReturnCloudStackTypedJArray(new { result = false, details = "VM is running on host, bailing out", vm = vmName, contextMap = contextMap }, CloudStackTypes.StopAnswer); - } - } - try - { - wmiCallsV2.DestroyVm(cmd); - result = true; - } - catch (Exception wmiEx) - { - details = CloudStackTypes.StopCommand + " fail on exception" + wmiEx.Message; - logger.Error(details, wmiEx); - } - - object ansContent = new - { - result = result, - details = details, - vm = cmd.vm, - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.StopAnswer); - } - } - - // POST api/HypervResource/CreateObjectCommand - [HttpPost] - [ActionName(CloudStackTypes.CreateObjectCommand)] - public JContainer CreateObjectCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.CreateObjectCommand + Utils.CleanString(cmd.ToString())); - - bool result = false; - string details = null; - object newData = null; - try - { - VolumeObjectTO volume = VolumeObjectTO.ParseJson(cmd.data); - PrimaryDataStoreTO primary = volume.primaryDataStore; - ulong volumeSize = volume.size; - string volumeName = volume.uuid + ".vhdx"; - string volumePath = null; - - if (primary.isLocal) - { - volumePath = Path.Combine(primary.Path, volumeName); - } - else - { - volumePath = @"\\" + primary.uri.Host + primary.uri.LocalPath + @"\" + volumeName; - volumePath = Utils.NormalizePath(volumePath); - } - volume.path = volume.uuid; - wmiCallsV2.CreateDynamicVirtualHardDisk(volumeSize, volumePath); - if (File.Exists(volumePath)) - { - result = true; - JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.VolumeObjectTO, volume); - newData = ansObj; - } - else - { - details = "Failed to create disk with name " + volumePath; - } - } - catch (Exception ex) - { - // Test by providing wrong key - details = CloudStackTypes.CreateObjectCommand + " failed on exception, " + ex.Message; - logger.Error(details, ex); - } - - object ansContent = new - { - result = result, - details = details, - data = newData, - contextMap = contextMap - }; - - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CreateObjectAnswer); - } - } - - // POST api/HypervResource/MaintainCommand - // TODO: should this be a NOP? - [HttpPost] - [ActionName(CloudStackTypes.MaintainCommand)] - public JContainer MaintainCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.MaintainCommand + Utils.CleanString(cmd.ToString())); - - object ansContent = new - { - result = true, - willMigrate = true, - details = "success - NOP for MaintainCommand", - _reconnect = false, - contextMap = contextMap - }; - - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.MaintainAnswer); - } - } - - // POST api/HypervResource/PingRoutingCommand - // TODO: should this be a NOP? - [HttpPost] - [ActionName(CloudStackTypes.PingRoutingCommand)] - public JContainer PingRoutingCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.PingRoutingCommand + Utils.CleanString(cmd.ToString())); - - object ansContent = new - { - result = true, - details = "success - NOP for PingRoutingCommand", - _reconnect = false, - contextMap = contextMap - }; - - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer); - } - } - - // POST api/HypervResource/PingCommand - // TODO: should this be a NOP? - [HttpPost] - [ActionName(CloudStackTypes.PingCommand)] - public JContainer PingCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.PingCommand + Utils.CleanString(cmd.ToString())); - - object ansContent = new - { - result = true, - details = "success - NOP for PingCommand", - _reconnect = false, - contextMap = contextMap - }; - - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer); - } - } - - // POST api/HypervResource/ModifyVmVnicVlanCommand - [HttpPost] - [ActionName(CloudStackTypes.ModifyVmNicConfigCommand)] - public JContainer ModifyVmNicConfigCommand([FromBody]dynamic cmd) - { - - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.ModifyVmNicConfigCommand + Utils.CleanString(cmd.ToString())); - bool result = false; - String vmName = cmd.vmName; - String vlan = cmd.vlan; - string macAddress = cmd.macAddress; - uint pos = cmd.index; - bool enable = cmd.enable; - string switchLableName = cmd.switchLableName; - if (macAddress != null) - { - wmiCallsV2.ModifyVmVLan(vmName, vlan, macAddress); - result = true; - } - else if (pos >= 1) - { - wmiCallsV2.ModifyVmVLan(vmName, vlan, pos, enable, switchLableName); - result = true; - } - - object ansContent = new - { - vmName = vmName, - result = result, - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.ModifyVmNicConfigAnswer); - } - - } - - // POST api/HypervResource/GetVmConfigCommand - [HttpPost] - [ActionName(CloudStackTypes.GetVmConfigCommand)] - public JContainer GetVmConfigCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.GetVmConfigCommand + Utils.CleanString(cmd.ToString())); - bool result = false; - String vmName = cmd.vmName; - ComputerSystem vm = wmiCallsV2.GetComputerSystem(vmName); - List nicDetails = new List(); - var nicSettingsViaVm = wmiCallsV2.GetEthernetPortSettings(vm); - NicDetails nic = null; - int index = 0; - int[] nicStates = new int[8]; - int[] nicVlan = new int[8]; - int vlanid = 1; - - var ethernetConnections = wmiCallsV2.GetEthernetConnections(vm); - foreach (EthernetPortAllocationSettingData item in ethernetConnections) - { - EthernetSwitchPortVlanSettingData vlanSettings = wmiCallsV2.GetVlanSettings(item); - if (vlanSettings == null) - { - vlanid = -1; - } - else - { - vlanid = vlanSettings.AccessVlanId; - } - nicStates[index] = (Int32)(item.EnabledState); - nicVlan[index] = vlanid; - index++; - } - - index = 0; - foreach (SyntheticEthernetPortSettingData item in nicSettingsViaVm) - { - nic = new NicDetails(item.Address, nicVlan[index], nicStates[index]); - index++; - nicDetails.Add(nic); - } - - - result = true; - - object ansContent = new - { - vmName = vmName, - nics = nicDetails, - result = result, - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.GetVmConfigAnswer); - } - } - - - - // POST api/HypervResource/GetVmStatsCommand - [HttpPost] - [ActionName(CloudStackTypes.GetVmStatsCommand)] - public JContainer GetVmStatsCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.GetVmStatsCommand + Utils.CleanString(cmd.ToString())); - bool result = false; - JArray vmNamesJson = cmd.vmNames; - string[] vmNames = vmNamesJson.ToObject(); - Dictionary vmProcessorInfo = new Dictionary(vmNames.Length); - - var vmsToInspect = new List(); - foreach (var vmName in vmNames) - { - var sys = wmiCallsV2.GetComputerSystem(vmName); - if (sys == null) - { - logger.InfoFormat("GetVmStatsCommand requested unknown VM {0}", vmNames); - continue; - } - var sysInfo = wmiCallsV2.GetVmSettings(sys); - vmsToInspect.Add(sysInfo.Path); - } - - wmiCallsV2.GetSummaryInfo(vmProcessorInfo, vmsToInspect); - - // TODO: Network usage comes from Performance Counter API; however it is only available in kb/s, and not in total terms. - // Curious about these? Use perfmon to inspect them, e.g. http://msdn.microsoft.com/en-us/library/xhcx5a20%28v=vs.100%29.aspx - // Recent post on these counter at http://blogs.technet.com/b/cedward/archive/2011/07/19/hyper-v-networking-optimizations-part-6-of-6-monitoring-hyper-v-network-consumption.aspx - result = true; - - object ansContent = new - { - vmStatsMap = vmProcessorInfo, - result = result, - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.GetVmStatsAnswer); - } - } - - // POST api/HypervResource/CopyCommand - [HttpPost] - [ActionName(CloudStackTypes.CopyCommand)] - public JContainer CopyCommand(dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - // Log command *after* we've removed security details from the command. - logger.Info(CloudStackTypes.CopyCommand + Utils.CleanString(cmd.ToString())); - - bool result = false; - string details = null; - object newData = null; - TemplateObjectTO destTemplateObjectTO = null; - VolumeObjectTO destVolumeObjectTO = null; - VolumeObjectTO srcVolumeObjectTO = null; - TemplateObjectTO srcTemplateObjectTO = null; - - try - { - dynamic timeout = cmd.wait; // TODO: Useful? - - srcTemplateObjectTO = TemplateObjectTO.ParseJson(cmd.srcTO); - destTemplateObjectTO = TemplateObjectTO.ParseJson(cmd.destTO); - srcVolumeObjectTO = VolumeObjectTO.ParseJson(cmd.srcTO); - destVolumeObjectTO = VolumeObjectTO.ParseJson(cmd.destTO); - - string destFile = null; - if (destTemplateObjectTO != null) - { - if (destTemplateObjectTO.primaryDataStore != null) - { - destFile = destTemplateObjectTO.FullFileName; - } - else if (destTemplateObjectTO.nfsDataStoreTO != null) - { - destFile = destTemplateObjectTO.FullFileName; - } - } - - // Template already downloaded? - if (destFile != null && File.Exists(destFile) && - !String.IsNullOrEmpty(destTemplateObjectTO.checksum)) - { - // TODO: checksum fails us, because it is of the compressed image. - // ASK: should we store the compressed or uncompressed version or is the checksum not calculated correctly? - logger.Debug(CloudStackTypes.CopyCommand + " calling VerifyChecksum to see if we already have the file at " + destFile); - result = VerifyChecksum(destFile, destTemplateObjectTO.checksum); - if (!result) - { - result = true; - logger.Debug(CloudStackTypes.CopyCommand + " existing file has different checksum " + destFile); - } - } - - // Do we have to create a new one? - if (!result) - { - // Create local copy of a template? - if (srcTemplateObjectTO != null && destTemplateObjectTO != null) - { - // S3 download to primary storage? - // NFS provider download to primary storage? - if ((srcTemplateObjectTO.s3DataStoreTO != null || srcTemplateObjectTO.nfsDataStoreTO != null) && destTemplateObjectTO.primaryDataStore != null) - { - if (File.Exists(destFile)) - { - logger.Info("Deleting existing file " + destFile); - File.Delete(destFile); - } - - if (srcTemplateObjectTO.s3DataStoreTO != null) - { - // Download from S3 to destination data storage - DownloadS3ObjectToFile(srcTemplateObjectTO.path, srcTemplateObjectTO.s3DataStoreTO, destFile); - } - else if (srcTemplateObjectTO.nfsDataStoreTO != null) - { - // Download from S3 to destination data storage - Utils.DownloadCifsFileToLocalFile(srcTemplateObjectTO.path, srcTemplateObjectTO.nfsDataStoreTO, destFile); - } - - // Uncompress, as required - if (srcTemplateObjectTO.path.EndsWith(".bz2")) - { - String uncompressedFile = destFile + ".tmp"; - String compressedFile = destFile; - using (var uncompressedOutStrm = new FileStream(uncompressedFile, FileMode.CreateNew, FileAccess.Write)) - { - using (var compressedInStrm = new FileStream(destFile, FileMode.Open, FileAccess.Read)) - { - using (var bz2UncompressorStrm = new Ionic.BZip2.BZip2InputStream(compressedInStrm, true) /* outer 'using' statement will close FileStream*/ ) - { - int count = 0; - int bufsize = 1024 * 1024; - byte[] buf = new byte[bufsize]; - - // EOF returns -1, see http://dotnetzip.codeplex.com/workitem/16069 - while (0 < (count = bz2UncompressorStrm.Read(buf, 0, bufsize))) - { - uncompressedOutStrm.Write(buf, 0, count); - } - } - } - } - File.Delete(compressedFile); - File.Move(uncompressedFile, compressedFile); - if (File.Exists(uncompressedFile)) - { - String errMsg = "Extra file left around called " + uncompressedFile + " when creating " + destFile; - logger.Error(errMsg); - throw new IOException(errMsg); - } - } - - // assert - if (!File.Exists(destFile)) - { - String errMsg = "Failed to create " + destFile + " , because the file is missing"; - logger.Error(errMsg); - throw new IOException(errMsg); - } - - newData = cmd.destTO; - result = true; - } - else - { - details = "Data store combination not supported"; - } - } - // Create volume from a template? - else if (srcTemplateObjectTO != null && destVolumeObjectTO != null) - { - // VolumeObjectTO guesses file extension based on existing files - // this can be wrong if the previous file had a different file type - var guessedDestFile = destVolumeObjectTO.FullFileName; - if (File.Exists(guessedDestFile)) - { - logger.Info("Deleting existing file " + guessedDestFile); - File.Delete(guessedDestFile); - } - - destVolumeObjectTO.format = srcTemplateObjectTO.format; - destFile = destVolumeObjectTO.FullFileName; - if (File.Exists(destFile)) - { - logger.Info("Deleting existing file " + destFile); - File.Delete(destFile); - } - - string srcFile = srcTemplateObjectTO.FullFileName; - if (!File.Exists(srcFile)) - { - details = "Local template file missing from " + srcFile; - } - else - { - // TODO: thin provision instead of copying the full file. - File.Copy(srcFile, destFile); - destVolumeObjectTO.path = destVolumeObjectTO.uuid; - JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.VolumeObjectTO, destVolumeObjectTO); - newData = ansObj; - result = true; - } - } - else if (srcVolumeObjectTO != null && destVolumeObjectTO != null) - { - var guessedDestFile = destVolumeObjectTO.FullFileName; - if (File.Exists(guessedDestFile)) - { - logger.Info("Deleting existing file " + guessedDestFile); - File.Delete(guessedDestFile); - } - - destVolumeObjectTO.format = srcVolumeObjectTO.format; - destFile = destVolumeObjectTO.FullFileName; - if (File.Exists(destFile)) - { - logger.Info("Deleting existing file " + destFile); - File.Delete(destFile); - } - - string srcFile = srcVolumeObjectTO.FullFileName; - if (!File.Exists(srcFile)) - { - details = "Local template file missing from " + srcFile; - } - else - { - // Create the directory before copying the files. CreateDirectory - // doesn't do anything if the directory is already present. - Directory.CreateDirectory(Path.GetDirectoryName(destFile)); - File.Copy(srcFile, destFile); - - if (srcVolumeObjectTO.nfsDataStore != null && srcVolumeObjectTO.primaryDataStore == null) - { - logger.Info("Copied volume from secondary data store to primary. Path: " + destVolumeObjectTO.path); - } - else if (srcVolumeObjectTO.primaryDataStore != null && srcVolumeObjectTO.nfsDataStore == null) - { - destVolumeObjectTO.path = destVolumeObjectTO.path + "/" + destVolumeObjectTO.uuid; - if (destVolumeObjectTO.format != null) - { - destVolumeObjectTO.path += "." + destVolumeObjectTO.format.ToLower(); - } - } - else - { - logger.Error("Destination volume path wasn't set. Unsupported source volume data store."); - } - - // Create volumeto object deserialize and send it - JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.VolumeObjectTO, destVolumeObjectTO); - newData = ansObj; - result = true; - } - } - else if (srcVolumeObjectTO != null && destTemplateObjectTO != null) - { - var guessedDestFile = destTemplateObjectTO.FullFileName; - if (File.Exists(guessedDestFile)) - { - logger.Info("Deleting existing file " + guessedDestFile); - File.Delete(guessedDestFile); - } - - destTemplateObjectTO.format = srcVolumeObjectTO.format; - destFile = destTemplateObjectTO.FullFileName; - if (File.Exists(destFile)) - { - logger.Info("Deleting existing file " + destFile); - File.Delete(destFile); - } - - string srcFile = srcVolumeObjectTO.FullFileName; - if (!File.Exists(srcFile)) - { - details = "Local template file missing from " + srcFile; - } - else - { - // Create the directory before copying the files. CreateDirectory - // doesn't do anything if the directory is already present. - Directory.CreateDirectory(Path.GetDirectoryName(destFile)); - File.Copy(srcFile, destFile); - - FileInfo destFileInfo = new FileInfo(destFile); - // Write the template.properties file - PostCreateTemplate(Path.GetDirectoryName(destFile), destTemplateObjectTO.id, destTemplateObjectTO.name, - destFileInfo.Length.ToString(), srcVolumeObjectTO.size.ToString(), destTemplateObjectTO.format); - - TemplateObjectTO destTemplateObject = new TemplateObjectTO(); - destTemplateObject.size = srcVolumeObjectTO.size.ToString(); - destTemplateObject.format = srcVolumeObjectTO.format; - destTemplateObject.path = destTemplateObjectTO.path + "/" + destTemplateObjectTO.uuid; - if (destTemplateObject.format != null) - { - destTemplateObject.path += "." + destTemplateObject.format.ToLower(); - } - destTemplateObject.nfsDataStoreTO = destTemplateObjectTO.nfsDataStoreTO; - destTemplateObject.checksum = destTemplateObjectTO.checksum; - newData = destTemplateObject; - JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.TemplateObjectTO, destTemplateObject); - newData = ansObj; - result = true; - } - } - else - { - details = "Data store combination not supported"; - } - } - } - catch (Exception ex) - { - // Test by providing wrong key - details = CloudStackTypes.CopyCommand + " failed on exception, " + ex.Message; - logger.Error(details, ex); - } - - object ansContent = new - { - result = result, - details = details, - newData = newData, - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CopyCmdAnswer); - } - } - - private static void PostCreateTemplate(string path, string templateId, string templateUuid, string physicalSize, string virtualSize, string format) - { - string templatePropFile = Path.Combine(path, "template.properties"); - using (StreamWriter sw = new StreamWriter(File.Open(templatePropFile, FileMode.Create), Encoding.GetEncoding("iso-8859-1"))) - { - if (format != null) - { - format = format.ToLower(); - } - - sw.NewLine = "\n"; - sw.WriteLine("id=" + templateId); - sw.WriteLine("filename=" + templateUuid + "." + format); - sw.WriteLine(format + ".filename=" + templateUuid + "." + format); - sw.WriteLine("uniquename=" + templateUuid); - sw.WriteLine(format + "=true"); - sw.WriteLine("virtualsize=" + virtualSize); - sw.WriteLine(format + ".virtualsize=" + virtualSize); - sw.WriteLine("size=" + physicalSize); - sw.WriteLine(format + ".size=" + physicalSize); - sw.WriteLine("public=false"); - } - } - - private static bool VerifyChecksum(string destFile, string checksum) - { - string localChecksum = BitConverter.ToString(CalcFileChecksum(destFile)).Replace("-", "").ToLower(); - logger.Debug("Checksum of " + destFile + " is " + checksum); - if (checksum.Equals(localChecksum)) - { - return true; - } - return false; - } - - /// - /// Match implmentation of DownloadManagerImpl.computeCheckSum - /// - /// - /// - private static byte[] CalcFileChecksum(string destFile) - { - // TODO: Add unit test to verify that checksum algorithm has not changed. - using (MD5 md5 = MD5.Create()) - { - using (FileStream stream = File.OpenRead(destFile)) - { - return md5.ComputeHash(stream); - } - } - } - - private static void DownloadS3ObjectToFile(string srcObjectKey, S3TO srcS3TO, string destFile) - { - AmazonS3Config S3Config = new AmazonS3Config - { - ServiceURL = srcS3TO.endpoint, - CommunicationProtocol = Amazon.S3.Model.Protocol.HTTP - }; - - if (srcS3TO.httpsFlag) - { - S3Config.CommunicationProtocol = Protocol.HTTPS; - } - - try - { - using (AmazonS3 client = Amazon.AWSClientFactory.CreateAmazonS3Client(srcS3TO.accessKey, srcS3TO.secretKey, S3Config)) - { - GetObjectRequest getObjectRequest = new GetObjectRequest().WithBucketName(srcS3TO.bucketName).WithKey(srcObjectKey); - - using (S3Response getObjectResponse = client.GetObject(getObjectRequest)) - { - using (Stream s = getObjectResponse.ResponseStream) - { - using (FileStream fs = new FileStream(destFile, FileMode.Create, FileAccess.Write)) - { - byte[] data = new byte[524288]; - int bytesRead = 0; - do - { - bytesRead = s.Read(data, 0, data.Length); - fs.Write(data, 0, bytesRead); - } - while (bytesRead > 0); - fs.Flush(); - } - } - } - } - } - catch (Exception ex) - { - string errMsg = "Download from S3 url" + srcS3TO.endpoint + " said: " + ex.Message; - logger.Error(errMsg, ex); - throw new Exception(errMsg, ex); - } - } - - // POST api/HypervResource/GetStorageStatsCommand - [HttpPost] - [ActionName(CloudStackTypes.GetStorageStatsCommand)] - public JContainer GetStorageStatsCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.GetStorageStatsCommand + Utils.CleanString(cmd.ToString())); - bool result = false; - string details = null; - long capacity = 0; - long available = 0; - long used = 0; - try - { - StoragePoolType poolType; - string poolId = (string)cmd.id; - string hostPath = null; - if (!Enum.TryParse((string)cmd.pooltype, out poolType)) - { - details = "Request to get unsupported pool type: " + ((string)cmd.pooltype == null ? "NULL" : (string)cmd.pooltype) + "in cmd " + - JsonConvert.SerializeObject(cmd); - logger.Error(details); - } - else if (poolType == StoragePoolType.Filesystem) - { - hostPath = (string)cmd.localPath;; - GetCapacityForLocalPath(hostPath, out capacity, out available); - used = capacity - available; - result = true; - } - else if (poolType == StoragePoolType.NetworkFilesystem || poolType == StoragePoolType.SMB) - { - string sharePath = config.getPrimaryStorage((string)cmd.id); - if (sharePath != null) - { - hostPath = sharePath; - Utils.GetShareDetails(sharePath, out capacity, out available); - used = capacity - available; - result = true; - } - } - else - { - result = false; - } - - if (result) - { - logger.Debug(CloudStackTypes.GetStorageStatsCommand + " set used bytes for " + hostPath + " to " + used); - } - } - catch (Exception ex) - { - details = CloudStackTypes.GetStorageStatsCommand + " failed on exception" + ex.Message; - logger.Error(details, ex); - } - - object ansContent = new - { - result = result, - details = details, - capacity = capacity, - used = used, - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.GetStorageStatsAnswer); - } - } - - // POST api/HypervResource/GetHostStatsCommand - [HttpPost] - [ActionName(CloudStackTypes.GetHostStatsCommand)] - public JContainer GetHostStatsCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.GetHostStatsCommand + Utils.CleanString(cmd.ToString())); - bool result = false; - string details = null; - object hostStats = null; - - var entityType = "host"; - ulong totalMemoryKBs; - ulong freeMemoryKBs; - double networkReadKBs; - double networkWriteKBs; - double cpuUtilization; - - try - { - long hostId = (long)cmd.hostId; - wmiCallsV2.GetMemoryResources(out totalMemoryKBs, out freeMemoryKBs); - wmiCallsV2.GetProcessorUsageInfo(out cpuUtilization); - - // TODO: can we assume that the host has only one adaptor? - string tmp; - var privateNic = GetNicInfoFromIpAddress(config.PrivateIpAddress, out tmp); - var nicStats = privateNic.GetIPv4Statistics(); //TODO: add IPV6 support, currentl - networkReadKBs = nicStats.BytesReceived; - networkWriteKBs = nicStats.BytesSent; - - // Generate GetHostStatsAnswer - hostStats = new - { - hostId = hostId, - entityType = entityType, - cpuUtilization = cpuUtilization, - networkReadKBs = networkReadKBs, - networkWriteKBs = networkWriteKBs, - totalMemoryKBs = (double)totalMemoryKBs, - freeMemoryKBs = (double)freeMemoryKBs - }; - result = true; - } - catch (Exception ex) - { - details = CloudStackTypes.GetHostStatsCommand + " failed on exception" + ex.Message; - logger.Error(details, ex); - } - - object ansContent = new - { - result = result, - hostStats = hostStats, - details = details, - contextMap = contextMap - }; - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.GetHostStatsAnswer); - } - } - - // POST api/HypervResource/PrepareForMigrationCommand - [HttpPost] - [ActionName(CloudStackTypes.PrepareForMigrationCommand)] - public JContainer PrepareForMigrationCommand([FromBody]dynamic cmd) - { - using (log4net.NDC.Push(Guid.NewGuid().ToString())) - { - logger.Info(CloudStackTypes.PrepareForMigrationCommand + Utils.CleanString(cmd.ToString())); - - string details = null; - bool result = true; try { @@ -2147,7 +887,302 @@ namespace HypervResource } } - // POST api/HypervResource/MigrateWithStorageCommand + + // POST api/HypervResource/CleanupNetworkRulesCmd + [HttpPost] + [ActionName(CloudStackTypes.CleanupNetworkRulesCmd)] + public JContainer CleanupNetworkRulesCmd([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.CleanupNetworkRulesCmd + Utils.CleanString(cmd.ToString())); + object ansContent = new + { + result = false, + details = "nothing to cleanup in our current implementation", + contextMap = contextMap + }; + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer); + } + } + + // POST api/HypervResource/CheckNetworkCommand + [HttpPost] + [ActionName(CloudStackTypes.CheckNetworkCommand)] + public JContainer CheckNetworkCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.CheckNetworkCommand + Utils.CleanString(cmd.ToString())); + object ansContent = new + { + result = true, + details = (string)null, + contextMap = contextMap + }; + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CheckNetworkAnswer); + } + } + + // POST api/HypervResource/ReadyCommand + [HttpPost] + [ActionName(CloudStackTypes.ReadyCommand)] + public JContainer ReadyCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.ReadyCommand + Utils.CleanString(cmd.ToString())); + object ansContent = new + { + result = true, + details = (string)null, + contextMap = contextMap + }; + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.ReadyAnswer); + } + + } + + // POST api/HypervResource/StartCommand + [HttpPost] + [ActionName(CloudStackTypes.StartCommand)] + public JContainer StartCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.StartCommand + Utils.CleanString(cmd.ToString())); + string details = null; + bool result = false; + + try + { + string systemVmIsoPath = systemVmIso; + lock (systemVmIso) + { + systemVmIsoPath = systemVmIso; + String uriStr = (String)cmd.secondaryStorage; + if (!String.IsNullOrEmpty(uriStr)) + { + if (String.IsNullOrEmpty(systemVmIsoPath) || !File.Exists(systemVmIsoPath)) + { + NFSTO share = new NFSTO(); + share.uri = new Uri(uriStr); + string defaultDataPath = wmiCallsV2.GetDefaultDataRoot(); + + string secondaryPath = Utils.NormalizePath(Path.Combine(share.UncPath, "systemvm")); + string[] choices = Directory.GetFiles(secondaryPath, "systemvm*.iso"); + if (choices.Length != 1) + { + String errMsg = "Couldn't locate the systemvm iso on " + secondaryPath; + logger.Debug(errMsg); + } + else + { + systemVmIsoPath = Utils.NormalizePath(Path.Combine(defaultDataPath, Path.GetFileName(choices[0]))); + if (!File.Exists(systemVmIsoPath)) + { + Utils.DownloadCifsFileToLocalFile(choices[0], share, systemVmIsoPath); + } + systemVmIso = systemVmIsoPath; + } + } + } + } + + wmiCallsV2.DeployVirtualMachine(cmd, systemVmIsoPath); + result = true; + } + catch (Exception wmiEx) + { + details = CloudStackTypes.StartCommand + " fail on exception" + wmiEx.Message; + logger.Error(details, wmiEx); + } + + object ansContent = new + { + result = result, + details = details, + vm = cmd.vm, + contextMap = contextMap + }; + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.StartAnswer); + } + } + + // POST api/HypervResource/StopCommand + [HttpPost] + [ActionName(CloudStackTypes.StopCommand)] + public JContainer StopCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.StopCommand + Utils.CleanString(cmd.ToString())); + string details = null; + bool result = false; + bool checkBeforeCleanup = cmd.checkBeforeCleanup; + String vmName = cmd.vmName; + + if (checkBeforeCleanup == true) + { + ComputerSystem vm = wmiCallsV2.GetComputerSystem(vmName); + if (vm == null || vm.EnabledState == 2) + { + // VM is not available or vm in running state + return ReturnCloudStackTypedJArray(new { result = false, details = "VM is running on host, bailing out", vm = vmName, contextMap = contextMap }, CloudStackTypes.StopAnswer); + } + } + try + { + wmiCallsV2.DestroyVm(cmd); + result = true; + } + catch (Exception wmiEx) + { + details = CloudStackTypes.StopCommand + " fail on exception" + wmiEx.Message; + logger.Error(details, wmiEx); + } + + object ansContent = new + { + result = result, + details = details, + vm = cmd.vm, + contextMap = contextMap + }; + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.StopAnswer); + } + } + + // POST api/HypervResource/CreateObjectCommand + [HttpPost] + [ActionName(CloudStackTypes.CreateObjectCommand)] + public JContainer CreateObjectCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.CreateObjectCommand + Utils.CleanString(cmd.ToString())); + + bool result = false; + string details = null; + object newData = null; + try + { + VolumeObjectTO volume = VolumeObjectTO.ParseJson(cmd.data); + PrimaryDataStoreTO primary = volume.primaryDataStore; + ulong volumeSize = volume.size; + string volumeName = volume.uuid + ".vhdx"; + string volumePath = null; + + if (primary.isLocal) + { + volumePath = Path.Combine(primary.Path, volumeName); + } + else + { + volumePath = @"\\" + primary.uri.Host + primary.uri.LocalPath + @"\" + volumeName; + volumePath = Utils.NormalizePath(volumePath); + Utils.ConnectToRemote(primary.UncPath, primary.Domain, primary.User, primary.Password); + } + volume.path = volume.uuid; + wmiCallsV2.CreateDynamicVirtualHardDisk(volumeSize, volumePath); + if (File.Exists(volumePath)) + { + result = true; + JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.VolumeObjectTO, volume); + newData = ansObj; + } + else + { + details = "Failed to create disk with name " + volumePath; + } + } + catch (Exception ex) + { + // Test by providing wrong key + details = CloudStackTypes.CreateObjectCommand + " failed on exception, " + ex.Message; + logger.Error(details, ex); + } + + object ansContent = new + { + result = result, + details = details, + data = newData, + contextMap = contextMap + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CreateObjectAnswer); + } + } + + // POST api/HypervResource/MaintainCommand + // TODO: should this be a NOP? + [HttpPost] + [ActionName(CloudStackTypes.MaintainCommand)] + public JContainer MaintainCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.MaintainCommand + Utils.CleanString(cmd.ToString())); + + object ansContent = new + { + result = true, + willMigrate = true, + details = "success - NOP for MaintainCommand", + _reconnect = false, + contextMap = contextMap + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.MaintainAnswer); + } + } + + // POST api/HypervResource/PingRoutingCommand + // TODO: should this be a NOP? + [HttpPost] + [ActionName(CloudStackTypes.PingRoutingCommand)] + public JContainer PingRoutingCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.PingRoutingCommand + Utils.CleanString(cmd.ToString())); + + object ansContent = new + { + result = true, + details = "success - NOP for PingRoutingCommand", + _reconnect = false, + contextMap = contextMap + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer); + } + } + + // POST api/HypervResource/PingCommand + // TODO: should this be a NOP? + [HttpPost] + [ActionName(CloudStackTypes.PingCommand)] + public JContainer PingCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.PingCommand + Utils.CleanString(cmd.ToString())); + + object ansContent = new + { + result = true, + details = "success - NOP for PingCommand", + _reconnect = false, + contextMap = contextMap + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer); + } + } + + // POST api/HypervResource/ModifyVmVnicVlanCommand [HttpPost] [ActionName(CloudStackTypes.MigrateWithStorageCommand)] public JContainer MigrateWithStorageCommand([FromBody]dynamic cmd) @@ -2373,6 +1408,911 @@ namespace HypervResource } } + + + // POST api/HypervResource/GetVmStatsCommand + [HttpPost] + [ActionName(CloudStackTypes.GetVmStatsCommand)] + public JContainer GetVmStatsCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.GetVmStatsCommand + Utils.CleanString(cmd.ToString())); + bool result = false; + JArray vmNamesJson = cmd.vmNames; + string[] vmNames = vmNamesJson.ToObject(); + Dictionary vmProcessorInfo = new Dictionary(vmNames.Length); + + var vmsToInspect = new List(); + foreach (var vmName in vmNames) + { + var sys = wmiCallsV2.GetComputerSystem(vmName); + if (sys == null) + { + logger.InfoFormat("GetVmStatsCommand requested unknown VM {0}", vmNames); + continue; + } + var sysInfo = wmiCallsV2.GetVmSettings(sys); + vmsToInspect.Add(sysInfo.Path); + } + + wmiCallsV2.GetSummaryInfo(vmProcessorInfo, vmsToInspect); + + // TODO: Network usage comes from Performance Counter API; however it is only available in kb/s, and not in total terms. + // Curious about these? Use perfmon to inspect them, e.g. http://msdn.microsoft.com/en-us/library/xhcx5a20%28v=vs.100%29.aspx + // Recent post on these counter at http://blogs.technet.com/b/cedward/archive/2011/07/19/hyper-v-networking-optimizations-part-6-of-6-monitoring-hyper-v-network-consumption.aspx + result = true; + + object ansContent = new + { + vmStatsMap = vmProcessorInfo, + result = result, + contextMap = contextMap + }; + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.GetVmStatsAnswer); + } + } + + // POST api/HypervResource/CopyCommand + [HttpPost] + [ActionName(CloudStackTypes.CopyCommand)] + public JContainer CopyCommand(dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + // Log command *after* we've removed security details from the command. + logger.Info(CloudStackTypes.CopyCommand + Utils.CleanString(cmd.ToString())); + + bool result = false; + string details = null; + object newData = null; + TemplateObjectTO destTemplateObjectTO = null; + VolumeObjectTO destVolumeObjectTO = null; + VolumeObjectTO srcVolumeObjectTO = null; + TemplateObjectTO srcTemplateObjectTO = null; + + try + { + srcTemplateObjectTO = TemplateObjectTO.ParseJson(cmd.srcTO); + destTemplateObjectTO = TemplateObjectTO.ParseJson(cmd.destTO); + srcVolumeObjectTO = VolumeObjectTO.ParseJson(cmd.srcTO); + destVolumeObjectTO = VolumeObjectTO.ParseJson(cmd.destTO); + + string destFile = null; + if (destTemplateObjectTO != null) + { + if (destTemplateObjectTO.primaryDataStore != null) + { + destFile = destTemplateObjectTO.FullFileName; + if (!destTemplateObjectTO.primaryDataStore.isLocal) + { + PrimaryDataStoreTO primary = destTemplateObjectTO.primaryDataStore; + Utils.ConnectToRemote(primary.UncPath, primary.Domain, primary.User, primary.Password); + } + } + else if (destTemplateObjectTO.nfsDataStoreTO != null) + { + destFile = destTemplateObjectTO.FullFileName; + NFSTO store = destTemplateObjectTO.nfsDataStoreTO; + Utils.ConnectToRemote(store.UncPath, store.Domain, store.User, store.Password); + } + } + + // Template already downloaded? + if (destFile != null && File.Exists(destFile) && + !String.IsNullOrEmpty(destTemplateObjectTO.checksum)) + { + // TODO: checksum fails us, because it is of the compressed image. + // ASK: should we store the compressed or uncompressed version or is the checksum not calculated correctly? + logger.Debug(CloudStackTypes.CopyCommand + " calling VerifyChecksum to see if we already have the file at " + destFile); + result = VerifyChecksum(destFile, destTemplateObjectTO.checksum); + if (!result) + { + result = true; + logger.Debug(CloudStackTypes.CopyCommand + " existing file has different checksum " + destFile); + } + } + + // Do we have to create a new one? + if (!result) + { + // Create local copy of a template? + if (srcTemplateObjectTO != null && destTemplateObjectTO != null) + { + // S3 download to primary storage? + // NFS provider download to primary storage? + if ((srcTemplateObjectTO.s3DataStoreTO != null || srcTemplateObjectTO.nfsDataStoreTO != null) && destTemplateObjectTO.primaryDataStore != null) + { + if (File.Exists(destFile)) + { + logger.Info("Deleting existing file " + destFile); + File.Delete(destFile); + } + + if (srcTemplateObjectTO.s3DataStoreTO != null) + { + // Download from S3 to destination data storage + DownloadS3ObjectToFile(srcTemplateObjectTO.path, srcTemplateObjectTO.s3DataStoreTO, destFile); + } + else if (srcTemplateObjectTO.nfsDataStoreTO != null) + { + // Download from S3 to destination data storage + Utils.DownloadCifsFileToLocalFile(srcTemplateObjectTO.path, srcTemplateObjectTO.nfsDataStoreTO, destFile); + } + + // Uncompress, as required + if (srcTemplateObjectTO.path.EndsWith(".bz2")) + { + String uncompressedFile = destFile + ".tmp"; + String compressedFile = destFile; + using (var uncompressedOutStrm = new FileStream(uncompressedFile, FileMode.CreateNew, FileAccess.Write)) + { + using (var compressedInStrm = new FileStream(destFile, FileMode.Open, FileAccess.Read)) + { + using (var bz2UncompressorStrm = new Ionic.BZip2.BZip2InputStream(compressedInStrm, true) /* outer 'using' statement will close FileStream*/ ) + { + int count = 0; + int bufsize = 1024 * 1024; + byte[] buf = new byte[bufsize]; + + // EOF returns -1, see http://dotnetzip.codeplex.com/workitem/16069 + while (0 < (count = bz2UncompressorStrm.Read(buf, 0, bufsize))) + { + uncompressedOutStrm.Write(buf, 0, count); + } + } + } + } + File.Delete(compressedFile); + File.Move(uncompressedFile, compressedFile); + if (File.Exists(uncompressedFile)) + { + String errMsg = "Extra file left around called " + uncompressedFile + " when creating " + destFile; + logger.Error(errMsg); + throw new IOException(errMsg); + } + } + + // assert + if (!File.Exists(destFile)) + { + String errMsg = "Failed to create " + destFile + " , because the file is missing"; + logger.Error(errMsg); + throw new IOException(errMsg); + } + + newData = cmd.destTO; + result = true; + } + else + { + details = "Data store combination not supported"; + } + } + // Create volume from a template? + else if (srcTemplateObjectTO != null && destVolumeObjectTO != null) + { + // VolumeObjectTO guesses file extension based on existing files + // this can be wrong if the previous file had a different file type + var guessedDestFile = destVolumeObjectTO.FullFileName; + if (File.Exists(guessedDestFile)) + { + logger.Info("Deleting existing file " + guessedDestFile); + File.Delete(guessedDestFile); + } + + destVolumeObjectTO.format = srcTemplateObjectTO.format; + destFile = destVolumeObjectTO.FullFileName; + if (File.Exists(destFile)) + { + logger.Info("Deleting existing file " + destFile); + File.Delete(destFile); + } + + string srcFile = srcTemplateObjectTO.FullFileName; + if (!File.Exists(srcFile)) + { + details = "Local template file missing from " + srcFile; + } + else + { + // TODO: thin provision instead of copying the full file. + File.Copy(srcFile, destFile); + destVolumeObjectTO.path = destVolumeObjectTO.uuid; + JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.VolumeObjectTO, destVolumeObjectTO); + newData = ansObj; + result = true; + } + } + else if (srcVolumeObjectTO != null && destVolumeObjectTO != null) + { + var guessedDestFile = destVolumeObjectTO.FullFileName; + if (File.Exists(guessedDestFile)) + { + logger.Info("Deleting existing file " + guessedDestFile); + File.Delete(guessedDestFile); + } + + destVolumeObjectTO.format = srcVolumeObjectTO.format; + destFile = destVolumeObjectTO.FullFileName; + if (File.Exists(destFile)) + { + logger.Info("Deleting existing file " + destFile); + File.Delete(destFile); + } + + string srcFile = srcVolumeObjectTO.FullFileName; + if (!File.Exists(srcFile)) + { + details = "Local template file missing from " + srcFile; + } + else + { + // Create the directory before copying the files. CreateDirectory + // doesn't do anything if the directory is already present. + Directory.CreateDirectory(Path.GetDirectoryName(destFile)); + File.Copy(srcFile, destFile); + + if (srcVolumeObjectTO.nfsDataStore != null && srcVolumeObjectTO.primaryDataStore == null) + { + logger.Info("Copied volume from secondary data store to primary. Path: " + destVolumeObjectTO.path); + } + else if (srcVolumeObjectTO.primaryDataStore != null && srcVolumeObjectTO.nfsDataStore == null) + { + destVolumeObjectTO.path = destVolumeObjectTO.path + "/" + destVolumeObjectTO.uuid; + if (destVolumeObjectTO.format != null) + { + destVolumeObjectTO.path += "." + destVolumeObjectTO.format.ToLower(); + } + } + else + { + logger.Error("Destination volume path wasn't set. Unsupported source volume data store."); + } + + // Create volumeto object deserialize and send it + JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.VolumeObjectTO, destVolumeObjectTO); + newData = ansObj; + result = true; + } + } + else if (srcVolumeObjectTO != null && destTemplateObjectTO != null) + { + var guessedDestFile = destTemplateObjectTO.FullFileName; + if (File.Exists(guessedDestFile)) + { + logger.Info("Deleting existing file " + guessedDestFile); + File.Delete(guessedDestFile); + } + + destTemplateObjectTO.format = srcVolumeObjectTO.format; + destFile = destTemplateObjectTO.FullFileName; + if (File.Exists(destFile)) + { + logger.Info("Deleting existing file " + destFile); + File.Delete(destFile); + } + + string srcFile = srcVolumeObjectTO.FullFileName; + if (!File.Exists(srcFile)) + { + details = "Local template file missing from " + srcFile; + } + else + { + // Create the directory before copying the files. CreateDirectory + // doesn't do anything if the directory is already present. + Directory.CreateDirectory(Path.GetDirectoryName(destFile)); + File.Copy(srcFile, destFile); + + FileInfo destFileInfo = new FileInfo(destFile); + // Write the template.properties file + PostCreateTemplate(Path.GetDirectoryName(destFile), destTemplateObjectTO.id, destTemplateObjectTO.name, + destFileInfo.Length.ToString(), srcVolumeObjectTO.size.ToString(), destTemplateObjectTO.format); + + TemplateObjectTO destTemplateObject = new TemplateObjectTO(); + destTemplateObject.size = srcVolumeObjectTO.size.ToString(); + destTemplateObject.format = srcVolumeObjectTO.format; + destTemplateObject.path = destTemplateObjectTO.path + "/" + destTemplateObjectTO.uuid; + if (destTemplateObject.format != null) + { + destTemplateObject.path += "." + destTemplateObject.format.ToLower(); + } + destTemplateObject.nfsDataStoreTO = destTemplateObjectTO.nfsDataStoreTO; + destTemplateObject.checksum = destTemplateObjectTO.checksum; + newData = destTemplateObject; + JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.TemplateObjectTO, destTemplateObject); + newData = ansObj; + result = true; + } + } + else + { + details = "Data store combination not supported"; + } + } + } + catch (Exception ex) + { + // Test by providing wrong key + details = CloudStackTypes.CopyCommand + " failed on exception, " + ex.Message; + logger.Error(details, ex); + } + + object ansContent = new + { + result = result, + details = details, + newData = newData, + contextMap = contextMap + }; + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CopyCmdAnswer); + } + } + + private static void PostCreateTemplate(string path, string templateId, string templateUuid, string physicalSize, string virtualSize, string format) + { + string templatePropFile = Path.Combine(path, "template.properties"); + using (StreamWriter sw = new StreamWriter(File.Open(templatePropFile, FileMode.Create), Encoding.GetEncoding("iso-8859-1"))) + { + if (format != null) + { + format = format.ToLower(); + } + + sw.NewLine = "\n"; + sw.WriteLine("id=" + templateId); + sw.WriteLine("filename=" + templateUuid + "." + format); + sw.WriteLine(format + ".filename=" + templateUuid + "." + format); + sw.WriteLine("uniquename=" + templateUuid); + sw.WriteLine(format + "=true"); + sw.WriteLine("virtualsize=" + virtualSize); + sw.WriteLine(format + ".virtualsize=" + virtualSize); + sw.WriteLine("size=" + physicalSize); + sw.WriteLine(format + ".size=" + physicalSize); + sw.WriteLine("public=false"); + } + } + + private static bool VerifyChecksum(string destFile, string checksum) + { + string localChecksum = BitConverter.ToString(CalcFileChecksum(destFile)).Replace("-", "").ToLower(); + logger.Debug("Checksum of " + destFile + " is " + checksum); + if (checksum.Equals(localChecksum)) + { + return true; + } + return false; + } + + /// + /// Match implmentation of DownloadManagerImpl.computeCheckSum + /// + /// + /// + private static byte[] CalcFileChecksum(string destFile) + { + // TODO: Add unit test to verify that checksum algorithm has not changed. + using (MD5 md5 = MD5.Create()) + { + using (FileStream stream = File.OpenRead(destFile)) + { + return md5.ComputeHash(stream); + } + } + } + + private static void DownloadS3ObjectToFile(string srcObjectKey, S3TO srcS3TO, string destFile) + { + AmazonS3Config S3Config = new AmazonS3Config + { + ServiceURL = srcS3TO.endpoint, + CommunicationProtocol = Amazon.S3.Model.Protocol.HTTP + }; + + if (srcS3TO.httpsFlag) + { + S3Config.CommunicationProtocol = Protocol.HTTPS; + } + + try + { + using (AmazonS3 client = Amazon.AWSClientFactory.CreateAmazonS3Client(srcS3TO.accessKey, srcS3TO.secretKey, S3Config)) + { + GetObjectRequest getObjectRequest = new GetObjectRequest().WithBucketName(srcS3TO.bucketName).WithKey(srcObjectKey); + + using (S3Response getObjectResponse = client.GetObject(getObjectRequest)) + { + using (Stream s = getObjectResponse.ResponseStream) + { + using (FileStream fs = new FileStream(destFile, FileMode.Create, FileAccess.Write)) + { + byte[] data = new byte[524288]; + int bytesRead = 0; + do + { + bytesRead = s.Read(data, 0, data.Length); + fs.Write(data, 0, bytesRead); + } + while (bytesRead > 0); + fs.Flush(); + } + } + } + } + } + catch (Exception ex) + { + string errMsg = "Download from S3 url" + srcS3TO.endpoint + " said: " + ex.Message; + logger.Error(errMsg, ex); + throw new Exception(errMsg, ex); + } + } + + // POST api/HypervResource/GetStorageStatsCommand + [HttpPost] + [ActionName(CloudStackTypes.GetStorageStatsCommand)] + public JContainer GetStorageStatsCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.GetStorageStatsCommand + Utils.CleanString(cmd.ToString())); + bool result = false; + string details = null; + long capacity = 0; + long available = 0; + long used = 0; + try + { + StoragePoolType poolType; + string hostPath = null; + if (!Enum.TryParse((string)cmd.pooltype, out poolType)) + { + details = "Request to get unsupported pool type: " + ((string)cmd.pooltype == null ? "NULL" : (string)cmd.pooltype) + "in cmd " + + JsonConvert.SerializeObject(cmd); + logger.Error(details); + } + else if (poolType == StoragePoolType.Filesystem) + { + hostPath = (string)cmd.localPath;; + GetCapacityForLocalPath(hostPath, out capacity, out available); + used = capacity - available; + result = true; + } + else if (poolType == StoragePoolType.NetworkFilesystem || poolType == StoragePoolType.SMB) + { + string sharePath = config.getPrimaryStorage((string)cmd.id); + if (sharePath != null) + { + hostPath = sharePath; + Utils.GetShareDetails(sharePath, out capacity, out available); + used = capacity - available; + result = true; + } + } + else + { + result = false; + } + + if (result) + { + logger.Debug(CloudStackTypes.GetStorageStatsCommand + " set used bytes for " + hostPath + " to " + used); + } + } + catch (Exception ex) + { + details = CloudStackTypes.GetStorageStatsCommand + " failed on exception" + ex.Message; + logger.Error(details, ex); + } + + object ansContent = new + { + result = result, + details = details, + capacity = capacity, + used = used, + contextMap = contextMap + }; + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.GetStorageStatsAnswer); + } + } + + // POST api/HypervResource/GetHostStatsCommand + [HttpPost] + [ActionName(CloudStackTypes.GetHostStatsCommand)] + public JContainer GetHostStatsCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.GetHostStatsCommand + Utils.CleanString(cmd.ToString())); + bool result = false; + string details = null; + object hostStats = null; + + var entityType = "host"; + ulong totalMemoryKBs; + ulong freeMemoryKBs; + double networkReadKBs; + double networkWriteKBs; + double cpuUtilization; + + try + { + long hostId = (long)cmd.hostId; + wmiCallsV2.GetMemoryResources(out totalMemoryKBs, out freeMemoryKBs); + wmiCallsV2.GetProcessorUsageInfo(out cpuUtilization); + + // TODO: can we assume that the host has only one adaptor? + string tmp; + var privateNic = GetNicInfoFromIpAddress(config.PrivateIpAddress, out tmp); + var nicStats = privateNic.GetIPv4Statistics(); //TODO: add IPV6 support, currentl + networkReadKBs = nicStats.BytesReceived; + networkWriteKBs = nicStats.BytesSent; + + // Generate GetHostStatsAnswer + hostStats = new + { + hostId = hostId, + entityType = entityType, + cpuUtilization = cpuUtilization, + networkReadKBs = networkReadKBs, + networkWriteKBs = networkWriteKBs, + totalMemoryKBs = (double)totalMemoryKBs, + freeMemoryKBs = (double)freeMemoryKBs + }; + result = true; + } + catch (Exception ex) + { + details = CloudStackTypes.GetHostStatsCommand + " failed on exception" + ex.Message; + logger.Error(details, ex); + } + + object ansContent = new + { + result = result, + hostStats = hostStats, + details = details, + contextMap = contextMap + }; + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.GetHostStatsAnswer); + } + } + + // POST api/HypervResource/PrepareForMigrationCommand + [HttpPost] + [ActionName(CloudStackTypes.PrepareForMigrationCommand)] + public JContainer PrepareForMigrationCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.PrepareForMigrationCommand + Utils.CleanString(cmd.ToString())); + + string details = null; + bool result = true; + + try + { + details = "NOP - success"; + } + catch (Exception sysEx) + { + result = false; + details = CloudStackTypes.PrepareForMigrationCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + details = details, + contextMap = contextMap + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.PrepareForMigrationAnswer); + } + } + + // POST api/HypervResource/MigrateCommand + [HttpPost] + [ActionName(CloudStackTypes.MigrateCommand)] + public JContainer MigrateCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.MigrateCommand + Utils.CleanString(cmd.ToString())); + + string details = null; + bool result = false; + + try + { + string vm = (string)cmd.vmName; + string destination = (string)cmd.destIp; + wmiCallsV2.MigrateVm(vm, destination); + result = true; + } + catch (Exception sysEx) + { + details = CloudStackTypes.MigrateCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + details = details, + contextMap = contextMap + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.MigrateAnswer); + } + } + + // POST api/HypervResource/MigrateVolumeCommand + [HttpPost] + [ActionName(CloudStackTypes.MigrateVolumeCommand)] + public JContainer MigrateVolumeCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.MigrateVolumeCommand + Utils.CleanString(cmd.ToString())); + + string details = null; + bool result = false; + + try + { + string vm = (string)cmd.attachedVmName; + string volume = (string)cmd.volumePath; + wmiCallsV2.MigrateVolume(vm, volume, GetStoragePoolPath(cmd.pool)); + result = true; + } + catch (Exception sysEx) + { + details = CloudStackTypes.MigrateVolumeCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + volumePath = (string)cmd.volumePath, + details = details, + contextMap = contextMap + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.MigrateVolumeAnswer); + } + } + + // POST api/HypervResource/MigrateWithStorageCommand + [HttpPost] + [ActionName(CloudStackTypes.MigrateWithStorageCommand)] + public JContainer MigrateWithStorageCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.MigrateWithStorageCommand + Utils.CleanString(cmd.ToString())); + + string details = null; + bool result = false; + List volumeTos = new List(); + + try + { + string vm = (string)cmd.vm.name; + string destination = (string)cmd.tgtHost; + var volumeToPoolList = cmd.volumeToFilerAsList; + var volumeToPool = new Dictionary(); + foreach (var item in volumeToPoolList) + { + volumeTos.Add(item.t); + string poolPath = GetStoragePoolPath(item.u); + volumeToPool.Add((string)item.t.path, poolPath); + } + + wmiCallsV2.MigrateVmWithVolume(vm, destination, volumeToPool); + result = true; + } + catch (Exception sysEx) + { + details = CloudStackTypes.MigrateWithStorageCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + volumeTos = JArray.FromObject(volumeTos), + details = details, + contextMap = contextMap + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.MigrateWithStorageAnswer); + } + } + + private string GetStoragePoolPath(dynamic pool) + { + string poolTypeStr = pool.type; + StoragePoolType poolType; + if (!Enum.TryParse(poolTypeStr, out poolType)) + { + throw new ArgumentException("Invalid pool type " + poolTypeStr); + } + else if (poolType == StoragePoolType.SMB) + { + NFSTO share = new NFSTO(); + String uriStr = "cifs://" + (string)pool.host + (string)pool.path; + share.uri = new Uri(uriStr); + return Utils.NormalizePath(share.UncPath); + } + else if (poolType == StoragePoolType.Filesystem) + { + return pool.path; + } + + throw new ArgumentException("Couldn't parse path for pool type " + poolTypeStr); + } + + // POST api/HypervResource/StartupCommand + [HttpPost] + [ActionName(CloudStackTypes.StartupCommand)] + public JContainer StartupCommand([FromBody]dynamic cmdArray) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(cmdArray.ToString()); + // Log agent configuration + logger.Info("Agent StartupRoutingCommand received " + cmdArray.ToString()); + dynamic strtRouteCmd = cmdArray[0][CloudStackTypes.StartupRoutingCommand]; + + // Insert networking details + string privateIpAddress = strtRouteCmd.privateIpAddress; + config.PrivateIpAddress = privateIpAddress; + string subnet; + System.Net.NetworkInformation.NetworkInterface privateNic = GetNicInfoFromIpAddress(privateIpAddress, out subnet); + strtRouteCmd.privateIpAddress = privateIpAddress; + strtRouteCmd.privateNetmask = subnet; + strtRouteCmd.privateMacAddress = privateNic.GetPhysicalAddress().ToString(); + string storageip = strtRouteCmd.storageIpAddress; + System.Net.NetworkInformation.NetworkInterface storageNic = GetNicInfoFromIpAddress(storageip, out subnet); + + strtRouteCmd.storageIpAddress = storageip; + strtRouteCmd.storageNetmask = subnet; + strtRouteCmd.storageMacAddress = storageNic.GetPhysicalAddress().ToString(); + strtRouteCmd.gatewayIpAddress = storageNic.GetPhysicalAddress().ToString(); + strtRouteCmd.hypervisorVersion = System.Environment.OSVersion.Version.Major.ToString() + "." + + System.Environment.OSVersion.Version.Minor.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); + details.Add("rdp.server.port", 2179); + } + + // Detect CPUs, speed, memory + uint cores; + uint mhz; + uint sockets; + wmiCallsV2.GetProcessorResources(out sockets, out cores, out mhz); + strtRouteCmd.cpus = cores; + strtRouteCmd.speed = mhz; + strtRouteCmd.cpuSockets = sockets; + ulong memoryKBs; + ulong freeMemoryKBs; + wmiCallsV2.GetMemoryResources(out memoryKBs, out freeMemoryKBs); + strtRouteCmd.memory = memoryKBs * 1024; // Convert to bytes + + // Need 2 Gig for DOM0, see http://technet.microsoft.com/en-us/magazine/hh750394.aspx + strtRouteCmd.dom0MinMemory = config.ParentPartitionMinMemoryMb * 1024 * 1024; // Convert to bytes + + // Insert storage pool details. + // + // Read the localStoragePath for virtual disks from the Hyper-V configuration + // See http://blogs.msdn.com/b/virtual_pc_guy/archive/2010/05/06/managing-the-default-virtual-machine-location-with-hyper-v.aspx + // for discussion of Hyper-V file locations paths. + string localStoragePath = wmiCallsV2.GetDefaultVirtualDiskFolder(); + if (localStoragePath != null) + { + // GUID arbitrary. Host agents deals with storage pool in terms of localStoragePath. + // We use HOST guid. + string poolGuid = strtRouteCmd.guid; + + if (poolGuid == null) + { + poolGuid = Guid.NewGuid().ToString(); + logger.InfoFormat("Setting Startup StoragePool GUID to " + poolGuid); + } + else + { + logger.InfoFormat("Setting Startup StoragePool GUID same as HOST, i.e. " + poolGuid); + } + + long capacity; + long available; + GetCapacityForLocalPath(localStoragePath, out capacity, out available); + + logger.Debug(CloudStackTypes.StartupStorageCommand + " set available bytes to " + available); + + string ipAddr = strtRouteCmd.privateIpAddress; + var vmStates = wmiCallsV2.GetVmSync(config.PrivateIpAddress); + strtRouteCmd.vms = Utils.CreateCloudStackMapObject(vmStates); + + StoragePoolInfo pi = new StoragePoolInfo( + poolGuid.ToString(), + ipAddr, + localStoragePath, + localStoragePath, + StoragePoolType.Filesystem.ToString(), + capacity, + available); + + // Build StartupStorageCommand using an anonymous type + // See http://stackoverflow.com/a/6029228/939250 + object ansContent = new + { + poolInfo = pi, + guid = pi.uuid, + dataCenter = strtRouteCmd.dataCenter, + resourceType = StorageResourceType.STORAGE_POOL.ToString(), // TODO: check encoding + contextMap = contextMap + }; + JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.StartupStorageCommand, ansContent); + cmdArray.Add(ansObj); + } + + // Convert result to array for type correctness? + logger.Info(CloudStackTypes.StartupCommand + " result is " + cmdArray.ToString()); + return cmdArray; + } + } + + // POST api/HypervResource/GetVncPortCommand + [HttpPost] + [ActionName(CloudStackTypes.GetVncPortCommand)] + public JContainer GetVncPortCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.GetVncPortCommand + Utils.CleanString(cmd.ToString())); + + string details = null; + bool result = false; + string address = null; + int port = -9; + + try + { + string vmName = (string)cmd.name; + var sys = wmiCallsV2.GetComputerSystem(vmName); + address = "instanceId=" + sys.Name ; + result = true; + } + catch (Exception sysEx) + { + details = CloudStackTypes.GetVncPortAnswer + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + details = details, + address = address, + port = port + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.GetVncPortAnswer); + } + } + // POST api/HypervResource/HostVmStateReportCommand [HttpPost] [ActionName(CloudStackTypes.HostVmStateReportCommand)] diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs index 07dd78b84b3..b40cb8f783b 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs @@ -1,4 +1,4 @@ -// Licensed to the Apache Software Foundation (ASF) under one +// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file @@ -88,7 +88,7 @@ namespace HypervResource { IntPtr token = IntPtr.Zero; - bool isSuccess = LogonUser(cifsShareDetails.User, cifsShareDetails.Domain, cifsShareDetails.Password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref token); + LogonUser(cifsShareDetails.User, cifsShareDetails.Domain, cifsShareDetails.Password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref token); using (WindowsImpersonationContext remoteIdentity = new WindowsIdentity(token).Impersonate()) { String dest = ""; diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs index 0214ad80f4e..104ee2d5e0d 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs @@ -267,186 +267,180 @@ namespace HypervResource else { errMsg = string.Format("Create VM failing, because there exists a VM with name {0}, state {1}", - vmName, - EnabledState.ToString(vmWmiObj.EnabledState)); - var ex = new WmiException(errMsg); - logger.Error(errMsg, ex); - throw ex; - } - } - - // Create vm carcase - logger.DebugFormat("Going ahead with create VM {0}, {1} vcpus, {2}MB RAM", vmName, vcpus, memSize); - var newVm = CreateVM(vmName, memSize, vcpus); - - // Add a SCSI controller for attaching/detaching data volumes. - AddScsiController(newVm); - - foreach (var diskDrive in diskDrives) - { - string vhdFile = null; - string diskName = null; - string isoPath = null; - VolumeObjectTO volInfo = VolumeObjectTO.ParseJson(diskDrive.data); - TemplateObjectTO templateInfo = TemplateObjectTO.ParseJson(diskDrive.data); - - if (volInfo != null) - { - // assert - errMsg = vmName + ": volume missing primaryDataStore for disk " + diskDrive.ToString(); - if (volInfo.primaryDataStore == null) - { - logger.Error(errMsg); - throw new ArgumentException(errMsg); - } - diskName = volInfo.name; - - // assert - errMsg = vmName + ": can't deal with DataStore type for disk " + diskDrive.ToString(); - if (volInfo.primaryDataStore == null) - { - logger.Error(errMsg); - throw new ArgumentException(errMsg); - } - errMsg = vmName + ": Malformed PrimaryDataStore for disk " + diskDrive.ToString(); - if (String.IsNullOrEmpty(volInfo.primaryDataStore.Path)) - { - logger.Error(errMsg); - throw new ArgumentException(errMsg); - } - errMsg = vmName + ": Missing folder PrimaryDataStore for disk " + diskDrive.ToString() + ", missing path: " + volInfo.primaryDataStore.Path; - if (!Directory.Exists(volInfo.primaryDataStore.Path)) - { - logger.Error(errMsg); - throw new ArgumentException(errMsg); - } - - vhdFile = volInfo.FullFileName; - if (!System.IO.File.Exists(vhdFile)) - { - errMsg = vmName + ": non-existent volume, missing " + vhdFile + " for drive " + diskDrive.ToString(); - logger.Error(errMsg); - throw new ArgumentException(errMsg); - } - logger.Debug("Going to create " + vmName + " with attached voluem " + diskName + " at " + vhdFile); - } - else if (templateInfo != null && templateInfo.nfsDataStoreTO != null) - { - NFSTO share = templateInfo.nfsDataStoreTO; - // The share is mapped, now attach the iso - isoPath = Utils.NormalizePath(Path.Combine(share.UncPath, templateInfo.path)); - } - - string driveType = diskDrive.type; - string ideCtrllr = "0"; - string driveResourceType = null; - switch (driveType) { - case "ROOT": - ideCtrllr = "0"; - driveResourceType = HARDDISK_DRIVE; - break; - case "ISO": - ideCtrllr = "1"; - driveResourceType = ISO_DRIVE; - break; - case "DATADISK": - break; - default: - // TODO: double check exception type - errMsg = string.Format("Unknown disk type {0} for disk {1}, vm named {2}", - string.IsNullOrEmpty(driveType) ? "NULL" : driveType, - string.IsNullOrEmpty(diskName) ? "NULL" : diskName, vmName); - var ex = new WmiException(errMsg); - logger.Error(errMsg, ex); - throw ex; - } - - 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); - if (driveType.Equals("DATADISK")) - { - AttachDisk(vmName, vhdFile, (string)diskDrive.diskSeq); - } - else - { - AddDiskDriveToIdeController(newVm, vhdFile, ideCtrllr, driveResourceType); - if (isoPath != null) - { - AttachIso(vmName, isoPath); - } - } - } - - String publicIpAddress = ""; - int nicCount = 0; - int enableState = 2; - - // Add the Nics to the VM in the deviceId order. - foreach (var nc in nicInfo) - { - foreach (var nic in nicInfo) - { - - int nicid = nic.deviceId; + vmName, + EnabledState.ToString(vmWmiObj.EnabledState)); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + } + + // Create vm carcase + logger.DebugFormat("Going ahead with create VM {0}, {1} vcpus, {2}MB RAM", vmName, vcpus, memSize); + var newVm = CreateVM(vmName, memSize, vcpus); + + // Add a SCSI controller for attaching/detaching data volumes. + AddScsiController(newVm); + + foreach (var diskDrive in diskDrives) + { + string vhdFile = null; + string diskName = null; + string isoPath = null; + VolumeObjectTO volInfo = VolumeObjectTO.ParseJson(diskDrive.data); + TemplateObjectTO templateInfo = TemplateObjectTO.ParseJson(diskDrive.data); + + if (volInfo != null) + { + // assert + errMsg = vmName + ": volume missing primaryDataStore for disk " + diskDrive.ToString(); + if (volInfo.primaryDataStore == null) + { + logger.Error(errMsg); + throw new ArgumentException(errMsg); + } + diskName = volInfo.name; + + // assert + errMsg = vmName + ": can't deal with DataStore type for disk " + diskDrive.ToString(); + if (volInfo.primaryDataStore == null) + { + logger.Error(errMsg); + throw new ArgumentException(errMsg); + } + errMsg = vmName + ": Malformed PrimaryDataStore for disk " + diskDrive.ToString(); + if (String.IsNullOrEmpty(volInfo.primaryDataStore.Path)) + { + logger.Error(errMsg); + throw new ArgumentException(errMsg); + } + errMsg = vmName + ": Missing folder PrimaryDataStore for disk " + diskDrive.ToString() + ", missing path: " + volInfo.primaryDataStore.Path; + if (!Directory.Exists(volInfo.primaryDataStore.Path)) + { + logger.Error(errMsg); + throw new ArgumentException(errMsg); + } + + vhdFile = volInfo.FullFileName; + if (!System.IO.File.Exists(vhdFile)) + { + errMsg = vmName + ": non-existent volume, missing " + vhdFile + " for drive " + diskDrive.ToString(); + logger.Error(errMsg); + throw new ArgumentException(errMsg); + } + logger.Debug("Going to create " + vmName + " with attached voluem " + diskName + " at " + vhdFile); + } + else if (templateInfo != null && templateInfo.nfsDataStoreTO != null) + { + NFSTO share = templateInfo.nfsDataStoreTO; + Utils.ConnectToRemote(share.UncPath, share.Domain, share.User, share.Password); + // The share is mapped, now attach the iso + isoPath = Utils.NormalizePath(Path.Combine(share.UncPath, templateInfo.path)); + } + + string driveType = diskDrive.type; + string ideCtrllr = "0"; + string driveResourceType = null; + switch (driveType) { + case "ROOT": + ideCtrllr = "0"; + driveResourceType = HARDDISK_DRIVE; + break; + case "ISO": + ideCtrllr = "1"; + driveResourceType = ISO_DRIVE; + break; + case "DATADISK": + break; + default: + // TODO: double check exception type + errMsg = string.Format("Unknown disk type {0} for disk {1}, vm named {2}", + string.IsNullOrEmpty(driveType) ? "NULL" : driveType, + string.IsNullOrEmpty(diskName) ? "NULL" : diskName, vmName); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + + 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); + if (driveType.Equals("DATADISK")) + { + AttachDisk(vmName, vhdFile, (string)diskDrive.diskSeq); + } + else + { + AddDiskDriveToIdeController(newVm, vhdFile, ideCtrllr, driveResourceType); + if (isoPath != null) + { + AttachIso(vmName, isoPath); + } + } + } + + int nicCount = 0; + // Add the Nics to the VM in the deviceId order. + foreach (var nc in nicInfo) + { + foreach (var nic in nicInfo) + { + + int nicid = nic.deviceId; Int32 networkRateMbps = nic.networkRateMbps; - string mac = nic.mac; - string vlan = null; - string isolationUri = nic.isolationUri; - string broadcastUri = nic.broadcastUri; + string mac = nic.mac; + string vlan = null; + string isolationUri = nic.isolationUri; + string broadcastUri = nic.broadcastUri; string nicIp = nic.ip; string nicNetmask = nic.netmask; - if ( (broadcastUri != null ) || (isolationUri != null && isolationUri.StartsWith("vlan://"))) - { - if (broadcastUri != null && broadcastUri.StartsWith("storage")) - { - vlan = broadcastUri.Substring("storage://".Length); - } - else - { - vlan = isolationUri.Substring("vlan://".Length); - } - int tmp; + if ( (broadcastUri != null ) || (isolationUri != null && isolationUri.StartsWith("vlan://"))) + { + if (broadcastUri != null && broadcastUri.StartsWith("storage")) + { + vlan = broadcastUri.Substring("storage://".Length); + } + else + { + vlan = isolationUri.Substring("vlan://".Length); + } + int tmp; if (vlan.Equals("untagged", StringComparison.CurrentCultureIgnoreCase) ) { // recevied vlan is untagged, don't parse for the vlan in the isolation uri vlan = null; } - else if (!int.TryParse(vlan, out tmp)) - { - // TODO: double check exception type - errMsg = string.Format("Invalid VLAN value {0} for on vm {1} for nic uuid {2}", isolationUri, vmName, nic.uuid); - var ex = new WmiException(errMsg); - logger.Error(errMsg, ex); - throw ex; - } + else if (!int.TryParse(vlan, out tmp)) + { + // TODO: double check exception type + errMsg = string.Format("Invalid VLAN value {0} for on vm {1} for nic uuid {2}", isolationUri, vmName, nic.uuid); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + } + if(nicIp.Equals("0.0.0.0") && nicNetmask.Equals("255.255.255.255") ) { + // this is the extra nic added to VR. + vlan = defaultvlan; } - - if (nicid == nicCount) - { - if (nicIp.Equals("0.0.0.0") && nicNetmask.Equals("255.255.255.255")) - { - // this is the extra nic added to VR. - vlan = null; - enableState = 3; - } - - // Create network adapter - var newAdapter = CreateNICforVm(newVm, mac); - String switchName =""; - if (nic.name != null) - { - switchName = nic.name; - } - EthernetPortAllocationSettingData portSettings = null; - // connection to vswitch - portSettings = AttachNicToPort(newVm, newAdapter, switchName, enableState); - //reset the flag for other nics - enableState = 2; - // set vlan - if (vlan != null) - { - SetPortVlan(vlan, portSettings); - } - + + if (nicid == nicCount) + { + // Create network adapter + var newAdapter = CreateNICforVm(newVm, mac); + String switchName =""; + if (nic.name != null) + { + switchName = nic.name; + } + + // connection to vswitch + var portSettings = AttachNicToPort(newVm, newAdapter, switchName); + + // set vlan + if (vlan != null) + { + SetPortVlan(vlan, portSettings); + } + if (networkRateMbps > 0) { SetBandWidthLimit((ulong)networkRateMbps, portSettings); @@ -486,6 +480,433 @@ namespace HypervResource SetState(newVm, RequiredState.Enabled); // Mark the VM as created by cloudstack tag TagVm(newVm); + + // we need to reboot to get the hv kvp daemon get started vr gets configured. + if (vmName.StartsWith("r-") || vmName.StartsWith("s-") || vmName.StartsWith("v-")) + { + System.Threading.Thread.Sleep(90000); + } + logger.InfoFormat("Started VM {0}", vmName); + return newVm; + } + + public static Boolean pingResource(String ip) + { + PingOptions pingOptions = null; + PingReply pingReply = null; + IPAddress ipAddress = null; + Ping pingSender = new Ping(); + int numberOfPings = 6; + int pingTimeout = 1000; + int byteSize = 32; + byte[] buffer = new byte[byteSize]; + ipAddress = IPAddress.Parse(ip); + pingOptions = new PingOptions(); + for (int i = 0; i < numberOfPings; i++) + { + pingReply = pingSender.Send(ipAddress, pingTimeout, buffer, pingOptions); + if (pingReply.Status == IPStatus.Success) + { + System.Threading.Thread.Sleep(30000); + return true; + } + else + { + // wait for the second boot and then return with suces + System.Threading.Thread.Sleep(30000); + } + } + return false; + } + + private EthernetPortAllocationSettingData AttachNicToPort(ComputerSystem newVm, SyntheticEthernetPortSettingData newAdapter, String vSwitchName) + { + // Get the virtual switch + VirtualEthernetSwitch vSwitch = GetExternalVirtSwitch(vSwitchName); + //check the the recevied vSwitch is the same as vSwitchName. + if (!vSwitchName.Equals("") && !vSwitch.ElementName.Equals(vSwitchName)) + { + var errMsg = string.Format("Internal error, coudl not find Virtual Switch with the name : " +vSwitchName); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + + // Create port for adapter + var defaultEthernetPortSettings = EthernetPortAllocationSettingData.GetInstances(vSwitch.Scope, "InstanceID LIKE \"%Default\""); + + // assert + if (defaultEthernetPortSettings.Count != 1) + { + var errMsg = string.Format("Internal error, coudl not find default EthernetPortAllocationSettingData instance"); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + + var defaultEthernetPortSettingsObj = defaultEthernetPortSettings.OfType().First(); + var newEthernetPortSettings = new EthernetPortAllocationSettingData((ManagementBaseObject)defaultEthernetPortSettingsObj.LateBoundObject.Clone()); + newEthernetPortSettings.LateBoundObject["Parent"] = newAdapter.Path.Path; + newEthernetPortSettings.LateBoundObject["HostResource"] = new string[] { vSwitch.Path.Path }; + + // Insert NIC into vm + string[] newResources = new string[] { newEthernetPortSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) }; + ManagementPath[] newResourcePaths = AddVirtualResource(newResources, newVm); + + // assert + if (newResourcePaths.Length != 1) + { + var errMsg = string.Format( + "Failed to properly insert a single NIC on VM {0} (GUID {1}): number of resource created {2}", + newVm.ElementName, + newVm.Name, + newResourcePaths.Length); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + + return new EthernetPortAllocationSettingData(newResourcePaths[0]); + } + + /// this method is to add a dvd drive and attach the systemvm iso. + /// + public void patchSystemVmIso(String vmName, String systemVmIso) + { + ComputerSystem vmObject = GetComputerSystem(vmName); + 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 = GetDiskDriveOnScsiController(vm, addressOnController); + if (newDrivePath == null) + { + newDrivePath = AttachDiskDriveToScsiController(vm, addressOnController); + } + InsertDiskImage(vm, diskPath, HARDDISK_DISK, newDrivePath); + } + } + + /// + /// + /// + /// IDE_HARDDISK_DRIVE or IDE_ISO_DRIVE + 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, + vm.Name, + vhdfile); + + // Determine disk type for drive and assert drive type valid + string diskResourceSubType = null; + switch(driveResourceType) { + case HARDDISK_DRIVE: + diskResourceSubType = HARDDISK_DISK; + break; + case ISO_DRIVE: + diskResourceSubType = ISO_DISK; + break; + default: + var errMsg = string.Format( + "Unrecognised disk drive type {0} for VM {1} (GUID {2})", + string.IsNullOrEmpty(driveResourceType) ? "NULL": driveResourceType, + vm.ElementName, + vm.Name); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + + ManagementPath newDrivePath = AttachNewDrive(vm, cntrllerAddr, driveResourceType); + + // If there's not disk to insert, we are done. + if (String.IsNullOrEmpty(vhdfile)) + { + logger.DebugFormat("No disk to be added to drive, disk drive {0} is complete", newDrivePath.Path); + } + else + { + InsertDiskImage(vm, vhdfile, diskResourceSubType, newDrivePath); + } + return newDrivePath; + } + + + public void DetachDisk(string displayName, string diskFileName) + { + logger.DebugFormat("Got request to detach virtual disk {0} from vm {1}", diskFileName, displayName); + + ComputerSystem vm = GetComputerSystem(displayName); + if (vm == null) + { + logger.DebugFormat("VM {0} not found", displayName); + return; + } + else + { + RemoveStorageImage(vm, diskFileName); + } + } + + /// + /// Removes a disk image from a drive, but does not remove the drive itself. + /// + /// + /// + private void RemoveStorageImage(ComputerSystem vm, string diskFileName) + { + // Obtain StorageAllocationSettingData for disk + StorageAllocationSettingData.StorageAllocationSettingDataCollection storageSettingsObjs = StorageAllocationSettingData.GetInstances(); + + StorageAllocationSettingData imageToRemove = null; + foreach (StorageAllocationSettingData item in storageSettingsObjs) + { + if (item.HostResource == null || item.HostResource.Length != 1) + { + continue; + } + + string hostResource = item.HostResource[0]; + if (Path.Equals(hostResource, diskFileName)) + { + imageToRemove = item; + break; + } + } + + // assert + if (imageToRemove == null) + { + var errMsg = string.Format( + "Failed to remove disk image {0} from VM {1} (GUID {2}): the disk image is not attached.", + diskFileName, + vm.ElementName, + vm.Name); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + + RemoveStorageResource(imageToRemove.Path, vm); + + logger.InfoFormat("Removed disk image {0} from VM {1} (GUID {2}): the disk image is not attached.", + diskFileName, + vm.ElementName, + vm.Name); + } + + 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); + var ctrller = GetIDEControllerSettings(vmSettings, cntrllerAddr); + + // 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\"", driveType); + 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"] = "0"; + 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, + driveType); + 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 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); + var scsiSettings = CloneResourceAllocationSetting(scsiQuery); + + scsiSettings.LateBoundObject["ElementName"] = "SCSI Controller"; + scsiSettings.CommitObject(); + + // Insert SCSI controller into vm + string[] newResources = new string[] { scsiSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) }; + ManagementPath[] newResourcePaths = AddVirtualResource(newResources, vm); + + // assert + if (newResourcePaths.Length != 1) + { + var errMsg = string.Format( + "Failed to add scsi controller to VM {0} (GUID {1}): number of resource created {2}", + vm.ElementName, + vm.Name, + newResourcePaths.Length); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + + logger.DebugFormat("New controller type {0} WMI path is {1}s", + scsiSettings.ResourceSubType, + newResourcePaths[0].Path); + return newResourcePaths[0]; + } + + private ManagementPath GetDiskDriveOnScsiController(ComputerSystem vm, string addrOnController) + { + VirtualSystemSettingData vmSettings = GetVmSettings(vm); + var wmiObjCollection = GetResourceAllocationSettings(vmSettings); + foreach (ResourceAllocationSettingData wmiObj in wmiObjCollection) + { + if (wmiObj.ResourceSubType == HARDDISK_DRIVE) + { + ResourceAllocationSettingData parent = new ResourceAllocationSettingData(new ManagementObject(wmiObj.Parent)); + if (parent.ResourceSubType == SCSI_CONTROLLER && wmiObj.AddressOnParent == addrOnController) + { + return wmiObj.Path; + } + } + } + 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) + { + // A description of the disk is created by modifying a clone of the default ResourceAllocationSettingData for that disk type + string defaultDiskQuery = String.Format("ResourceSubType LIKE \"{0}\" AND InstanceID LIKE \"%Default\"", diskResourceSubType); + var newDiskSettings = CloneStorageAllocationSetting(defaultDiskQuery); + + // Set file containing the disk image + newDiskSettings.LateBoundObject["Parent"] = drivePath.Path; + + // V2 API uses HostResource to specify image, see http://msdn.microsoft.com/en-us/library/hh859775(v=vs.85).aspx + newDiskSettings.LateBoundObject["HostResource"] = new string[] { diskImagePath }; + newDiskSettings.CommitObject(); + + // Add the new Msvm_StorageAllocationSettingData object as a virtual hard disk to the vm. + string[] newDiskResource = new string[] { newDiskSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) }; + ManagementPath[] newDiskPaths = AddStorageResource(newDiskResource, vm); + // assert + if (newDiskPaths.Length != 1) + { + var errMsg = string.Format( + "Failed to add disk image type {3} to VM {0} (GUID {1}): number of resource created {2}", + vm.ElementName, + vm.Name, + newDiskPaths.Length, + diskResourceSubType); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + logger.InfoFormat("Created disk {2} for VM {0} (GUID {1}), image {3} ", + vm.ElementName, + vm.Name, + newDiskPaths[0].Path, + diskImagePath); + } + + /// + /// Create Msvm_StorageAllocationSettingData corresponding to the ISO image, and + /// associate this with the VM's DVD drive. + /// + 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, ISO_DISK, driveWmiObj.Path); + } + + private static ResourceAllocationSettingData CloneResourceAllocationSetting(string wmiQuery) + { + var defaultDiskDriveSettingsObjs = ResourceAllocationSettingData.GetInstances(wmiQuery); + + // assert + if (defaultDiskDriveSettingsObjs.Count != 1) + { + var errMsg = string.Format("Failed to find Msvm_ResourceAllocationSettingData for the query {0}", wmiQuery); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + + ResourceAllocationSettingData defaultDiskDriveSettings = defaultDiskDriveSettingsObjs.OfType().First(); + return new ResourceAllocationSettingData((ManagementBaseObject)defaultDiskDriveSettings.LateBoundObject.Clone()); + } // we need to reboot to get the hv kvp daemon get started vr gets configured. if (vmName.StartsWith("r-") || vmName.StartsWith("s-") || vmName.StartsWith("v-")) @@ -2024,13 +2445,9 @@ namespace HypervResource public VirtualEthernetSwitchManagementService GetVirtualSwitchManagementService() { - // VirtualSwitchManagementService is a singleton, most anonymous way of lookup is by asking for the set - // of local instances, which should be size 1. - var virtSwtichSvcCollection = VirtualEthernetSwitchManagementService.GetInstances(); - foreach (VirtualEthernetSwitchManagementService item in virtSwtichSvcCollection) - { - return item; - } + ComputerSystem vm = GetComputerSystem(vmName); + // Obtain controller for Hyper-V virtualisation subsystem + VirtualSystemManagementService vmMgmtSvc = GetVirtualisationSystemManagementService(); var errMsg = string.Format("No Hyper-V subsystem on server"); var ex = new WmiException(errMsg); @@ -2221,157 +2638,221 @@ namespace HypervResource } } - public void GetProcessorResources(out uint sockets, out uint cores, out uint mhz) - { - // Processor processors - cores = 0; - mhz = 0; - sockets = 0; - Processor.ProcessorCollection procCol = Processor.GetInstances(); - foreach (Processor procInfo in procCol) - { - cores += procInfo.NumberOfCores; - mhz = procInfo.MaxClockSpeed; - sockets++; - } - } - - public void GetProcessorUsageInfo(out double cpuUtilization) - { - PerfFormattedData_Counters_ProcessorInformation.PerfFormattedData_Counters_ProcessorInformationCollection coll = - PerfFormattedData_Counters_ProcessorInformation.GetInstances("Name=\"_Total\""); - cpuUtilization = 100; - // Use the first one - foreach (PerfFormattedData_Counters_ProcessorInformation procInfo in coll) - { - // Idle during a given internal - // See http://library.wmifun.net/cimv2/win32_perfformatteddata_counters_processorinformation.html - cpuUtilization = 100.0 - (double)procInfo.PercentIdleTime; - } - } - - - public void GetMemoryResources(out ulong physicalRamKBs, out ulong freeMemoryKBs) - { - OperatingSystem0 os = new OperatingSystem0(); - physicalRamKBs = os.TotalVisibleMemorySize; - freeMemoryKBs = os.FreePhysicalMemory; - } - - public string GetDefaultVirtualDiskFolder() - { - VirtualSystemManagementServiceSettingData.VirtualSystemManagementServiceSettingDataCollection coll = VirtualSystemManagementServiceSettingData.GetInstances(); - string defaultVirtualHardDiskPath = null; - foreach (VirtualSystemManagementServiceSettingData settings in coll) - { - defaultVirtualHardDiskPath = settings.DefaultVirtualHardDiskPath; - } - - // assert - if (!System.IO.Directory.Exists(defaultVirtualHardDiskPath) ){ - var errMsg = string.Format( - "Hyper-V DefaultVirtualHardDiskPath is invalid!"); - logger.Error(errMsg); - return null; - } - - return defaultVirtualHardDiskPath; - } - - public ComputerSystem GetComputerSystem(string displayName) - { - var wmiQuery = String.Format("ElementName=\"{0}\"", displayName); - ComputerSystem.ComputerSystemCollection vmCollection = ComputerSystem.GetInstances(wmiQuery); - - // Return the first one - foreach (ComputerSystem vm in vmCollection) - { - return vm; - } - return null; - } - - public ComputerSystem.ComputerSystemCollection GetComputerSystemCollection() - { - var wmiQuery = String.Format("Caption=\"Virtual Machine\""); - return ComputerSystem.GetInstances(wmiQuery); - } - - public Dictionary GetVmSync(String privateIpAddress) - { - List vms = GetVmElementNames(); - Dictionary vmSyncStates = new Dictionary(); - String vmState; - foreach (String vm in vms) - { - vmState = EnabledState.ToCloudStackState(GetComputerSystem(vm).EnabledState); - vmSyncStates.Add(vm, new VmState(vmState, privateIpAddress)); - } - return vmSyncStates; - } - - public List GetVmElementNames() - { - List result = new List(); - ComputerSystem.ComputerSystemCollection vmCollection = ComputerSystem.GetInstances(); - - // Return the first one - foreach (ComputerSystem vm in vmCollection) - { - if (vm.Caption.StartsWith("Hosting Computer System") ) - { - continue; - } - result.Add(vm.ElementName); - } - return result; - } - - public ProcessorSettingData GetProcSettings(VirtualSystemSettingData vmSettings) - { - // An ASSOCIATOR object provides the cross reference from the VirtualSystemSettingData and the - // ProcessorSettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method. - // Instead, we use the System.Management to code the equivalant of - // string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vmSettings.path, resultclassName); - // - var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, ProcessorSettingData.CreatedClassName); - - // NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain - // the virtualisation objects. - var wmiObjectSearch = new ManagementObjectSearcher(vmSettings.Scope, wmiObjQuery); - var wmiObjCollection = new ProcessorSettingData.ProcessorSettingDataCollection(wmiObjectSearch.Get()); - - foreach (ProcessorSettingData wmiObj in wmiObjCollection) - { - return wmiObj; - } - - var errMsg = string.Format("No ProcessorSettingData in VirtualSystemSettingData {0}", vmSettings.Path.Path); - var ex = new WmiException(errMsg); - logger.Error(errMsg, ex); - throw ex; - } - - public MemorySettingData GetMemSettings(VirtualSystemSettingData vmSettings) - { - // An ASSOCIATOR object provides the cross reference from the VirtualSystemSettingData and the - // MemorySettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method. - // Instead, we use the System.Management to code the equivalant of - // string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vmSettings.path, resultclassName); - // - var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, MemorySettingData.CreatedClassName); - - EthernetPortAllocationSettingData[] ethernetConnections = GetEthernetConnections(vm); - EthernetSwitchPortVlanSettingData vlanSettings = GetVlanSettings(ethernetConnections[pos]); - - //Assign configuration to new NIC - vlanSettings.LateBoundObject["AccessVlanId"] = vlanid; - vlanSettings.LateBoundObject["OperationMode"] = 1; - ModifyFeatureVmResources(vmMgmtSvc, vm, new String[] { - vlanSettings.LateBoundObject.GetText(TextFormat.CimDtd20)}); - } - - public void AttachIso(string displayName, string iso) + /// + /// Migrates a vm to the given destination host + /// + /// + /// + public void MigrateVm(string vmName, string destination) + { + ComputerSystem vm = GetComputerSystem(vmName); + VirtualSystemMigrationSettingData migrationSettingData = VirtualSystemMigrationSettingData.CreateInstance(); + VirtualSystemMigrationService service = GetVirtualisationSystemMigrationService(); + + IPAddress addr = IPAddress.Parse(destination); + IPHostEntry entry = Dns.GetHostEntry(addr); + string[] destinationHost = new string[] { destination }; + + migrationSettingData.LateBoundObject["MigrationType"] = MigrationType.VirtualSystem; + migrationSettingData.LateBoundObject["TransportType"] = TransportType.TCP; + migrationSettingData.LateBoundObject["DestinationIPAddressList"] = destinationHost; + string migrationSettings = migrationSettingData.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20); + + ManagementPath jobPath; + var ret_val = service.MigrateVirtualSystemToHost(vm.Path, entry.HostName, migrationSettings, null, null, out jobPath); + if (ret_val == ReturnCode.Started) + { + MigrationJobCompleted(jobPath); + } + else if (ret_val != ReturnCode.Completed) + { + var errMsg = string.Format( + "Failed migrating VM {0} (GUID {1}) due to {2}", + vm.ElementName, + vm.Name, + ReturnCode.ToString(ret_val)); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + } + + /// + /// Migrates the volume of a vm to a given destination storage + /// + /// + /// + /// + public void MigrateVolume(string vmName, string volume, string destination) + { + ComputerSystem vm = GetComputerSystem(vmName); + VirtualSystemMigrationSettingData migrationSettingData = VirtualSystemMigrationSettingData.CreateInstance(); + VirtualSystemMigrationService service = GetVirtualisationSystemMigrationService(); + StorageAllocationSettingData[] sasd = GetStorageSettings(vm); + + string[] rasds = null; + if (sasd != null) + { + rasds = new string[1]; + foreach (StorageAllocationSettingData item in sasd) + { + string vhdFileName = Path.GetFileNameWithoutExtension(item.HostResource[0]); + if (!String.IsNullOrEmpty(vhdFileName) && vhdFileName.Equals(volume)) + { + string newVhdPath = Path.Combine(destination, Path.GetFileName(item.HostResource[0])); + item.LateBoundObject["HostResource"] = new string[] { newVhdPath }; + item.LateBoundObject["PoolId"] = ""; + rasds[0] = item.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20); + break; + } + } + } + + migrationSettingData.LateBoundObject["MigrationType"] = MigrationType.Storage; + migrationSettingData.LateBoundObject["TransportType"] = TransportType.TCP; + string migrationSettings = migrationSettingData.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20); + + ManagementPath jobPath; + var ret_val = service.MigrateVirtualSystemToHost(vm.Path, null, migrationSettings, rasds, null, out jobPath); + if (ret_val == ReturnCode.Started) + { + MigrationJobCompleted(jobPath); + } + else if (ret_val != ReturnCode.Completed) + { + var errMsg = string.Format( + "Failed migrating volume {0} of VM {1} (GUID {2}) due to {3}", + volume, + vm.ElementName, + vm.Name, + ReturnCode.ToString(ret_val)); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + } + + /// + /// Migrates the volume of a vm to a given destination storage + /// + /// + /// + /// volume to me migrated to which pool + public void MigrateVmWithVolume(string vmName, string destination, Dictionary volumeToPool) + { + ComputerSystem vm = GetComputerSystem(vmName); + VirtualSystemMigrationSettingData migrationSettingData = VirtualSystemMigrationSettingData.CreateInstance(); + VirtualSystemMigrationService service = GetVirtualisationSystemMigrationService(); + StorageAllocationSettingData[] sasd = GetStorageSettings(vm); + + string[] rasds = null; + if (sasd != null) + { + rasds = new string[sasd.Length]; + uint index = 0; + foreach (StorageAllocationSettingData item in sasd) + { + string vhdFileName = Path.GetFileNameWithoutExtension(item.HostResource[0]); + if (!String.IsNullOrEmpty(vhdFileName) && volumeToPool.ContainsKey(vhdFileName)) + { + string newVhdPath = Path.Combine(volumeToPool[vhdFileName], Path.GetFileName(item.HostResource[0])); + item.LateBoundObject["HostResource"] = new string[] { newVhdPath }; + item.LateBoundObject["PoolId"] = ""; + } + + rasds[index++] = item.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20); + } + } + + IPAddress addr = IPAddress.Parse(destination); + IPHostEntry entry = Dns.GetHostEntry(addr); + string[] destinationHost = new string[] { destination }; + + migrationSettingData.LateBoundObject["MigrationType"] = MigrationType.VirtualSystemAndStorage; + migrationSettingData.LateBoundObject["TransportType"] = TransportType.TCP; + migrationSettingData.LateBoundObject["DestinationIPAddressList"] = destinationHost; + string migrationSettings = migrationSettingData.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20); + + ManagementPath jobPath; + var ret_val = service.MigrateVirtualSystemToHost(vm.Path, entry.HostName, migrationSettings, rasds, null, out jobPath); + if (ret_val == ReturnCode.Started) + { + MigrationJobCompleted(jobPath); + } + else if (ret_val != ReturnCode.Completed) + { + var errMsg = string.Format( + "Failed migrating VM {0} and its volumes to destination {1} (GUID {2}) due to {3}", + vm.ElementName, + destination, + vm.Name, + ReturnCode.ToString(ret_val)); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + } + + /// + /// Create new storage media resources, e.g. hard disk images and ISO disk images + /// see http://msdn.microsoft.com/en-us/library/hh859775(v=vs.85).aspx + /// + /// + /// + private static StorageAllocationSettingData CloneStorageAllocationSetting(string wmiQuery) + { + var defaultDiskImageSettingsObjs = StorageAllocationSettingData.GetInstances(wmiQuery); + + // assert + if (defaultDiskImageSettingsObjs.Count != 1) + { + var errMsg = string.Format("Failed to find Msvm_StorageAllocationSettingData for the query {0}", wmiQuery); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + + StorageAllocationSettingData defaultDiskDriveSettings = defaultDiskImageSettingsObjs.OfType().First(); + return new StorageAllocationSettingData((ManagementBaseObject)defaultDiskDriveSettings.LateBoundObject.Clone()); + } + + /// < summary> + /// Removes a storage resource from a computer system. + /// + /// Path that uniquely identifies the resource. + /// VM to which the disk image will be attached. + // Add new + private void RemoveNetworkResource(ManagementPath resourcePath) + { + var virtSwitchMgmtSvc = GetVirtualSwitchManagementService(); + ManagementPath jobPath; + var ret_val = virtSwitchMgmtSvc.RemoveResourceSettings( + new ManagementPath[] { resourcePath }, + out jobPath); + + // If the Job is done asynchronously + if (ret_val == ReturnCode.Started) + { + JobCompleted(jobPath); + } + else if (ret_val != ReturnCode.Completed) + { + var errMsg = string.Format( + "Failed to remove network resources {0} from switch due to {1}", + resourcePath.Path, + ReturnCode.ToString(ret_val)); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + } + + /// < summary> + /// Removes a storage resource from a computer system. + /// + /// Path that uniquely identifies the resource. + /// VM to which the disk image will be attached. + private void RemoveStorageResource(ManagementPath resourcePath, ComputerSystem vm) { logger.DebugFormat("Got request to attach iso {0} to vm {1}", iso, displayName);