// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // using System; using System.Collections.Generic; using System.Data; using Microsoft.SqlServer.Management.Common; using Microsoft.SqlServer.Management.Smo.Agent; using Microsoft.SqlTools.ServiceLayer.Agent.Contracts; using Microsoft.SqlTools.ServiceLayer.Management; namespace Microsoft.SqlTools.ServiceLayer.Agent { /// /// Proxy class for the AgentOperators dialog and property pages. /// Performs lazy instantiation of groups of data based around the operators dialog property pages /// internal class AgentOperatorsData { #region members /// /// Data container /// CDataContainer dataContainer; /// /// Original operator name. Empty if we are creating a new operator /// string originalOperatorName = string.Empty; /// /// Indicates whether we are creating an operator or not /// bool createMode; /// /// Has then data for the general page been initialised /// bool generalInitialized = false; /// /// has the data for the history page been initialised /// bool historyInitialized = false; /// /// True if this operator cannot be modified /// bool readOnly = false; #region general items string name; bool enabled; string emailAddress; string pagerAddress; SqlServer.Management.Smo.Agent.WeekDays pagerDays; DateTime weekdayStartTime; DateTime weekdayEndTime; DateTime saturdayStartTime; DateTime saturdayEndTime; DateTime sundayStartTime; DateTime sundayEndTime; #endregion #region notification items /// /// will be null if the alert notifications have not been initialised /// IList alertNotifications; /// /// will be null if the job notifications have not been initialised /// IList jobNotifications; #endregion #region history items DateTime lastEmailDate; DateTime lastPagerDate; #endregion #endregion #region properties /// /// the current Operator SMO object /// public Operator Operator { get { JobServer jobServer = GetJobServer(); return jobServer.Operators[this.originalOperatorName]; } } /// /// indicates if the data is in create mode /// public bool Creating { get { return this.createMode; } } /// /// name of the object /// public string Name { get { LoadGeneralData(); return name; } set { LoadGeneralData(); name = value; } } /// /// Indicates if the dataobject is readonly /// public bool ReadOnly { get { return this.readOnly; } } #region general items /// /// indicates whether or not the operator is enabled /// public bool Enabled { get { LoadGeneralData(); return enabled; } set { LoadGeneralData(); enabled = value; } } /// /// email address of this operator /// public string EmailAddress { get { LoadGeneralData(); return this.emailAddress; } set { LoadGeneralData(); this.emailAddress = value; } } /// /// pager address of this operator /// public string PagerAddress { get { LoadGeneralData(); return this.pagerAddress; } set { LoadGeneralData(); this.pagerAddress = value; } } /// /// the days of the week the operator is active /// public SqlServer.Management.Smo.Agent.WeekDays PagerDays { get { LoadGeneralData(); return this.pagerDays; } set { LoadGeneralData(); this.pagerDays = value; } } /// /// Weekday start time for this operator to be active /// public DateTime WeekdayStartTime { get { LoadGeneralData(); return this.weekdayStartTime; } set { LoadGeneralData(); this.weekdayStartTime = value; } } /// /// Weekday end time for this operator to be active /// public DateTime WeekdayEndTime { get { LoadGeneralData(); return this.weekdayEndTime; } set { LoadGeneralData(); this.weekdayEndTime = value; } } /// /// Saturday start time for this operator to be active /// public DateTime SaturdayStartTime { get { LoadGeneralData(); return this.saturdayStartTime; } set { LoadGeneralData(); this.saturdayStartTime = value; } } /// /// Saturday end time for this operator to be active /// public DateTime SaturdayEndTime { get { LoadGeneralData(); return this.saturdayEndTime; } set { LoadGeneralData(); this.saturdayEndTime = value; } } /// /// Sunday start time for this operator to be active /// public DateTime SundayStartTime { get { LoadGeneralData(); return this.sundayStartTime; } set { LoadGeneralData(); this.sundayStartTime = value; } } /// /// Saturday end time for this operator to be active /// public DateTime SundayEndTime { get { LoadGeneralData(); return this.sundayEndTime; } set { LoadGeneralData(); this.sundayEndTime = value; } } #endregion #region notification items /// /// Alerts that notify this operator /// public IList AlertNotifications { get { LoadAlertNotificationData(); return this.alertNotifications; } set { this.alertNotifications = value; } } /// /// Jobs that notify this operator. This has to be set through the jobs dialog and is read only /// public IList JobNotifications { get { LoadJobNotificationData(); return this.jobNotifications; } } #endregion #region history items /// /// Date this operator was last emailed /// public DateTime LastEmailDate { get { LoadHistoryData(); return this.lastEmailDate; } } /// /// Date this operator was last paged /// public DateTime LastPagerDate { get { LoadHistoryData(); return this.lastPagerDate; } } #endregion #endregion #region Constructors public AgentOperatorsData(CDataContainer dataContainer, string operatorName, bool createMode) { if (dataContainer == null) { throw new ArgumentNullException("dataContainer"); } if (operatorName == null) { throw new ArgumentNullException("operatorName"); } this.dataContainer = dataContainer; this.readOnly = !this.dataContainer.Server.ConnectionContext.IsInFixedServerRole(FixedServerRoles.SysAdmin); this.originalOperatorName = operatorName; this.name = operatorName; this.createMode = createMode; } #endregion #region data loading /// /// load data for the general tab. This can be called multiple times but will only load the data once intially /// or after a reset /// private void LoadGeneralData() { if(this.generalInitialized) return; // load defaults if we're creating if(createMode) { LoadGeneralDefaults(); return; } // lookup the operator this will throw if it has been deleted. Microsoft.SqlServer.Management.Smo.Agent.Operator currentOperator = GetCurrentOperator(); // setup the members this.name = currentOperator.Name; this.enabled = currentOperator.Enabled; this.emailAddress = currentOperator.EmailAddress; this.pagerAddress = currentOperator.PagerAddress; this.pagerDays = currentOperator.PagerDays; this.weekdayStartTime = ConvertAgentTime(currentOperator.WeekdayPagerStartTime); this.weekdayEndTime = ConvertAgentTime(currentOperator.WeekdayPagerEndTime); this.saturdayStartTime = ConvertAgentTime(currentOperator.SaturdayPagerStartTime); this.saturdayEndTime = ConvertAgentTime(currentOperator.SaturdayPagerEndTime); this.sundayStartTime = ConvertAgentTime(currentOperator.SundayPagerStartTime); this.sundayEndTime = ConvertAgentTime(currentOperator.SundayPagerEndTime); this.generalInitialized = true; } /// /// Load the data for the jobs that notify this operator. Can be called multiple times and will /// only load the data initially, or after a reset. /// private void LoadJobNotificationData() { if(this.jobNotifications != null) return; // just set defaults if we're creating as no jobs will point to this operator yet. if(createMode) { LoadJobNotificationDefaults(); return; } JobServer jobServer = GetJobServer(); this.jobNotifications = new List(); // we have to loop through each job and see if it notifies us. foreach(Job job in jobServer.Jobs) { bool emailOperator = (job.OperatorToEmail == this.originalOperatorName); bool pageOperator = (job.OperatorToPage == this.originalOperatorName); if(emailOperator || pageOperator ) { // only return jobs that notify this operator AgentJobNotificationHelper notification = new AgentJobNotificationHelper(job.Name , job.EmailLevel , job.PageLevel ); this.jobNotifications.Add(notification); } } } /// /// Load alerts that notify this operator /// private void LoadAlertNotificationData() { if (this.alertNotifications != null) { return; } // defaults in create ode if (createMode) { LoadAlertNotificationDefaults(); return; } this.alertNotifications = new List(); Microsoft.SqlServer.Management.Smo.Agent.Operator agentOperator = GetCurrentOperator(); JobServer jobServer = GetJobServer(); // see all alerts that notifuy this operator DataTable notifications = agentOperator.EnumNotifications(); DataRow alertRow; bool notifyEmail; bool notifyPager; AgentAlertNotificationHelper alertNotification; // Add every alert to the structure foreach (Alert alert in jobServer.Alerts) { alertRow = null; // see if the alert notifies us already foreach (DataRow row in notifications.Rows) { if((string)row["AlertName"] == alert.Name) { alertRow = row; break; } } // set if the current alert notifies this operator // if so so how if (alertRow != null) { notifyEmail = (bool)alertRow["UseEmail"]; notifyPager = (bool)alertRow["UsePager"]; } else { notifyEmail = false; notifyPager = false; } alertNotification = new AgentAlertNotificationHelper(alert.Name ,notifyEmail ,notifyPager ,alert); this.alertNotifications.Add(alertNotification); } } /// /// load the notifiaction history for the operator /// private void LoadHistoryData() { if (this.historyInitialized) { return; } if (this.createMode) { LoadHistoryDefaults(); return; } Microsoft.SqlServer.Management.Smo.Agent.Operator currentOperator = GetCurrentOperator(); this.lastEmailDate = currentOperator.LastEmailDate; this.lastPagerDate = currentOperator.LastPagerDate; } #endregion #region saving /// /// apply any changes to the operator. If the operator does not exist create it. /// public void ApplyChanges(AgentOperatorInfo operatorInfo) { // do nothing if we are read only if (this.readOnly) { return; } JobServer jobServer = GetJobServer(); // get the operator. This will create a new one if it does not already exist Microsoft.SqlServer.Management.Smo.Agent.Operator currentOperator = GetCurrentOperator(); // general tab currentOperator.Enabled = operatorInfo.Enabled; if (!string.IsNullOrWhiteSpace(operatorInfo.EmailAddress)) { currentOperator.EmailAddress = operatorInfo.EmailAddress; } if (!string.IsNullOrWhiteSpace(operatorInfo.PagerAddress)) { currentOperator.PagerAddress = operatorInfo.PagerAddress; } currentOperator.PagerDays = this.pagerDays; if ((operatorInfo.PagerDays & Contracts.WeekDays.WeekDays) > 0) { TimeSpan weekdayPagerStartTime = default(TimeSpan); if (!string.IsNullOrWhiteSpace(operatorInfo.WeekdayPagerStartTime)) { weekdayPagerStartTime = TimeSpan.Parse(operatorInfo.WeekdayPagerStartTime); } TimeSpan weekdayPagerEndTime = default(TimeSpan); if (!string.IsNullOrWhiteSpace(operatorInfo.WeekdayPagerEndTime)) { weekdayPagerEndTime = TimeSpan.Parse(operatorInfo.WeekdayPagerEndTime); } currentOperator.WeekdayPagerStartTime = weekdayPagerStartTime; currentOperator.WeekdayPagerEndTime = weekdayPagerEndTime; } if ((operatorInfo.PagerDays & Contracts.WeekDays.Saturday) > 0) { TimeSpan saturdayPagerStartTime = default(TimeSpan); if (!string.IsNullOrWhiteSpace(operatorInfo.SaturdayPagerStartTime)) { saturdayPagerStartTime = TimeSpan.Parse(operatorInfo.SaturdayPagerStartTime); } TimeSpan saturdayPagerEndTime = default(TimeSpan); if (!string.IsNullOrWhiteSpace(operatorInfo.SaturdayPagerEndTime)) { saturdayPagerEndTime = TimeSpan.Parse(operatorInfo.SaturdayPagerEndTime); } currentOperator.SaturdayPagerStartTime = saturdayPagerStartTime; currentOperator.SaturdayPagerEndTime = saturdayPagerEndTime; } if ((operatorInfo.PagerDays & Contracts.WeekDays.Sunday) > 0) { TimeSpan sundayPagerStartTime = default(TimeSpan); if (!string.IsNullOrWhiteSpace(operatorInfo.SundayPagerStartTime)) { sundayPagerStartTime = TimeSpan.Parse(operatorInfo.SundayPagerStartTime); } TimeSpan sundayPagerEndTime = default(TimeSpan); if (!string.IsNullOrWhiteSpace(operatorInfo.SundayPagerEndTime)) { sundayPagerEndTime = TimeSpan.Parse(operatorInfo.SundayPagerEndTime); } currentOperator.SundayPagerStartTime = sundayPagerStartTime; currentOperator.SundayPagerEndTime = sundayPagerEndTime; } if (this.createMode) { // create the object currentOperator.Create(); this.originalOperatorName = this.name; } else { // alter the object currentOperator.Alter(); } // only set this up if the notifications has been set if (this.alertNotifications != null) { SqlServer.Management.Smo.Agent.NotifyMethods notifyMethods; for (int i = 0; i < alertNotifications.Count; ++i) { notifyMethods = 0; if (alertNotifications[i].NotifyEmail) { notifyMethods |= SqlServer.Management.Smo.Agent.NotifyMethods.NotifyEmail; } if (alertNotifications[i].NotifyPager) { notifyMethods |= SqlServer.Management.Smo.Agent.NotifyMethods.Pager; } bool alertAlreadyNotifiesOperator = false; // if we're not creating see if the current alert already notifies this operator if (!createMode) { DataTable notifications = alertNotifications[i].Alert.EnumNotifications(this.originalOperatorName); if (notifications.Rows.Count > 0) { alertAlreadyNotifiesOperator = true; } } // either update or clear existing notifications if (alertAlreadyNotifiesOperator) { if(notifyMethods != SqlServer.Management.Smo.Agent.NotifyMethods.None) { alertNotifications[i].Alert.UpdateNotification(this.originalOperatorName, notifyMethods); } else { alertNotifications[i].Alert.RemoveNotification(this.originalOperatorName); } } else if(notifyMethods != SqlServer.Management.Smo.Agent.NotifyMethods.None) { // add a new notification alertNotifications[i].Alert.AddNotification(this.originalOperatorName, notifyMethods); } } } // see if we need to rename. This has to be done last otherwise any scripts generated will be incorrect. if (!this.createMode && currentOperator.Name != this.originalOperatorName) { currentOperator.Rename(this.name); if(this.dataContainer.Server.ConnectionContext.SqlExecutionModes != SqlExecutionModes.CaptureSql) { this.originalOperatorName = this.name; } } // update state if we aren't scripting if (this.createMode && this.dataContainer.Server.ConnectionContext.SqlExecutionModes != SqlExecutionModes.CaptureSql) { this.createMode = false; } } #endregion #region reset /// /// Reset the object to it's original state / reload any data from the erver /// public void Reset() { JobServer jobServer = GetJobServer(); this.generalInitialized = false; if (this.jobNotifications != null) { // ensure the individual jobs are reset also jobServer.Jobs.Refresh(true); this.jobNotifications = null; } if (this.alertNotifications != null) { // ensure the individual jobs are reset also jobServer.Alerts.Refresh(true); this.alertNotifications = null; } this.historyInitialized = false; } #endregion #region defaults /// /// set general tab defaults /// private void LoadGeneralDefaults() { name = string.Empty; this.emailAddress = string.Empty; this.pagerAddress = string.Empty; enabled = true; pagerDays = 0; weekdayStartTime = saturdayStartTime = sundayStartTime = new DateTime(2000, 1, 1, 8, 0, 0); weekdayEndTime = saturdayEndTime = sundayEndTime = new DateTime(2000, 1, 1, 18, 0, 0); this.generalInitialized = true; } /// /// Set job notification defaults. This is just an empty list /// private void LoadJobNotificationDefaults() { this.jobNotifications = new List(); } /// /// set the alert notification defaults. This list will contain all of the alerts /// private void LoadAlertNotificationDefaults() { this.alertNotifications = new List(); JobServer jobServer = GetJobServer(); AgentAlertNotificationHelper alertNotification; foreach(Alert alert in jobServer.Alerts) { alertNotification = new AgentAlertNotificationHelper(alert.Name, notifyEmail:false, notifyPager:false, alert: alert); this.alertNotifications.Add(alertNotification); } } /// /// load defaults for the history page /// private void LoadHistoryDefaults() { this.lastEmailDate = DateTime.MinValue; this.lastPagerDate = DateTime.MinValue; this.historyInitialized = true; } #endregion #region helpers /// /// Get the job server. Will throw if it is not available /// /// Job server object private JobServer GetJobServer() { JobServer jobServer = this.dataContainer.Server.JobServer; if (jobServer == null) { throw new ApplicationException(SR.JobServerIsNotAvailable); } return jobServer; } /// /// Get the current operator. If we are creating this will be a new operator. If we are modifying /// an existing operator it will be the existing operator, and will throw if the operator has been /// deleted. /// /// Operator object private Operator GetCurrentOperator() { JobServer jobServer = GetJobServer(); Operator currentOperator = jobServer.Operators[this.originalOperatorName]; this.createMode = (currentOperator == null); if (this.createMode) { currentOperator = new Microsoft.SqlServer.Management.Smo.Agent.Operator(jobServer, this.name); } return currentOperator; } /// /// /// /// /// static public TimeSpan ConvertAgentTime(DateTime dateTime) { return new TimeSpan(dateTime.Hour, dateTime.Minute, dateTime.Second); } /// /// /// /// /// static public DateTime ConvertAgentTime(TimeSpan dateTime) { return new DateTime(2000, 1, 1, dateTime.Hours, dateTime.Minutes, dateTime.Seconds); } /// /// /// /// /// static public int ConvertAgentTimeToInt(DateTime dateTime) { return dateTime.Hour * 10000 + dateTime.Minute * 100 + dateTime.Second; } /// /// /// /// /// static public DateTime ConvertAgentTime(int dateTime) { return new DateTime(2000, 1, 1, (int)(dateTime / 10000), (int)((dateTime - (dateTime / 10000) * 10000) / 100), (int)(dateTime - (dateTime / 100) * 100)); } #endregion } #region internal structures /// /// Provides data to be consumed in the job notification grid /// internal struct AgentJobNotificationHelper { /// /// constructor /// /// job name /// /// public AgentJobNotificationHelper(string name, CompletionAction notifyEmail, CompletionAction notifyPager) { this.Name = name; this.NotifyEmail = notifyEmail; this.NotifyPager = notifyPager; } /// /// Name of the job /// public string Name; /// /// job email notification action /// public CompletionAction NotifyEmail; /// /// job pager notification action /// public CompletionAction NotifyPager; } /// /// Provides data to be consumed in the alert notification grid /// internal struct AgentAlertNotificationHelper { /// /// constructor /// /// Name of the alert /// /// /// Alert object public AgentAlertNotificationHelper(string name, bool notifyEmail, bool notifyPager, Alert alert) { this.Name = name; this.NotifyEmail = notifyEmail; this.NotifyPager = notifyPager; this.Alert = alert; } /// /// Alert name /// public string Name; /// /// Indicates whether the alert will notify the operator through email /// public bool NotifyEmail; /// /// Indicates whether the alert will notify the operator through pager /// public bool NotifyPager; /// /// Alert object. optimisation to stop us having to lookup the alert object when needed /// public Alert Alert; } #endregion }