diff --git a/src/Microsoft.SqlTools.ServiceLayer/Admin/AdminService.cs b/src/Microsoft.SqlTools.ServiceLayer/Admin/AdminService.cs
index 316cad97..56a2bc78 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Admin/AdminService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Admin/AdminService.cs
@@ -190,12 +190,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
}
///
- /// Create database task helper
+ /// Create a data container object
///
/// connection info
/// flag indicating whether to create taskhelper for existing database or not
- ///
- internal static DatabaseTaskHelper CreateDatabaseTaskHelper(ConnectionInfo connInfo, bool databaseExists = false)
+ internal static CDataContainer CreateDataContainer(ConnectionInfo connInfo, bool databaseExists = false)
{
XmlDocument xmlDoc = CreateDataContainerDocument(connInfo, databaseExists);
CDataContainer dataContainer;
@@ -232,6 +231,18 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
xmlDoc.InnerXml);
}
+ return dataContainer;
+ }
+
+ ///
+ /// Create database task helper
+ ///
+ /// connection info
+ /// flag indicating whether to create taskhelper for existing database or not
+ ///
+ internal static DatabaseTaskHelper CreateDatabaseTaskHelper(ConnectionInfo connInfo, bool databaseExists = false)
+ {
+ var dataContainer = CreateDataContainer(connInfo, databaseExists);
var taskHelper = new DatabaseTaskHelper(dataContainer);
return taskHelper;
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Admin/Common/IManagedConnection.cs b/src/Microsoft.SqlTools.ServiceLayer/Admin/Common/IManagedConnection.cs
index fcb9b5f4..f078637b 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Admin/Common/IManagedConnection.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Admin/Common/IManagedConnection.cs
@@ -1,4 +1,9 @@
-using System;
+//
+// 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;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Diagnostics;
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Admin/Common/Utils.cs b/src/Microsoft.SqlTools.ServiceLayer/Admin/Common/Utils.cs
index 104fd42c..c53c2eeb 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Admin/Common/Utils.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Admin/Common/Utils.cs
@@ -1,6 +1,7 @@
-//------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//------------------------------------------------------------
+//
+// 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.Text;
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/AgentService.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/AgentService.cs
index 0f6c545b..d94698b0 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Agent/AgentService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/AgentService.cs
@@ -13,6 +13,7 @@ using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Smo.Agent;
using Microsoft.SqlTools.Hosting.Protocol;
+using Microsoft.SqlTools.ServiceLayer.Admin;
using Microsoft.SqlTools.ServiceLayer.Agent.Contracts;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Hosting;
@@ -21,10 +22,17 @@ using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.Agent
{
///
- /// Main class for Profiler Service functionality
+ /// Main class for Agent Service functionality
///
- public sealed class AgentService
+ public class AgentService
{
+ internal enum AgentConfigAction
+ {
+ Create,
+ Update,
+ Drop
+ }
+
private Dictionary jobs = null;
private ConnectionService connectionService = null;
private static readonly Lazy instance = new Lazy(() => new AgentService());
@@ -64,7 +72,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
}
}
-
///
/// Service host object for sending/receiving requests/events.
/// Internal for testing purposes.
@@ -84,47 +91,70 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
this.ServiceHost.SetRequestHandler(AgentJobsRequest.Type, HandleAgentJobsRequest);
this.ServiceHost.SetRequestHandler(AgentJobHistoryRequest.Type, HandleJobHistoryRequest);
this.ServiceHost.SetRequestHandler(AgentJobActionRequest.Type, HandleJobActionRequest);
+
+ // Alerts request handlers
+ this.ServiceHost.SetRequestHandler(AgentAlertsRequest.Type, HandleAgentAlertsRequest);
+ this.ServiceHost.SetRequestHandler(CreateAgentAlertRequest.Type, HandleCreateAgentAlertRequest);
+ this.ServiceHost.SetRequestHandler(UpdateAgentAlertRequest.Type, HandleUpdateAgentAlertRequest);
+ this.ServiceHost.SetRequestHandler(DeleteAgentAlertRequest.Type, HandleDeleteAgentAlertRequest);
+
+ // Operators request handlers
+ this.ServiceHost.SetRequestHandler(AgentOperatorsRequest.Type, HandleAgentOperatorsRequest);
+ this.ServiceHost.SetRequestHandler(CreateAgentOperatorRequest.Type, HandleCreateAgentOperatorRequest);
+ this.ServiceHost.SetRequestHandler(UpdateAgentOperatorRequest.Type, HandleUpdateAgentOperatorRequest);
+ this.ServiceHost.SetRequestHandler(DeleteAgentOperatorRequest.Type, HandleDeleteAgentOperatorRequest);
+
+ // Proxy Accounts request handlers
+ this.ServiceHost.SetRequestHandler(AgentProxiesRequest.Type, HandleAgentProxiesRequest);
+ this.ServiceHost.SetRequestHandler(CreateAgentProxyRequest.Type, HandleCreateAgentProxyRequest);
+ this.ServiceHost.SetRequestHandler(UpdateAgentProxyRequest.Type, HandleUpdateAgentProxyRequest);
+ this.ServiceHost.SetRequestHandler(DeleteAgentProxyRequest.Type, HandleDeleteAgentProxyRequest);
}
+
+ #region "Jobs Handlers"
///
/// Handle request to get Agent job activities
///
internal async Task HandleAgentJobsRequest(AgentJobsParams parameters, RequestContext requestContext)
{
- try
+ await Task.Run(async () =>
{
- var result = new AgentJobsResult();
- ConnectionInfo connInfo;
- ConnectionServiceInstance.TryFindConnection(
- parameters.OwnerUri,
- out connInfo);
-
- if (connInfo != null)
+ try
{
- var sqlConnection = ConnectionService.OpenSqlConnection(connInfo);
- var serverConnection = new ServerConnection(sqlConnection);
- var fetcher = new JobFetcher(serverConnection);
- var filter = new JobActivityFilter();
- this.jobs = fetcher.FetchJobs(filter);
- var agentJobs = new List();
- if (this.jobs != null)
+ var result = new AgentJobsResult();
+ ConnectionInfo connInfo;
+ ConnectionServiceInstance.TryFindConnection(
+ parameters.OwnerUri,
+ out connInfo);
+
+ if (connInfo != null)
{
-
- foreach (var job in this.jobs.Values)
+ var sqlConnection = ConnectionService.OpenSqlConnection(connInfo);
+ var serverConnection = new ServerConnection(sqlConnection);
+ var fetcher = new JobFetcher(serverConnection);
+ var filter = new JobActivityFilter();
+ this.jobs = fetcher.FetchJobs(filter);
+ var agentJobs = new List();
+ if (this.jobs != null)
{
- agentJobs.Add(JobUtilities.ConvertToAgentJobInfo(job));
+
+ foreach (var job in this.jobs.Values)
+ {
+ agentJobs.Add(JobUtilities.ConvertToAgentJobInfo(job));
+ }
}
+ result.Succeeded = true;
+ result.Jobs = agentJobs.ToArray();
+ sqlConnection.Close();
}
- result.Succeeded = true;
- result.Jobs = agentJobs.ToArray();
- sqlConnection.Close();
+ await requestContext.SendResult(result);
}
- await requestContext.SendResult(result);
- }
- catch (Exception e)
- {
- await requestContext.SendError(e);
- }
+ catch (Exception e)
+ {
+ await requestContext.SendError(e);
+ }
+ });
}
///
@@ -132,44 +162,47 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
///
internal async Task HandleJobHistoryRequest(AgentJobHistoryParams parameters, RequestContext requestContext)
{
- try
+ await Task.Run(async () =>
{
- var result = new AgentJobHistoryResult();
- ConnectionInfo connInfo;
- ConnectionServiceInstance.TryFindConnection(
- parameters.OwnerUri,
- out connInfo);
- if (connInfo != null)
+ try
{
- Tuple tuple = CreateSqlConnection(connInfo, parameters.JobId);
- SqlConnectionInfo sqlConnInfo = tuple.Item1;
- DataTable dt = tuple.Item2;
- ServerConnection connection = tuple.Item3;
- int count = dt.Rows.Count;
- List jobHistories = new List();
- if (count > 0)
+ var result = new AgentJobHistoryResult();
+ ConnectionInfo connInfo;
+ ConnectionServiceInstance.TryFindConnection(
+ parameters.OwnerUri,
+ out connInfo);
+ if (connInfo != null)
{
- var job = dt.Rows[0];
- string jobName = Convert.ToString(job[JobUtilities.UrnJobName], System.Globalization.CultureInfo.InvariantCulture);
- Guid jobId = (Guid) job[JobUtilities.UrnJobId];
- int runStatus = Convert.ToInt32(job[JobUtilities.UrnRunStatus], System.Globalization.CultureInfo.InvariantCulture);
- var t = new LogSourceJobHistory(jobName, sqlConnInfo, null, runStatus, jobId, null);
- var tlog = t as ILogSource;
- tlog.Initialize();
- var logEntries = t.LogEntries;
- jobHistories = JobUtilities.ConvertToAgentJobHistoryInfo(logEntries, job);
- tlog.CloseReader();
+ Tuple tuple = CreateSqlConnection(connInfo, parameters.JobId);
+ SqlConnectionInfo sqlConnInfo = tuple.Item1;
+ DataTable dt = tuple.Item2;
+ ServerConnection connection = tuple.Item3;
+ int count = dt.Rows.Count;
+ List jobHistories = new List();
+ if (count > 0)
+ {
+ var job = dt.Rows[0];
+ string jobName = Convert.ToString(job[JobUtilities.UrnJobName], System.Globalization.CultureInfo.InvariantCulture);
+ Guid jobId = (Guid) job[JobUtilities.UrnJobId];
+ int runStatus = Convert.ToInt32(job[JobUtilities.UrnRunStatus], System.Globalization.CultureInfo.InvariantCulture);
+ var t = new LogSourceJobHistory(jobName, sqlConnInfo, null, runStatus, jobId, null);
+ var tlog = t as ILogSource;
+ tlog.Initialize();
+ var logEntries = t.LogEntries;
+ jobHistories = JobUtilities.ConvertToAgentJobHistoryInfo(logEntries, job);
+ tlog.CloseReader();
+ }
+ result.Jobs = jobHistories.ToArray();
+ result.Succeeded = true;
+ connection.Disconnect();
+ await requestContext.SendResult(result);
}
- result.Jobs = jobHistories.ToArray();
- result.Succeeded = true;
- connection.Disconnect();
- await requestContext.SendResult(result);
}
- }
- catch (Exception e)
- {
- await requestContext.SendError(e);
- }
+ catch (Exception e)
+ {
+ await requestContext.SendError(e);
+ }
+ });
}
///
@@ -177,49 +210,52 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
///
internal async Task HandleJobActionRequest(AgentJobActionParams parameters, RequestContext requestContext)
{
- var result = new AgentJobActionResult();
- try
+ await Task.Run(async () =>
{
- ConnectionInfo connInfo;
- ConnectionServiceInstance.TryFindConnection(
- parameters.OwnerUri,
- out connInfo);
- if (connInfo != null)
+ var result = new AgentJobActionResult();
+ try
{
- var sqlConnection = ConnectionService.OpenSqlConnection(connInfo);
- var serverConnection = new ServerConnection(sqlConnection);
- var jobHelper = new JobHelper(serverConnection);
- jobHelper.JobName = parameters.JobName;
- switch(parameters.Action)
+ ConnectionInfo connInfo;
+ ConnectionServiceInstance.TryFindConnection(
+ parameters.OwnerUri,
+ out connInfo);
+ if (connInfo != null)
{
- case "run":
- jobHelper.Start();
- break;
- case "stop":
- jobHelper.Stop();
- break;
- case "delete":
- jobHelper.Delete();
- break;
- case "enable":
- jobHelper.Enable(true);
- break;
- case "disable":
- jobHelper.Enable(false);
- break;
- default:
- break;
+ var sqlConnection = ConnectionService.OpenSqlConnection(connInfo);
+ var serverConnection = new ServerConnection(sqlConnection);
+ var jobHelper = new JobHelper(serverConnection);
+ jobHelper.JobName = parameters.JobName;
+ switch(parameters.Action)
+ {
+ case "run":
+ jobHelper.Start();
+ break;
+ case "stop":
+ jobHelper.Stop();
+ break;
+ case "delete":
+ jobHelper.Delete();
+ break;
+ case "enable":
+ jobHelper.Enable(true);
+ break;
+ case "disable":
+ jobHelper.Enable(false);
+ break;
+ default:
+ break;
+ }
+ result.Succeeded = true;
+ await requestContext.SendResult(result);
}
- result.Succeeded = true;
+ }
+ catch (Exception e)
+ {
+ result.Succeeded = false;
+ result.ErrorMessage = e.Message;
await requestContext.SendResult(result);
}
- }
- catch (Exception e)
- {
- result.Succeeded = false;
- result.ErrorMessage = e.Message;
- await requestContext.SendResult(result);
- }
+ });
}
private Tuple CreateSqlConnection(ConnectionInfo connInfo, String jobId)
@@ -234,5 +270,268 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
return new Tuple(sqlConnInfo, dt, serverConnection);
}
+ #endregion // "Jobs Handlers"
+
+ #region "Alert Handlers"
+
+ ///
+ /// Handle request to get the alerts list
+ ///
+ internal async Task HandleAgentAlertsRequest(AgentAlertsParams parameters, RequestContext requestContext)
+ {
+ await Task.Run(async () =>
+ {
+ var result = new AgentAlertsResult();
+ result.Alerts = new List().ToArray();
+
+ ConnectionInfo connInfo;
+ ConnectionServiceInstance.TryFindConnection(
+ parameters.OwnerUri,
+ out connInfo);
+
+ if (connInfo != null)
+ {
+ CDataContainer dataContainer = AdminService.CreateDataContainer(connInfo, databaseExists: true);
+ AlertCollection alerts = dataContainer.Server.JobServer.Alerts;
+
+
+ }
+
+ await requestContext.SendResult(result);
+ });
+ }
+
+ private bool ValidateAgentAlertInfo(AgentAlertInfo alert)
+ {
+ return alert != null
+ && !string.IsNullOrWhiteSpace(alert.JobName);
+ }
+
+ ///
+ /// Handle request to create an alert
+ ///
+ internal async Task HandleCreateAgentAlertRequest(CreateAgentAlertParams parameters, RequestContext requestContext)
+ {
+ await Task.Run(async () =>
+ {
+ var result = new CreateAgentAlertResult();
+ ConnectionInfo connInfo;
+ ConnectionServiceInstance.TryFindConnection(
+ parameters.OwnerUri,
+ out connInfo);
+
+ CreateOrUpdateAgentAlert(connInfo, parameters.Alert);
+
+ await requestContext.SendResult(result);
+ });
+ }
+
+ ///
+ /// Handle request to update an alert
+ ///
+ internal async Task HandleUpdateAgentAlertRequest(UpdateAgentAlertParams parameters, RequestContext requestContext)
+ {
+ await Task.Run(async () =>
+ {
+ var result = new UpdateAgentAlertResult();
+ ConnectionInfo connInfo;
+ ConnectionServiceInstance.TryFindConnection(
+ parameters.OwnerUri,
+ out connInfo);
+
+ CreateOrUpdateAgentAlert(connInfo, parameters.Alert);
+
+ await requestContext.SendResult(result);
+ });
+ }
+
+ private void CreateOrUpdateAgentAlert(ConnectionInfo connInfo, AgentAlertInfo alert)
+ {
+ if (connInfo != null && ValidateAgentAlertInfo(alert))
+ {
+ CDataContainer dataContainer = AdminService.CreateDataContainer(connInfo, databaseExists: true);
+ STParameters param = new STParameters(dataContainer.Document);
+ param.SetParam("alert", alert.JobName);
+
+ using (AgentAlert agentAlert = new AgentAlert(dataContainer, alert))
+ {
+ agentAlert.CreateOrUpdate();
+ }
+ }
+ }
+
+ ///
+ /// Handle request to delete an alert
+ ///
+ internal async Task HandleDeleteAgentAlertRequest(DeleteAgentAlertParams parameters, RequestContext requestContext)
+ {
+ await Task.Run(async () =>
+ {
+ var result = new DeleteAgentAlertResult();
+ ConnectionInfo connInfo;
+ ConnectionServiceInstance.TryFindConnection(
+ parameters.OwnerUri,
+ out connInfo);
+
+ AgentAlertInfo alert = parameters.Alert;
+ if (connInfo != null && ValidateAgentAlertInfo(alert))
+ {
+ CDataContainer dataContainer = AdminService.CreateDataContainer(connInfo, databaseExists: true);
+ STParameters param = new STParameters(dataContainer.Document);
+ param.SetParam("alert", alert.JobName);
+
+ using (AgentAlert agentAlert = new AgentAlert(dataContainer, alert))
+ {
+ agentAlert.Drop();
+ }
+ }
+
+ await requestContext.SendResult(result);
+ });
+ }
+
+ #endregion // "Alert Handlers"
+
+ #region "Operator Handlers"
+
+ internal async Task HandleAgentOperatorsRequest(AgentOperatorsParams parameters, RequestContext requestContext)
+ {
+ await requestContext.SendResult(null);
+ }
+
+ internal async Task HandleCreateAgentOperatorRequest(CreateAgentOperatorParams parameters, RequestContext requestContext)
+ {
+ await Task.Run(async () =>
+ {
+ var result = new CreateAgentOperatorResult();
+ ConnectionInfo connInfo;
+ ConnectionServiceInstance.TryFindConnection(
+ parameters.OwnerUri,
+ out connInfo);
+
+ AgentOperatorInfo operatorInfo = parameters.Operator;
+ CDataContainer dataContainer = AdminService.CreateDataContainer(connInfo, databaseExists: true);
+ STParameters param = new STParameters(dataContainer.Document);
+ param.SetParam("operator", operatorInfo.Name);
+
+ using (AgentOperator agentOperator = new AgentOperator(dataContainer, operatorInfo))
+ {
+ agentOperator.CreateOrUpdate();
+ }
+
+ await requestContext.SendResult(result);
+ });
+ }
+
+ internal async Task HandleUpdateAgentOperatorRequest(UpdateAgentOperatorParams parameters, RequestContext requestContext)
+ {
+ await requestContext.SendResult(null);
+ }
+
+ internal async Task HandleDeleteAgentOperatorRequest(DeleteAgentOperatorParams parameters, RequestContext requestContext)
+ {
+ await requestContext.SendResult(null);
+ }
+
+ #endregion // "Operator Handlers"
+
+
+ #region "Proxy Handlers"
+
+ internal async Task HandleAgentProxiesRequest(AgentProxiesParams parameters, RequestContext requestContext)
+ {
+ await requestContext.SendResult(null);
+ }
+
+ internal async Task HandleCreateAgentProxyRequest(CreateAgentProxyParams parameters, RequestContext requestContext)
+ {
+ bool succeeded = await ConfigureAgentProxy(
+ parameters.OwnerUri,
+ parameters.Proxy.AccountName,
+ parameters.Proxy,
+ AgentConfigAction.Create);
+
+ await requestContext.SendResult(new CreateAgentProxyResult()
+ {
+ Succeeded = succeeded
+ });
+ }
+
+ internal async Task HandleUpdateAgentProxyRequest(UpdateAgentProxyParams parameters, RequestContext requestContext)
+ {
+ bool succeeded = await ConfigureAgentProxy(
+ parameters.OwnerUri,
+ parameters.OriginalProxyName,
+ parameters.Proxy,
+ AgentConfigAction.Update);
+
+ await requestContext.SendResult(new UpdateAgentProxyResult()
+ {
+ Succeeded = succeeded
+ });
+ }
+
+ internal async Task HandleDeleteAgentProxyRequest(DeleteAgentProxyParams parameters, RequestContext requestContext)
+ {
+ bool succeeded = await ConfigureAgentProxy(
+ parameters.OwnerUri,
+ parameters.Proxy.AccountName,
+ parameters.Proxy,
+ AgentConfigAction.Drop);
+
+ await requestContext.SendResult(new DeleteAgentProxyResult()
+ {
+ Succeeded = succeeded
+ });
+ }
+
+ internal async Task ConfigureAgentProxy(
+ string ownerUri,
+ string accountName,
+ AgentProxyInfo proxy,
+ AgentConfigAction configAction)
+ {
+ return await Task.Run(() =>
+ {
+ try
+ {
+ ConnectionInfo connInfo;
+ ConnectionServiceInstance.TryFindConnection(
+ ownerUri,
+ out connInfo);
+
+ CDataContainer dataContainer = AdminService.CreateDataContainer(connInfo, databaseExists: true);
+ STParameters param = new STParameters(dataContainer.Document);
+ param.SetParam("proxyaccount", accountName);
+
+ using (AgentProxyAccount agentProxy = new AgentProxyAccount(dataContainer, proxy))
+ {
+ if (configAction == AgentConfigAction.Create)
+ {
+ return agentProxy.Create();
+ }
+ else if (configAction == AgentConfigAction.Update)
+ {
+ return agentProxy.Update();
+ }
+ else if (configAction == AgentConfigAction.Drop)
+ {
+ return agentProxy.Drop();
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ catch (Exception)
+ {
+ // log exception here
+ return false;
+ }
+ });
+ }
+
+ #endregion // "Proxy Handlers"
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/AgentActions.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/AgentActions.cs
new file mode 100644
index 00000000..d4380b21
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/AgentActions.cs
@@ -0,0 +1,976 @@
+//
+// 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.Data;
+using System.Drawing;
+using System.Threading;
+using System.Collections;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Xml;
+using Microsoft.SqlServer.Management.Sdk.Sfc;
+using Microsoft.SqlServer.Management.Common;
+using Microsoft.SqlServer.Management.Diagnostics;
+using Microsoft.SqlServer.Management.Smo;
+using Microsoft.SqlServer.Management.Smo.Agent;
+using Microsoft.SqlServer.Management.UI;
+using Microsoft.SqlTools.ServiceLayer.Admin;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent
+{
+
+ #region AgentAction class
+
+ ///
+ /// Main class all af the "immediate" agent actions derive from. These actions execute immediately
+ /// are not scriptable. We use the progress reporting dialog to give the user feedback on progress
+ /// etc.
+ ///
+ internal abstract class AgentAction
+ {
+ #region private members
+
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ protected Microsoft.SqlServer.Management.Smo.Server smoServer = null;
+ protected IManagedConnection managedConnection;
+ protected Urn[] urnParameters;
+ protected STParameters param = null;
+ protected ProgressItemCollection actions = new ProgressItemCollection();
+
+ #endregion
+
+ protected object ActionObject;
+
+ #region construction
+
+ public AgentAction(XmlDocument document, IServiceProvider source)
+ : this(document, source, null)
+ {
+ }
+
+ public AgentAction(XmlDocument document, IServiceProvider source, object actionObject)
+ {
+ // parameter check
+ if (document == null)
+ {
+ throw new ArgumentNullException("document");
+ }
+
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+
+ if (actionObject != null)
+ {
+ this.ActionObject = actionObject;
+ }
+
+ // get the managed connection
+ managedConnection = source.GetService(typeof (IManagedConnection)) as IManagedConnection;
+
+ // get the connection
+ SqlOlapConnectionInfoBase ci = managedConnection.Connection;
+ // get the server connection
+ ServerConnection serverConnection =
+ ((SqlConnectionInfoWithConnection) managedConnection.Connection).ServerConnection;
+
+ smoServer = new Microsoft.SqlServer.Management.Smo.Server(serverConnection);
+
+ // get the list or urn's that have been passed in
+ param = new STParameters(document);
+ StringCollection urnStrings = new StringCollection();
+
+ // get a list of urns that have been passed in.
+ param.GetParam("urn", urnStrings);
+
+ // store the Urn's as real Urns
+ urnParameters = new Urn[urnStrings.Count];
+ for (int i = 0; i < urnStrings.Count; i++)
+ {
+ urnParameters[i] = new Urn(urnStrings[i]);
+ }
+ }
+
+ protected void OnLoad(EventArgs e)
+ {
+ // ask derived classes to build a list of actions to be
+ // performed. Do not remove this call from OnLoad method!
+ GenerateActions();
+ }
+
+ #endregion
+
+ #region cleanup
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ protected void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (components != null)
+ {
+ components.Dispose();
+ }
+ }
+
+ if (this.managedConnection != null)
+ {
+ try
+ {
+ if (disposing)
+ {
+ this.managedConnection.Close();
+ }
+ this.managedConnection = null;
+ }
+ catch (Exception)
+ {
+ }
+ }
+ }
+
+ #endregion
+
+ #region abstract methods
+
+ ///
+ /// Generate the actions the dialog will perform. Derived classes should add
+ /// IAction based actions to the actions collection.
+ ///
+ protected abstract void GenerateActions();
+
+ #endregion
+ }
+
+ #endregion
+
+ #region Enable Alerts
+
+ ///
+ /// Enables one or more alerts.
+ ///
+ internal class EnableAgentAlerts : AgentAction
+ {
+ public EnableAgentAlerts(XmlDocument document, IServiceProvider source)
+ : base(document, source)
+ {
+ }
+
+ protected override void GenerateActions()
+ {
+ if (this.smoServer != null)
+ {
+ for (int i = 0; i < this.urnParameters.Length; i++)
+ {
+ Alert alert = this.smoServer.GetSmoObject(urnParameters[i]) as Alert;
+
+ // check that the urn really points to an alert
+ this.actions.AddAction(new EnableAlertAction(alert));
+ }
+ }
+ }
+
+ ///
+ /// Performs the actual enabling
+ ///
+ internal class EnableAlertAction : IProgressItem
+ {
+ private Alert alert;
+
+ public EnableAlertAction(Alert alert)
+ {
+ if (alert == null)
+ {
+ throw new ArgumentNullException("alert");
+ }
+
+ this.alert = alert;
+ }
+
+ ///
+ /// Generate a user friendly description of this task.Used in the description
+ /// of the progress dialog.
+ ///
+ /// Description of the aler
+ public override string ToString()
+ {
+ if (this.alert == null)
+ {
+ return base.ToString();
+ }
+ else
+ {
+ return "AgentActionSR.EnableAlertDescription(this.alert.Name)";
+ }
+ }
+
+
+ ///
+ /// Enable the alert
+ ///
+ /// Actions collection
+ /// this actions index into the actions collection
+ ///
+ public ProgressStatus DoAction(ProgressItemCollection actions, int index)
+ {
+ //parameter check
+ if (actions == null)
+ {
+ throw new ArgumentNullException("actions");
+ }
+
+ // in progress
+ actions.Progress.UpdateActionStatus(index, ProgressStatus.InProgress);
+ actions.Progress.AddActionInfoString(index, "AgentActionSR.EnablingAlert(this.alert.Name)");
+
+ this.alert.IsEnabled = true;
+ this.alert.Alter();
+
+ // done
+ actions.Progress.AddActionInfoString(index, "AgentActionSR.EnabledAlert(this.alert.Name)");
+ actions.Progress.UpdateActionStatus(index, ProgressStatus.Success);
+ return ProgressStatus.Success;
+ }
+ }
+ }
+
+ #endregion
+
+ #region Disable Alerts
+
+ ///
+ /// Disable one or more alerts
+ ///
+ internal class DisableAgentAlerts : AgentAction
+ {
+ public DisableAgentAlerts(XmlDocument document, IServiceProvider source)
+ : base(document, source)
+ {
+ }
+
+ protected override void GenerateActions()
+ {
+ if (this.smoServer != null)
+ {
+ for (int i = 0; i < this.urnParameters.Length; i++)
+ {
+ Alert alert = this.smoServer.GetSmoObject(urnParameters[i]) as Alert;
+
+ // check that the urn really points to an alert
+ this.actions.AddAction(new DisableAlertAction(alert));
+ }
+ }
+ }
+
+ ///
+ /// Actually disable the alert
+ ///
+ internal class DisableAlertAction : IProgressItem
+ {
+ private Alert alert;
+
+ public DisableAlertAction(Alert alert)
+ {
+ if (alert == null)
+ {
+ throw new ArgumentNullException("alert");
+ }
+
+ this.alert = alert;
+ }
+
+ public override string ToString()
+ {
+ if (this.alert == null)
+ {
+ return base.ToString();
+ }
+ else
+ {
+ return "AgentActionSR.DisableAlertDescription(this.alert.Name)";
+ }
+ }
+
+ ///
+ /// Disable the alert
+ ///
+ /// Actions collection
+ /// this actions index into the actions collection
+ ///
+ public ProgressStatus DoAction(ProgressItemCollection actions, int index)
+ {
+ //parameter check
+ if (actions == null)
+ {
+ throw new ArgumentNullException("actions");
+ }
+
+ // in progress
+ actions.Progress.UpdateActionStatus(index, ProgressStatus.InProgress);
+ actions.Progress.AddActionInfoString(index, "AgentActionSR.DisablingAlert(this.alert.Name)");
+
+ this.alert.IsEnabled = false;
+ this.alert.Alter();
+
+ actions.Progress.AddActionInfoString(index, "AgentActionSR.DisabledAlert(this.alert.Name)");
+
+ /// done
+ actions.Progress.UpdateActionStatus(index, ProgressStatus.Success);
+ return ProgressStatus.Success;
+ }
+ }
+ }
+
+ #endregion
+
+ #region JobAction
+
+ internal class JobAction : AgentAction
+ {
+ public JobAction(XmlDocument document, IServiceProvider source)
+ : base(document, source)
+ {
+
+ }
+
+ protected override void GenerateActions()
+ {
+ return;
+ }
+
+ ///
+ /// Initialize context for actions on Jobs
+ /// Job Activity monitor can call with list if jobids.
+ /// All other existing callers may call with list of urns
+ /// To support above 2 scenarios, this method in base class initializes urnParameters if it was not initialized by
+ /// AgentAction class's constructor
+ ///
+ protected void InitializeContext()
+ {
+ // If Urn parameters were not initialized it is possible that
+ // jobids were passed in by caller instead of list of urns
+ if (null == urnParameters || urnParameters.Length == 0)
+ {
+ StringCollection jobIdStrings = new StringCollection();
+
+ // get list of job ids that were passed in
+ param.GetParam("jobid", jobIdStrings);
+
+ urnParameters = new Urn[jobIdStrings.Count];
+ int index = 0;
+ if (jobIdStrings.Count > 0)
+ {
+ foreach (string jobIdString in jobIdStrings)
+ {
+ Job job = smoServer.JobServer.Jobs.ItemById(Guid.Parse(jobIdString));
+ urnParameters[index++] = new Urn(job.Urn);
+ }
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region Enable Jobs
+
+ internal class EnableAgentJobs : JobAction
+ {
+ public EnableAgentJobs(XmlDocument document, IServiceProvider source)
+ : base(document, source)
+ {
+ }
+
+ protected override void GenerateActions()
+ {
+ if (this.smoServer != null)
+ {
+ InitializeContext();
+ for (int i = 0; i < this.urnParameters.Length; i++)
+ {
+ Job job = this.smoServer.GetSmoObject(urnParameters[i]) as Job;
+
+ // check that the urn really points to a Job
+ this.actions.AddAction(new EnableJobAction(job));
+ }
+ }
+ }
+
+ // class that actually enables the job
+ internal class EnableJobAction : IProgressItem
+ {
+ private Job job;
+
+ public EnableJobAction(Job job)
+ {
+ if (job == null)
+ {
+ throw new ArgumentNullException("job");
+ }
+
+ this.job = job;
+ }
+
+ ///
+ /// Generate user friendly description of the action. This is displayed in the
+ /// progress dialog.
+ ///
+ ///
+ public override string ToString()
+ {
+ if (this.job == null)
+ {
+ return base.ToString();
+ }
+ else
+ {
+ return "AgentActionSR.EnableJobDescription(this.job.Name)";
+ }
+ }
+
+ ///
+ /// Enable the Job
+ ///
+ /// Actions collection
+ /// this actions index into the actions collection
+ ///
+ public ProgressStatus DoAction(ProgressItemCollection actions, int index)
+ {
+ //parameter check
+ if (actions == null)
+ {
+ throw new ArgumentNullException("actions");
+ }
+
+ // in progress
+ actions.Progress.UpdateActionStatus(index, ProgressStatus.InProgress);
+ actions.Progress.AddActionInfoString(index, "AgentActionSR.EnablingJob(this.job.Name)");
+
+ this.job.IsEnabled = true;
+ this.job.Alter();
+
+
+ // done
+ actions.Progress.AddActionInfoString(index, "AgentActionSR.EnabledJob(this.job.Name)");
+ actions.Progress.UpdateActionStatus(index, ProgressStatus.Success);
+ return ProgressStatus.Success;
+ }
+ }
+ }
+
+ #endregion
+
+ #region Disable Jobs
+
+ ///
+ /// Disable a job
+ ///
+ internal class DisableAgentJobs : JobAction
+ {
+ public DisableAgentJobs(XmlDocument document, IServiceProvider source)
+ : base(document, source)
+ {
+ }
+
+ protected override void GenerateActions()
+ {
+ if (this.smoServer != null)
+ {
+ InitializeContext();
+ for (int i = 0; i < this.urnParameters.Length; i++)
+ {
+ Job job = this.smoServer.GetSmoObject(urnParameters[i]) as Job;
+
+ // check that the urn really points to an job
+ this.actions.AddAction(new DisableJobAction(job));
+ }
+ }
+ }
+
+ internal class DisableJobAction : IProgressItem
+ {
+ private Job job;
+
+ public DisableJobAction(Job job)
+ {
+ if (job == null)
+ {
+ throw new ArgumentNullException("job");
+ }
+
+ this.job = job;
+ }
+
+ public override string ToString()
+ {
+ if (this.job == null)
+ {
+ return base.ToString();
+ }
+ else
+ {
+ return "AgentActionSR.DisableJobDescription(this.job.Name)";
+ }
+ }
+
+ ///
+ /// Disable the Job
+ ///
+ /// Actions collection
+ /// this actions index into the actions collection
+ ///
+ public ProgressStatus DoAction(ProgressItemCollection actions, int index)
+ {
+ //parameter check
+ if (actions == null)
+ {
+ throw new ArgumentNullException("actions");
+ }
+
+ // in progress
+ actions.Progress.UpdateActionStatus(index, ProgressStatus.InProgress);
+ actions.Progress.AddActionInfoString(index, "AgentActionSR.DisablingJob(this.job.Name)");
+
+ this.job.IsEnabled = false;
+ this.job.Alter();
+
+ // done
+ actions.Progress.AddActionInfoString(index, "AgentActionSR.DisabledJob(this.job.Name)");
+ actions.Progress.UpdateActionStatus(index, ProgressStatus.Success);
+ return ProgressStatus.Success;
+ }
+ }
+ }
+
+ #endregion
+
+ #region Start Job
+
+ ///
+ /// Start an agent job. If the jobs have multiple steps we will show a dialog that asks
+ /// which step the job should be started on.
+ ///
+ internal class StartAgentJobs : JobAction
+ {
+ public StartAgentJobs(XmlDocument document, IServiceProvider source)
+ : base(document, source)
+ {
+ this.actions.CloseOnUserCancel = true;
+ this.actions.QuitOnError = true;
+
+ }
+
+ ///
+ /// The method is generates list of actions and it is gets called from the OnLaod of base Form method
+ ///
+ protected override void GenerateActions()
+ {
+ if (this.smoServer != null)
+ {
+ InitializeContext();
+
+ for (int i = 0; i < this.urnParameters.Length; i++)
+ {
+ Job job = this.smoServer.GetSmoObject(urnParameters[i]) as Job;
+
+
+ string selectedStep = null;
+
+ DataTable dtSteps = GetJobDataSteps(job);
+ if (dtSteps == null || dtSteps.Rows == null)
+ continue;
+
+ if (dtSteps.Rows.Count > 1) //check if job is multi step job
+ {
+ // selectedStep = ShowStepDialog(job, dtSteps);
+ if (selectedStep == null) //check if the job was canceled
+ continue;
+ }
+
+ //Copy the LastRunTime of the job into prevRunTime before the job started.
+ DateTime prevRunTime = job.LastRunDate;
+ this.actions.AddAction(new StartJobAction(job, selectedStep));
+ this.actions.AddAction(new WaitForJobToFinishAction(job, prevRunTime));
+ }
+
+ if (this.actions.Count <= 0)
+ {
+ //This is a workaround for the class dialog invocation problem
+ //This dialog is invoked from two places
+ //JobsActivityMonitor (JobsPanel.cs) OnStartJobAtStep method
+ // and SSMS Context menu mpu\ssms\shared\SqlMgmt\src\RunningFormsTable.cs method StartFormExecution
+ //It is no way for us to prevent the dialog execution from the context menu (when job was canceled), besides redisgning the architecture
+ //this.Close();
+ }
+ }
+ }
+
+ ///
+ /// Returns list of steps of the given job
+ ///
+ ///
+ /// returns list of steps
+ private DataTable GetJobDataSteps(Job job)
+ {
+ if (job == null || job.Parent == null || job.Parent.Parent == null)
+ return null;
+
+ // perform an enumerator query to get the steps. We could use the
+ // SMO step object but this is too inefficient as it generates a batch
+ // per step.
+ Request request = new Request();
+
+ request.Fields = new string[] {"Name", "ID", "SubSystem"};
+ request.Urn = job.Urn + "/Step";
+ request.OrderByList = new OrderBy[] {new OrderBy("ID", OrderBy.Direction.Asc)};
+
+ Enumerator en = new Enumerator();
+ return en.Process(job.Parent.Parent.ConnectionContext, request);
+ }
+
+ ///
+ /// This class implements the feature described in project tracking bug 37519.
+ /// The point is to poll the server for the status of the job to give the user
+ /// some indication of whether the job succeeded or failed. Polls every 3 seconds.
+ ///
+ internal class WaitForJobToFinishAction : IProgressItem
+ {
+ private Job job;
+ private DateTime prevRunTime;
+ private ManualResetEvent abortEvent;
+ private const int ServerPollingInterval = 3000;
+
+ public WaitForJobToFinishAction(Job job, DateTime prevRunTime)
+ {
+ this.job = job;
+ this.prevRunTime = prevRunTime;
+ this.abortEvent = new ManualResetEvent(false); //initial set to busy
+ }
+
+ ///
+ /// Prevent default constructor
+ ///
+ private WaitForJobToFinishAction()
+ {
+ }
+
+ ///
+ /// generates a friendly description of this step. Used by the progress dialog
+ ///
+ ///
+ public override string ToString()
+ {
+ if (this.job == null)
+ {
+ return base.ToString();
+ }
+ else
+ {
+ return "AgentActionSR.ExecuteJob(job.Name)";
+ }
+ }
+
+ ///
+ /// This method triggers abort event for the action thread
+ ///
+ public void Abort()
+ {
+ this.abortEvent.Set();
+ }
+
+ ///
+ /// Perform the action for this class
+ ///
+ /// Actions collection
+ /// array index of this particular action
+ ///
+ public ProgressStatus DoAction(ProgressItemCollection actions, int index)
+ {
+ ProgressStatus status = ProgressStatus.Error;
+
+ bool jobFinished = false;
+
+ JobServer jobServer = job.Parent;
+ JobCategory category = jobServer.JobCategories[job.Category];
+
+ if (category.CategoryType == CategoryType.MultiServerJob)
+ {
+ actions.Progress.UpdateActionDescription(index, "AgentActionSR.RequestPostedToTargetServers");
+ actions.Progress.UpdateActionStatus(index, ProgressStatus.Success);
+ return ProgressStatus.Success;
+ }
+
+ status = ProgressStatus.Aborted;
+
+ // now wait for job to finish...
+ while (!this.abortEvent.WaitOne(WaitForJobToFinishAction.ServerPollingInterval))
+ {
+ if (actions.Progress.IsAborted)
+ break;
+
+ this.job.Refresh();
+ // If this job hasn't started yet then don't check for its status
+ if (this.prevRunTime != job.LastRunDate)
+ {
+ switch (this.job.CurrentRunStatus)
+ {
+ case JobExecutionStatus.Idle:
+
+ actions.Progress.UpdateActionProgress(index, 100);
+
+ // see if the job succeeded.
+ if (this.job.LastRunOutcome == CompletionResult.Failed)
+ {
+ actions.Progress.UpdateActionStatus(index, ProgressStatus.Error);
+ actions.Progress.AddActionException(index,
+ new Exception("AgentActionSR.JobFailed(job.Name)"));
+ status = ProgressStatus.Error;
+ }
+ else
+ {
+ actions.Progress.UpdateActionDescription(index, "AgentActionSR.ExecuteJob(job.Name)");
+ actions.Progress.UpdateActionStatus(index, ProgressStatus.Success);
+ status = ProgressStatus.Success;
+ }
+
+ jobFinished = true;
+
+ break;
+
+ case JobExecutionStatus.Suspended:
+
+ actions.Progress.UpdateActionProgress(index, "AgentActionSR.Suspended");
+ break;
+
+ case JobExecutionStatus.BetweenRetries:
+
+ actions.Progress.UpdateActionProgress(index, "AgentActionSR.BetweenRetries");
+ break;
+
+ case JobExecutionStatus.Executing:
+
+ actions.Progress.UpdateActionProgress(index, "AgentActionSR.Executing");
+ break;
+
+ case JobExecutionStatus.PerformingCompletionAction:
+
+ actions.Progress.UpdateActionProgress(index, "AgentActionSR.PerformingCompletionAction");
+ break;
+
+ case JobExecutionStatus.WaitingForStepToFinish:
+
+ actions.Progress.UpdateActionProgress(index, "AgentActionSR.WaitingForStepToFinish");
+ break;
+
+ case JobExecutionStatus.WaitingForWorkerThread:
+
+ actions.Progress.UpdateActionProgress(index, "AgentActionSR.WaitingForWorkerThread");
+ break;
+
+ default:
+ // unknown JobExecutionStatus, keep waiting.
+ System.Diagnostics.Debug.Assert(false,
+ "Unknown JobExecutionStatus found while waiting for job execution to finish");
+ break;
+ }
+ }
+
+ if (jobFinished)
+ {
+ break;
+ }
+
+ actions.Progress.UpdateActionStatus(index, ProgressStatus.InProgress);
+ }
+
+ return status;
+ }
+ }
+
+ ///
+ /// starts a job
+ ///
+ internal class StartJobAction : IProgressItem
+ {
+ private Job job;
+ //Represent selected job step if any
+ private string currentJobStep;
+
+ public StartJobAction(Job job, string jobStep)
+ {
+ // need a job. The delegate can be null
+ if (job == null)
+ {
+ throw new ArgumentNullException("job");
+ }
+
+ this.job = job;
+ this.currentJobStep = jobStep;
+ }
+
+ ///
+ /// generates a friendly description of this step. Used by the progress dialog
+ ///
+ ///
+ public override string ToString()
+ {
+ if (this.job == null)
+ {
+ return base.ToString();
+ }
+ else
+ {
+ return "AgentActionSR.StartJobDescription(this.job.Name)";
+ }
+ }
+
+
+ ///
+ /// Start the Job
+ ///
+ /// Actions collection
+ /// this actions index into the actions collection
+ ///
+ public ProgressStatus DoAction(ProgressItemCollection actions, int index)
+ {
+ ProgressStatus status = ProgressStatus.Success;
+ //parameter check
+ if (actions == null)
+ {
+ throw new ArgumentNullException("actions");
+ }
+
+ // in progress
+ actions.Progress.UpdateActionStatus(index, ProgressStatus.InProgress);
+
+ // perform an enumerator query to get the steps. We could use the
+ // SMO step object but this is too inefficient as it generates a batch
+ // per step.
+ Request request = new Request();
+
+ request.Fields = new string[] {"Name", "ID", "SubSystem"};
+ request.Urn = this.job.Urn + "/Step";
+ request.OrderByList = new OrderBy[] {new OrderBy("ID", OrderBy.Direction.Asc)};
+
+ if (this.currentJobStep != null)
+ {
+ actions.Progress.AddActionInfoString(index,
+ "AgentActionSR.StartJobWithStep(this.job.Name, this.currentJobStep)");
+ this.job.Start(this.currentJobStep);
+ }
+ else
+ {
+ actions.Progress.AddActionInfoString(index, "AgentActionSR.StartingJob(this.job.Name)");
+ this.job.Start();
+ }
+
+ // done
+ actions.Progress.UpdateActionStatus(index, status);
+ return status;
+ }
+ }
+ }
+
+ #endregion
+
+ #region Stop Job
+
+ ///
+ /// stop a job
+ ///
+ internal class StopAgentJobs : JobAction
+ {
+ public StopAgentJobs(XmlDocument document, IServiceProvider source)
+ : base(document, source)
+ {
+ }
+
+ protected override void GenerateActions()
+ {
+ if (this.smoServer != null)
+ {
+ InitializeContext();
+
+ for (int i = 0; i < this.urnParameters.Length; i++)
+ {
+ Job job = this.smoServer.GetSmoObject(urnParameters[i]) as Job;
+
+ // check that the urn really points to an job
+ this.actions.AddAction(new StopJobAction(job));
+ }
+ }
+ }
+
+ ///
+ /// class that actually stops a running job
+ ///
+ internal class StopJobAction : IProgressItem
+ {
+ private Job job;
+
+ public StopJobAction(Job job)
+ {
+ if (job == null)
+ {
+ throw new ArgumentNullException("job");
+ }
+
+ this.job = job;
+ }
+
+ ///
+ /// Generate a user friendly description of this task. Used in the description
+ /// of the progress dialog.
+ ///
+ /// Description of the action
+ public override string ToString()
+ {
+ if (this.job == null)
+ {
+ return base.ToString();
+ }
+ else
+ {
+ return "AgentActionSR.StopJobDescription(this.job.Name)";
+ }
+ }
+
+ ///
+ /// Stop the Job
+ ///
+ /// Actions collection
+ /// this actions index into the actions collection
+ ///
+ public ProgressStatus DoAction(ProgressItemCollection actions, int index)
+ {
+ //parameter check
+ if (actions == null)
+ {
+ throw new ArgumentNullException("actions");
+ }
+
+ // in progress
+ actions.Progress.UpdateActionStatus(index, ProgressStatus.InProgress);
+ actions.Progress.AddActionInfoString(index, "AgentActionSR.StoppingJob(this.job.Name)");
+ job.Stop();
+
+ // done
+ actions.Progress.AddActionInfoString(index, "AgentActionSR.StoppedJob(this.job.Name)");
+ actions.Progress.UpdateActionStatus(index, ProgressStatus.Success);
+ return ProgressStatus.Success;
+ }
+ }
+ }
+
+ #endregion
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/AgentInterfaces.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/AgentInterfaces.cs
similarity index 100%
rename from src/Microsoft.SqlTools.ServiceLayer/Agent/AgentInterfaces.cs
rename to src/Microsoft.SqlTools.ServiceLayer/Agent/Common/AgentInterfaces.cs
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ExecutionMode.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ExecutionMode.cs
new file mode 100644
index 00000000..110f334c
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ExecutionMode.cs
@@ -0,0 +1,32 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent
+{
+ ///
+ /// Execution mode enumeration Success if execution succeeded of Failure otherwise for now.
+ /// This enumeration might be refined more as there are needs for it
+ ///
+ public enum ExecutionMode
+ {
+ ///
+ /// indicates that the operation failed
+ ///
+ Failure = 0,
+
+ ///
+ /// indicates that the operation succeded
+ ///
+ Success,
+
+ ///
+ /// indicates that the operation was canceled
+ ///
+ Cancel
+ };
+
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/IExecutionAwareSqlControlCollection.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/IExecutionAwareSqlControlCollection.cs
new file mode 100644
index 00000000..a1a33add
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/IExecutionAwareSqlControlCollection.cs
@@ -0,0 +1,103 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent
+{
+ public class PreProcessExecutionInfo
+ {
+ private RunType runType;
+ private string script;
+ private PreProcessExecutionInfo() {}
+
+ internal PreProcessExecutionInfo(RunType runType)
+ {
+ this.runType = runType;
+ }
+
+ public RunType RunType
+ {
+ get
+ {
+ return this.runType;
+ }
+ }
+
+ public string Script
+ {
+ get
+ {
+ return this.script;
+ }
+
+ set
+ {
+ this.script = value;
+ }
+ }
+ }
+
+
+ ///
+ /// IExecutionAwareSqlControlCollection allows control's container to do pre and post
+ /// processing of the execution commands
+ ///
+ public interface IExecutionAwareSqlControlCollection : ISqlControlCollection
+ {
+ ///
+ /// called before dialog's host executes actions on all panels in the dialog one by one.
+ /// If something fails inside this function and the execution should be aborted,
+ /// it can either raise an exception [in which case the framework will show message box with exception text]
+ /// or set executionResult out parameter to be ExecutionMode.Failure
+ /// NOTE: it might be called from worker thread
+ ///
+ /// information about execution action
+ /// result of the execution
+ ///
+ /// true if regular execution should take place, false if everything
+ /// has been done by this function
+ /// NOTE: in case of returning false during scripting operation
+ /// PreProcessExecutionInfo.Script property of executionInfo parameter
+ /// MUST be set by this function [if execution result is success]
+ ///
+ bool PreProcessExecution(PreProcessExecutionInfo executionInfo, out ExecutionMode executionResult);
+
+ ///
+ /// called when the host received Cancel request. NOTE: this method can return while
+ /// operation is still being canceled
+ ///
+ ///
+ /// true if the host should do standard cancel for the currently running view or
+ /// false if the Cancel operation was done entirely inside this method and there is nothing
+ /// extra that should be done
+ ///
+ bool Cancel();
+
+ ///
+ /// called after dialog's host executes actions on all panels in the dialog one by one
+ /// NOTE: it might be called from worker thread
+ ///
+ /// result of the execution
+ /// type of execution
+ void PostProcessExecution(RunType runType, ExecutionMode executionMode);
+
+ ///
+ /// called before dialog's host executes OnReset method on all panels in the dialog one by one
+ /// NOTE: it might be called from worker thread
+ ///
+ ///
+ /// true if regular execution should take place, false if everything
+ /// has been done by this function
+ ///
+ bool PreProcessReset();
+
+ ///
+ /// called after dialog's host executes OnReset method on all panels in the dialog one by one
+ /// NOTE: it might be called from worker thread
+ ///
+ void PostProcessReset();
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/IProgressItem.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/IProgressItem.cs
new file mode 100644
index 00000000..68ae98f3
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/IProgressItem.cs
@@ -0,0 +1,26 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent
+{
+#region interfaces
+ ///
+ /// Interface that supports the delegation of individual actions in the progress dialog
+ /// to individual classes.
+ ///
+ public interface IProgressItem
+ {
+ ///
+ /// Perform the action for this class
+ ///
+ /// Actions collection
+ /// array index of this particular action
+ ///
+ ProgressStatus DoAction(ProgressItemCollection actions, int index);
+ }
+#endregion
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ISqlControlCollection.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ISqlControlCollection.cs
new file mode 100644
index 00000000..6b2ff9a1
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ISqlControlCollection.cs
@@ -0,0 +1,24 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent
+{
+ ///
+ /// defines notion of sitable object
+ ///
+ public interface IObjectWithSite
+ {
+ void SetSite(System.IServiceProvider sp);
+ }
+
+ ///
+ /// ISqlControlCollection allows access to a collection of dialog views
+ ///
+ public interface ISqlControlCollection : IObjectWithSite
+ {
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/JobActivityFilter.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/JobActivityFilter.cs
similarity index 100%
rename from src/Microsoft.SqlTools.ServiceLayer/Agent/JobActivityFilter.cs
rename to src/Microsoft.SqlTools.ServiceLayer/Agent/Common/JobActivityFilter.cs
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/LogAggregator.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/LogAggregator.cs
similarity index 100%
rename from src/Microsoft.SqlTools.ServiceLayer/Agent/LogAggregator.cs
rename to src/Microsoft.SqlTools.ServiceLayer/Agent/Common/LogAggregator.cs
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/LogInterfaces.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/LogInterfaces.cs
similarity index 100%
rename from src/Microsoft.SqlTools.ServiceLayer/Agent/LogInterfaces.cs
rename to src/Microsoft.SqlTools.ServiceLayer/Agent/Common/LogInterfaces.cs
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ManagementActionBase.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ManagementActionBase.cs
new file mode 100644
index 00000000..06ee7469
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ManagementActionBase.cs
@@ -0,0 +1,644 @@
+//
+// 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.Drawing;
+using System.Collections;
+using System.Text;
+using System.ComponentModel;
+using System.Data;
+using System.Xml;
+using System.IO;
+using System.Threading;
+using System.Diagnostics;
+using System.Collections.Specialized;
+using Microsoft.SqlServer.Management.Smo;
+using Microsoft.SqlServer.Management.Common;
+using Microsoft.SqlServer.Management.Diagnostics;
+using Microsoft.SqlTools.ServiceLayer.Admin;
+using Microsoft.SqlTools.ServiceLayer.Agent;
+
+namespace Microsoft.SqlTools.ServiceLayer.Management
+{
+ ///
+ /// base class that can be used to derived from for the main classes [containers] of the dialogs
+ ///
+ public class ManagementActionBase : IDisposable
+ {
+#region Members
+
+ ///
+ /// selected node as specified to SelectNode method
+ ///
+ //private TreeNode selectedNode;
+
+ ///
+ /// service provider of our host. We should direct all host-specific requests to the services
+ /// implemented by this provider
+ ///
+ private IServiceProvider serviceProvider;
+
+ ///
+ /// data container with initialization-related information
+ ///
+ private CDataContainer dataContainer;
+ //whether we assume complete ownership over it.
+ //We set this member once the dataContainer is set to be non-null
+ private bool ownDataContainer = true;
+
+ //if derived class tries to call a protected method that relies on service provider,
+ //and the service provider hasn't been set yet, we will cache the values and will
+ //propagate them when we get the provider set
+ //private System.Drawing.Icon cachedIcon = null;
+ private string cachedCaption = null;
+
+ //SMO Server connection that MUST be used for all enumerator calls
+ //We'll get this object out of CDataContainer, that must be initialized
+ //property by the initialization code
+ private ServerConnection serverConnection;
+
+#endregion
+
+#region Constructors
+
+ ///
+ /// Constructor
+ ///
+ public ManagementActionBase()
+ {
+ }
+
+#endregion
+
+#region IDisposable implementation
+
+ void IDisposable.Dispose()
+ {
+ //BUGBUG - do we need finalizer
+ Dispose(true);//call protected virtual method
+ }
+
+ ///
+ /// do the deterministic cleanup
+ ///
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ //dispose CDataContainer if needed
+ if (this.dataContainer != null)
+ {
+ if (this.ownDataContainer)
+ {
+ this.dataContainer.Dispose();
+ }
+ this.dataContainer = null;
+ }
+ }
+
+#endregion
+
+#region IObjectWithSite implementation
+
+ public virtual void SetSite(IServiceProvider sp)
+ {
+ if (sp == null)
+ {
+ throw new ArgumentNullException("sp");
+ }
+
+ //allow to be sited only once
+ if (this.serviceProvider == null)
+ {
+ //cache the service provider
+ this.serviceProvider = sp;
+
+ //call protected virtual method to enable derived classes to do initialization
+ //OnHosted();
+ }
+ }
+
+#endregion
+
+#region IExecutionAwareSqlControlCollection implementation
+
+ ///
+ /// called before dialog's host executes actions on all panels in the dialog one by one.
+ /// If something fails inside this function and the execution should be aborted,
+ /// it can either raise an exception [in which case the framework will show message box with exception text]
+ /// or set executionResult out parameter to be ExecutionMode.Failure
+ /// NOTE: it might be called from worker thread
+ ///
+ /// information about execution action
+ /// result of the execution
+ ///
+ /// true if regular execution should take place, false if everything
+ /// has been done by this function
+ /// NOTE: in case of returning false during scripting operation
+ /// PreProcessExecutionInfo.Script property of executionInfo parameter
+ /// MUST be set by this function [if execution result is success]
+ ///
+ public bool PreProcessExecution(PreProcessExecutionInfo executionInfo, out ExecutionMode executionResult)
+ {
+ //we start from failure
+ executionResult = ExecutionMode.Failure;
+
+ //OK, we do server switching for scripting for SQL/Analysis Server execution here
+ RunType runType = executionInfo.RunType;
+ if (IsScripting(runType))
+ {
+ if (!PreProcessScripting(executionInfo, out executionResult))
+ {
+ return false;
+ }
+ }
+
+ if (DataContainer != null)
+ {
+ //we take over execution here. We substitute the server here for AMO and SQL
+ //dialogs
+ if (DataContainer.ContainerServerType == CDataContainer.ServerType.SQL)
+ {
+ ExecuteForSql(executionInfo, out executionResult);
+ return false;//execution of the entire control was done here
+ }
+ }
+
+
+ // call virtual function to do regular execution
+ return DoPreProcessExecution(executionInfo.RunType, out executionResult);
+ }
+#endregion
+
+
+ ///
+ /// whether we own our DataContainer or not. Depending on this value it will or won't be
+ /// disposed in our Dispose method
+ ///
+ protected virtual bool OwnDataContainer
+ {
+ get
+ {
+ //by default we own it
+ return true;
+ }
+ }
+
+
+ ///
+ /// called by IExecutionAwareSqlControlCollection.PreProcessExecution to enable derived
+ /// classes to take over execution of the dialog and do entire execution in this method
+ /// rather than having the framework to execute dialog views one by one.
+ ///
+ /// NOTE: it might be called from non-UI thread
+ ///
+ ///
+ ///
+ ///
+ /// true if regular execution should take place, false if everything,
+ /// has been done by this function
+ ///
+ protected virtual bool DoPreProcessExecution(RunType runType, out ExecutionMode executionResult)
+ {
+ //ask the framework to do normal execution by calling OnRunNOw methods
+ //of the views one by one
+ executionResult = ExecutionMode.Success;
+ return true;
+ }
+
+ ///
+ /// called before dialog's host executes OnReset method on all panels in the dialog one by one
+ /// NOTE: it might be called from worker thread
+ ///
+ ///
+ /// true if regular execution should take place, false if everything
+ /// has been done by this function
+ ///
+ ///
+ protected virtual bool DoPreProcessReset()
+ {
+ if ((this.dataContainer != null) && this.dataContainer.IsNewObject)
+ {
+ this.dataContainer.Reset();
+ }
+
+ return true;
+ }
+
+ ///
+ /// called after dialog's host executes OnReset method on all panels in the dialog one by one
+ /// NOTE: it might be called from worker thread
+ ///
+ protected virtual void DoPostProcessReset()
+ {
+ //nothing in the base class
+ }
+
+ ///
+ /// Called to intercept scripting operation
+ ///
+ ///
+ ///
+ ///
+ /// true if regular execution should take place, false the script
+ /// has been created by this function
+ ///
+ protected virtual bool PreProcessScripting(PreProcessExecutionInfo executionInfo, out ExecutionMode executionResult)
+ {
+ //we don't do anything here, but we enable derived classes to do something...
+ executionResult = ExecutionMode.Success;
+ return true;
+ }
+
+ ///
+ /// CDataContainer accessor
+ ///
+ protected CDataContainer DataContainer
+ {
+ get
+ {
+ return this.dataContainer;
+ }
+ set
+ {
+ this.dataContainer = value;
+ this.ownDataContainer = OwnDataContainer; //cache the value
+ }
+ }
+
+
+// ///
+// /// SMO Server connection that MUST be used for all enumerator calls
+// /// We'll get this object out of CDataContainer, that must be initialized
+// /// property by the initialization code
+// ///
+ protected ServerConnection ServerConnection
+ {
+ get
+ {
+ if (this.serverConnection == null && this.dataContainer != null &&
+ this.dataContainer.ContainerServerType == CDataContainer.ServerType.SQL)
+ {
+ this.serverConnection = this.dataContainer.ServerConnection;
+ }
+
+ //it CAN be null here if dataContainer hasn't been set or the container type is non SQL
+ return this.serverConnection;
+ }
+ }
+
+ ///
+ /// checks whether given run time represents one of scripting options
+ ///
+ ///
+ ///
+ protected static bool IsScripting(RunType runType)
+ {
+ return(runType != RunType.RunNow && runType != RunType.RunNowAndExit);
+ }
+
+ ///
+ /// calls DoPreProcessExecution and if it returns false, then it will execute all initialized views
+ /// one by one.
+ /// This method allows derived classes to do both preprocessing and normal execution in a way
+ /// that the framework would normally do. Use this method for special cases when derived class
+ /// should really handle entire execution while doing exactly the same actions as the framework
+ /// would do
+ ///
+ ///
+ ///
+ /// result of the execution. It will let exception fly out if it was raised during execution
+ ///
+ protected ExecutionMode DoPreProcessExecutionAndRunViews(RunType runType)
+ {
+ ExecutionMode executionResult;
+ if (DoPreProcessExecution(runType, out executionResult))
+ {
+ //true return value means that we need to do execution ourselves
+ //executionResult = PanelExecutionHandler.Run(runType, this);
+ }
+
+ return executionResult;
+ }
+
+ ///
+ /// determines whether we need to substitute SMO/AMO server objects with the
+ /// temporary ones while doing scripting
+ ///
+ ///
+ private bool NeedToSwitchServer
+ {
+ get
+ {
+ ServerSwitchingAttribute switchingAttrib =
+ Utils.GetCustomAttribute(this, typeof(ServerSwitchingAttribute)) as ServerSwitchingAttribute;
+
+ if (DataContainer == null)
+ {
+ if (switchingAttrib != null)
+ {
+ return switchingAttrib.NeedToSwtichServerObject;
+ }
+ else
+ {
+ throw new InvalidOperationException();
+ }
+ }
+
+ if (DataContainer.ContainerServerType == CDataContainer.ServerType.SQL)
+ {
+ if (switchingAttrib != null)
+ {
+ return switchingAttrib.NeedToSwtichServerObject;
+ }
+ else
+ {
+ return true;//switch by default in SQL case
+ }
+ }
+ else
+ {
+ throw new InvalidOperationException();
+ }
+ }
+ }
+
+ protected virtual ServerConnection GetServerConnectionForScript()
+ {
+ return this.dataContainer.Server.ConnectionContext;
+ }
+
+ ///
+ /// builds a script string from SMO string collections. This function should
+ /// probably be moved to SMO. Also we need a way to specify the tsql batch
+ /// separator which for now is GO
+ ///
+ ///
+ private string BuildSqlScript()
+ {
+ SqlSmoObject sqlDialogSubject = null;
+ try
+ {
+ sqlDialogSubject = this.DataContainer.SqlDialogSubject;
+ }
+ catch (System.Exception)
+ {
+ //We may not have a valid dialog subject here (such as if the object hasn't been created yet)
+ //so in that case we'll just ignore it as that's a normal scenario.
+ }
+
+ StringCollection sc = GetServerConnectionForScript().CapturedSql.Text;
+ //Scripting may happen on either the server ExecutionManager or the
+ //ExecutionManager of the object itself. So we make sure to check
+ //the subject text if the server ExecutionManager didn't have any
+ //scripts after doing the scripting
+ if (sc.Count == 0 && sqlDialogSubject != null)
+ {
+ sc = sqlDialogSubject.ExecutionManager.ConnectionContext.CapturedSql.Text;
+ }
+ int i;
+ StringBuilder script = new StringBuilder(4096);
+
+ if (sc != null)
+ {
+ for (i = 0; i < sc.Count; i ++)
+ {
+ script.AppendFormat("{0}\r\nGO\r\n", sc[i].ToString());
+ }
+ }
+
+ return script.ToString();
+ }
+
+
+ ///
+ /// called when we need to script a Sql server dlg.
+ ///
+ ///
+ ///
+ private void ExecuteForSql(PreProcessExecutionInfo executionInfo, out ExecutionMode executionResult)
+ {
+ Microsoft.SqlServer.Management.Smo.Server oldServer = null;
+ if (NeedToSwitchServer)
+ {
+ // We use a new instance of the SMO Server object every time we script
+ // so that any changes that are made to the SMO server while scripting are
+ // not kept when the script operation is completed.
+ oldServer = DataContainer.Server;
+
+ //BUGBUG - see if we can use copy ctor instead
+ DataContainer.Server = new Microsoft.SqlServer.Management.Smo.Server(DataContainer.ServerConnection);
+ }
+
+ String szScript = null;
+ bool isScripting = IsScripting(executionInfo.RunType);
+ var executionModeOriginal = GetServerConnectionForScript().SqlExecutionModes;
+ //For Azure the ExecutionManager is different depending on which ExecutionManager
+ //used - one at the Server level and one at the Database level. So to ensure we
+ //don't use the wrong execution mode we need to set the mode for both (for on-prem
+ //this will essentially be a no-op)
+ SqlExecutionModes subjectExecutionModeOriginal = executionModeOriginal;
+ SqlSmoObject sqlDialogSubject = null;
+ try
+ {
+ sqlDialogSubject = this.DataContainer.SqlDialogSubject;
+ }
+ catch (System.Exception)
+ {
+ //We may not have a valid dialog subject here (such as if the object hasn't been created yet)
+ //so in that case we'll just ignore it as that's a normal scenario.
+ }
+
+ if (sqlDialogSubject != null)
+ {
+ subjectExecutionModeOriginal = sqlDialogSubject.ExecutionManager.ConnectionContext.SqlExecutionModes;
+ }
+ try
+ {
+ SqlExecutionModes newMode = isScripting
+ ? SqlExecutionModes.CaptureSql
+ : SqlExecutionModes.ExecuteSql;
+ //now, do the execution itself
+ GetServerConnectionForScript().SqlExecutionModes = newMode;
+ if (sqlDialogSubject != null)
+ {
+ sqlDialogSubject.ExecutionManager.ConnectionContext.SqlExecutionModes = newMode;
+ }
+
+ executionResult = DoPreProcessExecutionAndRunViews(executionInfo.RunType);
+
+ if (isScripting)
+ {
+ if (executionResult == ExecutionMode.Success)
+ {
+ szScript = BuildSqlScript();
+ }
+ }
+
+ }
+ finally
+ {
+ GetServerConnectionForScript().SqlExecutionModes = executionModeOriginal;
+
+ if (isScripting)
+ {
+ GetServerConnectionForScript().CapturedSql.Clear();
+ }
+
+ if (sqlDialogSubject != null)
+ {
+ sqlDialogSubject.ExecutionManager.ConnectionContext.SqlExecutionModes = subjectExecutionModeOriginal;
+ if (isScripting)
+ {
+ sqlDialogSubject.ExecutionManager.ConnectionContext.CapturedSql.Clear();
+ }
+ }
+
+ //see if we need to restore the server
+ if (oldServer != null)
+ {
+ DataContainer.Server = oldServer;
+ }
+ }
+
+ if (isScripting)
+ {
+ executionInfo.Script = szScript;
+ }
+ }
+
+// #region ICustomAttributeProvider
+// object[] System.Reflection.ICustomAttributeProvider.GetCustomAttributes(bool inherit)
+// {
+// //we merge attributes from 2 sources: type attributes and the derived classes, giving preference
+// //to the derived classes
+// return GetMergedArray(DoGetCustomAttributes(inherit), GetType().GetCustomAttributes(inherit));
+// }
+// object[] System.Reflection.ICustomAttributeProvider.GetCustomAttributes(Type attributeType, bool inherit)
+// {
+// //we merge attributes from 2 sources: type attributes and the derived classes, giving preference
+// //to the derived classes
+// return GetMergedArray(DoGetCustomAttributes(attributeType, inherit),
+// GetType().GetCustomAttributes(attributeType, inherit));
+// }
+// bool System.Reflection.ICustomAttributeProvider.IsDefined(Type attributeType, bool inherit)
+// {
+// //we merge attributes from 2 sources: type attributes and the derived classes, giving preference
+// //to the derived classes
+// if (!DoIsDefined(attributeType, inherit))
+// {
+// return GetType().IsDefined(attributeType, inherit);
+// }
+// else
+// {
+// return true;
+// }
+// }
+// #endregion
+// #region ICustomAttributeProvider helpers
+// protected virtual object[] DoGetCustomAttributes(bool inherit)
+// {
+// return GetMergedArray(DoGetCustomAttributes(typeof(ScriptTypeAttribute), inherit),
+// DoGetCustomAttributes(typeof(DialogScriptableAttribute), inherit));
+// }
+// protected virtual object[] DoGetCustomAttributes(Type attributeType, bool inherit)
+// {
+// //if the type specifies this attribute, we don't bother - it overrides
+// //our behavior
+// object[] typeAttribs = GetType().GetCustomAttributes(attributeType, inherit);
+// if (typeAttribs != null && typeAttribs.Length > 0)
+// {
+// return null;
+// }
+// //we expose default custom attribute for script type
+// if (attributeType.Equals(typeof(ScriptTypeAttribute)))
+// {
+// string scriptType = ScriptType;
+// if (scriptType != null)
+// {
+// return new object[] {new ScriptTypeAttribute(scriptType)};
+// }
+// else
+// {
+// return null;
+// }
+// }
+// else if (attributeType.Equals(typeof(DialogScriptableAttribute)))
+// {
+// bool canScriptToWindow = true;
+// bool canScriptToFile = true;
+// bool canScriptToClipboard = true;
+// bool canScriptToJob = true;
+// GetScriptableOptions(out canScriptToWindow,
+// out canScriptToFile,
+// out canScriptToClipboard,
+// out canScriptToJob);
+// return new object[] {new DialogScriptableAttribute(canScriptToWindow,
+// canScriptToFile,
+// canScriptToClipboard,
+// canScriptToJob)};
+// }
+// return null;
+// }
+// protected virtual bool DoIsDefined(Type attributeType, bool inherit)
+// {
+// return false;
+// }
+// ///
+// /// detects whether script types are applicable for this dlg or not. By default
+// /// the framework relies on DialogScriptableAttribute set on the dlg class and won't
+// /// call this method if the attribute is specified
+// /// By default we assume that all script types are enabled
+// ///
+// ///
+// ///
+// ///
+// protected virtual void GetScriptableOptions(out bool canScriptToWindow,
+// out bool canScriptToFile,
+// out bool canScriptToClipboard,
+// out bool canScriptToJob)
+// {
+// canScriptToWindow = canScriptToFile = canScriptToClipboard = canScriptToJob = true;
+// }
+// #endregion
+// protected IServiceProvider ServiceProvider
+// {
+// get
+// {
+// if (this.serviceProvider == null)
+// {
+// STrace.Assert(false, "Cannot work without service provider!");
+// STrace.LogExThrow();
+// //BUGBUG - should we have our own exception here?
+// throw new InvalidOperationException();
+// }
+// return this.serviceProvider;
+// }
+// }
+// ///
+// /// returns combination of the given 2 arrays
+// ///
+// ///
+// ///
+// ///
+// protected object[] GetMergedArray(object[] array1, object[] array2)
+// {
+// if (array1 == null)
+// {
+// return array2;
+// }
+// else if (array2 == null)
+// {
+// return array1;
+// }
+// else
+// {
+// object[] finalReturnValue = new object[array1.Length + array2.Length];
+// array1.CopyTo(finalReturnValue, 0);
+// array2.CopyTo(finalReturnValue, array1.Length);
+// return finalReturnValue;
+// }
+// }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ProgressItemCollection.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ProgressItemCollection.cs
new file mode 100644
index 00000000..84073f9f
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ProgressItemCollection.cs
@@ -0,0 +1,331 @@
+//
+// 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.Globalization;
+using System.Collections;
+using System.Threading;
+using System.Text;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent
+{
+ ///
+ /// Allows for the mapping of objects that implement IProgressItem to individual items in the
+ /// progress dialog.
+ ///
+ public class ProgressItemCollection : ICollection
+ {
+ #region internal helper classes
+ ///
+ /// Allows us to map an action to its index in the progress dialog.
+ ///
+ public class ActionIndexMap
+ {
+ ///
+ /// action
+ ///
+ public IProgressItem Action;
+ ///
+ /// index
+ ///
+ public int Index;
+
+ public ActionIndexMap(IProgressItem action)
+ {
+ this.Action = action;
+ // index isn't known yet
+ this.Index = -1;
+ }
+ }
+ #endregion
+
+ #region private data members
+ ///
+ /// list of actions we will perform.
+ ///
+ private ArrayList actions = new ArrayList();
+ #endregion
+
+ #region construction
+ public ProgressItemCollection()
+ {
+ }
+ #endregion
+
+ #region properties
+
+ private bool closeOnUserCancel = false;
+ ///
+ /// Indicates whether to close the dialog immediately if the user cancels an operation
+ ///
+ public bool CloseOnUserCancel
+ {
+ get
+ {
+ return closeOnUserCancel;
+ }
+ set
+ {
+ closeOnUserCancel = value;
+ }
+ }
+
+ private bool automaticClose = false;
+ ///
+ /// Indicates whether to automatically close the dialog when all actions are complete
+ /// successfully.
+ ///
+ public bool CloseOnSuccessfulCompletion
+ {
+ get
+ {
+ return automaticClose;
+ }
+ set
+ {
+ automaticClose = value;
+ }
+ }
+
+ private bool quitOnError = false;
+ ///
+ /// Indicates whether the operation should be terminated if any individual step fails.
+ ///
+ public bool QuitOnError
+ {
+ get
+ {
+ return this.quitOnError;
+ }
+ set
+ {
+ this.quitOnError = value;
+ }
+ }
+ private OperationStatus operationStatus = OperationStatus.Invalid;
+ ///
+ /// Indicates the status of the operation.
+ ///
+ public OperationStatus OperationStatus
+ {
+ get
+ {
+ return this.operationStatus;
+ }
+ }
+ ///
+ /// Progress object this action collection will work with
+ ///
+ private IProgress progress = null;
+ public IProgress Progress
+ {
+ get
+ {
+ return this.progress;
+ }
+ set
+ {
+ if (this.progress != value)
+ {
+ this.progress = value;
+ if (this.progress != null)
+ {
+ // add the actions to the progress dialog, and
+ // fixup our event handler
+ FixUpActionsToProgress();
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region public overrides
+ ///
+ /// Generate a string representaion of this object. It will convert all of it's IProgressItem members
+ /// to strings in a new line.
+ ///
+ /// string description of the actions this object contains
+ public override string ToString()
+ {
+ // if there are no actions then just return the default ToString
+ if (this.actions == null || this.actions.Count == 0)
+ {
+ return base.ToString();
+ }
+ else
+ {
+ // convert all of the actions to strings on their own line
+ StringBuilder sb = new StringBuilder(((ActionIndexMap)actions[0]).Action.ToString());
+ for (int i = 1; i < this.actions.Count; i++)
+ {
+ sb.AppendFormat(CultureInfo.InvariantCulture, "\r\n{0}", ((ActionIndexMap)actions[i]).Action.ToString());
+ }
+ return sb.ToString();
+ }
+ }
+ #endregion
+
+ #region ICollection implementation
+ ///
+ /// Gets the number of actions in this collection
+ ///
+ public int Count
+ {
+ get
+ {
+ return this.actions.Count;
+ }
+ }
+ ///
+ /// not supported
+ ///
+ public bool IsSynchronized
+ {
+ get
+ {
+ throw new NotSupportedException();
+ }
+ }
+ ///
+ /// not supported
+ ///
+ public object SyncRoot
+ {
+ get
+ {
+ throw new NotSupportedException();
+ }
+ }
+ public void CopyTo(IProgressItem[] array, int start)
+ {
+ this.actions.CopyTo(array, start);
+ }
+ public void CopyTo(Array array, int start)
+ {
+ this.actions.CopyTo(array, start);
+ }
+ public IEnumerator GetEnumerator()
+ {
+ return this.actions.GetEnumerator();
+ }
+ #endregion
+
+ #region public methods
+ ///
+ /// Add an action to the collection
+ ///
+ /// action to be added
+ public void AddAction(IProgressItem action)
+ {
+ ActionIndexMap map = new ActionIndexMap(action);
+ this.actions.Add(map);
+ }
+
+ #endregion
+
+ #region internal implementation
+ ///
+ /// delegate called when the progress dialog wants us to perform work on a new thread.
+ ///
+ private void DoWorkOnThread()
+ {
+ if (this.Progress == null)
+ {
+ return;
+ }
+
+ try
+ {
+ System.Threading.Thread.CurrentThread.Name = "Worker thread for " + progress.GetType();
+ }
+ catch (InvalidOperationException)
+ { }
+
+ // default to succeeded.
+ operationStatus = OperationStatus.Success;
+
+ // carry out each action.
+ foreach (ActionIndexMap map in this.actions)
+ {
+ // abort if the user has decided to cancel.
+ if (this.Progress.IsAborted)
+ {
+ this.Progress.UpdateActionStatus(map.Index, ProgressStatus.Aborted);
+ operationStatus = OperationStatus.Aborted;
+ break;
+ }
+ ProgressStatus stepStatus = ProgressStatus.Invalid;
+ try
+ {
+ // perform the action.
+ stepStatus = map.Action.DoAction(this, map.Index);
+ this.Progress.UpdateActionStatus(map.Index, stepStatus);
+ }
+ catch (Exception e)
+ {
+ // fail the step with errors, add the error messages to the control.
+ this.Progress.AddActionException(map.Index, e);
+ this.Progress.UpdateActionStatus(map.Index, ProgressStatus.Error);
+ stepStatus = ProgressStatus.Error;
+ }
+ if (stepStatus == ProgressStatus.Error)
+ {
+ // see if we're supposed to fail if any step fails
+ if (this.QuitOnError == true)
+ {
+ // fail and quit
+ this.operationStatus = OperationStatus.Error;
+ break;
+ }
+ else
+ {
+ this.operationStatus = OperationStatus.CompletedWithErrors;
+ }
+ }
+ else if (stepStatus != ProgressStatus.Success)
+ {
+ this.operationStatus = OperationStatus.CompletedWithErrors;
+ }
+ }
+
+ // tell the dialog we're finishing.
+ this.Progress.WorkerThreadExiting(operationStatus);
+
+ // close the dialog if asked to. We have to put this after
+ // the WorkerThreadExiting call because the progress dialog
+ // won't allow itself to be closed until worker thread says
+ // it's finished.
+ if ((this.CloseOnSuccessfulCompletion && (this.operationStatus == OperationStatus.Success)) ||
+ (this.CloseOnUserCancel && this.Progress.IsAborted))
+ {
+ //((Form)this.Progress).BeginInvoke(new CloseProgressWindowCallback(CloseProgressWindowHandler), new object[] { this.Progress });
+ }
+ }
+
+ private delegate void CloseProgressWindowCallback(IProgress progress);
+ private void CloseProgressWindowHandler(IProgress progress)
+ {
+ }
+
+ ///
+ /// Adds the actions to an IProgress interface.
+ ///
+ private void FixUpActionsToProgress()
+ {
+ if (this.Progress == null)
+ {
+ return;
+ }
+ // add actions
+ foreach (ActionIndexMap map in this.actions)
+ {
+ map.Index = this.Progress.AddAction(map.Action.ToString());
+ }
+ // add our delegate
+ this.Progress.WorkerThreadStart = new ThreadStart(this.DoWorkOnThread);
+ }
+ #endregion
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ProgressReportCommon.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ProgressReportCommon.cs
new file mode 100644
index 00000000..4c66c7e9
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ProgressReportCommon.cs
@@ -0,0 +1,301 @@
+//
+// 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.Drawing;
+using System.Threading;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent
+{
+ ///
+ /// Enumeration for status of individual actions
+ ///
+ public enum ProgressStatus
+ {
+ Invalid = -1,
+ NotStarted, // Not started
+ InProgress, // In progress
+ Success, // Completed
+ SuccessWithInfo, // Completed, display additional info
+ Warning, // Completed with warning, display exceptions
+ Error, // Not completed because of error, display exceptions
+ Aborted, // Aborted
+ RolledBack, // Rolled back because of a subsequent error
+ StatusCount // = Number of status values - For validation only
+ }
+
+ ///
+ /// Enumeration for status of the overall operation
+ ///
+ public enum OperationStatus
+ {
+ Invalid = -1,
+ InProgress, // In progress
+ Success, // Completed successfully
+ CompletedWithErrors, // Completed with non-fatal errors
+ Error, // Not completed because of error
+ Aborted, // Abort complete
+ StatusCount // = Number of status values - For validation only
+ }
+
+ ///
+ /// Interface defining core functionality of a progress control container
+ ///
+ /// NOTE: Refer to the comments for each method/property individually to determine
+ /// whether it is thread-safe and may be used from the worker thread. Also note
+ /// that some members are asynchronous.
+ ///
+ public interface IProgress
+ {
+ //-----------
+ // Properties
+ //-----------
+
+ ///
+ /// The property that determines if the user should be allowed to abort the
+ /// operation. The default value is true.
+ ///
+ /// NOTE: This property is not thread safe. Set from UI thread.
+ ///
+ bool AllowAbort
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// The confirmation prompt to display if the user hits the Abort button.
+ /// The abort prompt should be worded such that the abort is confirmed
+ /// if the user hits "Yes".
+ ///
+ /// NOTE: This property is not thread safe. Set from UI thread.
+ ///
+ string AbortPrompt
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// The ThreadStart delegate. The method referenced by this delegate is run by
+ /// the worker thread to perform the operation.
+ ///
+ /// NOTE: This property is not thread safe. Set from UI thread.
+ ///
+ ThreadStart WorkerThreadStart
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// Aborted status of the operation.
+ ///
+ /// NOTE: This property is thread safe and may be called from the worker
+ /// thread. Accessing this property may cause caller to block if UI
+ /// is waiting for user confirmation of abort.
+ ///
+ bool IsAborted
+ {
+ get;
+ }
+
+ ///
+ /// This property determines whether updates should be allowed for
+ /// actions. If this is set to false, any calls that are made
+ /// to add or update an action are ignored. The default value is true.
+ ///
+ /// NOTE: This property is thread safe and may be called from the worker
+ /// thread.
+ ///
+ bool ActionUpdateEnabled
+ {
+ get;
+ set;
+ }
+
+ //--------
+ // Methods
+ //--------
+
+ ///
+ /// Add an action to the displayed list of actions. Actions are
+ /// displayed in the order they are added. An action can be referenced
+ /// in future calls using the zero-based index that is returned.
+ ///
+ /// The description must be a non-empty string.
+ ///
+ /// NOTE: This method is not thread safe. Call from the UI thread.
+ ///
+ /// Description of the action
+ /// The index of the newly added action.
+ int AddAction(string description);
+
+ ///
+ /// Add an action to the displayed list of actions. This is meant
+ /// to be called from the worker thread to add an action after
+ /// the operation is already in progress.
+ ///
+ /// Actions are displayed in the order they are added. Use the
+ /// zero-based index of the action based on past actions added
+ /// to reference it in future calls. The description must be a
+ /// non-empty string.
+ ///
+ /// NOTE: This method is thread safe and asynchronous. It may be
+ /// called from the worker thread.
+ ///
+ /// Description of the action
+ void AddActionDynamic(string description);
+
+ ///
+ /// Update the description of an action
+ ///
+ /// The description must be a non-empty string.
+ ///
+ /// NOTE: This method is thread safe and asynchronous. It may be
+ /// called from the worker thread.
+ ///
+ /// Index of the action
+ /// New description of the action
+ void UpdateActionDescription(int actionIndex, string description);
+
+ ///
+ /// Update the status of an action
+ ///
+ /// NOTE: This method is thread safe and asynchronous. It may be
+ /// called from the worker thread.
+ ///
+ /// Index of the action
+ /// New status of the action
+ void UpdateActionStatus(int actionIndex, ProgressStatus status);
+
+ ///
+ /// Update the progress of an action in terms of percentage complete
+ ///
+ /// NOTE: This method is thread safe and asynchronous. It may be
+ /// called from the worker thread.
+ ///
+ /// Index of the action
+ /// Percentage of the action that is complete (0-100)
+ void UpdateActionProgress(int actionIndex, int percentComplete);
+
+ ///
+ /// Update the progress of an action with a text description of
+ /// the progress
+ ///
+ /// The description must be a non-empty string.
+ ///
+ /// NOTE: This method is thread safe and asynchronous. It may be
+ /// called from the worker thread.
+ ///
+ /// Index of the action
+ /// Description of progress
+ void UpdateActionProgress(int actionIndex, string description);
+
+ ///
+ /// Add an exception to an action
+ ///
+ /// Exceptions are displayed in the action grid only for actions
+ /// with "Error" or "Warning" status.
+ ///
+ /// NOTE: This method is thread safe and asynchronous. It may be
+ /// called from the worker thread.
+ ///
+ /// Index of the action
+ /// Exception to be added
+ void AddActionException(int actionIndex, Exception e);
+
+ ///
+ /// Add an info string to an action in the progress report control
+ ///
+ /// Information strings are displayed in the action grid only for
+ /// actions with "SuccessWithInfo" status. The info string must
+ /// be a non-empty string. It should not be formatted or contain
+ /// newline characters.
+ ///
+ /// NOTE: This method is thread safe and asynchronous. It may be
+ /// called from the worker thread.
+ ///
+ /// Index of the action
+ /// Information string to be added
+ void AddActionInfoString(int actionIndex, string infoString);
+
+ ///
+ /// Call this method when the worker thread performing the operation
+ /// is about to exit. The final result of the operation is supplied in
+ /// the form of a OperationStatus value.
+ ///
+ /// NOTE: This method is thread safe and asynchronous. It may be
+ /// called from the worker thread.
+ ///
+ /// Result of the operation
+ void WorkerThreadExiting(OperationStatus result);
+ }
+
+ ///
+ /// Enumeration for status of the progress report control w.r.t the operation
+ ///
+ [System.Runtime.InteropServices.ComVisible(false)]
+ public enum ProgressCtrlStatus
+ {
+ Invalid = -1,
+ InProgress, // In progress
+ Success, // Completed successfully
+ CompletedWithErrors, // Completed with non-fatal errors
+ Error, // Not completed because of error
+ Aborting, // User clicked "Abort", aborting operation
+ Aborted, // Abort complete
+ Closed, // User clicked "Close"
+ StatusCount // = Number of status values - For validation only
+ }
+
+ ///
+ /// Delegate used with ProgressCtrlStatusChanged event.
+ ///
+ public delegate void ProgressCtrlStatusChangedEventHandler(object source, ProgressCtrlStatusChangedEventArgs e);
+
+ ///
+ /// EventArgs class for use with ProgressCtrlStatusChanged event
+ ///
+ sealed public class ProgressCtrlStatusChangedEventArgs : EventArgs
+ {
+ //------------------
+ // Public Properties
+ //------------------
+ public ProgressCtrlStatus Status
+ {
+ get { return m_status; }
+ set { m_status = value; }
+ }
+
+ //-------------
+ // Private Data
+ //-------------
+ private ProgressCtrlStatus m_status = ProgressCtrlStatus.Invalid;
+ }
+
+ // Enumeration for progress action grid columns
+ internal enum ProgressActionColumn
+ {
+ Invalid = -1,
+ ActionStatusBitmap,
+ ActionDescription,
+ ActionStatusText,
+ ActionMessage,
+ ActionColumnCount // = Number of columns - For validation only
+ }
+
+ // Enumeration for progress action display filter
+ internal enum ProgressActionDisplayMode
+ {
+ Invalid = -1,
+ DisplayAllActions,
+ DisplayErrors,
+ DisplaySuccess,
+ DisplayWarnings,
+ ActionDisplayModeCount // = Number of display modes - For validation only
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/RunType.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/RunType.cs
new file mode 100644
index 00000000..5117d2e2
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/RunType.cs
@@ -0,0 +1,23 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent
+{
+ ///
+ /// what type of actions does the worker know to execute
+ ///
+ public enum RunType
+ {
+ RunNow = 0,
+ RunNowAndExit,
+ ScriptToFile,
+ ScriptToWindow,
+ ScriptToClipboard,
+ ScriptToJob
+ }
+
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ServerSwitchingAttribute.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ServerSwitchingAttribute.cs
new file mode 100644
index 00000000..44cc1941
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Common/ServerSwitchingAttribute.cs
@@ -0,0 +1,35 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent
+{
+ ///
+ /// Custom attribute that can be applied on particular DB commander to
+ /// indicate whether the base class should switch SMO servers before
+ /// execution or not.
+ ///
+ [AttributeUsage(AttributeTargets.Class)]
+ public sealed class ServerSwitchingAttribute : Attribute
+ {
+ private bool needToSwitch = false;
+
+ private ServerSwitchingAttribute() {}
+
+ public ServerSwitchingAttribute(bool needToSwitchServer)
+ {
+ this.needToSwitch = needToSwitchServer;
+ }
+
+ public bool NeedToSwtichServerObject
+ {
+ get
+ {
+ return this.needToSwitch;
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentAlertInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentAlertInfo.cs
new file mode 100644
index 00000000..98982e2f
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentAlertInfo.cs
@@ -0,0 +1,58 @@
+//
+// 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 Microsoft.SqlTools.ServiceLayer.Agent;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts
+{
+ [Flags]
+ public enum NotifyMethods
+ {
+ None = 0,
+ NotifyEmail = 1,
+ Pager = 2,
+ NetSend = 4,
+ NotifyAll = 7
+ }
+
+ public enum AlertType
+ {
+ SqlServerEvent = 1,
+ SqlServerPerformanceCondition = 2,
+ NonSqlServerEvent = 3,
+ WmiEvent = 4
+ }
+
+ ///
+ /// a class for storing various properties of agent alerts
+ ///
+ public class AgentAlertInfo
+ {
+ public int Id { get; set; }
+ public int DelayBetweenResponses { get; set; }
+ public string EventDescriptionKeyword { get; set; }
+ public string EventSource { get; set; }
+ public int HasNotification { get; set; }
+ public NotifyMethods IncludeEventDescription { get; set; }
+ public bool IsEnabled { get; set; }
+ public Guid JobId { get; set; }
+ public string JobName { get; set; }
+ public DateTime LastOccurrenceDate { get; set; }
+ public DateTime LastResponseDate { get; set; }
+ public int MessageId { get; set; }
+ public string NotificationMessage { get; set; }
+ public int OccurrenceCount { get; }
+ public string PerformanceCondition { get; set; }
+ public int Severity { get; set; }
+ public string DatabaseName { get; set; }
+ public DateTime CountResetDate { get; }
+ public string CategoryName { get; set; }
+ public AlertType AlertType { get; set; }
+ public string WmiEventNamespace { get; set; }
+ public string WmiEventQuery { get; set; }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentAlertsRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentAlertsRequest.cs
new file mode 100644
index 00000000..29955836
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentAlertsRequest.cs
@@ -0,0 +1,144 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using Microsoft.SqlTools.Hosting.Protocol.Contracts;
+using Microsoft.SqlTools.ServiceLayer.Utility;
+using Microsoft.SqlTools.Utility;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts
+{
+ ///
+ /// SQL Agent Job activity parameters
+ ///
+ public class AgentAlertsParams : GeneralRequestDetails
+ {
+ public string OwnerUri { get; set; }
+ }
+
+ ///
+ /// SQL Agent Job activity result
+ ///
+ public class AgentAlertsResult
+ {
+
+ public bool Succeeded { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+ public AgentAlertInfo[] Alerts { get; set; }
+ }
+
+ ///
+ /// SQL Agent Alerts request type
+ ///
+ public class AgentAlertsRequest
+ {
+ ///
+ /// Request definition
+ ///
+ public static readonly
+ RequestType Type =
+ RequestType.Create("agent/alerts");
+ }
+
+ ///
+ /// SQL Agent create Alert params
+ ///
+ public class CreateAgentAlertParams : GeneralRequestDetails
+ {
+ public string OwnerUri { get; set; }
+
+ public AgentAlertInfo Alert { get; set; }
+ }
+
+ ///
+ /// SQL Agent create Alert result
+ ///
+ public class CreateAgentAlertResult
+ {
+ public bool Succeeded { get; set; }
+
+ public string ErrorMessage { get; set; }
+ }
+
+ ///
+ /// SQL Agent create Alert request type
+ ///
+ public class CreateAgentAlertRequest
+ {
+ ///
+ /// Request definition
+ ///
+ public static readonly
+ RequestType Type =
+ RequestType.Create("agent/createalert");
+ }
+
+ ///
+ /// SQL Agent delete Alert params
+ ///
+ public class DeleteAgentAlertParams : GeneralRequestDetails
+ {
+ public string OwnerUri { get; set; }
+
+ public AgentAlertInfo Alert { get; set; }
+ }
+
+ ///
+ /// SQL Agent delete Alert result
+ ///
+ public class DeleteAgentAlertResult
+ {
+ public bool Succeeded { get; set; }
+
+ public string ErrorMessage { get; set; }
+ }
+
+ ///
+ /// SQL Agent delete Alert request type
+ ///
+ public class DeleteAgentAlertRequest
+ {
+ ///
+ /// Request definition
+ ///
+ public static readonly
+ RequestType Type =
+ RequestType.Create("agent/deletealert");
+ }
+
+ ///
+ /// SQL Agent update Alert params
+ ///
+ public class UpdateAgentAlertParams : GeneralRequestDetails
+ {
+ public string OwnerUri { get; set; }
+
+ public AgentAlertInfo Alert { get; set; }
+ }
+
+ ///
+ /// SQL Agent update Alert result
+ ///
+ public class UpdateAgentAlertResult
+ {
+ public bool Succeeded { get; set; }
+
+ public string ErrorMessage { get; set; }
+ }
+
+ ///
+ /// SQL Agent update Alert request type
+ ///
+ public class UpdateAgentAlertRequest
+ {
+ ///
+ /// Request definition
+ ///
+ public static readonly
+ RequestType Type =
+ RequestType.Create("agent/updatealert");
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentOperatorInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentOperatorInfo.cs
new file mode 100644
index 00000000..4aa2316e
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentOperatorInfo.cs
@@ -0,0 +1,50 @@
+//
+// 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 Microsoft.SqlTools.ServiceLayer.Agent;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts
+{
+ [Flags]
+ public enum WeekDays
+ {
+ Sunday = 1,
+ Monday = 2,
+ Tuesday = 4,
+ Wednesday = 8,
+ Thursday = 16,
+ Friday = 32,
+ WeekDays = 62,
+ Saturday = 64,
+ WeekEnds = 65,
+ EveryDay = 127
+ }
+
+ ///
+ /// a class for storing various properties of agent operators
+ ///
+ public class AgentOperatorInfo
+ {
+ public string Name { get; set; }
+ public int Id { get; set; }
+ public string EmailAddress { get; set; }
+ public bool Enabled { get; set; }
+ public DateTime LastEmailDate { get; set; }
+ public DateTime LastNetSendDate { get; set; }
+ public DateTime LastPagerDate { get; set; }
+ public string PagerAddress { get; set; }
+ public string CategoryName { get; set; }
+ public WeekDays PagerDays { get; set; }
+ public TimeSpan SaturdayPagerEndTime { get; set; }
+ public TimeSpan SaturdayPagerStartTime { get; set; }
+ public TimeSpan SundayPagerEndTime { get; set; }
+ public TimeSpan SundayPagerStartTime { get; set; }
+ public string NetSendAddress { get; set; }
+ public TimeSpan WeekdayPagerStartTime { get; set; }
+ public TimeSpan WeekdayPagerEndTime { get; set; }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentOperatorsRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentOperatorsRequest.cs
new file mode 100644
index 00000000..c082e954
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentOperatorsRequest.cs
@@ -0,0 +1,143 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using Microsoft.SqlTools.Hosting.Protocol.Contracts;
+using Microsoft.SqlTools.ServiceLayer.Utility;
+using Microsoft.SqlTools.Utility;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts
+{
+ ///
+ /// SQL Agent Operators request parameters
+ ///
+ public class AgentOperatorsParams : GeneralRequestDetails
+ {
+ public string OwnerUri { get; set; }
+ }
+
+ ///
+ /// SQL Agent Operators request result
+ ///
+ public class AgentOperatorsResult
+ {
+ public bool Succeeded { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+ public AgentOperatorInfo[] Operators { get; set; }
+ }
+
+ ///
+ /// SQL Agent Operators request type
+ ///
+ public class AgentOperatorsRequest
+ {
+ ///
+ /// Request definition
+ ///
+ public static readonly
+ RequestType Type =
+ RequestType.Create("agent/operators");
+ }
+
+ ///
+ /// SQL Agent create Operator params
+ ///
+ public class CreateAgentOperatorParams : GeneralRequestDetails
+ {
+ public string OwnerUri { get; set; }
+
+ public AgentOperatorInfo Operator { get; set; }
+ }
+
+ ///
+ /// SQL Agent create Operator result
+ ///
+ public class CreateAgentOperatorResult
+ {
+ public bool Succeeded { get; set; }
+
+ public string ErrorMessage { get; set; }
+ }
+
+ ///
+ /// SQL Agent create Operator request type
+ ///
+ public class CreateAgentOperatorRequest
+ {
+ ///
+ /// Request definition
+ ///
+ public static readonly
+ RequestType Type =
+ RequestType.Create("agent/createoperator");
+ }
+
+ ///
+ /// SQL Agent delete Operator params
+ ///
+ public class DeleteAgentOperatorParams : GeneralRequestDetails
+ {
+ public string OwnerUri { get; set; }
+
+ public AgentOperatorInfo Operator { get; set; }
+ }
+
+ ///
+ /// SQL Agent delete Operator result
+ ///
+ public class DeleteAgentOperatorResult
+ {
+ public bool Succeeded { get; set; }
+
+ public string ErrorMessage { get; set; }
+ }
+
+ ///
+ /// SQL Agent delete Operator request type
+ ///
+ public class DeleteAgentOperatorRequest
+ {
+ ///
+ /// Request definition
+ ///
+ public static readonly
+ RequestType Type =
+ RequestType.Create("agent/deleteoperator");
+ }
+
+ ///
+ /// SQL Agent update Operator params
+ ///
+ public class UpdateAgentOperatorParams : GeneralRequestDetails
+ {
+ public string OwnerUri { get; set; }
+
+ public AgentOperatorInfo Operator { get; set; }
+ }
+
+ ///
+ /// SQL Agent update Operator result
+ ///
+ public class UpdateAgentOperatorResult
+ {
+ public bool Succeeded { get; set; }
+
+ public string ErrorMessage { get; set; }
+ }
+
+ ///
+ /// SQL Agent update Operator request type
+ ///
+ public class UpdateAgentOperatorRequest
+ {
+ ///
+ /// Request definition
+ ///
+ public static readonly
+ RequestType Type =
+ RequestType.Create("agent/updateoperator");
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentProxyInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentProxyInfo.cs
new file mode 100644
index 00000000..a8f9572a
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentProxyInfo.cs
@@ -0,0 +1,25 @@
+//
+// 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 Microsoft.SqlTools.ServiceLayer.Agent;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts
+{
+ ///
+ /// a class for storing various properties of agent proxy accounts
+ ///
+ public class AgentProxyInfo
+ {
+ public int Id { get; set; }
+ public string AccountName { get; set; }
+ public string Description { get; set; }
+ public string CredentialName { get; set; }
+ public string CredentialIdentity { get; set; }
+ public int CredentialId { get; set; }
+ public bool IsEnabled { get; set; }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentProxyRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentProxyRequest.cs
new file mode 100644
index 00000000..7e5a3e7f
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentProxyRequest.cs
@@ -0,0 +1,145 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using Microsoft.SqlTools.Hosting.Protocol.Contracts;
+using Microsoft.SqlTools.ServiceLayer.Utility;
+using Microsoft.SqlTools.Utility;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts
+{
+ ///
+ /// SQL Agent proxy accounts parameters
+ ///
+ public class AgentProxiesParams : GeneralRequestDetails
+ {
+ public string OwnerUri { get; set; }
+ }
+
+ ///
+ /// SQL Agent proxy accounts result
+ ///
+ public class AgentProxiesResult
+ {
+ public bool Succeeded { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+ public AgentProxyInfo[] Proxies { get; set; }
+ }
+
+ ///
+ /// SQL Agent Proxy Accounts request type
+ ///
+ public class AgentProxiesRequest
+ {
+ ///
+ /// Request definition
+ ///
+ public static readonly
+ RequestType Type =
+ RequestType.Create("agent/proxies");
+ }
+
+ ///
+ /// SQL Agent create Proxy Account params
+ ///
+ public class CreateAgentProxyParams : GeneralRequestDetails
+ {
+ public string OwnerUri { get; set; }
+
+ public AgentProxyInfo Proxy { get; set; }
+ }
+
+ ///
+ /// SQL Agent create Proxy result
+ ///
+ public class CreateAgentProxyResult
+ {
+ public bool Succeeded { get; set; }
+
+ public string ErrorMessage { get; set; }
+ }
+
+ ///
+ /// SQL Agent create Proxy request type
+ ///
+ public class CreateAgentProxyRequest
+ {
+ ///
+ /// Request definition
+ ///
+ public static readonly
+ RequestType Type =
+ RequestType.Create("agent/createproxy");
+ }
+
+ ///
+ /// SQL Agent delete Proxy params
+ ///
+ public class DeleteAgentProxyParams : GeneralRequestDetails
+ {
+ public string OwnerUri { get; set; }
+
+ public AgentProxyInfo Proxy { get; set; }
+ }
+
+ ///
+ /// SQL Agent delete Proxy result
+ ///
+ public class DeleteAgentProxyResult
+ {
+ public bool Succeeded { get; set; }
+
+ public string ErrorMessage { get; set; }
+ }
+
+ ///
+ /// SQL Agent delete Proxy request type
+ ///
+ public class DeleteAgentProxyRequest
+ {
+ ///
+ /// Request definition
+ ///
+ public static readonly
+ RequestType Type =
+ RequestType.Create("agent/deleteproxy");
+ }
+
+ ///
+ /// SQL Agent update Proxy params
+ ///
+ public class UpdateAgentProxyParams : GeneralRequestDetails
+ {
+ public string OwnerUri { get; set; }
+
+ public string OriginalProxyName { get; set; }
+
+ public AgentProxyInfo Proxy { get; set; }
+ }
+
+ ///
+ /// SQL Agent update Proxy result
+ ///
+ public class UpdateAgentProxyResult
+ {
+ public bool Succeeded { get; set; }
+
+ public string ErrorMessage { get; set; }
+ }
+
+ ///
+ /// SQL Agent update Proxy request type
+ ///
+ public class UpdateAgentProxyRequest
+ {
+ ///
+ /// Request definition
+ ///
+ public static readonly
+ RequestType Type =
+ RequestType.Create("agent/updateproxy");
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/AgentAlert.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/AgentAlert.cs
new file mode 100644
index 00000000..24a2395d
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/AgentAlert.cs
@@ -0,0 +1,166 @@
+//
+// 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;
+using System.Data;
+using Microsoft.SqlServer.Management.Common;
+using Microsoft.SqlServer.Management.Diagnostics;
+using Microsoft.SqlServer.Management.Sdk.Sfc;
+using Microsoft.SqlServer.Management.Smo;
+using Microsoft.SqlServer.Management.Smo.Agent;
+using Microsoft.SqlTools.ServiceLayer.Admin;
+using Microsoft.SqlTools.ServiceLayer.Agent.Contracts;
+using Microsoft.SqlTools.ServiceLayer.Management;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent
+{
+ ///
+ /// AgentAlert class
+ ///
+ internal class AgentAlert : ManagementActionBase
+ {
+ ///
+ /// Agent alert info instance
+ ///
+ private AgentAlertInfo alertInfo = null;
+
+ ///
+ /// Default constructor that will be used to create dialog
+ ///
+ ///
+ public AgentAlert(CDataContainer dataContainer, AgentAlertInfo alertInfo)
+ {
+ this.alertInfo = alertInfo;
+ this.DataContainer = dataContainer;
+ }
+
+ private static string GetAlertName(CDataContainer container)
+ {
+ string alertName = null;
+ STParameters parameters = new STParameters();
+ parameters.SetDocument(container.Document);
+ if (parameters.GetParam("alert", ref alertName) == false || string.IsNullOrWhiteSpace(alertName))
+ {
+ throw new Exception("SRError.AlertNameCannotBeBlank");
+ }
+ return alertName.Trim();
+ }
+
+ public bool Drop()
+ {
+ // fail if the user is not in the sysadmin role
+ if (!this.DataContainer.Server.ConnectionContext.IsInFixedServerRole(FixedServerRoles.SysAdmin))
+ {
+ return false;
+ }
+
+ string alertName = GetAlertName(this.DataContainer);
+ if (this.DataContainer.Server.JobServer.Alerts.Contains(alertName))
+ {
+ this.DataContainer.Server.JobServer.Alerts.Refresh();
+ if (this.DataContainer.Server.JobServer.Alerts.Contains(alertName))
+ {
+ Alert alert = this.DataContainer.Server.JobServer.Alerts[alertName];
+ if (alert != null)
+ {
+ alert.DropIfExists();
+ }
+ }
+ }
+ return true;
+ }
+
+ public bool CreateOrUpdate()
+ {
+ Alert alert = null;
+ string alertName = GetAlertName(this.DataContainer);
+ bool createNewAlert = true;
+
+ try
+ {
+ // check if alert already exists
+ if (this.DataContainer.Server.JobServer.Alerts.Contains(alertName))
+ {
+ this.DataContainer.Server.JobServer.Alerts.Refresh(); // Try to recover
+ if (this.DataContainer.Server.JobServer.Alerts.Contains(alertName)) // If still no luck
+ {
+ // use the existing alert
+ alert = this.DataContainer.Server.JobServer.Alerts[alertName];
+ createNewAlert = false;
+ }
+ }
+
+ // create a new alert
+ if (createNewAlert)
+ {
+ alert = new Alert(this.DataContainer.Server.JobServer, alertName);
+ }
+
+ // apply changes from input parameter to SMO alert object
+ UpdateAlertProperties(alert);
+
+ if (createNewAlert)
+ {
+ alert.Create();
+ }
+ else
+ {
+ // don't bother trying to update the alert unless they are sysadmin.
+ if (!this.DataContainer.Server.ConnectionContext.IsInFixedServerRole(FixedServerRoles.SysAdmin))
+ {
+ return false;
+ }
+ alert.Alter();
+ }
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ ApplicationException applicationException;
+ if (createNewAlert)
+ {
+ applicationException = new ApplicationException("AgentAlertSR.CannotCreateNewAlert", e);
+ }
+ else
+ {
+ applicationException = new ApplicationException("AgentAlertSR.CannotAlterAlert", e);
+ }
+ throw applicationException;
+ }
+ }
+
+ private void UpdateAlertProperties(Alert alert)
+ {
+ if (alert == null)
+ {
+ throw new ArgumentNullException("alert");
+ }
+
+ if (!string.IsNullOrWhiteSpace(this.DataContainer.ConnectionInfo.DatabaseName))
+ {
+ alert.DatabaseName = this.DataContainer.ConnectionInfo.DatabaseName;
+ }
+
+ if (!string.IsNullOrWhiteSpace(this.alertInfo.CategoryName))
+ {
+ alert.CategoryName = this.alertInfo.CategoryName;
+ }
+
+ alert.IsEnabled = this.alertInfo.IsEnabled;
+
+ if (alertInfo.AlertType == Contracts.AlertType.SqlServerEvent)
+ {
+ alert.Severity = this.alertInfo.Severity;
+ alert.MessageID = this.alertInfo.MessageId;
+ if (!string.IsNullOrWhiteSpace(this.alertInfo.EventDescriptionKeyword))
+ {
+ alert.EventDescriptionKeyword = this.alertInfo.EventDescriptionKeyword;
+ }
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/AgentOperator.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/AgentOperator.cs
new file mode 100644
index 00000000..c26ad534
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/AgentOperator.cs
@@ -0,0 +1,947 @@
+//
+// 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;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Diagnostics;
+using System.Globalization;
+using System.Threading;
+using Microsoft.SqlServer.Management.Common;
+using Microsoft.SqlServer.Management.Diagnostics;
+using Microsoft.SqlServer.Management.Sdk.Sfc;
+using Microsoft.SqlServer.Management.Smo;
+using Microsoft.SqlServer.Management.Smo.Agent;
+using Microsoft.SqlTools.ServiceLayer.Admin;
+using Microsoft.SqlTools.ServiceLayer.Agent.Contracts;
+using Microsoft.SqlTools.ServiceLayer.Management;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent
+{
+ ///
+ /// Agent Operators management class
+ ///
+ internal class AgentOperator : ManagementActionBase
+ {
+ private AgentOperatorInfo operatorInfo;
+
+ AgentOperatorsData operatorsData = null;
+
+ ///
+ /// Constructor
+ ///
+ public AgentOperator(CDataContainer dataContainer, AgentOperatorInfo operatorInfo)
+ {
+ try
+ {
+ if (dataContainer == null)
+ {
+ throw new ArgumentNullException("dataContainer");
+ }
+
+ if (operatorInfo == null)
+ {
+ throw new ArgumentNullException("operatorInfo");
+ }
+
+ this.operatorInfo = operatorInfo;
+ this.DataContainer = dataContainer;
+
+ STParameters parameters = new STParameters();
+ parameters.SetDocument(dataContainer.Document);
+
+ string agentOperatorName = null;
+ if (parameters.GetParam("operator", ref agentOperatorName))
+ {
+ this.operatorsData = new AgentOperatorsData(dataContainer, agentOperatorName);
+ }
+ else
+ {
+ throw new ArgumentNullException("agentOperatorName");
+ }
+ }
+ catch(Exception e)
+ {
+ throw new ApplicationException("AgentOperatorsSR.FailedToCreateInitializeAgentOperatorDialog", e);
+ }
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if(disposing)
+ {
+ }
+ base.Dispose(disposing);
+ }
+
+ public bool CreateOrUpdate()
+ {
+ this.operatorsData.ApplyChanges(this.operatorInfo);
+ return true;
+ }
+ }
+
+ #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
+ ///
+ /// 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
+ ///
+ /// 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)
+ {
+ 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 = operatorName.Length == 0;
+ }
+ #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)
+ {
+ currentOperator.WeekdayPagerStartTime = operatorInfo.WeekdayPagerStartTime;
+ currentOperator.WeekdayPagerEndTime = operatorInfo.WeekdayPagerEndTime;
+ }
+
+ if ((operatorInfo.PagerDays & Contracts.WeekDays.Saturday) > 0)
+ {
+ currentOperator.SaturdayPagerStartTime = operatorInfo.SaturdayPagerStartTime;
+ currentOperator.SaturdayPagerEndTime = operatorInfo.SaturdayPagerEndTime;
+ }
+
+ if ((operatorInfo.PagerDays & Contracts.WeekDays.Sunday) > 0)
+ {
+ currentOperator.SundayPagerStartTime = operatorInfo.SundayPagerStartTime;
+ currentOperator.SundayPagerEndTime = operatorInfo.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("AgentOperatorsSR.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
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/AgentProxyAccount.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/AgentProxyAccount.cs
new file mode 100644
index 00000000..6b07c0cb
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/AgentProxyAccount.cs
@@ -0,0 +1,467 @@
+//
+// 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.ComponentModel;
+using System.Collections;
+using System.Data;
+using System.Data.SqlClient;
+using Microsoft.SqlServer.Management.Common;
+using Microsoft.SqlServer.Management.Diagnostics;
+using Microsoft.SqlServer.Management.Sdk.Sfc;
+using Microsoft.SqlServer.Management.Smo;
+using Microsoft.SqlServer.Management.Smo.Agent;
+using Microsoft.SqlTools.ServiceLayer.Admin;
+using Microsoft.SqlTools.ServiceLayer.Agent.Contracts;
+using Microsoft.SqlTools.ServiceLayer.Management;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent
+{
+ internal class AgentProxyAccount : ManagementActionBase
+ {
+ #region Constants
+ internal const string ProxyAccountPropertyName = "proxyaccount";
+ internal const string ProxyAccountSubsystem = "SubSystem";
+ internal const string ProxyAccountMode = "Mode";
+ internal const string ProxyAccountDuplicateMode = "Duplicate";
+ internal const int SysnameLength = 256;
+ internal const int DescriptionLength = 512;
+ #endregion
+
+ internal enum ProxyPrincipalType
+ {
+ SqlLogin,
+ MsdbRole,
+ ServerRole
+ }
+
+ // Collections of principals for logins/server roles/msdb roles
+ private ArrayList[] principals;
+
+ // Name of the proxy account we work with
+ private string proxyAccountName;
+
+ private AgentProxyInfo proxyInfo;
+
+ // Flag indicating that proxy account should be duplicated
+ private bool duplicate;
+
+ public static string SysadminAccount
+ {
+ get { return "AgentProxyAccountSR.SysadminAccount"; }
+ }
+
+ ///
+ /// Main constructor. Creates all pages and adds them
+ /// to the tree control.
+ ///
+ public AgentProxyAccount(CDataContainer dataContainer, AgentProxyInfo proxyInfo)
+ {
+ this.DataContainer = dataContainer;
+ this.proxyInfo = proxyInfo;
+
+ // Find out if we are creating a new proxy account or
+ // modifying an existing one.
+ GetProxyAccountName(dataContainer, ref this.proxyAccountName, ref this.duplicate);
+ }
+
+ ///
+ /// It creates a new ProxyAccount or gets an existing
+ /// one from JobServer and updates all properties.
+ ///
+ private bool CreateOrUpdateProxyAccount(
+ AgentProxyInfo proxyInfo,
+ bool isUpdate)
+ {
+ ProxyAccount proxyAccount = null;
+ if (!isUpdate)
+ {
+ proxyAccount = new ProxyAccount(this.DataContainer.Server.JobServer,
+ proxyInfo.AccountName,
+ proxyInfo.CredentialName,
+ proxyInfo.IsEnabled,
+ proxyInfo.Description);
+
+ UpdateProxyAccount(proxyAccount);
+ proxyAccount.Create();
+ }
+ else if (this.DataContainer.Server.JobServer.ProxyAccounts.Contains(this.proxyAccountName))
+ {
+ // Try refresh and check again
+ this.DataContainer.Server.JobServer.ProxyAccounts.Refresh();
+ if (this.DataContainer.Server.JobServer.ProxyAccounts.Contains(this.proxyAccountName))
+ {
+ // fail since account exists and asked to create a new one
+ if (!isUpdate)
+ {
+ return false;
+ }
+
+ proxyAccount = AgentProxyAccount.GetProxyAccount(this.proxyAccountName, this.DataContainer.Server.JobServer);
+ // Set the other properties
+ proxyAccount.CredentialName = proxyInfo.CredentialName;
+ proxyAccount.Description = proxyInfo.Description;
+
+ UpdateProxyAccount(proxyAccount);
+ proxyAccount.Alter();
+
+ // Rename the proxy if needed
+ // This has to be done after Alter, in order to
+ // work correcly when scripting this action.
+ if (this.proxyAccountName != proxyInfo.AccountName)
+ {
+ proxyAccount.Rename(proxyInfo.AccountName);
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
+
+#if false // @TODO - reenable subsystem code below
+
+ // Update the subsystems
+ foreach (AgentSubSystem subsystem in this.addSubSystems)
+ {
+ proxyAccount.AddSubSystem(subsystem);
+ }
+
+ foreach (AgentSubSystem subsystem in this.removeSubSystems)
+ {
+ proxyAccount.RemoveSubSystem(subsystem);
+
+ // Update jobsteps that use this proxy accunt
+ // when some subsystems are removed from it
+ string reassignToProxyName = this.reassignToProxyNames[(int)subsystem];
+
+ if (reassignToProxyName != null)
+ {
+ // if version is sql 11 and above call SMO API to reassign proxy account
+ if (Utils.IsSql11OrLater(this.DataContainer.Server.ServerVersion))
+ {
+ proxyAccount.Reassign(reassignToProxyName);
+ }
+ else
+ {
+ // legacy code
+ // Get a list of all job step objects that use this proxy and this subsystem
+ Request req = new Request();
+ req.Urn = string.Format(System.Globalization.CultureInfo.InvariantCulture,
+ "Server/JobServer/Job/Step[@ProxyName=\'{0}\' and @SubSystem={1}]",
+ Urn.EscapeString(proxyAccount.Name),
+ (int)subsystem);
+ req.Fields = new string[] { "Name" };
+ req.ParentPropertiesRequests = new PropertiesRequest[1] { new PropertiesRequest() };
+ req.ParentPropertiesRequests[0].Fields = new string[] { "Name" };
+
+ Enumerator en = new Enumerator();
+ DataTable table = en.Process(this.DataContainer.ServerConnection, req);
+ foreach (DataRow row in table.Rows)
+ {
+ // Get the actual job step object using urn
+ string urnString = string.Format(System.Globalization.CultureInfo.InvariantCulture, "Server/JobServer/Job[@Name=\"{0}\"/Step[@Name=\"{1}\"", row["Job_Name"], row["Name"]);
+ Urn urn = new Urn(urnString);
+ JobStep jobStep = (JobStep)this.DataContainer.Server.GetSmoObject(urn);
+
+ jobStep.ProxyName = reassignToProxyName;
+ jobStep.Alter();
+ }
+ }
+ }
+ }
+#endif
+ }
+
+ public bool Create()
+ {
+ CreateOrUpdateProxyAccount(this.proxyInfo, false);
+ return true;
+ }
+
+ public bool Update()
+ {
+ CreateOrUpdateProxyAccount(this.proxyInfo, true);
+ return true;
+ }
+
+ public bool Drop()
+ {
+ if (this.DataContainer.Server.JobServer.ProxyAccounts.Contains(this.proxyAccountName))
+ {
+ // Try refresh and check again
+ this.DataContainer.Server.JobServer.ProxyAccounts.Refresh();
+ if (this.DataContainer.Server.JobServer.ProxyAccounts.Contains(this.proxyAccountName))
+ {
+ ProxyAccount proxyAccount = AgentProxyAccount.GetProxyAccount(this.proxyAccountName, this.DataContainer.Server.JobServer);
+ proxyAccount.DropIfExists();
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Called to update the proxy object with properties
+ /// from this page.
+ ///
+ public void UpdateProxyAccount(ProxyAccount proxyAccount)
+ {
+ if (proxyAccount == null)
+ {
+ throw new ArgumentNullException("proxyAccount");
+ }
+
+ ArrayList principalsToAdd = new ArrayList();
+ ArrayList principalsToRemove = new ArrayList();
+
+ // Process Sql Logins
+ if (ExtractPermissionsToAddAndRemove(this.proxyAccountName != null? proxyAccount.EnumLogins() : null, this.principals[(int) ProxyPrincipalType.SqlLogin], principalsToAdd, principalsToRemove))
+ {
+ foreach (string principal in principalsToRemove)
+ {
+ proxyAccount.RemoveLogin(principal);
+ }
+
+ foreach (string principal in principalsToAdd)
+ {
+ proxyAccount.AddLogin(principal);
+ }
+ }
+
+ // Process Server Roles
+ if (ExtractPermissionsToAddAndRemove(this.proxyAccountName != null? proxyAccount.EnumServerRoles() : null, this.principals[(int) ProxyPrincipalType.ServerRole], principalsToAdd, principalsToRemove))
+ {
+ foreach (string principal in principalsToRemove)
+ {
+ proxyAccount.RemoveServerRole(principal);
+ }
+
+ foreach (string principal in principalsToAdd)
+ {
+ proxyAccount.AddServerRole(principal);
+ }
+ }
+
+ // Process Msdb Roles
+ if (ExtractPermissionsToAddAndRemove(this.proxyAccountName != null? proxyAccount.EnumMsdbRoles() : null, this.principals[(int) ProxyPrincipalType.MsdbRole], principalsToAdd, principalsToRemove))
+ {
+ foreach (string principal in principalsToRemove)
+ {
+ proxyAccount.RemoveMsdbRole(principal);
+ }
+
+ foreach (string principal in principalsToAdd)
+ {
+ proxyAccount.AddMsdbRole(principal);
+ }
+ }
+ }
+
+
+ ///
+ /// This method scans two list of principals - an existing one extracted from ProxyAccount object
+ /// and a new one obtained from this panel and then it creates a two differential lists: one of
+ /// principals to add and the other of principals to remove.
+ ///
+ /// true if there are any changes between existingPermissions and newPermissions lists
+ private bool ExtractPermissionsToAddAndRemove(DataTable existingPermissions, ArrayList newPermissions, ArrayList principalsToAdd, ArrayList principalsToRemove)
+ {
+ // Reset both output lists
+ principalsToAdd.Clear();
+ principalsToRemove.Clear();
+
+ // Sort both input lists
+ DataRow[] existingRows = existingPermissions != null? existingPermissions.Select(string.Empty, "Name DESC") : new DataRow[] {};
+ newPermissions.Sort();
+
+ // Go through both lists at the same time and find differences
+ int existingPos = 0;
+ int newPos = 0;
+
+ while (newPos < newPermissions.Count && existingPos < existingRows.Length)
+ {
+ int result = string.Compare(existingRows[existingPos]["Name"] as string, newPermissions[newPos] as string,StringComparison.Ordinal);
+
+ if (result < 0)
+ {
+ // element in existingRows is lower then element in newPermissions
+ // mark element in existingRows for removal
+ principalsToRemove.Add(existingRows[existingPos]["Name"]);
+ ++existingPos;
+ }
+ else if (result > 0)
+ {
+ // element in existingRows is greater then element in newPermissions
+ // mark element in newPermissions for adding
+ principalsToAdd.Add(newPermissions[newPos]);
+ ++newPos;
+ }
+ else
+ {
+ // Both elements are equal.
+ // Advance to the next element
+ ++existingPos;
+ ++newPos;
+ }
+ }
+
+ while (newPos < newPermissions.Count)
+ {
+ // Some elements are still left
+ // Copy them all to Add collection
+ principalsToAdd.Add(newPermissions[newPos++]);
+ }
+
+ while (existingPos < existingRows.Length)
+ {
+ // Some elements are still left
+ // Copy them all to Remove collection
+ principalsToRemove.Add(existingRows[existingPos++]["Name"]);
+ }
+
+ return(principalsToAdd.Count > 0 || principalsToRemove.Count > 0);
+ }
+
+ private void RefreshData()
+ {
+ // List all the jobsteps that use current
+ // proxy account
+ Request req = new Request();
+ req.Urn = string.Format(System.Globalization.CultureInfo.InvariantCulture,
+ "Server/JobServer/Job/Step[@ProxyName=\'{0}\']",
+ Urn.EscapeString(this.proxyAccountName));
+ req.ResultType = ResultType.IDataReader;
+ req.Fields = new string[] {"Name", "SubSystem"};
+ req.ParentPropertiesRequests = new PropertiesRequest[1];
+ req.ParentPropertiesRequests[0] = new PropertiesRequest(new string[] {"Name"});
+
+ Enumerator en = new Enumerator();
+ using (IDataReader reader = en.Process(this.DataContainer.ServerConnection, req).Data as IDataReader)
+ {
+ while (reader.Read())
+ {
+ //JobStepSubSystems.
+ // @TODO - write to output collection
+ // new GridCell(reader.GetString(0)), // Job Name (parent property is first)
+ // new GridCell(reader.GetString(1)), // JobStep Name
+ // new GridCell(JobStepSubSystems.LookupFriendlyName((AgentSubSystem) reader.GetInt32(2))) // JobStep SubSystem
+ }
+ }
+ }
+
+ #region Static methods
+ ///
+ /// Retrieves an instance of ProxyAccount from job server using name provided.
+ /// If proxy does not exist it throws an exception.
+ ///
+ /// Name of the proxy to get
+ /// Job server to get the proxy from
+ /// A valid proxy account.
+ internal static ProxyAccount GetProxyAccount(string proxyAccountName, JobServer jobServer)
+ {
+ if (proxyAccountName == null || proxyAccountName.Length == 0)
+ {
+ throw new ArgumentException("proxyAccountName");
+ }
+
+ if (jobServer == null)
+ {
+ throw new ArgumentNullException("jobServer");
+ }
+
+ ProxyAccount proxyAccount = jobServer.ProxyAccounts[proxyAccountName];
+ if (proxyAccount == null)
+ {
+ // proxy not found. Try refreshing the collection
+ jobServer.ProxyAccounts.Refresh();
+ proxyAccount = jobServer.ProxyAccounts[proxyAccountName];
+
+ // if still cannot get the proxy throw an exception
+ if (proxyAccount == null)
+ {
+ throw new ApplicationException("SRError.ProxyAccountNotFound(proxyAccountName)");
+ }
+ }
+ return proxyAccount;
+ }
+
+ ///
+ /// Retrieves a proxy account name from shared data containter.
+ ///
+ internal static void GetProxyAccountName(CDataContainer dataContainer, ref string proxyAccountName, ref bool duplicate)
+ {
+ STParameters parameters = new STParameters();
+ parameters.SetDocument(dataContainer.Document);
+
+ // Get proxy name
+ parameters.GetParam(AgentProxyAccount.ProxyAccountPropertyName, ref proxyAccountName);
+ if (proxyAccountName != null && proxyAccountName.Length == 0)
+ {
+ // Reset empty name back to null
+ proxyAccountName = null;
+ }
+
+ // Get duplicate flag
+ string mode = string.Empty;
+ if (parameters.GetParam(AgentProxyAccount.ProxyAccountMode, ref mode) &&
+ 0 == string.Compare(mode, AgentProxyAccount.ProxyAccountDuplicateMode, StringComparison.Ordinal))
+ {
+ duplicate = true;
+ }
+ }
+
+ ///
+ /// Uses enumerator to list names of all proxy accounts that use specified Agent SubSystem.
+ ///
+ /// Connection to use.
+ /// Requested SubSystem name
+ /// If set to true, 'sysadmin' account is added as a first entry in
+ /// the list of proxy accounts.
+ /// An array containing names of proxy accounts
+ internal static string[] ListProxyAccountsForSubsystem(
+ ServerConnection serverConnection,
+ string subsystemName,
+ bool includeSysadmin)
+ {
+ ArrayList proxyAccounts = new ArrayList();
+
+ // This method is valid only on Yukon and later
+ if (serverConnection.ServerVersion.Major >= 9)
+ {
+ if (includeSysadmin)
+ {
+ proxyAccounts.Add(AgentProxyAccount.SysadminAccount);
+ }
+
+ // Get the list of proxy accounts
+ Request req = new Request();
+
+ req.Urn = string.Format(System.Globalization.CultureInfo.InvariantCulture,
+ "Server/JobServer/ProxyAccount/AgentSubSystem[@Name = \"{0}\"]",
+ Urn.EscapeString(subsystemName));
+ req.ResultType = ResultType.IDataReader;
+ req.Fields = new string[] { "Name" };
+ req.ParentPropertiesRequests = new PropertiesRequest[1] { new PropertiesRequest() };
+ req.ParentPropertiesRequests[0].Fields = new string[] { "Name" };
+
+ Enumerator en = new Enumerator();
+
+ using (IDataReader reader = en.Process(serverConnection, req).Data as IDataReader)
+ {
+ while (reader.Read())
+ {
+ proxyAccounts.Add(reader.GetString(0));
+ }
+ }
+ }
+
+ return (string[]) proxyAccounts.ToArray(typeof(string));
+ }
+ #endregion
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/JobFetcher.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobFetcher.cs
similarity index 74%
rename from src/Microsoft.SqlTools.ServiceLayer/Agent/JobFetcher.cs
rename to src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobFetcher.cs
index 70b79234..0f3adfc0 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Agent/JobFetcher.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobFetcher.cs
@@ -403,153 +403,4 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
return foundJobs;
}
}
-
- ///
- /// a class for storing various properties of agent jobs,
- /// used by the Job Activity Monitor
- ///
- public class JobProperties
- {
- private string name;
- private int currentExecutionStatus;
- private int lastRunOutcome;
- private string currentExecutionStep;
- private bool enabled;
- private bool hasTarget;
- private bool hasSchedule;
- private bool hasStep;
- private bool runnable;
- private string category;
- private int categoryID;
- private int categoryType;
- private DateTime lastRun;
- private DateTime nextRun;
- private Guid jobId;
-
- private JobProperties()
- {
- }
-
- public JobProperties(DataRow row)
- {
- System.Diagnostics.Debug.Assert(row["Name"] != DBNull.Value, "Name is null!");
- System.Diagnostics.Debug.Assert(row["IsEnabled"] != DBNull.Value, "IsEnabled is null!");
- System.Diagnostics.Debug.Assert(row["Category"] != DBNull.Value, "Category is null!");
- System.Diagnostics.Debug.Assert(row["CategoryID"] != DBNull.Value, "CategoryID is null!");
- System.Diagnostics.Debug.Assert(row["CategoryType"] != DBNull.Value, "CategoryType is null!");
- System.Diagnostics.Debug.Assert(row["CurrentRunStatus"] != DBNull.Value, "CurrentRunStatus is null!");
- System.Diagnostics.Debug.Assert(row["CurrentRunStep"] != DBNull.Value, "CurrentRunStep is null!");
- System.Diagnostics.Debug.Assert(row["HasSchedule"] != DBNull.Value, "HasSchedule is null!");
- System.Diagnostics.Debug.Assert(row["HasStep"] != DBNull.Value, "HasStep is null!");
- System.Diagnostics.Debug.Assert(row["HasServer"] != DBNull.Value, "HasServer is null!");
- System.Diagnostics.Debug.Assert(row["LastRunOutcome"] != DBNull.Value, "LastRunOutcome is null!");
- System.Diagnostics.Debug.Assert(row["JobID"] != DBNull.Value, "JobID is null!");
-
- this.name = row["Name"].ToString();
- this.enabled = Convert.ToBoolean(row["IsEnabled"], CultureInfo.InvariantCulture);
- this.category = row["Category"].ToString();
- this.categoryID = Convert.ToInt32(row["CategoryID"], CultureInfo.InvariantCulture);
- this.categoryType = Convert.ToInt32(row["CategoryType"], CultureInfo.InvariantCulture);
- this.currentExecutionStatus = Convert.ToInt32(row["CurrentRunStatus"], CultureInfo.InvariantCulture);
- this.currentExecutionStep = row["CurrentRunStep"].ToString();
- this.hasSchedule = Convert.ToBoolean(row["HasSchedule"], CultureInfo.InvariantCulture);
- this.hasStep = Convert.ToBoolean(row["HasStep"], CultureInfo.InvariantCulture);
- this.hasTarget = Convert.ToBoolean(row["HasServer"], CultureInfo.InvariantCulture);
- this.lastRunOutcome = Convert.ToInt32(row["LastRunOutcome"], CultureInfo.InvariantCulture);
- this.jobId = Guid.Parse(row["JobID"].ToString()); ;
-
- // for a job to be runnable, it must:
- // 1. have a target server
- // 2. have some steps
- this.runnable = this.hasTarget && this.hasStep;
-
- if (row["LastRunDate"] != DBNull.Value)
- {
- this.lastRun = Convert.ToDateTime(row["LastRunDate"], CultureInfo.InvariantCulture);
- }
-
- if (row["NextRunDate"] != DBNull.Value)
- {
- this.nextRun = Convert.ToDateTime(row["NextRunDate"], CultureInfo.InvariantCulture);
- }
- }
-
- public bool Runnable
- {
- get{ return runnable;}
- }
-
- public string Name
- {
- get{ return name;}
- }
-
- public string Category
- {
- get{ return category;}
- }
-
- public int CategoryID
- {
- get{ return categoryID;}
- }
-
- public int CategoryType
- {
- get{ return categoryType;}
- }
-
- public int LastRunOutcome
- {
- get{ return lastRunOutcome;}
- }
-
- public int CurrentExecutionStatus
- {
- get{ return currentExecutionStatus;}
- }
-
- public string CurrentExecutionStep
- {
- get{ return currentExecutionStep;}
- }
-
- public bool Enabled
- {
- get{ return enabled;}
- }
-
- public bool HasTarget
- {
- get{ return hasTarget;}
- }
-
- public bool HasStep
- {
- get{ return hasStep;}
- }
-
- public bool HasSchedule
- {
- get{ return hasSchedule;}
- }
-
- public DateTime NextRun
- {
- get{ return nextRun;}
- }
-
- public DateTime LastRun
- {
- get{ return lastRun;}
- }
-
- public Guid JobID
- {
- get
- {
- return this.jobId;
- }
- }
- }
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/JobHelper.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobHelper.cs
similarity index 100%
rename from src/Microsoft.SqlTools.ServiceLayer/Agent/JobHelper.cs
rename to src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobHelper.cs
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/JobHistoryItem.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobHistoryItem.cs
similarity index 100%
rename from src/Microsoft.SqlTools.ServiceLayer/Agent/JobHistoryItem.cs
rename to src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobHistoryItem.cs
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobProperties.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobProperties.cs
new file mode 100644
index 00000000..d9ba727b
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobProperties.cs
@@ -0,0 +1,167 @@
+//
+// 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.Text;
+using System.Data;
+using System.Globalization;
+using System.Collections.Generic;
+using Microsoft.SqlServer.Management.Common;
+using Microsoft.SqlServer.Management.Sdk.Sfc;
+using Microsoft.SqlServer.Management.Smo.Agent;
+using SMO = Microsoft.SqlServer.Management.Smo;
+using Microsoft.SqlTools.ServiceLayer.Agent.Contracts;
+
+namespace Microsoft.SqlTools.ServiceLayer.Agent
+{
+ ///
+ /// a class for storing various properties of agent jobs,
+ /// used by the Job Activity Monitor
+ ///
+ public class JobProperties
+ {
+ private string name;
+ private int currentExecutionStatus;
+ private int lastRunOutcome;
+ private string currentExecutionStep;
+ private bool enabled;
+ private bool hasTarget;
+ private bool hasSchedule;
+ private bool hasStep;
+ private bool runnable;
+ private string category;
+ private int categoryID;
+ private int categoryType;
+ private DateTime lastRun;
+ private DateTime nextRun;
+ private Guid jobId;
+
+ private JobProperties()
+ {
+ }
+
+ public JobProperties(DataRow row)
+ {
+ System.Diagnostics.Debug.Assert(row["Name"] != DBNull.Value, "Name is null!");
+ System.Diagnostics.Debug.Assert(row["IsEnabled"] != DBNull.Value, "IsEnabled is null!");
+ System.Diagnostics.Debug.Assert(row["Category"] != DBNull.Value, "Category is null!");
+ System.Diagnostics.Debug.Assert(row["CategoryID"] != DBNull.Value, "CategoryID is null!");
+ System.Diagnostics.Debug.Assert(row["CategoryType"] != DBNull.Value, "CategoryType is null!");
+ System.Diagnostics.Debug.Assert(row["CurrentRunStatus"] != DBNull.Value, "CurrentRunStatus is null!");
+ System.Diagnostics.Debug.Assert(row["CurrentRunStep"] != DBNull.Value, "CurrentRunStep is null!");
+ System.Diagnostics.Debug.Assert(row["HasSchedule"] != DBNull.Value, "HasSchedule is null!");
+ System.Diagnostics.Debug.Assert(row["HasStep"] != DBNull.Value, "HasStep is null!");
+ System.Diagnostics.Debug.Assert(row["HasServer"] != DBNull.Value, "HasServer is null!");
+ System.Diagnostics.Debug.Assert(row["LastRunOutcome"] != DBNull.Value, "LastRunOutcome is null!");
+ System.Diagnostics.Debug.Assert(row["JobID"] != DBNull.Value, "JobID is null!");
+
+ this.name = row["Name"].ToString();
+ this.enabled = Convert.ToBoolean(row["IsEnabled"], CultureInfo.InvariantCulture);
+ this.category = row["Category"].ToString();
+ this.categoryID = Convert.ToInt32(row["CategoryID"], CultureInfo.InvariantCulture);
+ this.categoryType = Convert.ToInt32(row["CategoryType"], CultureInfo.InvariantCulture);
+ this.currentExecutionStatus = Convert.ToInt32(row["CurrentRunStatus"], CultureInfo.InvariantCulture);
+ this.currentExecutionStep = row["CurrentRunStep"].ToString();
+ this.hasSchedule = Convert.ToBoolean(row["HasSchedule"], CultureInfo.InvariantCulture);
+ this.hasStep = Convert.ToBoolean(row["HasStep"], CultureInfo.InvariantCulture);
+ this.hasTarget = Convert.ToBoolean(row["HasServer"], CultureInfo.InvariantCulture);
+ this.lastRunOutcome = Convert.ToInt32(row["LastRunOutcome"], CultureInfo.InvariantCulture);
+ this.jobId = Guid.Parse(row["JobID"].ToString()); ;
+
+ // for a job to be runnable, it must:
+ // 1. have a target server
+ // 2. have some steps
+ this.runnable = this.hasTarget && this.hasStep;
+
+ if (row["LastRunDate"] != DBNull.Value)
+ {
+ this.lastRun = Convert.ToDateTime(row["LastRunDate"], CultureInfo.InvariantCulture);
+ }
+
+ if (row["NextRunDate"] != DBNull.Value)
+ {
+ this.nextRun = Convert.ToDateTime(row["NextRunDate"], CultureInfo.InvariantCulture);
+ }
+ }
+
+ public bool Runnable
+ {
+ get{ return runnable;}
+ }
+
+ public string Name
+ {
+ get{ return name;}
+ }
+
+ public string Category
+ {
+ get{ return category;}
+ }
+
+ public int CategoryID
+ {
+ get{ return categoryID;}
+ }
+
+ public int CategoryType
+ {
+ get{ return categoryType;}
+ }
+
+ public int LastRunOutcome
+ {
+ get{ return lastRunOutcome;}
+ }
+
+ public int CurrentExecutionStatus
+ {
+ get{ return currentExecutionStatus;}
+ }
+
+ public string CurrentExecutionStep
+ {
+ get{ return currentExecutionStep;}
+ }
+
+ public bool Enabled
+ {
+ get{ return enabled;}
+ }
+
+ public bool HasTarget
+ {
+ get{ return hasTarget;}
+ }
+
+ public bool HasStep
+ {
+ get{ return hasStep;}
+ }
+
+ public bool HasSchedule
+ {
+ get{ return hasSchedule;}
+ }
+
+ public DateTime NextRun
+ {
+ get{ return nextRun;}
+ }
+
+ public DateTime LastRun
+ {
+ get{ return lastRun;}
+ }
+
+ public Guid JobID
+ {
+ get
+ {
+ return this.jobId;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/JobUtilities.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobUtilities.cs
similarity index 100%
rename from src/Microsoft.SqlTools.ServiceLayer/Agent/JobUtilities.cs
rename to src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobUtilities.cs
diff --git a/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs b/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs
index f0aa83c8..790574ea 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs
@@ -20,6 +20,7 @@ using Microsoft.SqlTools.ServiceLayer.Metadata;
using Microsoft.SqlTools.ServiceLayer.Profiler;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.Scripting;
+using Microsoft.SqlTools.ServiceLayer.Security;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.Workspace;
@@ -106,6 +107,9 @@ namespace Microsoft.SqlTools.ServiceLayer
ProfilerService.Instance.InitializeService(serviceHost);
serviceProvider.RegisterSingleService(ProfilerService.Instance);
+ SecurityService.Instance.InitializeService(serviceHost);
+ serviceProvider.RegisterSingleService(SecurityService.Instance);
+
InitializeHostedServices(serviceProvider, serviceHost);
serviceHost.ServiceProvider = serviceProvider;
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs
index e03d89d0..b23073c2 100755
--- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs
@@ -3661,6 +3661,518 @@ namespace Microsoft.SqlTools.ServiceLayer
}
}
+ public static string UserCancelledSelectStep
+ {
+ get
+ {
+ return Keys.GetString(Keys.UserCancelledSelectStep);
+ }
+ }
+
+ public static string RequestPostedToTargetServers
+ {
+ get
+ {
+ return Keys.GetString(Keys.RequestPostedToTargetServers);
+ }
+ }
+
+ public static string Executing
+ {
+ get
+ {
+ return Keys.GetString(Keys.Executing);
+ }
+ }
+
+ public static string BetweenRetries
+ {
+ get
+ {
+ return Keys.GetString(Keys.BetweenRetries);
+ }
+ }
+
+ public static string Suspended
+ {
+ get
+ {
+ return Keys.GetString(Keys.Suspended);
+ }
+ }
+
+ public static string PerformingCompletionAction
+ {
+ get
+ {
+ return Keys.GetString(Keys.PerformingCompletionAction);
+ }
+ }
+
+ public static string WaitingForStepToFinish
+ {
+ get
+ {
+ return Keys.GetString(Keys.WaitingForStepToFinish);
+ }
+ }
+
+ public static string WaitingForWorkerThread
+ {
+ get
+ {
+ return Keys.GetString(Keys.WaitingForWorkerThread);
+ }
+ }
+
+ public static string AllDatabases
+ {
+ get
+ {
+ return Keys.GetString(Keys.AllDatabases);
+ }
+ }
+
+ public static string Severity001
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity001);
+ }
+ }
+
+ public static string Severity002
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity002);
+ }
+ }
+
+ public static string Severity003
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity003);
+ }
+ }
+
+ public static string Severity004
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity004);
+ }
+ }
+
+ public static string Severity005
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity005);
+ }
+ }
+
+ public static string Severity006
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity006);
+ }
+ }
+
+ public static string Severity007
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity007);
+ }
+ }
+
+ public static string Severity008
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity008);
+ }
+ }
+
+ public static string Severity009
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity009);
+ }
+ }
+
+ public static string Severity010
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity010);
+ }
+ }
+
+ public static string Severity011
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity011);
+ }
+ }
+
+ public static string Severity012
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity012);
+ }
+ }
+
+ public static string Severity013
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity013);
+ }
+ }
+
+ public static string Severity014
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity014);
+ }
+ }
+
+ public static string Severity015
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity015);
+ }
+ }
+
+ public static string Severity016
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity016);
+ }
+ }
+
+ public static string Severity017
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity017);
+ }
+ }
+
+ public static string Severity018
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity018);
+ }
+ }
+
+ public static string Severity019
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity019);
+ }
+ }
+
+ public static string Severity020
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity020);
+ }
+ }
+
+ public static string Severity021
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity021);
+ }
+ }
+
+ public static string Severity022
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity022);
+ }
+ }
+
+ public static string Severity023
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity023);
+ }
+ }
+
+ public static string Severity024
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity024);
+ }
+ }
+
+ public static string Severity025
+ {
+ get
+ {
+ return Keys.GetString(Keys.Severity025);
+ }
+ }
+
+ public static string PagerScheduleMonFri
+ {
+ get
+ {
+ return Keys.GetString(Keys.PagerScheduleMonFri);
+ }
+ }
+
+ public static string PagerScheduleSatSun
+ {
+ get
+ {
+ return Keys.GetString(Keys.PagerScheduleSatSun);
+ }
+ }
+
+ public static string PagerScheduleWarning
+ {
+ get
+ {
+ return Keys.GetString(Keys.PagerScheduleWarning);
+ }
+ }
+
+ public static string General
+ {
+ get
+ {
+ return Keys.GetString(Keys.General);
+ }
+ }
+
+ public static string Notifications
+ {
+ get
+ {
+ return Keys.GetString(Keys.Notifications);
+ }
+ }
+
+ public static string History
+ {
+ get
+ {
+ return Keys.GetString(Keys.History);
+ }
+ }
+
+ public static string Day
+ {
+ get
+ {
+ return Keys.GetString(Keys.Day);
+ }
+ }
+
+ public static string StartTime
+ {
+ get
+ {
+ return Keys.GetString(Keys.StartTime);
+ }
+ }
+
+ public static string EndTime
+ {
+ get
+ {
+ return Keys.GetString(Keys.EndTime);
+ }
+ }
+
+ public static string ColumnIndexIsInvalid
+ {
+ get
+ {
+ return Keys.GetString(Keys.ColumnIndexIsInvalid);
+ }
+ }
+
+ public static string RowIndexIsInvalid
+ {
+ get
+ {
+ return Keys.GetString(Keys.RowIndexIsInvalid);
+ }
+ }
+
+ public static string NewOperatorProperties
+ {
+ get
+ {
+ return Keys.GetString(Keys.NewOperatorProperties);
+ }
+ }
+
+ public static string FailedToCreateInitializeAgentOperatorDialog
+ {
+ get
+ {
+ return Keys.GetString(Keys.FailedToCreateInitializeAgentOperatorDialog);
+ }
+ }
+
+ public static string JobServerIsNotAvailable
+ {
+ get
+ {
+ return Keys.GetString(Keys.JobServerIsNotAvailable);
+ }
+ }
+
+ public static string CannotCreateInitializeGeneralPage
+ {
+ get
+ {
+ return Keys.GetString(Keys.CannotCreateInitializeGeneralPage);
+ }
+ }
+
+ public static string CannotCreateInitializeNotificationsPage
+ {
+ get
+ {
+ return Keys.GetString(Keys.CannotCreateInitializeNotificationsPage);
+ }
+ }
+
+ public static string CannotCreateInitializeHistoryPage
+ {
+ get
+ {
+ return Keys.GetString(Keys.CannotCreateInitializeHistoryPage);
+ }
+ }
+
+ public static string CannotResetOperator
+ {
+ get
+ {
+ return Keys.GetString(Keys.CannotResetOperator);
+ }
+ }
+
+ public static string AlertList
+ {
+ get
+ {
+ return Keys.GetString(Keys.AlertList);
+ }
+ }
+
+ public static string JobList
+ {
+ get
+ {
+ return Keys.GetString(Keys.JobList);
+ }
+ }
+
+ public static string Email
+ {
+ get
+ {
+ return Keys.GetString(Keys.Email);
+ }
+ }
+
+ public static string Pager
+ {
+ get
+ {
+ return Keys.GetString(Keys.Pager);
+ }
+ }
+
+ public static string AlertName
+ {
+ get
+ {
+ return Keys.GetString(Keys.AlertName);
+ }
+ }
+
+ public static string JobName
+ {
+ get
+ {
+ return Keys.GetString(Keys.JobName);
+ }
+ }
+
+ public static string Always
+ {
+ get
+ {
+ return Keys.GetString(Keys.Always);
+ }
+ }
+
+ public static string Never
+ {
+ get
+ {
+ return Keys.GetString(Keys.Never);
+ }
+ }
+
+ public static string OnFailure
+ {
+ get
+ {
+ return Keys.GetString(Keys.OnFailure);
+ }
+ }
+
+ public static string OnSuccess
+ {
+ get
+ {
+ return Keys.GetString(Keys.OnSuccess);
+ }
+ }
+
+ public static string CannotModifyAlerts
+ {
+ get
+ {
+ return Keys.GetString(Keys.CannotModifyAlerts);
+ }
+ }
+
+ public static string CannotCreateScriptForModifyAlerts
+ {
+ get
+ {
+ return Keys.GetString(Keys.CannotCreateScriptForModifyAlerts);
+ }
+ }
+
public static string ConnectionServiceListDbErrorNotConnected(string uri)
{
return Keys.GetString(Keys.ConnectionServiceListDbErrorNotConnected, uri);
@@ -3751,6 +4263,151 @@ namespace Microsoft.SqlTools.ServiceLayer
return Keys.GetString(Keys.EditDataIncorrectTable, tableName);
}
+ public static string EnableAlertsTitle(String serverName)
+ {
+ return Keys.GetString(Keys.EnableAlertsTitle, serverName);
+ }
+
+ public static string EnableAlertDescription(String alertName)
+ {
+ return Keys.GetString(Keys.EnableAlertDescription, alertName);
+ }
+
+ public static string EnablingAlert(String alertName)
+ {
+ return Keys.GetString(Keys.EnablingAlert, alertName);
+ }
+
+ public static string EnabledAlert(String alertName)
+ {
+ return Keys.GetString(Keys.EnabledAlert, alertName);
+ }
+
+ public static string DisableAlertsTitle(String serverName)
+ {
+ return Keys.GetString(Keys.DisableAlertsTitle, serverName);
+ }
+
+ public static string DisableAlertDescription(String alertName)
+ {
+ return Keys.GetString(Keys.DisableAlertDescription, alertName);
+ }
+
+ public static string DisablingAlert(String alertName)
+ {
+ return Keys.GetString(Keys.DisablingAlert, alertName);
+ }
+
+ public static string DisabledAlert(String alertName)
+ {
+ return Keys.GetString(Keys.DisabledAlert, alertName);
+ }
+
+ public static string EnableJobsTitle(String serverName)
+ {
+ return Keys.GetString(Keys.EnableJobsTitle, serverName);
+ }
+
+ public static string EnableJobDescription(String jobName)
+ {
+ return Keys.GetString(Keys.EnableJobDescription, jobName);
+ }
+
+ public static string EnablingJob(String jobName)
+ {
+ return Keys.GetString(Keys.EnablingJob, jobName);
+ }
+
+ public static string EnabledJob(String jobName)
+ {
+ return Keys.GetString(Keys.EnabledJob, jobName);
+ }
+
+ public static string DisableJobsTitle(String serverName)
+ {
+ return Keys.GetString(Keys.DisableJobsTitle, serverName);
+ }
+
+ public static string DisableJobDescription(String jobName)
+ {
+ return Keys.GetString(Keys.DisableJobDescription, jobName);
+ }
+
+ public static string DisablingJob(String jobName)
+ {
+ return Keys.GetString(Keys.DisablingJob, jobName);
+ }
+
+ public static string DisabledJob(String jobName)
+ {
+ return Keys.GetString(Keys.DisabledJob, jobName);
+ }
+
+ public static string StartJobsTitle(String serverName)
+ {
+ return Keys.GetString(Keys.StartJobsTitle, serverName);
+ }
+
+ public static string StartJobDescription(String jobName)
+ {
+ return Keys.GetString(Keys.StartJobDescription, jobName);
+ }
+
+ public static string GettingStartStep(String jobName)
+ {
+ return Keys.GetString(Keys.GettingStartStep, jobName);
+ }
+
+ public static string StartingJob(String jobName)
+ {
+ return Keys.GetString(Keys.StartingJob, jobName);
+ }
+
+ public static string StartJobWithStep(String jobName, String stepName)
+ {
+ return Keys.GetString(Keys.StartJobWithStep, jobName, stepName);
+ }
+
+ public static string ExecuteJob(string jobName)
+ {
+ return Keys.GetString(Keys.ExecuteJob, jobName);
+ }
+
+ public static string JobFailed(string jobName)
+ {
+ return Keys.GetString(Keys.JobFailed, jobName);
+ }
+
+ public static string StopJobsTitle(String serverName)
+ {
+ return Keys.GetString(Keys.StopJobsTitle, serverName);
+ }
+
+ public static string StopJobDescription(String jobName)
+ {
+ return Keys.GetString(Keys.StopJobDescription, jobName);
+ }
+
+ public static string StoppingJob(String jobName)
+ {
+ return Keys.GetString(Keys.StoppingJob, jobName);
+ }
+
+ public static string StoppedJob(String jobName)
+ {
+ return Keys.GetString(Keys.StoppedJob, jobName);
+ }
+
+ public static string UnknownSeverity(int severity)
+ {
+ return Keys.GetString(Keys.UnknownSeverity, severity);
+ }
+
+ public static string OperatorProperties(string operatorName)
+ {
+ return Keys.GetString(Keys.OperatorProperties, operatorName);
+ }
+
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Keys
{
@@ -5175,6 +5832,285 @@ namespace Microsoft.SqlTools.ServiceLayer
public const string ProfilerConnectionNotFound = "ProfilerConnectionNotFound";
+ public const string EnableAlertsTitle = "EnableAlertsTitle";
+
+
+ public const string EnableAlertDescription = "EnableAlertDescription";
+
+
+ public const string EnablingAlert = "EnablingAlert";
+
+
+ public const string EnabledAlert = "EnabledAlert";
+
+
+ public const string DisableAlertsTitle = "DisableAlertsTitle";
+
+
+ public const string DisableAlertDescription = "DisableAlertDescription";
+
+
+ public const string DisablingAlert = "DisablingAlert";
+
+
+ public const string DisabledAlert = "DisabledAlert";
+
+
+ public const string EnableJobsTitle = "EnableJobsTitle";
+
+
+ public const string EnableJobDescription = "EnableJobDescription";
+
+
+ public const string EnablingJob = "EnablingJob";
+
+
+ public const string EnabledJob = "EnabledJob";
+
+
+ public const string DisableJobsTitle = "DisableJobsTitle";
+
+
+ public const string DisableJobDescription = "DisableJobDescription";
+
+
+ public const string DisablingJob = "DisablingJob";
+
+
+ public const string DisabledJob = "DisabledJob";
+
+
+ public const string StartJobsTitle = "StartJobsTitle";
+
+
+ public const string StartJobDescription = "StartJobDescription";
+
+
+ public const string GettingStartStep = "GettingStartStep";
+
+
+ public const string UserCancelledSelectStep = "UserCancelledSelectStep";
+
+
+ public const string StartingJob = "StartingJob";
+
+
+ public const string StartJobWithStep = "StartJobWithStep";
+
+
+ public const string RequestPostedToTargetServers = "RequestPostedToTargetServers";
+
+
+ public const string ExecuteJob = "ExecuteJob";
+
+
+ public const string JobFailed = "JobFailed";
+
+
+ public const string Executing = "Executing";
+
+
+ public const string BetweenRetries = "BetweenRetries";
+
+
+ public const string Suspended = "Suspended";
+
+
+ public const string PerformingCompletionAction = "PerformingCompletionAction";
+
+
+ public const string WaitingForStepToFinish = "WaitingForStepToFinish";
+
+
+ public const string WaitingForWorkerThread = "WaitingForWorkerThread";
+
+
+ public const string StopJobsTitle = "StopJobsTitle";
+
+
+ public const string StopJobDescription = "StopJobDescription";
+
+
+ public const string StoppingJob = "StoppingJob";
+
+
+ public const string StoppedJob = "StoppedJob";
+
+
+ public const string AllDatabases = "AllDatabases";
+
+
+ public const string UnknownSeverity = "UnknownSeverity";
+
+
+ public const string Severity001 = "Severity001";
+
+
+ public const string Severity002 = "Severity002";
+
+
+ public const string Severity003 = "Severity003";
+
+
+ public const string Severity004 = "Severity004";
+
+
+ public const string Severity005 = "Severity005";
+
+
+ public const string Severity006 = "Severity006";
+
+
+ public const string Severity007 = "Severity007";
+
+
+ public const string Severity008 = "Severity008";
+
+
+ public const string Severity009 = "Severity009";
+
+
+ public const string Severity010 = "Severity010";
+
+
+ public const string Severity011 = "Severity011";
+
+
+ public const string Severity012 = "Severity012";
+
+
+ public const string Severity013 = "Severity013";
+
+
+ public const string Severity014 = "Severity014";
+
+
+ public const string Severity015 = "Severity015";
+
+
+ public const string Severity016 = "Severity016";
+
+
+ public const string Severity017 = "Severity017";
+
+
+ public const string Severity018 = "Severity018";
+
+
+ public const string Severity019 = "Severity019";
+
+
+ public const string Severity020 = "Severity020";
+
+
+ public const string Severity021 = "Severity021";
+
+
+ public const string Severity022 = "Severity022";
+
+
+ public const string Severity023 = "Severity023";
+
+
+ public const string Severity024 = "Severity024";
+
+
+ public const string Severity025 = "Severity025";
+
+
+ public const string PagerScheduleMonFri = "PagerScheduleMonFri";
+
+
+ public const string PagerScheduleSatSun = "PagerScheduleSatSun";
+
+
+ public const string PagerScheduleWarning = "PagerScheduleWarning";
+
+
+ public const string General = "General";
+
+
+ public const string Notifications = "Notifications";
+
+
+ public const string History = "History";
+
+
+ public const string Day = "Day";
+
+
+ public const string StartTime = "StartTime";
+
+
+ public const string EndTime = "EndTime";
+
+
+ public const string ColumnIndexIsInvalid = "ColumnIndexIsInvalid";
+
+
+ public const string RowIndexIsInvalid = "RowIndexIsInvalid";
+
+
+ public const string NewOperatorProperties = "NewOperatorProperties";
+
+
+ public const string OperatorProperties = "OperatorProperties";
+
+
+ public const string FailedToCreateInitializeAgentOperatorDialog = "FailedToCreateInitializeAgentOperatorDialog";
+
+
+ public const string JobServerIsNotAvailable = "JobServerIsNotAvailable";
+
+
+ public const string CannotCreateInitializeGeneralPage = "CannotCreateInitializeGeneralPage";
+
+
+ public const string CannotCreateInitializeNotificationsPage = "CannotCreateInitializeNotificationsPage";
+
+
+ public const string CannotCreateInitializeHistoryPage = "CannotCreateInitializeHistoryPage";
+
+
+ public const string CannotResetOperator = "CannotResetOperator";
+
+
+ public const string AlertList = "AlertList";
+
+
+ public const string JobList = "JobList";
+
+
+ public const string Email = "Email";
+
+
+ public const string Pager = "Pager";
+
+
+ public const string AlertName = "AlertName";
+
+
+ public const string JobName = "JobName";
+
+
+ public const string Always = "Always";
+
+
+ public const string Never = "Never";
+
+
+ public const string OnFailure = "OnFailure";
+
+
+ public const string OnSuccess = "OnSuccess";
+
+
+ public const string CannotModifyAlerts = "CannotModifyAlerts";
+
+
+ public const string CannotCreateScriptForModifyAlerts = "CannotCreateScriptForModifyAlerts";
+
+
private Keys()
{ }
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx
index cbae3db6..84a5652b 100755
--- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx
+++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx
@@ -2023,4 +2023,405 @@
Connection not found
+
+ Enable Alerts - {0}
+ .
+ Parameters: 0 - serverName (String)
+
+
+ Enable Alert '{0}'
+ .
+ Parameters: 0 - alertName (String)
+
+
+ Enabling Alert '{0}'
+ .
+ Parameters: 0 - alertName (String)
+
+
+ Enabled Alert '{0}'
+ .
+ Parameters: 0 - alertName (String)
+
+
+ Disable Alerts - {0}
+ .
+ Parameters: 0 - serverName (String)
+
+
+ Disable Alert '{0}'
+ .
+ Parameters: 0 - alertName (String)
+
+
+ Disabling Alert '{0}'
+ .
+ Parameters: 0 - alertName (String)
+
+
+ Disabled Alert '{0}'
+ .
+ Parameters: 0 - alertName (String)
+
+
+ Enable Jobs - {0}
+ .
+ Parameters: 0 - serverName (String)
+
+
+ Enable Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Enabling Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Enabled Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Disable Jobs - {0}
+ .
+ Parameters: 0 - serverName (String)
+
+
+ Disable Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Disabling Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Disabled Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Start Jobs - {0}
+ .
+ Parameters: 0 - serverName (String)
+
+
+ Start Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Job '{0}' has more than one step. Getting step to start
+ .
+ Parameters: 0 - jobName (String)
+
+
+ User canceled select step. Job will not be started
+
+
+
+ Starting Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Start Job '{0}' with step '{1}'
+ .
+ Parameters: 0 - jobName (String), 1 - stepName (String)
+
+
+ Posted remote job execution request
+
+
+
+ Execute job '{0}'
+ .
+ Parameters: 0 - jobName (string)
+
+
+ Execution of job '{0}' failed. See the history log for details.
+ .
+ Parameters: 0 - jobName (string)
+
+
+ Executing
+
+
+
+ Between retries
+
+
+
+ Suspended
+
+
+
+ Performing completion action
+
+
+
+ Waiting for step to finish
+
+
+
+ Waiting for worker thread
+
+
+
+ Stop Jobs - {0}
+ .
+ Parameters: 0 - serverName (String)
+
+
+ Stop Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Stopping Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Stopped Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ <all databases>
+ First item in database name drop down list
+
+
+ Unknown severity: {0}
+ Exception thrown when agent alert has unknown severity level.
+ Parameters: 0 - severity (int)
+
+
+ 001 - Miscellaneous System Information
+
+
+
+ 002 - Reserved
+
+
+
+ 003 - Reserved
+
+
+
+ 004 - Reserved
+
+
+
+ 005 - Reserved
+
+
+
+ 006 - Reserved
+
+
+
+ 007 - Notification: Status Information
+
+
+
+ 008 - Notification: User Intervention Required
+
+
+
+ 009 - User Defined
+
+
+
+ 010 - Information
+
+
+
+ 011 - Specified Database Object Not Found
+
+
+
+ 012 - Unused
+
+
+
+ 013 - User Transaction Syntax Error
+
+
+
+ 014 - Insufficient Permission
+
+
+
+ 015 - Syntax Error in SQL Statements
+
+
+
+ 016 - Miscellaneous User Error
+
+
+
+ 017 - Insufficient Resources
+
+
+
+ 018 - Nonfatal Internal Error
+
+
+
+ 019 - Fatal Error in Resource
+
+
+
+ 020 - Fatal Error in Current Process
+
+
+
+ 021 - Fatal Error in Database Processes
+
+
+
+ 022 - Fatal Error: Table Integrity Suspect
+
+
+
+ 023 - Fatal Error: Database Integrity Suspect
+
+
+
+ 024 - Fatal Error: Hardware Error
+
+
+
+ 025 - Fatal Error
+
+
+
+ Pager schedule end time is earlier than start time on Mon-Fri. Do you want to continue?
+ Message box that displayed if start time is more than end time on Mon-Fri
+
+
+ Pager schedule end time is earlier than start time on Sat-Sun. Do you want to continue?
+ Message box that displayed if start time is more than end time on Sat-Sun
+
+
+ Pager schedule warning
+ Message box caption
+
+
+ General
+ Tree node name
+
+
+ Notifications
+ Tree node name
+
+
+ History
+ Tree node name
+
+
+ Day
+ Pager schedule grid column name
+
+
+ Start Time
+ Pager schedule grid column name
+
+
+ End Time
+ Pager schedule grid column name
+
+
+ Column index is invalid.
+ Exception thrown when column index is invalid
+
+
+ Row index is invalid.
+ Exception thrown when row index is invalid
+
+
+ New Operator
+ Name of the operator dialog in create new operator mode
+
+
+ {0} Properties
+ Name of the operator dialog in modify operator mode.
+ Parameters: 0 - operatorName (string)
+
+
+ Unable to create/initialize Agent Operator dialog.
+ Exception thrown when dialog cannot be created/intialized.
+
+
+ Job server is not available.
+ Exception thrown when job server is not available
+
+
+ Cannot create/initialize General page.
+ Exception thrown when we cannot create/initialize agent operators general page
+
+
+ Cannot create/initialize Notifications page.
+ Exception thrown when we cannot create/initialize agent operators notifications page
+
+
+ Cannot create/initialize History page.
+ Exception thrown when we cannot create/initialize agent operators history page
+
+
+ Cannot reset operator.
+ Exception throw when dialog cannot refresh operator
+
+
+ Alert list:
+ Name of label on notifications page
+
+
+ Job list:
+ Name of label on notifications page
+
+
+ E-mail
+ Name of column on notifications page
+
+
+ Pager
+ Name of column on notifications page
+
+
+ Alert name
+ Name of column on notifications page
+
+
+ Job name
+ Name of column on notifications page
+
+
+ Always
+ Completion Action
+
+
+ Never
+ Completion Action
+
+
+ On failure
+ Completion Action
+
+
+ On success
+ Completion Action
+
+
+ Cannot modify alerts.
+ Exception thrown when we cannot modify alerts
+
+
+ Cannot create script for modify alerts.
+ Exception thrown when we cannot create script that modify alerts
+
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
index 1c14741b..d4a4cd27 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
+++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
@@ -887,3 +887,158 @@ InvalidPathError = Cannot access the specified path on the server: {0}
############################################################################
# Profiler
ProfilerConnectionNotFound = Connection not found
+
+
+
+
+
+
+
+
+
+
+
+
+
+#############################################################################
+# SQL Agent
+EnableAlertsTitle(String serverName) = Enable Alerts - {0}
+EnableAlertDescription(String alertName) = Enable Alert '{0}'
+EnablingAlert(String alertName) = Enabling Alert '{0}'
+EnabledAlert(String alertName) = Enabled Alert '{0}'
+
+DisableAlertsTitle(String serverName) = Disable Alerts - {0}
+DisableAlertDescription(String alertName) = Disable Alert '{0}'
+DisablingAlert(String alertName) = Disabling Alert '{0}'
+DisabledAlert(String alertName) = Disabled Alert '{0}'
+
+EnableJobsTitle(String serverName) = Enable Jobs - {0}
+EnableJobDescription(String jobName) = Enable Job '{0}'
+EnablingJob(String jobName) = Enabling Job '{0}'
+EnabledJob(String jobName) = Enabled Job '{0}'
+
+DisableJobsTitle(String serverName) = Disable Jobs - {0}
+DisableJobDescription(String jobName) = Disable Job '{0}'
+DisablingJob(String jobName) = Disabling Job '{0}'
+DisabledJob(String jobName) = Disabled Job '{0}'
+
+StartJobsTitle(String serverName) = Start Jobs - {0}
+StartJobDescription(String jobName) = Start Job '{0}'
+GettingStartStep(String jobName) = Job '{0}' has more than one step. Getting step to start
+UserCancelledSelectStep = User canceled select step. Job will not be started
+StartingJob(String jobName) = Starting Job '{0}'
+StartJobWithStep(String jobName, String stepName) = Start Job '{0}' with step '{1}'
+RequestPostedToTargetServers = Posted remote job execution request
+
+ExecuteJob(string jobName) = Execute job '{0}'
+JobFailed(string jobName) = Execution of job '{0}' failed. See the history log for details.
+Executing = Executing
+BetweenRetries = Between retries
+Suspended = Suspended
+PerformingCompletionAction = Performing completion action
+WaitingForStepToFinish = Waiting for step to finish
+WaitingForWorkerThread = Waiting for worker thread
+
+StopJobsTitle(String serverName) = Stop Jobs - {0}
+StopJobDescription(String jobName) = Stop Job '{0}'
+StoppingJob(String jobName) = Stopping Job '{0}'
+StoppedJob(String jobName) = Stopped Job '{0}'
+
+; First item in database name drop down list
+AllDatabases =
+; Exception thrown when agent alert has unknown severity level
+UnknownSeverity(int severity) = Unknown severity: {0}
+
+#severity types
+Severity001 = 001 - Miscellaneous System Information
+Severity002 = 002 - Reserved
+Severity003 = 003 - Reserved
+Severity004 = 004 - Reserved
+Severity005 = 005 - Reserved
+Severity006 = 006 - Reserved
+Severity007 = 007 - Notification: Status Information
+Severity008 = 008 - Notification: User Intervention Required
+Severity009 = 009 - User Defined
+Severity010 = 010 - Information
+Severity011 = 011 - Specified Database Object Not Found
+Severity012 = 012 - Unused
+Severity013 = 013 - User Transaction Syntax Error
+Severity014 = 014 - Insufficient Permission
+Severity015 = 015 - Syntax Error in SQL Statements
+Severity016 = 016 - Miscellaneous User Error
+Severity017 = 017 - Insufficient Resources
+Severity018 = 018 - Nonfatal Internal Error
+Severity019 = 019 - Fatal Error in Resource
+Severity020 = 020 - Fatal Error in Current Process
+Severity021 = 021 - Fatal Error in Database Processes
+Severity022 = 022 - Fatal Error: Table Integrity Suspect
+Severity023 = 023 - Fatal Error: Database Integrity Suspect
+Severity024 = 024 - Fatal Error: Hardware Error
+Severity025 = 025 - Fatal Error
+
+; Message box that displayed if start time is more than end time on Mon-Fri
+PagerScheduleMonFri = Pager schedule end time is earlier than start time on Mon-Fri. Do you want to continue?
+; Message box that displayed if start time is more than end time on Sat-Sun
+PagerScheduleSatSun = Pager schedule end time is earlier than start time on Sat-Sun. Do you want to continue?
+; Message box caption
+PagerScheduleWarning = Pager schedule warning
+
+; Tree node name
+General = General
+; Tree node name
+Notifications = Notifications
+; Tree node name
+History = History
+; Pager schedule grid column name
+Day = Day
+; Pager schedule grid column name
+StartTime = Start Time
+; Pager schedule grid column name
+EndTime = End Time
+; Exception thrown when column index is invalid
+ColumnIndexIsInvalid = Column index is invalid.
+; Exception thrown when row index is invalid
+RowIndexIsInvalid = Row index is invalid.
+; Name of the operator dialog in create new operator mode
+NewOperatorProperties = New Operator
+; Name of the operator dialog in modify operator mode
+OperatorProperties(string operatorName) = {0} Properties
+; Exception thrown when dialog cannot be created/intialized.
+FailedToCreateInitializeAgentOperatorDialog = Unable to create/initialize Agent Operator dialog.
+; Exception thrown when job server is not available
+JobServerIsNotAvailable = Job server is not available.
+; Exception thrown when we cannot create/initialize agent operators general page
+CannotCreateInitializeGeneralPage = Cannot create/initialize General page.
+; Exception thrown when we cannot create/initialize agent operators notifications page
+CannotCreateInitializeNotificationsPage = Cannot create/initialize Notifications page.
+; Exception thrown when we cannot create/initialize agent operators history page
+CannotCreateInitializeHistoryPage = Cannot create/initialize History page.
+; Exception throw when dialog cannot refresh operator
+CannotResetOperator = Cannot reset operator.
+
+
+#
+; Name of label on notifications page
+AlertList = Alert list:
+; Name of label on notifications page
+JobList = Job list:
+; Name of column on notifications page
+Email = E-mail
+; Name of column on notifications page
+Pager = Pager
+; Name of column on notifications page
+AlertName = Alert name
+; Name of column on notifications page
+JobName = Job name
+; Completion Action
+Always = Always
+; Completion Action
+Never = Never
+; Completion Action
+OnFailure = On failure
+; Completion Action
+OnSuccess = On success
+; Exception thrown when we cannot modify alerts
+CannotModifyAlerts = Cannot modify alerts.
+; Exception thrown when we cannot create script that modify alerts
+CannotCreateScriptForModifyAlerts = Cannot create script for modify alerts.
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf
index 4070e2df..664ab869 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf
+++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf
@@ -2379,6 +2379,500 @@
.
Parameters: 0 - tableName (string)
+
+ Enable Alerts - {0}
+ Enable Alerts - {0}
+ .
+ Parameters: 0 - serverName (String)
+
+
+ Enable Alert '{0}'
+ Enable Alert '{0}'
+ .
+ Parameters: 0 - alertName (String)
+
+
+ Enabling Alert '{0}'
+ Enabling Alert '{0}'
+ .
+ Parameters: 0 - alertName (String)
+
+
+ Enabled Alert '{0}'
+ Enabled Alert '{0}'
+ .
+ Parameters: 0 - alertName (String)
+
+
+ Disable Alerts - {0}
+ Disable Alerts - {0}
+ .
+ Parameters: 0 - serverName (String)
+
+
+ Disable Alert '{0}'
+ Disable Alert '{0}'
+ .
+ Parameters: 0 - alertName (String)
+
+
+ Disabling Alert '{0}'
+ Disabling Alert '{0}'
+ .
+ Parameters: 0 - alertName (String)
+
+
+ Disabled Alert '{0}'
+ Disabled Alert '{0}'
+ .
+ Parameters: 0 - alertName (String)
+
+
+ Enable Jobs - {0}
+ Enable Jobs - {0}
+ .
+ Parameters: 0 - serverName (String)
+
+
+ Enable Job '{0}'
+ Enable Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Enabling Job '{0}'
+ Enabling Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Enabled Job '{0}'
+ Enabled Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Disable Jobs - {0}
+ Disable Jobs - {0}
+ .
+ Parameters: 0 - serverName (String)
+
+
+ Disable Job '{0}'
+ Disable Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Disabling Job '{0}'
+ Disabling Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Disabled Job '{0}'
+ Disabled Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Start Jobs - {0}
+ Start Jobs - {0}
+ .
+ Parameters: 0 - serverName (String)
+
+
+ Start Job '{0}'
+ Start Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Job '{0}' has more than one step. Getting step to start
+ Job '{0}' has more than one step. Getting step to start
+ .
+ Parameters: 0 - jobName (String)
+
+
+ User canceled select step. Job will not be started
+ User canceled select step. Job will not be started
+
+
+
+ Starting Job '{0}'
+ Starting Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Start Job '{0}' with step '{1}'
+ Start Job '{0}' with step '{1}'
+ .
+ Parameters: 0 - jobName (String), 1 - stepName (String)
+
+
+ Posted remote job execution request
+ Posted remote job execution request
+
+
+
+ Execute job '{0}'
+ Execute job '{0}'
+ .
+ Parameters: 0 - jobName (string)
+
+
+ Execution of job '{0}' failed. See the history log for details.
+ Execution of job '{0}' failed. See the history log for details.
+ .
+ Parameters: 0 - jobName (string)
+
+
+ Executing
+ Executing
+
+
+
+ Between retries
+ Between retries
+
+
+
+ Suspended
+ Suspended
+
+
+
+ Performing completion action
+ Performing completion action
+
+
+
+ Waiting for step to finish
+ Waiting for step to finish
+
+
+
+ Waiting for worker thread
+ Waiting for worker thread
+
+
+
+ Stop Jobs - {0}
+ Stop Jobs - {0}
+ .
+ Parameters: 0 - serverName (String)
+
+
+ Stop Job '{0}'
+ Stop Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Stopping Job '{0}'
+ Stopping Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ Stopped Job '{0}'
+ Stopped Job '{0}'
+ .
+ Parameters: 0 - jobName (String)
+
+
+ <all databases>
+ <all databases>
+ First item in database name drop down list
+
+
+ Unknown severity: {0}
+ Unknown severity: {0}
+ Exception thrown when agent alert has unknown severity level.
+ Parameters: 0 - severity (int)
+
+
+ 001 - Miscellaneous System Information
+ 001 - Miscellaneous System Information
+
+
+
+ 002 - Reserved
+ 002 - Reserved
+
+
+
+ 003 - Reserved
+ 003 - Reserved
+
+
+
+ 004 - Reserved
+ 004 - Reserved
+
+
+
+ 005 - Reserved
+ 005 - Reserved
+
+
+
+ 006 - Reserved
+ 006 - Reserved
+
+
+
+ 007 - Notification: Status Information
+ 007 - Notification: Status Information
+
+
+
+ 008 - Notification: User Intervention Required
+ 008 - Notification: User Intervention Required
+
+
+
+ 009 - User Defined
+ 009 - User Defined
+
+
+
+ 010 - Information
+ 010 - Information
+
+
+
+ 011 - Specified Database Object Not Found
+ 011 - Specified Database Object Not Found
+
+
+
+ 012 - Unused
+ 012 - Unused
+
+
+
+ 013 - User Transaction Syntax Error
+ 013 - User Transaction Syntax Error
+
+
+
+ 014 - Insufficient Permission
+ 014 - Insufficient Permission
+
+
+
+ 015 - Syntax Error in SQL Statements
+ 015 - Syntax Error in SQL Statements
+
+
+
+ 016 - Miscellaneous User Error
+ 016 - Miscellaneous User Error
+
+
+
+ 017 - Insufficient Resources
+ 017 - Insufficient Resources
+
+
+
+ 018 - Nonfatal Internal Error
+ 018 - Nonfatal Internal Error
+
+
+
+ 019 - Fatal Error in Resource
+ 019 - Fatal Error in Resource
+
+
+
+ 020 - Fatal Error in Current Process
+ 020 - Fatal Error in Current Process
+
+
+
+ 021 - Fatal Error in Database Processes
+ 021 - Fatal Error in Database Processes
+
+
+
+ 022 - Fatal Error: Table Integrity Suspect
+ 022 - Fatal Error: Table Integrity Suspect
+
+
+
+ 023 - Fatal Error: Database Integrity Suspect
+ 023 - Fatal Error: Database Integrity Suspect
+
+
+
+ 024 - Fatal Error: Hardware Error
+ 024 - Fatal Error: Hardware Error
+
+
+
+ 025 - Fatal Error
+ 025 - Fatal Error
+
+
+
+
+
+
+ General
+ General
+ Tree node name
+
+
+ Notifications
+ Notifications
+ Tree node name
+
+
+ History
+ History
+ Tree node name
+
+
+ Day
+ Day
+ Pager schedule grid column name
+
+
+ Start Time
+ Start Time
+ Pager schedule grid column name
+
+
+ End Time
+ End Time
+ Pager schedule grid column name
+
+
+ Column index is invalid.
+ Column index is invalid.
+ Exception thrown when column index is invalid
+
+
+ Row index is invalid.
+ Row index is invalid.
+ Exception thrown when row index is invalid
+
+
+ New Operator
+ New Operator
+ Name of the operator dialog in create new operator mode
+
+
+ {0} Properties
+ {0} Properties
+ Name of the operator dialog in modify operator mode.
+ Parameters: 0 - operatorName (string)
+
+
+ Unable to create/initialize Agent Operator dialog.
+ Unable to create/initialize Agent Operator dialog.
+ Exception thrown when dialog cannot be created/intialized.
+
+
+ Job server is not available.
+ Job server is not available.
+ Exception thrown when job server is not available
+
+
+ Cannot create/initialize General page.
+ Cannot create/initialize General page.
+ Exception thrown when we cannot create/initialize agent operators general page
+
+
+ Cannot create/initialize Notifications page.
+ Cannot create/initialize Notifications page.
+ Exception thrown when we cannot create/initialize agent operators notifications page
+
+
+ Cannot create/initialize History page.
+ Cannot create/initialize History page.
+ Exception thrown when we cannot create/initialize agent operators history page
+
+
+ Cannot reset operator.
+ Cannot reset operator.
+ Exception throw when dialog cannot refresh operator
+
+
+ Alert list:
+ Alert list:
+ Name of label on notifications page
+
+
+ Job list:
+ Job list:
+ Name of label on notifications page
+
+
+ E-mail
+ E-mail
+ Name of column on notifications page
+
+
+
+ Alert name
+ Alert name
+ Name of column on notifications page
+
+
+ Job name
+ Job name
+ Name of column on notifications page
+
+
+ Always
+ Always
+ Completion Action
+
+
+ Never
+ Never
+ Completion Action
+
+
+ On failure
+ On failure
+ Completion Action
+
+
+ On success
+ On success
+ Completion Action
+
+
+ Cannot modify alerts.
+ Cannot modify alerts.
+ Exception thrown when we cannot modify alerts
+
+
+ Cannot create script for modify alerts.
+ Cannot create script for modify alerts.
+ Exception thrown when we cannot create script that modify alerts
+