diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs index ae595fcc215..41b2bac81b3 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs @@ -279,6 +279,27 @@ namespace HypervResource } } + public class DiskTO + { + public string type; + public TemplateObjectTO templateObjectTO = null; + + public static DiskTO ParseJson(dynamic json) + { + DiskTO result = null; + if (json != null) + { + result = new DiskTO() + { + templateObjectTO = TemplateObjectTO.ParseJson(json.data), + type = (string)json.type, + }; + } + + return result; + } + } + enum VolumeType { UNKNOWN, @@ -589,6 +610,7 @@ namespace HypervResource public const string SwiftTO = "com.cloud.agent.api.to.SwiftTO"; public const string VirtualMachineTO = "com.cloud.agent.api.to.VirtualMachineTO"; public const string VolumeTO = "com.cloud.agent.api.to.VolumeTO"; + public const string DiskTO = "com.cloud.agent.api.to.DiskTO"; public const string InternalErrorException = "com.cloud.exception.InternalErrorException"; public const string HostType = "com.cloud.host.Host.Type"; public const string HypervisorType = "com.cloud.hypervisor.Hypervisor.HypervisorType"; diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs index ed3736b072b..43b44dfa00b 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs @@ -196,7 +196,19 @@ namespace HypervResource try { string vmName = (string)cmd.vmName; - result = true; + DiskTO disk = DiskTO.ParseJson(cmd.disk); + TemplateObjectTO dataStore = disk.templateObjectTO; + + if (dataStore.nfsDataStoreTO != null) + { + NFSTO share = dataStore.nfsDataStoreTO; + Utils.connectToRemote(share.UncPath, share.Domain, share.User, share.Password); + + // The share is mapped, now attach the iso + string isoPath = Path.Combine(share.UncPath.Replace('/', Path.DirectorySeparatorChar), dataStore.path); + wmiCallsV2.AttachIso(vmName, isoPath); + result = true; + } } catch (Exception sysEx) { @@ -229,7 +241,18 @@ namespace HypervResource try { string vmName = (string)cmd.vmName; - result = true; + DiskTO disk = DiskTO.ParseJson(cmd.disk); + TemplateObjectTO dataStore = disk.templateObjectTO; + + if (dataStore.nfsDataStoreTO != null) + { + NFSTO share = dataStore.nfsDataStoreTO; + // The share is mapped, now attach the iso + string isoPath = Path.Combine(share.UncPath.Replace('/', Path.DirectorySeparatorChar), + dataStore.path.Replace('/', Path.DirectorySeparatorChar)); + wmiCallsV2.DetachDisk(vmName, isoPath); + result = true; + } } catch (Exception sysEx) { @@ -247,6 +270,49 @@ namespace HypervResource } } + // 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 + cmd.ToString()); + + string details = null; + bool result = false; + + try + { + string vmName = (string)cmd.vmName; + var sys = wmiCallsV2.GetComputerSystem(vmName); + if (sys == null) + { + details = CloudStackTypes.RebootCommand + " requested unknown VM " + vmName; + logger.Error(details); + } + else + { + wmiCallsV2.SetState(sys, RequiredState.Reset); + result = true; + } + } + catch (Exception sysEx) + { + details = CloudStackTypes.RebootCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + details = details + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.RebootAnswer); + } + } + // POST api/HypervResource/DestroyCommand [HttpPost] [ActionName(CloudStackTypes.DestroyCommand)] diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs index e55f2ad1e99..631a7bd2cf4 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs @@ -66,30 +66,30 @@ namespace HypervResource bool isSuccess = 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 = ""; - if (filePathRelativeToShare.EndsWith(".iso") || filePathRelativeToShare.EndsWith(".vhd") || filePathRelativeToShare.EndsWith(".vhdx")) - { - dest = Path.Combine(cifsShareDetails.UncPath, filePathRelativeToShare); - dest = dest.Replace('/', Path.DirectorySeparatorChar); - } - // if the filePathRelativeToShare string don't have filename and only a dir point then find the vhd files in that folder and use - // In the clean setup, first copy command wont be having the filename it contains onlyu dir path. - // we need to scan the folder point and then copy the file to destination. - else if (!filePathRelativeToShare.EndsWith(".vhd") || !filePathRelativeToShare.EndsWith(".vhdx")) - { - // scan the folder and get the vhd filename. - String uncPath = Path.Combine(cifsShareDetails.UncPath, Path.Combine(filePathRelativeToShare.Split('/'))); - //uncPath = uncPath.Replace("/", "\\"); - DirectoryInfo dir = new DirectoryInfo(uncPath); - FileInfo[] vhdFiles = dir.GetFiles("*.vhd*"); - if (vhdFiles.Length > 0) - { - FileInfo file = vhdFiles[0]; - dest = file.FullName; - } - } - s_logger.Info(CloudStackTypes.CopyCommand + ": copy " + Path.Combine(cifsShareDetails.UncPath, filePathRelativeToShare) + " to " + destFile); + String dest = ""; + if (filePathRelativeToShare.EndsWith(".iso") || filePathRelativeToShare.EndsWith(".vhd") || filePathRelativeToShare.EndsWith(".vhdx")) + { + dest = Path.Combine(cifsShareDetails.UncPath, filePathRelativeToShare); + dest = dest.Replace('/', Path.DirectorySeparatorChar); + } + // if the filePathRelativeToShare string don't have filename and only a dir point then find the vhd files in that folder and use + // In the clean setup, first copy command wont be having the filename it contains onlyu dir path. + // we need to scan the folder point and then copy the file to destination. + else if (!filePathRelativeToShare.EndsWith(".vhd") || !filePathRelativeToShare.EndsWith(".vhdx")) + { + // scan the folder and get the vhd filename. + String uncPath = Path.Combine(cifsShareDetails.UncPath, Path.Combine(filePathRelativeToShare.Split('/'))); + //uncPath = uncPath.Replace("/", "\\"); + DirectoryInfo dir = new DirectoryInfo(uncPath); + FileInfo[] vhdFiles = dir.GetFiles("*.vhd*"); + if (vhdFiles.Length > 0) + { + FileInfo file = vhdFiles[0]; + dest = file.FullName; + } + } + s_logger.Info(CloudStackTypes.CopyCommand + ": copy " + Path.Combine(cifsShareDetails.UncPath, filePathRelativeToShare) + " to " + destFile); File.Copy(dest, destFile, true); remoteIdentity.Undo(); } @@ -103,6 +103,32 @@ namespace HypervResource } } + public static void connectToRemote(string remoteUNC, string domain, string username, string password) + { + NETRESOURCE nr = new NETRESOURCE(); + nr.dwType = RESOURCETYPE_DISK; + nr.lpRemoteName = remoteUNC.Replace('/', Path.DirectorySeparatorChar); + if (domain != null) + { + username = domain + @"\" + username; + } + + int ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null); + if (ret != NO_ERROR) + { + throw new ArgumentException("net use of share " + remoteUNC + "failed with "+ getErrorForNumber(ret)); + } + } + + public static void disconnectRemote(string remoteUNC) + { + int ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false); + if (ret != NO_ERROR) + { + throw new ArgumentException("net disconnect of share " + remoteUNC + "failed with " + getErrorForNumber(ret)); + } + } + // from http://stackoverflow.com/a/2541569/939250 #region imports [DllImport("advapi32.dll", SetLastError = true)] @@ -113,9 +139,16 @@ namespace HypervResource [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public extern static bool DuplicateToken(IntPtr existingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr duplicateTokenHandle); + + [DllImport("Mpr.dll")] + private static extern int WNetUseConnection(IntPtr hwndOwner, NETRESOURCE lpNetResource, string lpPassword, string lpUserID, int dwFlags, + string lpAccessName, string lpBufferSize, string lpResult); + + [DllImport("Mpr.dll")] + private static extern int WNetCancelConnection2(string lpName, int dwFlags, bool fForce); #endregion - #region logon consts + #region consts // logon types const int LOGON32_LOGON_INTERACTIVE = 2; const int LOGON32_LOGON_NETWORK = 3; @@ -126,6 +159,115 @@ namespace HypervResource const int LOGON32_PROVIDER_WINNT50 = 3; const int LOGON32_PROVIDER_WINNT40 = 2; const int LOGON32_PROVIDER_WINNT35 = 1; + + const int RESOURCE_CONNECTED = 0x00000001; + const int RESOURCE_GLOBALNET = 0x00000002; + const int RESOURCE_REMEMBERED = 0x00000003; + + const int RESOURCETYPE_ANY = 0x00000000; + const int RESOURCETYPE_DISK = 0x00000001; + const int RESOURCETYPE_PRINT = 0x00000002; + + const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000; + const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001; + const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002; + const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003; + const int RESOURCEDISPLAYTYPE_FILE = 0x00000004; + const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005; + + const int RESOURCEUSAGE_CONNECTABLE = 0x00000001; + const int RESOURCEUSAGE_CONTAINER = 0x00000002; + + + const int CONNECT_INTERACTIVE = 0x00000008; + const int CONNECT_PROMPT = 0x00000010; + const int CONNECT_REDIRECT = 0x00000080; + const int CONNECT_UPDATE_PROFILE = 0x00000001; + const int CONNECT_COMMANDLINE = 0x00000800; + const int CONNECT_CMD_SAVECRED = 0x00001000; + + const int CONNECT_LOCALDRIVE = 0x00000100; #endregion + + #region Errors + const int NO_ERROR = 0; + + const int ERROR_ACCESS_DENIED = 5; + const int ERROR_ALREADY_ASSIGNED = 85; + const int ERROR_BAD_DEVICE = 1200; + const int ERROR_BAD_NET_NAME = 67; + const int ERROR_BAD_PROVIDER = 1204; + const int ERROR_CANCELLED = 1223; + const int ERROR_EXTENDED_ERROR = 1208; + const int ERROR_INVALID_ADDRESS = 487; + const int ERROR_INVALID_PARAMETER = 87; + const int ERROR_INVALID_PASSWORD = 1216; + const int ERROR_MORE_DATA = 234; + const int ERROR_NO_MORE_ITEMS = 259; + const int ERROR_NO_NET_OR_BAD_PATH = 1203; + const int ERROR_NO_NETWORK = 1222; + + const int ERROR_BAD_PROFILE = 1206; + const int ERROR_CANNOT_OPEN_PROFILE = 1205; + const int ERROR_DEVICE_IN_USE = 2404; + const int ERROR_NOT_CONNECTED = 2250; + const int ERROR_OPEN_FILES = 2401; + + private struct ErrorClass + { + public int num; + public string message; + public ErrorClass(int num, string message) + { + this.num = num; + this.message = message; + } + } + + private static ErrorClass[] ERROR_LIST = new ErrorClass[] { + new ErrorClass(ERROR_ACCESS_DENIED, "Error: Access Denied"), + new ErrorClass(ERROR_ALREADY_ASSIGNED, "Error: Already Assigned"), + new ErrorClass(ERROR_BAD_DEVICE, "Error: Bad Device"), + new ErrorClass(ERROR_BAD_NET_NAME, "Error: Bad Net Name"), + new ErrorClass(ERROR_BAD_PROVIDER, "Error: Bad Provider"), + new ErrorClass(ERROR_CANCELLED, "Error: Cancelled"), + new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), + new ErrorClass(ERROR_INVALID_ADDRESS, "Error: Invalid Address"), + new ErrorClass(ERROR_INVALID_PARAMETER, "Error: Invalid Parameter"), + new ErrorClass(ERROR_INVALID_PASSWORD, "Error: Invalid Password"), + new ErrorClass(ERROR_MORE_DATA, "Error: More Data"), + new ErrorClass(ERROR_NO_MORE_ITEMS, "Error: No More Items"), + new ErrorClass(ERROR_NO_NET_OR_BAD_PATH, "Error: No Net Or Bad Path"), + new ErrorClass(ERROR_NO_NETWORK, "Error: No Network"), + new ErrorClass(ERROR_BAD_PROFILE, "Error: Bad Profile"), + new ErrorClass(ERROR_CANNOT_OPEN_PROFILE, "Error: Cannot Open Profile"), + new ErrorClass(ERROR_DEVICE_IN_USE, "Error: Device In Use"), + new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), + new ErrorClass(ERROR_NOT_CONNECTED, "Error: Not Connected"), + new ErrorClass(ERROR_OPEN_FILES, "Error: Open Files"), + }; + + private static string getErrorForNumber(int errNum) + { + foreach (ErrorClass er in ERROR_LIST) + { + if (er.num == errNum) return er.message; + } + return "Error: Unknown, " + errNum; + } + #endregion + + [StructLayout(LayoutKind.Sequential)] + private class NETRESOURCE + { + public int dwScope = 0; + public int dwType = 0; + public int dwDisplayType = 0; + public int dwUsage = 0; + public string lpLocalName = ""; + public string lpRemoteName = ""; + public string lpComment = ""; + public string lpProvider = ""; + } } } diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs index 18b96cc6ac1..547a6b7cdfd 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs @@ -447,6 +447,7 @@ namespace HypervResource pingReply = pingSender.Send(ipAddress, pingTimeout, buffer, pingOptions); if (pingReply.Status == IPStatus.Success) { + System.Threading.Thread.Sleep(30000); return true; } else @@ -591,11 +592,11 @@ namespace HypervResource } string hostResource = item.HostResource[0]; - if (!hostResource.Equals(diskFileName)) + if (Path.Equals(hostResource, diskFileName)) { - continue; + imageToRemove = item; + break; } - imageToRemove = item; } // assert @@ -739,7 +740,6 @@ namespace HypervResource // Disk drives are attached to a 'Parent' IDE controller. We IDE Controller's settings for the 'Path', which our new Disk drive will use to reference it. VirtualSystemSettingData vmSettings = GetVmSettings(vm); var driveWmiObj = GetDvdDriveSettings(vmSettings); - InsertDiskImage(vm, isoPath, IDE_ISO_DISK, driveWmiObj.Path); }