/* Copyright (c) Citrix Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided * that the following conditions are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ using System; using System.Collections.Generic; using XenAdmin.Network; using XenAPI; using XenAdmin.Model; namespace XenServerHealthCheck { public class RequestUploadTask { private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private static T Get(Dictionary d, string k) where T : class { return d != null && d.ContainsKey(k) ? d[k] : null; } private static int IntKey(Dictionary d, string k, int def) { int result; string s = Get(d, k); return s != null && int.TryParse(s, out result) ? result : def; } private static bool BoolKey(Dictionary d, string k) { string v = Get(d, k); return v == null ? false : v == "true"; } private static double DueAfterHour = 24; private static bool CanLock(string UploadLock, bool onDemand) { if (string.IsNullOrEmpty(UploadLock)) return true; List currentLock = new List(UploadLock.Split('|')); DateTime currentTime = DateTime.UtcNow; DateTime lockTime = DateTime.Parse(currentLock[1]); if (currentLock[0] == Properties.Settings.Default.UUID && onDemand) return true; if (currentLock[0] == Properties.Settings.Default.UUID) { if ((DateTime.Compare(lockTime.AddHours(DueAfterHour), currentTime) <= 0)) return true; } else if ((DateTime.Compare(lockTime.AddHours(DueAfterHour), currentTime) <= 0)) return true; return false; } private static int SleepForLockConfirm = 10 * 1000; // 10 seconds private static bool getLock(IXenConnection connection, Session session) { Dictionary config = Pool.get_health_check_config(session, connection.Cache.Pools[0].opaque_ref); string newUploadLock = Properties.Settings.Default.UUID; newUploadLock += "|" + HealthCheckSettings.DateTimeToString(DateTime.UtcNow); config[HealthCheckSettings.UPLOAD_LOCK] = newUploadLock; Pool.set_health_check_config(session, connection.Cache.Pools[0].opaque_ref, config); System.Threading.Thread.Sleep(SleepForLockConfirm); config = Pool.get_health_check_config(session, connection.Cache.Pools[0].opaque_ref); return config[HealthCheckSettings.UPLOAD_LOCK] == newUploadLock; } public static bool Request(IXenConnection connection, Session session) { bool needRetry = false; Dictionary config = Pool.get_health_check_config(session, connection.Cache.Pools[0].opaque_ref); if (BoolKey(config, HealthCheckSettings.STATUS) == false) { ServerListHelper.instance.removeServerCredential(connection.Hostname); log.InfoFormat("Will not report for XenServer {0} that was not Enroll", connection.Hostname); return false; } //Check if there already some service doing the uploading already if (CanLock(Get(config, HealthCheckSettings.UPLOAD_LOCK), false) == false) { log.InfoFormat("Will not report for XenServer {0} that already locked", connection.Hostname); return false; } //No Lock has been set before, Check upload is due int intervalInDays = IntKey(config, HealthCheckSettings.INTERVAL_IN_DAYS, HealthCheckSettings.DEFAULT_INTERVAL_IN_DAYS); DateTime lastSuccessfulUpload = DateTime.UtcNow; bool haveSuccessfulUpload = false; if (config.ContainsKey(HealthCheckSettings.LAST_SUCCESSFUL_UPLOAD)) { try { lastSuccessfulUpload = HealthCheckSettings.StringToDateTime(Get(config, HealthCheckSettings.LAST_SUCCESSFUL_UPLOAD)); haveSuccessfulUpload = true; } catch (Exception exn) { log.Error("Catch exception when Parse LAST_SUCCESSFUL_UPLOAD", exn); } } if (haveSuccessfulUpload) {//If last successful update not due. return if (DateTime.Compare(DateTime.UtcNow, lastSuccessfulUpload.AddDays(intervalInDays)) < 0) { log.InfoFormat("Will not report for XenServer {0} that was not due {1} days", connection.Hostname, intervalInDays); return false; } } if (config.ContainsKey(HealthCheckSettings.LAST_FAILED_UPLOAD)) { try { DateTime LastFailedUpload = HealthCheckSettings.StringToDateTime(Get(config, HealthCheckSettings.LAST_FAILED_UPLOAD)); if (haveSuccessfulUpload) { if (DateTime.Compare(lastSuccessfulUpload, LastFailedUpload) > 0) return false; //A retry is not needed } int retryInterval = IntKey(config, HealthCheckSettings.RETRY_INTERVAL, HealthCheckSettings.DEFAULT_RETRY_INTERVAL); if (DateTime.Compare(LastFailedUpload.AddDays(retryInterval), DateTime.UtcNow) <= 0) { log.InfoFormat("Retry since retryInterval{0} - {1} > {2} meeted", LastFailedUpload, DateTime.UtcNow, retryInterval); needRetry = true; } else return false; } catch (Exception exn) { log.Error("Catch exception when check if retry was needed", exn); } } DateTime currentTime = DateTime.Now; if (!needRetry) {//Check if uploading schedule meet only for new upload DayOfWeek dayOfWeek; if (!Enum.TryParse(Get(config, HealthCheckSettings.DAY_OF_WEEK), out dayOfWeek)) { log.Error("DAY_OF_WEEK not existed"); return false; } if (!config.ContainsKey(HealthCheckSettings.TIME_OF_DAY)) { log.Error("TIME_OF_DAY not existed"); return false; } int TimeOfDay = IntKey(config, HealthCheckSettings.TIME_OF_DAY, HealthCheckSettings.GetDefaultTime()); if (currentTime.DayOfWeek != dayOfWeek || currentTime.Hour != TimeOfDay) { log.InfoFormat("Will not report for XenServer {0} for incorrect schedule", connection.Hostname); return false; } log.InfoFormat("Upload schedule for {0} is {1}:{2}, meet current time {3}", connection.Hostname, dayOfWeek, TimeOfDay, currentTime.ToString()); } return getLock(connection, session); } public static bool OnDemandRequest(IXenConnection connection, Session session) { Dictionary config = Pool.get_health_check_config(session, connection.Cache.Pools[0].opaque_ref); if (BoolKey(config, HealthCheckSettings.STATUS) == false) { log.InfoFormat("Will not report on demand for XenServer {0} that was not Enroll", connection.Hostname); return false; } //Check if there already some service doing the uploading already if (CanLock(Get(config, HealthCheckSettings.UPLOAD_LOCK), true) == false) { log.InfoFormat("Will not report for XenServer {0} that already locked", connection.Hostname); return false; } var newUploadRequest = Get(config, HealthCheckSettings.NEW_UPLOAD_REQUEST); if (!string.IsNullOrEmpty(newUploadRequest)) { DateTime newUploadRequestTime; try { newUploadRequestTime = HealthCheckSettings.StringToDateTime(newUploadRequest); } catch (Exception exn) { log.Error("Exception while parsing NEW_UPLOAD_REQUEST", exn); return false; } DateTime newUploadRequestDueTime = newUploadRequestTime.AddMinutes(HealthCheckSettings.UPLOAD_REQUEST_VALIDITY_INTERVAL); if (DateTime.Compare(newUploadRequestDueTime, DateTime.UtcNow) >= 1) { log.InfoFormat("Will report on demand for XenServer {0} since the demand was requested on {1} (UTC time)", connection.Hostname, newUploadRequestTime); return getLock(connection, session); } else { log.InfoFormat("Will not report on demand for XenServer {0} since the demand requested on {1} (UTC time) expired after {2} minutes", connection.Hostname, newUploadRequestTime, HealthCheckSettings.UPLOAD_REQUEST_VALIDITY_INTERVAL); return false; } } else return false; } } }