From 55f53e13e82179c2cbde3f667869495ec944f496 Mon Sep 17 00:00:00 2001 From: Anshul Gangwar Date: Mon, 14 Apr 2014 02:32:56 -0700 Subject: [PATCH] CLOUDSTACK-6401: [hyper-v] fixed correct host state, if host is up and hyper-v agent is down then the host state will be shown as up(actual host state) in cloudstack --- .../HypervResource/HypervResource.csproj | 5 + .../HypervResourceController.cs | 92 ++++++++++++++++++- .../ServerResource/HypervResource/Utils.cs | 43 +++++++++ .../HypervResource/heartbeat.bat | 23 +++++ 4 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/heartbeat.bat diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResource.csproj b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResource.csproj index ec4456881cf..8bebfffbeef 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResource.csproj +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResource.csproj @@ -87,6 +87,11 @@ + + Always + + + {db824727-bdc3-437c-a364-7a811d8a160f} WmiWrappers diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs index 52307af4209..0f084dbe679 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs @@ -107,6 +107,21 @@ namespace HypervResource config.Save(ConfigurationSaveMode.Modified); ConfigurationManager.RefreshSection("appSettings"); } + + public List getAllPrimaryStorages() + { + List poolPaths = new List(); + Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); + KeyValueConfigurationCollection settings = config.AppSettings.Settings; + foreach (string key in settings.AllKeys) + { + if (key.Contains("primary_storage_")) + { + poolPaths.Add(settings[key].Value); + } + } + return poolPaths; + } } /// @@ -775,16 +790,67 @@ namespace HypervResource 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 = true, - details = "resource is alive", + 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] @@ -956,6 +1022,27 @@ namespace HypervResource 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); } } @@ -2151,6 +2238,7 @@ namespace HypervResource // Insert networking details string privateIpAddress = strtRouteCmd.privateIpAddress; + config.PrivateIpAddress = privateIpAddress; string subnet; System.Net.NetworkInformation.NetworkInterface privateNic = GetNicInfoFromIpAddress(privateIpAddress, out subnet); strtRouteCmd.privateIpAddress = privateIpAddress; diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs index 5ddcbef2b96..d0d3d838288 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs @@ -24,6 +24,8 @@ using System.Runtime.InteropServices; using System.Security.Principal; using System.Text; using System.Threading.Tasks; +using System.Diagnostics; +using System.Reflection; namespace HypervResource { @@ -31,6 +33,9 @@ namespace HypervResource { private static ILog s_logger = LogManager.GetLogger(typeof(Utils)); + private const string TASK_PREFIX = "cloudstack-heartbeat-"; + private const string BATCH_FILE = "heartbeat.bat"; + /// /// Associate CloudStack object's content with a fully qualified type name. /// @@ -174,6 +179,44 @@ namespace HypervResource return cleanString; } + public static void AddHeartBeatTask(string poolGuid, string poolPath, string hostPrivateIp) + { + string taskName = TASK_PREFIX + poolGuid; + UriBuilder uri = new UriBuilder(Assembly.GetExecutingAssembly().CodeBase); + string alocation = Uri.UnescapeDataString(uri.Path); + string batchFileLocation = Path.Combine(Path.GetDirectoryName(alocation), BATCH_FILE); + string hbFile = Path.Combine(poolPath, "hb-" + hostPrivateIp); + ExecuteTask("schtasks.exe", "/Create /RU \"SYSTEM\" /SC MINUTE /MO 1 /TN " + taskName + " /F /TR \"" + batchFileLocation + " " + hbFile + "\""); + } + + public static void RemoveHeartBeatTask(string poolGuid) + { + string taskName = TASK_PREFIX + poolGuid; + ExecuteTask("schtasks.exe", "/Delete /TN " + taskName + " /F"); + } + + public static void ExecuteTask(string command, string args) + { + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = true; + startInfo.FileName = command; + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.Arguments = args; + + try + { + using (Process exeProcess = Process.Start(startInfo)) + { + exeProcess.WaitForExit(); + } + } + catch (Exception e) + { + s_logger.Error("Error occurred in deleting or adding a scheduled task " + e.Message); + } + } + // from http://stackoverflow.com/a/2541569/939250 #region imports [DllImport("advapi32.dll", SetLastError = true)] diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/heartbeat.bat b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/heartbeat.bat new file mode 100644 index 00000000000..85f6e7b8fbe --- /dev/null +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/heartbeat.bat @@ -0,0 +1,23 @@ +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM +@echo off +@REM This script is based on: +@REM http://stackoverflow.com/questions/9871499/how-to-get-utc-time-with-windows-batch-file + +for /f %%x in ('wmic path win32_utctime get /format:list ^| findstr "="') do set %%x +echo %Year%-%Month%-%Day%@%Hour%:%Minute%:%Second%>%1 \ No newline at end of file