/* Copyright (c) Cloud Software Group, Inc. * * 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; namespace XenAdmin.Wlb { public class WlbScheduledTask : WlbConfigurationBase { private const string KEY_TASK_NAME = "TaskName"; private const string KEY_TASK_DESCRIPTION = "TaskDescription"; private const string KEY_TASK_ENABLED = "TaskEnabled"; private const string KEY_TASK_OWNER = "TaskOwner"; private const string KEY_TASK_LAST_RUN_RESULT = "TaskLastRunResult"; private const string KEY_TASK_LAST_TOUCHED_BY = "TaskLastTouchedBy"; private const string KEY_TASK_LAST_TOUCHED = "TaskLastTouched"; private const string KEY_TRIGGER_TYPE = "TriggerType"; private const string KEY_TRIGGER_DAYS_OF_WEEK = "TriggerDaysOfWeek"; private const string KEY_TRIGGER_EXECUTE_TIME = "TriggerExecuteTime"; private const string KEY_TRIGGER_LAST_RUN = "TriggerLastRun"; private const string KEY_TRIGGER_ENABLED_DATE = "TriggerEndabledDate"; private const string KEY_TRIGGER_DISABLED_DATE = "TriggerDisabledDate"; private const string KEY_DELETE_TASK = "TaskDelete"; private const string KEY_ACTION_TYPE = "ActionType"; /// /// Public enumeration describing the interval period of a WlbTaskTrigger /// public enum WlbTaskTriggerType : int { /// /// A single-shot trigger /// Once = 0, /// /// A trigger that occurs every day at a particular time /// Daily = 1, /// /// A trigger that occurs every week on a set of days at a particulat time /// Weekly = 2, /// /// A trigger that occurs once every month on a given date /// Monthly = 3 } public enum WlbTaskActionType : int { Unknown = 0, SetOptimizationMode = 1, ReportSubscription = 2 } /// /// Public enumeration of the days on which a weekly WlbTaskTrigger will run /// [FlagsAttribute] public enum WlbTaskDaysOfWeek { /// /// None /// None = 0, /// /// Sunday /// Sunday = 1, /// /// Monday /// Monday = 2, /// /// Tuesday /// Tuesday = 4, /// /// Wednesday /// Wednesday = 8, /// /// Thursday /// Thursday = 16, /// /// Friday /// Friday = 32, /// /// Saturday /// Saturday = 64, /// /// All weekdays /// Weekends = Sunday | Saturday, /// /// Only weekend days /// Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday, /// /// All days /// All = Weekdays | Weekends } public static string DaysOfWeekL10N(WlbTaskDaysOfWeek days) { switch (days) { case WlbTaskDaysOfWeek.Sunday: return Messages.SUNDAY_LONG; case WlbTaskDaysOfWeek.Monday: return Messages.MONDAY_LONG; case WlbTaskDaysOfWeek.Tuesday: return Messages.TUESDAY_LONG; case WlbTaskDaysOfWeek.Wednesday: return Messages.WEDNESDAY_LONG; case WlbTaskDaysOfWeek.Thursday: return Messages.THURSDAY_LONG; case WlbTaskDaysOfWeek.Friday: return Messages.FRIDAY_LONG; case WlbTaskDaysOfWeek.Saturday: return Messages.SATURDAY_LONG; case WlbTaskDaysOfWeek.Weekdays: return Messages.WLB_DAY_WEEKDAYS; case WlbTaskDaysOfWeek.Weekends: return Messages.WLB_DAY_WEEKENDS; case WlbTaskDaysOfWeek.All: return Messages.WLB_DAY_ALL; default: return ""; } } public static WlbPoolPerformanceMode GetTaskOptMode(WlbScheduledTask task) { WlbPoolPerformanceMode mode = WlbPoolPerformanceMode.MaximizePerformance; if (task.TaskParameters["OptMode"] == "0") { mode = WlbPoolPerformanceMode.MaximizePerformance; } else { mode = WlbPoolPerformanceMode.MaximizeDensity; } return mode; } /// /// Returns the offset minutes between Utc and local time /// Add this to local time to get UTC /// Subtract from UTC to get local time /// /// (double) number of minutes between Utc and Local time private static double LocalOffsetMinutes() { TimeSpan difference = DateTime.UtcNow.Subtract(DateTime.Now); return difference.TotalMinutes; } /// /// Accepts a client's local time DayOfWeek and RunTime of a scheduled task /// and returns the DaysOfWeek and RunTime adjusted to UTC time /// /// Task's DaysOfWeek value in local time /// Task's RunTime in local time /// (Output) Task's DaysOfWeek value adjusted to UTC /// (Output) Task's RunTime value adjusted to UTC public static void GetUTCTaskTimes(WlbScheduledTask.WlbTaskDaysOfWeek LocalDaysOfWeek, DateTime LocalRunTime, out WlbScheduledTask.WlbTaskDaysOfWeek UtcDaysOfWeek, out DateTime UtcRunTime) { UtcDaysOfWeek = LocalDaysOfWeek; UtcRunTime = LocalRunTime.AddMinutes(LocalOffsetMinutes()); if (DateTime.Compare(LocalRunTime.Date, UtcRunTime.Date) < 0) { UtcDaysOfWeek = WlbScheduledTask.NextDay(LocalDaysOfWeek); } else if (DateTime.Compare(LocalRunTime.Date, UtcRunTime.Date) > 0) { UtcDaysOfWeek = WlbScheduledTask.PreviousDay(LocalDaysOfWeek); } } /// /// Accepts UTC DayOfWeek and RunTime of a scheduled task /// and returns the DaysOfWeek and RunTime adjusted to client's local time /// /// Task's DaysOfWeek value in UTC /// Task's RunTime in UTC /// (Output) Task's DaysOfWeek value adjusted to local time /// (Output) Task's RunTime value adjusted to local time public static void GetLocalTaskTimes(WlbScheduledTask.WlbTaskDaysOfWeek UtcDaysOfWeek, DateTime UtcRunTime, out WlbScheduledTask.WlbTaskDaysOfWeek LocalDaysOfWeek, out DateTime LocalRunTime) { LocalDaysOfWeek = UtcDaysOfWeek; LocalRunTime = UtcRunTime.AddMinutes(LocalOffsetMinutes() * -1); if (UtcDaysOfWeek != WlbTaskDaysOfWeek.None && UtcDaysOfWeek != WlbTaskDaysOfWeek.All && UtcDaysOfWeek != WlbTaskDaysOfWeek.Weekdays && UtcDaysOfWeek != WlbTaskDaysOfWeek.Weekends) { if (DateTime.Compare(UtcRunTime.Date, LocalRunTime.Date) < 0) { LocalDaysOfWeek = WlbScheduledTask.NextDay(UtcDaysOfWeek); } else if (DateTime.Compare(UtcRunTime.Date, LocalRunTime.Date) > 0) { LocalDaysOfWeek = WlbScheduledTask.PreviousDay(UtcDaysOfWeek); } } } public static WlbTaskDaysOfWeek NextDay(WlbTaskDaysOfWeek daysOfWeek) { // Doing some hackery here to shift days in the enumeration switch (daysOfWeek) { case WlbTaskDaysOfWeek.Saturday: { return WlbTaskDaysOfWeek.Sunday; } case WlbTaskDaysOfWeek.Weekends: { return (WlbTaskDaysOfWeek.Sunday | WlbTaskDaysOfWeek.Monday); } case WlbTaskDaysOfWeek.Weekdays: { return (WlbTaskDaysOfWeek.Tuesday | WlbTaskDaysOfWeek.Wednesday | WlbTaskDaysOfWeek.Thursday | WlbTaskDaysOfWeek.Friday | WlbTaskDaysOfWeek.Saturday); } case WlbTaskDaysOfWeek.All: { return daysOfWeek; } // single days, Sunday through Friday, which can easily be // shifted back by one. This also handles None (0). default: { //do the circular shift of rightmost 7 bits, discard the rest int tempDays = (int)daysOfWeek; tempDays = (tempDays << 1 | tempDays >> 6) & 0x0000007F; return (WlbTaskDaysOfWeek)tempDays; //return (WlbTaskDaysOfWeek)(((int)daysOfWeek) * 2); } } } public static WlbTaskDaysOfWeek PreviousDay(WlbTaskDaysOfWeek daysOfWeek) { // Doing some hackery here to shift days in the enumeration switch (daysOfWeek) { case WlbTaskDaysOfWeek.Sunday: { return WlbTaskDaysOfWeek.Saturday; } case WlbTaskDaysOfWeek.Weekends: { return (WlbTaskDaysOfWeek.Friday | WlbTaskDaysOfWeek.Saturday); } case WlbTaskDaysOfWeek.Weekdays: { return (WlbTaskDaysOfWeek.Sunday | WlbTaskDaysOfWeek.Monday | WlbTaskDaysOfWeek.Tuesday | WlbTaskDaysOfWeek.Wednesday | WlbTaskDaysOfWeek.Thursday); } case WlbTaskDaysOfWeek.All: { return daysOfWeek; } // single days, monday through saturday, which can easily be // shifted back by one. This also handles None (0). default: { //do the circular shift of rightmost 7 bits, discard the rest int tempDays = (int)daysOfWeek; tempDays = (tempDays >> 1 | tempDays << 6) & 0x0000007F; return (WlbTaskDaysOfWeek)tempDays; //return (WlbTaskDaysOfWeek)(((int)daysOfWeek) / 2); } } } public static WlbScheduledTask.WlbTaskDaysOfWeek ConvertToWlbTaskDayOfWeek(DayOfWeek dayOfWeek) { return (WlbScheduledTask.WlbTaskDaysOfWeek)(Math.Pow(2,(int)dayOfWeek) % 127); } public static DayOfWeek ConvertFromWlbTaskDayOfWeek(WlbScheduledTask.WlbTaskDaysOfWeek wlbDayOfWeek) { return (DayOfWeek)(Math.Log((int)wlbDayOfWeek, 2)); } public WlbScheduledTask(string TaskId) { base.Configuration = new Dictionary(); base.ItemId = TaskId; //Define the key base base.KeyBase = WlbConfigurationKeyBase.schedTask; //Define the known keys WlbConfigurationKeys = new List(new string[] { KEY_TASK_NAME, KEY_TASK_DESCRIPTION, KEY_TASK_ENABLED, KEY_TASK_OWNER, KEY_TASK_LAST_RUN_RESULT, KEY_TASK_LAST_TOUCHED_BY, KEY_TASK_LAST_TOUCHED, KEY_TRIGGER_TYPE, KEY_TRIGGER_DAYS_OF_WEEK, KEY_TRIGGER_EXECUTE_TIME, KEY_TRIGGER_LAST_RUN, KEY_TRIGGER_ENABLED_DATE, KEY_TRIGGER_DISABLED_DATE, KEY_DELETE_TASK, KEY_ACTION_TYPE }); } public bool DeleteTask { get { return GetConfigValueBool(BuildComplexKey(KEY_DELETE_TASK)); } set { SetConfigValueBool(BuildComplexKey(KEY_DELETE_TASK), value, true); } } public string Name { get { return GetConfigValueString(BuildComplexKey(KEY_TASK_NAME)); } set { SetConfigValueString(BuildComplexKey(KEY_TASK_NAME), value, true); } } public string Description { get { return GetConfigValueString(BuildComplexKey(KEY_TASK_DESCRIPTION)); } set { SetConfigValueString(BuildComplexKey(KEY_TASK_DESCRIPTION), value, true); } } public bool Enabled { get { return GetConfigValueBool(BuildComplexKey(KEY_TASK_ENABLED)); } set { SetConfigValueBool(BuildComplexKey(KEY_TASK_ENABLED), value, true); } } public string Owner { get { return GetConfigValueString(BuildComplexKey(KEY_TASK_OWNER)); } set { SetConfigValueString(BuildComplexKey(KEY_TASK_OWNER), value, true); } } public bool LastRunResult { get { return GetConfigValueBool(BuildComplexKey(KEY_TASK_LAST_RUN_RESULT)); } set { SetConfigValueBool(BuildComplexKey(KEY_TASK_LAST_RUN_RESULT), value, true); } } public string LastTouchedBy { get { return GetConfigValueString(BuildComplexKey(KEY_TASK_LAST_TOUCHED_BY)); } set { SetConfigValueString(BuildComplexKey(KEY_TASK_LAST_TOUCHED_BY), value, true); } } public DateTime LastTouched { get { return GetConfigValueUTCDateTime(BuildComplexKey(KEY_TASK_LAST_TOUCHED)); } set { SetConfigValueUTCDateTime(BuildComplexKey(KEY_TASK_LAST_TOUCHED), value, true); } } public WlbTaskTriggerType TriggerInterval { get { return (WlbTaskTriggerType)GetConfigValueInt(BuildComplexKey(KEY_TRIGGER_TYPE)); } set { SetConfigValueInt(BuildComplexKey(KEY_TRIGGER_TYPE), (int)value, true); } } public WlbTaskDaysOfWeek DaysOfWeek { get { return (WlbTaskDaysOfWeek)GetConfigValueInt(BuildComplexKey(KEY_TRIGGER_DAYS_OF_WEEK)); } set { SetConfigValueInt(BuildComplexKey(KEY_TRIGGER_DAYS_OF_WEEK), (int)value, true); } } public DateTime RunTime { get { return GetConfigValueUTCDateTime(BuildComplexKey(KEY_TRIGGER_EXECUTE_TIME)); } set { SetConfigValueUTCDateTime(BuildComplexKey(KEY_TRIGGER_EXECUTE_TIME), value, true); } } public DateTime LastRunDate { get { return GetConfigValueUTCDateTime(BuildComplexKey(KEY_TRIGGER_LAST_RUN)); } set { SetConfigValueUTCDateTime(BuildComplexKey(KEY_TRIGGER_LAST_RUN), value, true); } } public DateTime EnableDate { get { return GetConfigValueUTCDateTime(BuildComplexKey(KEY_TRIGGER_ENABLED_DATE)); } set { SetConfigValueUTCDateTime(BuildComplexKey(KEY_TRIGGER_ENABLED_DATE), value, true); } } public DateTime DisableTime { get { return GetConfigValueUTCDateTime(BuildComplexKey(KEY_TRIGGER_DISABLED_DATE)); } set { SetConfigValueUTCDateTime(BuildComplexKey(KEY_TRIGGER_DISABLED_DATE), value, true); } } public WlbTaskActionType ActionType { get { return (WlbTaskActionType)GetConfigValueInt(BuildComplexKey(KEY_ACTION_TYPE)); } set { SetConfigValueInt(BuildComplexKey(KEY_ACTION_TYPE), (int)value, true); } } public Dictionary TaskParameters { get { return GetOtherParameters(); } set { SetOtherParameters(value); } } public void AddTaskParameter(string key, string value) { SetOtherParameter(key, value); } public int TaskId { get { int taskId = 0; Int32.TryParse(ItemId, out taskId); return taskId; } } public WlbScheduledTask Clone() { WlbScheduledTask newTask = new WlbScheduledTask(this.TaskId.ToString()); newTask.ActionType = this.ActionType; newTask.DaysOfWeek = this.DaysOfWeek; newTask.DeleteTask = this.DeleteTask; newTask.Description = this.Description; newTask.DisableTime = this.DisableTime; newTask.Enabled = this.Enabled; newTask.EnableDate = this.EnableDate; newTask.RunTime = this.RunTime; newTask.LastRunDate = this.LastRunDate; newTask.LastRunResult = this.LastRunResult; newTask.LastTouched = this.LastTouched; newTask.LastTouchedBy = this.LastTouchedBy; newTask.Name = this.Name; newTask.Owner = this.Owner; newTask.TaskParameters = this.TaskParameters; newTask.TriggerInterval = this.TriggerInterval; return newTask; } } public class WlbScheduledTasks { private Dictionary _tasks = new Dictionary(); public WlbScheduledTasks() { ;} /// /// Exposes the actual list of WlbScheduledTasks /// public Dictionary TaskList { set { _tasks = value; } get { return _tasks; } } /// /// Exposes a sorted version of the WlbScheduledTasks collection /// public SortedDictionary SortedTaskList { get { SortedDictionary sortedTasks = new SortedDictionary(); foreach (WlbScheduledTask task in _tasks.Values) { if (!task.DeleteTask) { WlbScheduledTask.WlbTaskDaysOfWeek localDaysOfWeek; DateTime localRunTime; WlbScheduledTask.GetLocalTaskTimes((task.DaysOfWeek), task.RunTime, out localDaysOfWeek, out localRunTime); int sortKey = GetSortKey(localDaysOfWeek, localRunTime); //if the task is disabled, bump the sort key to prevent conficts // with any new tasks. This could start to get wierd after 100 duplicate // disabled tasks, but then it will be the user's problem. if (!task.Enabled) { sortKey += 1; while (sortedTasks.ContainsKey(sortKey)) { sortKey += 1; } } WlbScheduledTask virtualTask = task.Clone(); virtualTask.DaysOfWeek = task.DaysOfWeek; if (!sortedTasks.ContainsKey(sortKey)) { sortedTasks.Add(sortKey, virtualTask); } } } return sortedTasks; } } /// /// Exposes a virtual representation of the WlbScheduledTasks collection, in which aggregate days /// are separated into individual days. The entire list is also presorted chronologically. /// public SortedDictionary VirtualTaskList { get { SortedDictionary virtualTasks = new SortedDictionary(); foreach (WlbScheduledTask task in _tasks.Values) { if (!task.DeleteTask) { foreach (WlbScheduledTask.WlbTaskDaysOfWeek dayValue in Enum.GetValues(typeof(WlbScheduledTask.WlbTaskDaysOfWeek))) { if (dayValue != WlbScheduledTask.WlbTaskDaysOfWeek.None && dayValue != WlbScheduledTask.WlbTaskDaysOfWeek.All && dayValue != WlbScheduledTask.WlbTaskDaysOfWeek.Weekdays && dayValue != WlbScheduledTask.WlbTaskDaysOfWeek.Weekends && ((task.DaysOfWeek & dayValue) == dayValue)) { DateTime localRunTime; WlbScheduledTask.WlbTaskDaysOfWeek localDaysOfWeek; WlbScheduledTask.GetLocalTaskTimes((task.DaysOfWeek & dayValue), task.RunTime, out localDaysOfWeek, out localRunTime); int sortKey = GetSortKey(localDaysOfWeek, localRunTime); //if the task is disabled, bump the sort key to prevent conficts // with any new tasks. This could start to get wierd after 100 duplicate // disabled tasks, but then it will be the user's problem. if (!task.Enabled) { sortKey += 1; while (virtualTasks.ContainsKey(sortKey)) { sortKey += 1; } } WlbScheduledTask virtualTask = task.Clone(); virtualTask.DaysOfWeek = dayValue; if (!virtualTasks.ContainsKey(sortKey)) { virtualTasks.Add(sortKey, virtualTask); } } } } } return virtualTasks; } } /// /// Creates an artificial sort key that is used to sort the presentation of scheduled tasks. /// /// WlbScheduledTask.DaysOfWeek enumeration of the task denoting on which days it will run /// DateTime run time of the task denoting on when the task /// private static int GetSortKey(WlbScheduledTask.WlbTaskDaysOfWeek localDaysOfWeek, DateTime localRunTime) { int sortKey; //The day of week is the primary sort item switch(localDaysOfWeek) { //Put ALL tasks at the front of the list case WlbScheduledTask.WlbTaskDaysOfWeek.All: { sortKey = 0; break; } //Next are WEEKDAY tasks case WlbScheduledTask.WlbTaskDaysOfWeek.Weekdays: { sortKey = 200000; break; } //Next are WEEKEND tasks case WlbScheduledTask.WlbTaskDaysOfWeek.Weekends: { sortKey = 400000; break; } //Finally, single-day tasks default: { sortKey = (int)localDaysOfWeek * 1000000; break; } } //Add the run time of day as a secondary sort item //Multiply it by 100 to allow room for disabled tasks sortKey += (int)localRunTime.TimeOfDay.TotalMinutes * 100; return sortKey; } public WlbScheduledTasks(Dictionary Configuration) { foreach (string key in Configuration.Keys) { if (key.StartsWith("schedTask_")) { string[] keyElements = key.Split('_'); string taskId = keyElements[1]; if (!_tasks.ContainsKey(taskId)) { _tasks.Add(taskId, new WlbScheduledTask(taskId)); _tasks[taskId].AddParameter(key, Configuration[key]); } else { _tasks[taskId].AddParameter(key, Configuration[key]); } } } } public Dictionary ToDictionary() { Dictionary collectionDictionary = null; foreach (WlbScheduledTask scheduleTask in _tasks.Values) { Dictionary taskDictionary = scheduleTask.ToDictionary(); foreach (string key in taskDictionary.Keys) { if (collectionDictionary == null) { collectionDictionary = new Dictionary(); } collectionDictionary.Add(key, taskDictionary[key]); } if (collectionDictionary != null) { foreach (var key in scheduleTask.TaskParameters.Keys) { var complexKey = $"{scheduleTask.KeyBase}_{scheduleTask.TaskId}_{key}"; collectionDictionary[complexKey] = scheduleTask.TaskParameters[key]; } } } return collectionDictionary; } public WlbScheduledTask GetNextRunningTask() { WlbScheduledTask firstTask = null; int currentTimeSortKey = GetSortKey(WlbScheduledTask.ConvertToWlbTaskDayOfWeek(DateTime.Now.DayOfWeek), DateTime.Now); foreach (int key in this.VirtualTaskList.Keys) { //only consider Enabled tasks if (this.VirtualTaskList[key].Enabled) { if (null == firstTask) { firstTask = this.VirtualTaskList[key]; } if (key > currentTimeSortKey) { return this.VirtualTaskList[key]; } } } //we are still here, so we have not found the next task. this means that we // need to account for week wrapping. This shoudl be the first task of the Virtual // Task List return firstTask; } public WlbScheduledTask GetLastRunningTask() { int[] taskKeys = new int[this.VirtualTaskList.Keys.Count]; this.VirtualTaskList.Keys.CopyTo(taskKeys,0); WlbScheduledTask lastTask = this.VirtualTaskList[taskKeys[taskKeys.Length-1]]; int currentTimeSortKey = GetSortKey(WlbScheduledTask.ConvertToWlbTaskDayOfWeek(DateTime.Now.DayOfWeek), DateTime.Now); for (int i = taskKeys.Length-1; i>=0; i--) { //Only consider Enabled tasks if (this.VirtualTaskList[taskKeys[i]].Enabled) { if (taskKeys[i] < currentTimeSortKey) { return this.VirtualTaskList[taskKeys[i]]; } } } //we are still here, so we have not found the previous task. this means that we // need to account for week wrapping. This should be the last task of the Virtual // Task List return lastTask; } public WlbPoolPerformanceMode GetCurrentScheduledPerformanceMode() { WlbScheduledTask lastTask = GetLastRunningTask(); return (WlbPoolPerformanceMode)int.Parse(lastTask.TaskParameters["OptMode"]); } } }