SQL Agent configuration for Operators, Alerts and Proxies (WIP) (#621)

* Initial non-refactored SQL Agent alert classes (WIP)

* Move agent classes into subdirectories

* Refactor the agent config code a bit more

* Add more implementation for handlers

* Add more code to the create alert handler

* Clean up agent alert class

* Clean up alert methods a bit

* Initial Operator contracts

* Additonal SQL Agent config changes

* More Proxy config cleanup

* Cleanup AgentProxy class

* Additional cleanups

* Run SRGen

* Add security service to create credential objects
This commit is contained in:
Karl Burtram
2018-05-30 08:58:02 -07:00
committed by GitHub
parent f5efe18e1b
commit 4f214f43e9
49 changed files with 8152 additions and 257 deletions

View File

@@ -190,12 +190,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
}
/// <summary>
/// Create database task helper
/// Create a data container object
/// </summary>
/// <param name="connInfo">connection info</param>
/// <param name="databaseExists">flag indicating whether to create taskhelper for existing database or not</param>
/// <returns></returns>
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;
}
/// <summary>
/// Create database task helper
/// </summary>
/// <param name="connInfo">connection info</param>
/// <param name="databaseExists">flag indicating whether to create taskhelper for existing database or not</param>
/// <returns></returns>
internal static DatabaseTaskHelper CreateDatabaseTaskHelper(ConnectionInfo connInfo, bool databaseExists = false)
{
var dataContainer = CreateDataContainer(connInfo, databaseExists);
var taskHelper = new DatabaseTaskHelper(dataContainer);
return taskHelper;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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
{
/// <summary>
/// Main class for Profiler Service functionality
/// Main class for Agent Service functionality
/// </summary>
public sealed class AgentService
public class AgentService
{
internal enum AgentConfigAction
{
Create,
Update,
Drop
}
private Dictionary<Guid, JobProperties> jobs = null;
private ConnectionService connectionService = null;
private static readonly Lazy<AgentService> instance = new Lazy<AgentService>(() => new AgentService());
@@ -64,7 +72,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
}
}
/// <summary>
/// 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"
/// <summary>
/// Handle request to get Agent job activities
/// </summary>
internal async Task HandleAgentJobsRequest(AgentJobsParams parameters, RequestContext<AgentJobsResult> 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<AgentJobInfo>();
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<AgentJobInfo>();
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);
}
});
}
/// <summary>
@@ -132,44 +162,47 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
/// </summary>
internal async Task HandleJobHistoryRequest(AgentJobHistoryParams parameters, RequestContext<AgentJobHistoryResult> requestContext)
{
try
await Task.Run(async () =>
{
var result = new AgentJobHistoryResult();
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(
parameters.OwnerUri,
out connInfo);
if (connInfo != null)
try
{
Tuple<SqlConnectionInfo, DataTable, ServerConnection> tuple = CreateSqlConnection(connInfo, parameters.JobId);
SqlConnectionInfo sqlConnInfo = tuple.Item1;
DataTable dt = tuple.Item2;
ServerConnection connection = tuple.Item3;
int count = dt.Rows.Count;
List<AgentJobHistoryInfo> jobHistories = new List<AgentJobHistoryInfo>();
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<SqlConnectionInfo, DataTable, ServerConnection> tuple = CreateSqlConnection(connInfo, parameters.JobId);
SqlConnectionInfo sqlConnInfo = tuple.Item1;
DataTable dt = tuple.Item2;
ServerConnection connection = tuple.Item3;
int count = dt.Rows.Count;
List<AgentJobHistoryInfo> jobHistories = new List<AgentJobHistoryInfo>();
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);
}
});
}
/// <summary>
@@ -177,49 +210,52 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
/// </summary>
internal async Task HandleJobActionRequest(AgentJobActionParams parameters, RequestContext<AgentJobActionResult> 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<SqlConnectionInfo, DataTable, ServerConnection> CreateSqlConnection(ConnectionInfo connInfo, String jobId)
@@ -234,5 +270,268 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
return new Tuple<SqlConnectionInfo, DataTable, ServerConnection>(sqlConnInfo, dt, serverConnection);
}
#endregion // "Jobs Handlers"
#region "Alert Handlers"
/// <summary>
/// Handle request to get the alerts list
/// </summary>
internal async Task HandleAgentAlertsRequest(AgentAlertsParams parameters, RequestContext<AgentAlertsResult> requestContext)
{
await Task.Run(async () =>
{
var result = new AgentAlertsResult();
result.Alerts = new List<AgentAlertInfo>().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);
}
/// <summary>
/// Handle request to create an alert
/// </summary>
internal async Task HandleCreateAgentAlertRequest(CreateAgentAlertParams parameters, RequestContext<CreateAgentAlertResult> 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);
});
}
/// <summary>
/// Handle request to update an alert
/// </summary>
internal async Task HandleUpdateAgentAlertRequest(UpdateAgentAlertParams parameters, RequestContext<UpdateAgentAlertResult> 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();
}
}
}
/// <summary>
/// Handle request to delete an alert
/// </summary>
internal async Task HandleDeleteAgentAlertRequest(DeleteAgentAlertParams parameters, RequestContext<DeleteAgentAlertResult> 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<AgentOperatorsResult> requestContext)
{
await requestContext.SendResult(null);
}
internal async Task HandleCreateAgentOperatorRequest(CreateAgentOperatorParams parameters, RequestContext<CreateAgentOperatorResult> 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<UpdateAgentOperatorResult> requestContext)
{
await requestContext.SendResult(null);
}
internal async Task HandleDeleteAgentOperatorRequest(DeleteAgentOperatorParams parameters, RequestContext<DeleteAgentOperatorResult> requestContext)
{
await requestContext.SendResult(null);
}
#endregion // "Operator Handlers"
#region "Proxy Handlers"
internal async Task HandleAgentProxiesRequest(AgentProxiesParams parameters, RequestContext<AgentProxiesResult> requestContext)
{
await requestContext.SendResult(null);
}
internal async Task HandleCreateAgentProxyRequest(CreateAgentProxyParams parameters, RequestContext<CreateAgentProxyResult> 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<UpdateAgentProxyResult> 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<DeleteAgentProxyResult> requestContext)
{
bool succeeded = await ConfigureAgentProxy(
parameters.OwnerUri,
parameters.Proxy.AccountName,
parameters.Proxy,
AgentConfigAction.Drop);
await requestContext.SendResult(new DeleteAgentProxyResult()
{
Succeeded = succeeded
});
}
internal async Task<bool> ConfigureAgentProxy(
string ownerUri,
string accountName,
AgentProxyInfo proxy,
AgentConfigAction configAction)
{
return await Task<bool>.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"
}
}

View File

@@ -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
/// <summary>
/// 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.
/// </summary>
internal abstract class AgentAction
{
#region private members
/// <summary>
/// Required designer variable.
/// </summary>
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
/// <summary>
/// Clean up any resources being used.
/// </summary>
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
/// <summary>
/// Generate the actions the dialog will perform. Derived classes should add
/// IAction based actions to the actions collection.
/// </summary>
protected abstract void GenerateActions();
#endregion
}
#endregion
#region Enable Alerts
/// <summary>
/// Enables one or more alerts.
/// </summary>
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));
}
}
}
/// <summary>
/// Performs the actual enabling
/// </summary>
internal class EnableAlertAction : IProgressItem
{
private Alert alert;
public EnableAlertAction(Alert alert)
{
if (alert == null)
{
throw new ArgumentNullException("alert");
}
this.alert = alert;
}
/// <summary>
/// Generate a user friendly description of this task.Used in the description
/// of the progress dialog.
/// </summary>
/// <returns>Description of the aler</returns>
public override string ToString()
{
if (this.alert == null)
{
return base.ToString();
}
else
{
return "AgentActionSR.EnableAlertDescription(this.alert.Name)";
}
}
/// <summary>
/// Enable the alert
/// </summary>
/// <param name="actions">Actions collection</param>
/// <param name="index">this actions index into the actions collection</param>
/// <returns></returns>
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
/// <summary>
/// Disable one or more alerts
/// </summary>
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));
}
}
}
/// <summary>
/// Actually disable the alert
/// </summary>
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)";
}
}
/// <summary>
/// Disable the alert
/// </summary>
/// <param name="actions">Actions collection</param>
/// <param name="index">this actions index into the actions collection</param>
/// <returns></returns>
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;
}
/// <summary>
/// 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
/// </summary>
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;
}
/// <summary>
/// Generate user friendly description of the action. This is displayed in the
/// progress dialog.
/// </summary>
/// <returns></returns>
public override string ToString()
{
if (this.job == null)
{
return base.ToString();
}
else
{
return "AgentActionSR.EnableJobDescription(this.job.Name)";
}
}
/// <summary>
/// Enable the Job
/// </summary>
/// <param name="actions">Actions collection</param>
/// <param name="index">this actions index into the actions collection</param>
/// <returns></returns>
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
/// <summary>
/// Disable a job
/// </summary>
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)";
}
}
/// <summary>
/// Disable the Job
/// </summary>
/// <param name="actions">Actions collection</param>
/// <param name="index">this actions index into the actions collection</param>
/// <returns></returns>
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
/// <summary>
/// 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.
/// </summary>
internal class StartAgentJobs : JobAction
{
public StartAgentJobs(XmlDocument document, IServiceProvider source)
: base(document, source)
{
this.actions.CloseOnUserCancel = true;
this.actions.QuitOnError = true;
}
/// <summary>
/// The method is generates list of actions and it is gets called from the OnLaod of base Form method
/// </summary>
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();
}
}
}
/// <summary>
/// Returns list of steps of the given job
/// </summary>
/// <param name="job"></param>
/// <returns>returns list of steps</returns>
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);
}
/// <summary>
/// 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.
/// </summary>
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
}
/// <summary>
/// Prevent default constructor
/// </summary>
private WaitForJobToFinishAction()
{
}
/// <summary>
/// generates a friendly description of this step. Used by the progress dialog
/// </summary>
/// <returns></returns>
public override string ToString()
{
if (this.job == null)
{
return base.ToString();
}
else
{
return "AgentActionSR.ExecuteJob(job.Name)";
}
}
/// <summary>
/// This method triggers abort event for the action thread
/// </summary>
public void Abort()
{
this.abortEvent.Set();
}
/// <summary>
/// Perform the action for this class
/// </summary>
/// <param name="actions">Actions collection</param>
/// <param name="index">array index of this particular action</param>
/// <returns></returns>
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;
}
}
/// <summary>
/// starts a job
/// </summary>
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;
}
/// <summary>
/// generates a friendly description of this step. Used by the progress dialog
/// </summary>
/// <returns></returns>
public override string ToString()
{
if (this.job == null)
{
return base.ToString();
}
else
{
return "AgentActionSR.StartJobDescription(this.job.Name)";
}
}
/// <summary>
/// Start the Job
/// </summary>
/// <param name="actions">Actions collection</param>
/// <param name="index">this actions index into the actions collection</param>
/// <returns></returns>
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
/// <summary>
/// stop a job
/// </summary>
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));
}
}
}
/// <summary>
/// class that actually stops a running job
/// </summary>
internal class StopJobAction : IProgressItem
{
private Job job;
public StopJobAction(Job job)
{
if (job == null)
{
throw new ArgumentNullException("job");
}
this.job = job;
}
/// <summary>
/// Generate a user friendly description of this task. Used in the description
/// of the progress dialog.
/// </summary>
/// <returns>Description of the action</returns>
public override string ToString()
{
if (this.job == null)
{
return base.ToString();
}
else
{
return "AgentActionSR.StopJobDescription(this.job.Name)";
}
}
/// <summary>
/// Stop the Job
/// </summary>
/// <param name="actions">Actions collection</param>
/// <param name="index">this actions index into the actions collection</param>
/// <returns></returns>
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
}

View File

@@ -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
{
/// <summary>
/// Execution mode enumeration Success if execution succeeded of Failure otherwise for now.
/// This enumeration might be refined more as there are needs for it
/// </summary>
public enum ExecutionMode
{
/// <summary>
/// indicates that the operation failed
/// </summary>
Failure = 0,
/// <summary>
/// indicates that the operation succeded
/// </summary>
Success,
/// <summary>
/// indicates that the operation was canceled
/// </summary>
Cancel
};
}

View File

@@ -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;
}
}
}
/// <summary>
/// IExecutionAwareSqlControlCollection allows control's container to do pre and post
/// processing of the execution commands
/// </summary>
public interface IExecutionAwareSqlControlCollection : ISqlControlCollection
{
/// <summary>
/// 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
/// </summary>
/// <param name="executionInfo">information about execution action</param>
/// <param name="executionResult">result of the execution</param>
/// <returns>
/// 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]
/// </returns>
bool PreProcessExecution(PreProcessExecutionInfo executionInfo, out ExecutionMode executionResult);
/// <summary>
/// called when the host received Cancel request. NOTE: this method can return while
/// operation is still being canceled
/// </summary>
/// <returns>
/// 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
/// </returns>
bool Cancel();
/// <summary>
/// called after dialog's host executes actions on all panels in the dialog one by one
/// NOTE: it might be called from worker thread
/// </summary>
/// <param name="executionMode">result of the execution</param>
/// <param name="runType">type of execution</param>
void PostProcessExecution(RunType runType, ExecutionMode executionMode);
/// <summary>
/// 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
/// </summary>
/// <returns>
/// true if regular execution should take place, false if everything
/// has been done by this function
/// </returns>
bool PreProcessReset();
/// <summary>
/// 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
/// </summary>
void PostProcessReset();
}
}

View File

@@ -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
/// <summary>
/// Interface that supports the delegation of individual actions in the progress dialog
/// to individual classes.
/// </summary>
public interface IProgressItem
{
/// <summary>
/// Perform the action for this class
/// </summary>
/// <param name="actions">Actions collection</param>
/// <param name="index">array index of this particular action</param>
/// <returns></returns>
ProgressStatus DoAction(ProgressItemCollection actions, int index);
}
#endregion
}

View File

@@ -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
{
/// <summary>
/// defines notion of sitable object
/// </summary>
public interface IObjectWithSite
{
void SetSite(System.IServiceProvider sp);
}
/// <summary>
/// ISqlControlCollection allows access to a collection of dialog views
/// </summary>
public interface ISqlControlCollection : IObjectWithSite
{
}
}

View File

@@ -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
{
/// <summary>
/// base class that can be used to derived from for the main classes [containers] of the dialogs
/// </summary>
public class ManagementActionBase : IDisposable
{
#region Members
/// <summary>
/// selected node as specified to SelectNode method
/// </summary>
//private TreeNode selectedNode;
/// <summary>
/// service provider of our host. We should direct all host-specific requests to the services
/// implemented by this provider
/// </summary>
private IServiceProvider serviceProvider;
/// <summary>
/// data container with initialization-related information
/// </summary>
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
/// <summary>
/// Constructor
/// </summary>
public ManagementActionBase()
{
}
#endregion
#region IDisposable implementation
void IDisposable.Dispose()
{
//BUGBUG - do we need finalizer
Dispose(true);//call protected virtual method
}
/// <summary>
/// do the deterministic cleanup
/// </summary>
/// <param name="disposing"></param>
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
/// <summary>
/// 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
/// </summary>
/// <param name="executionInfo">information about execution action</param>
/// <param name="executionResult">result of the execution</param>
/// <returns>
/// 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]
/// </returns>
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
/// <summary>
/// whether we own our DataContainer or not. Depending on this value it will or won't be
/// disposed in our Dispose method
/// </summary>
protected virtual bool OwnDataContainer
{
get
{
//by default we own it
return true;
}
}
/// <summary>
/// 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
/// </summary>
/// <param name="runType"></param>
/// <param name="executionResult"></param>
/// <returns>
/// true if regular execution should take place, false if everything,
/// has been done by this function
/// </returns>
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;
}
/// <summary>
/// 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
/// </summary>
/// <returns>
/// true if regular execution should take place, false if everything
/// has been done by this function
/// </returns>
/// <returns></returns>
protected virtual bool DoPreProcessReset()
{
if ((this.dataContainer != null) && this.dataContainer.IsNewObject)
{
this.dataContainer.Reset();
}
return true;
}
/// <summary>
/// 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
/// </summary>
protected virtual void DoPostProcessReset()
{
//nothing in the base class
}
/// <summary>
/// Called to intercept scripting operation
/// </summary>
/// <param name="executionInfo"></param>
/// <param name="executionResult"></param>
/// <returns>
/// true if regular execution should take place, false the script
/// has been created by this function
/// </returns>
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;
}
/// <summary>
/// CDataContainer accessor
/// </summary>
protected CDataContainer DataContainer
{
get
{
return this.dataContainer;
}
set
{
this.dataContainer = value;
this.ownDataContainer = OwnDataContainer; //cache the value
}
}
// /// <summary>
// /// 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
// /// </summary>
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;
}
}
/// <summary>
/// checks whether given run time represents one of scripting options
/// </summary>
/// <param name="runType"></param>
/// <returns></returns>
protected static bool IsScripting(RunType runType)
{
return(runType != RunType.RunNow && runType != RunType.RunNowAndExit);
}
/// <summary>
/// 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
/// </summary>
/// <param name="runType"></param>
/// <returns>
/// result of the execution. It will let exception fly out if it was raised during execution
/// </returns>
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;
}
/// <summary>
/// determines whether we need to substitute SMO/AMO server objects with the
/// temporary ones while doing scripting
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// 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
/// </summary>
/// <returns></returns>
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();
}
/// <summary>
/// called when we need to script a Sql server dlg.
/// </summary>
/// <param name="executionInfo"></param>
/// <param name="executionResult"></param>
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;
// }
// /// <summary>
// /// 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
// /// </summary>
// /// <param name="?"></param>
// /// <param name="?"></param>
// /// <param name="?"></param>
// 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;
// }
// }
// /// <summary>
// /// returns combination of the given 2 arrays
// /// </summary>
// /// <param name="array1"></param>
// /// <param name="array2"></param>
// /// <returns></returns>
// 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;
// }
// }
}
}

View File

@@ -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
{
/// <summary>
/// Allows for the mapping of objects that implement IProgressItem to individual items in the
/// progress dialog.
/// </summary>
public class ProgressItemCollection : ICollection
{
#region internal helper classes
/// <summary>
/// Allows us to map an action to its index in the progress dialog.
/// </summary>
public class ActionIndexMap
{
/// <summary>
/// action
/// </summary>
public IProgressItem Action;
/// <summary>
/// index
/// </summary>
public int Index;
public ActionIndexMap(IProgressItem action)
{
this.Action = action;
// index isn't known yet
this.Index = -1;
}
}
#endregion
#region private data members
/// <summary>
/// list of actions we will perform.
/// </summary>
private ArrayList actions = new ArrayList();
#endregion
#region construction
public ProgressItemCollection()
{
}
#endregion
#region properties
private bool closeOnUserCancel = false;
/// <summary>
/// Indicates whether to close the dialog immediately if the user cancels an operation
/// </summary>
public bool CloseOnUserCancel
{
get
{
return closeOnUserCancel;
}
set
{
closeOnUserCancel = value;
}
}
private bool automaticClose = false;
/// <summary>
/// Indicates whether to automatically close the dialog when all actions are complete
/// successfully.
/// </summary>
public bool CloseOnSuccessfulCompletion
{
get
{
return automaticClose;
}
set
{
automaticClose = value;
}
}
private bool quitOnError = false;
/// <summary>
/// Indicates whether the operation should be terminated if any individual step fails.
/// </summary>
public bool QuitOnError
{
get
{
return this.quitOnError;
}
set
{
this.quitOnError = value;
}
}
private OperationStatus operationStatus = OperationStatus.Invalid;
/// <summary>
/// Indicates the status of the operation.
/// </summary>
public OperationStatus OperationStatus
{
get
{
return this.operationStatus;
}
}
/// <summary>
/// Progress object this action collection will work with
/// </summary>
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
/// <summary>
/// Generate a string representaion of this object. It will convert all of it's IProgressItem members
/// to strings in a new line.
/// </summary>
/// <returns>string description of the actions this object contains</returns>
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
/// <summary>
/// Gets the number of actions in this collection
/// </summary>
public int Count
{
get
{
return this.actions.Count;
}
}
/// <summary>
/// not supported
/// </summary>
public bool IsSynchronized
{
get
{
throw new NotSupportedException();
}
}
/// <summary>
/// not supported
/// </summary>
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
/// <summary>
/// Add an action to the collection
/// </summary>
/// <param name="action">action to be added</param>
public void AddAction(IProgressItem action)
{
ActionIndexMap map = new ActionIndexMap(action);
this.actions.Add(map);
}
#endregion
#region internal implementation
/// <summary>
/// delegate called when the progress dialog wants us to perform work on a new thread.
/// </summary>
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)
{
}
/// <summary>
/// Adds the actions to an IProgress interface.
/// </summary>
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
}
}

View File

@@ -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
{
/// <summary>
/// Enumeration for status of individual actions
/// </summary>
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
}
/// <summary>
/// Enumeration for status of the overall operation
/// </summary>
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
}
/// <summary>
/// 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.
/// </summary>
public interface IProgress
{
//-----------
// Properties
//-----------
/// <summary>
/// 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.
/// </summary>
bool AllowAbort
{
get;
set;
}
/// <summary>
/// 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.
/// </summary>
string AbortPrompt
{
get;
set;
}
/// <summary>
/// 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.
/// </summary>
ThreadStart WorkerThreadStart
{
get;
set;
}
/// <summary>
/// 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.
/// </summary>
bool IsAborted
{
get;
}
/// <summary>
/// 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.
/// </summary>
bool ActionUpdateEnabled
{
get;
set;
}
//--------
// Methods
//--------
/// <summary>
/// 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.
/// </summary>
/// <param name="description">Description of the action</param>
/// <returns>The index of the newly added action.</returns>
int AddAction(string description);
/// <summary>
/// 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.
/// </summary>
/// <param name="description">Description of the action</param>
void AddActionDynamic(string description);
/// <summary>
/// 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.
/// </summary>
/// <param name="actionIndex">Index of the action</param>
/// <param name="description">New description of the action</param>
void UpdateActionDescription(int actionIndex, string description);
/// <summary>
/// Update the status of an action
///
/// NOTE: This method is thread safe and asynchronous. It may be
/// called from the worker thread.
/// </summary>
/// <param name="actionIndex">Index of the action</param>
/// <param name="status">New status of the action</param>
void UpdateActionStatus(int actionIndex, ProgressStatus status);
/// <summary>
/// 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.
/// </summary>
/// <param name="actionIndex">Index of the action</param>
/// <param name="percentComplete">Percentage of the action that is complete (0-100)</param>
void UpdateActionProgress(int actionIndex, int percentComplete);
/// <summary>
/// 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.
/// </summary>
/// <param name="actionIndex">Index of the action</param>
/// <param name="description">Description of progress</param>
void UpdateActionProgress(int actionIndex, string description);
/// <summary>
/// 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.
/// </summary>
/// <param name="actionIndex">Index of the action</param>
/// <param name="e">Exception to be added</param>
void AddActionException(int actionIndex, Exception e);
/// <summary>
/// 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.
/// </summary>
/// <param name="actionIndex">Index of the action</param>
/// <param name="infoString">Information string to be added</param>
void AddActionInfoString(int actionIndex, string infoString);
/// <summary>
/// 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.
/// </summary>
/// <param name="result">Result of the operation</param>
void WorkerThreadExiting(OperationStatus result);
}
/// <summary>
/// Enumeration for status of the progress report control w.r.t the operation
/// </summary>
[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
}
/// <summary>
/// Delegate used with ProgressCtrlStatusChanged event.
/// </summary>
public delegate void ProgressCtrlStatusChangedEventHandler(object source, ProgressCtrlStatusChangedEventArgs e);
/// <summary>
/// EventArgs class for use with ProgressCtrlStatusChanged event
/// </summary>
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
}
}

View File

@@ -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
{
/// <summary>
/// what type of actions does the worker know to execute
/// </summary>
public enum RunType
{
RunNow = 0,
RunNowAndExit,
ScriptToFile,
ScriptToWindow,
ScriptToClipboard,
ScriptToJob
}
}

View File

@@ -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
{
/// <summary>
/// Custom attribute that can be applied on particular DB commander to
/// indicate whether the base class should switch SMO servers before
/// execution or not.
/// </summary>
[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;
}
}
}
}

View File

@@ -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
}
/// <summary>
/// a class for storing various properties of agent alerts
/// </summary>
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; }
}
}

View File

@@ -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
{
/// <summary>
/// SQL Agent Job activity parameters
/// </summary>
public class AgentAlertsParams : GeneralRequestDetails
{
public string OwnerUri { get; set; }
}
/// <summary>
/// SQL Agent Job activity result
/// </summary>
public class AgentAlertsResult
{
public bool Succeeded { get; set; }
public string ErrorMessage { get; set; }
public AgentAlertInfo[] Alerts { get; set; }
}
/// <summary>
/// SQL Agent Alerts request type
/// </summary>
public class AgentAlertsRequest
{
/// <summary>
/// Request definition
/// </summary>
public static readonly
RequestType<AgentAlertsParams, AgentAlertsResult> Type =
RequestType<AgentAlertsParams, AgentAlertsResult>.Create("agent/alerts");
}
/// <summary>
/// SQL Agent create Alert params
/// </summary>
public class CreateAgentAlertParams : GeneralRequestDetails
{
public string OwnerUri { get; set; }
public AgentAlertInfo Alert { get; set; }
}
/// <summary>
/// SQL Agent create Alert result
/// </summary>
public class CreateAgentAlertResult
{
public bool Succeeded { get; set; }
public string ErrorMessage { get; set; }
}
/// <summary>
/// SQL Agent create Alert request type
/// </summary>
public class CreateAgentAlertRequest
{
/// <summary>
/// Request definition
/// </summary>
public static readonly
RequestType<CreateAgentAlertParams, CreateAgentAlertResult> Type =
RequestType<CreateAgentAlertParams, CreateAgentAlertResult>.Create("agent/createalert");
}
/// <summary>
/// SQL Agent delete Alert params
/// </summary>
public class DeleteAgentAlertParams : GeneralRequestDetails
{
public string OwnerUri { get; set; }
public AgentAlertInfo Alert { get; set; }
}
/// <summary>
/// SQL Agent delete Alert result
/// </summary>
public class DeleteAgentAlertResult
{
public bool Succeeded { get; set; }
public string ErrorMessage { get; set; }
}
/// <summary>
/// SQL Agent delete Alert request type
/// </summary>
public class DeleteAgentAlertRequest
{
/// <summary>
/// Request definition
/// </summary>
public static readonly
RequestType<DeleteAgentAlertParams, DeleteAgentAlertResult> Type =
RequestType<DeleteAgentAlertParams, DeleteAgentAlertResult>.Create("agent/deletealert");
}
/// <summary>
/// SQL Agent update Alert params
/// </summary>
public class UpdateAgentAlertParams : GeneralRequestDetails
{
public string OwnerUri { get; set; }
public AgentAlertInfo Alert { get; set; }
}
/// <summary>
/// SQL Agent update Alert result
/// </summary>
public class UpdateAgentAlertResult
{
public bool Succeeded { get; set; }
public string ErrorMessage { get; set; }
}
/// <summary>
/// SQL Agent update Alert request type
/// </summary>
public class UpdateAgentAlertRequest
{
/// <summary>
/// Request definition
/// </summary>
public static readonly
RequestType<UpdateAgentAlertParams, UpdateAgentAlertResult> Type =
RequestType<UpdateAgentAlertParams, UpdateAgentAlertResult>.Create("agent/updatealert");
}
}

View File

@@ -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
}
/// <summary>
/// a class for storing various properties of agent operators
/// </summary>
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; }
}
}

View File

@@ -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
{
/// <summary>
/// SQL Agent Operators request parameters
/// </summary>
public class AgentOperatorsParams : GeneralRequestDetails
{
public string OwnerUri { get; set; }
}
/// <summary>
/// SQL Agent Operators request result
/// </summary>
public class AgentOperatorsResult
{
public bool Succeeded { get; set; }
public string ErrorMessage { get; set; }
public AgentOperatorInfo[] Operators { get; set; }
}
/// <summary>
/// SQL Agent Operators request type
/// </summary>
public class AgentOperatorsRequest
{
/// <summary>
/// Request definition
/// </summary>
public static readonly
RequestType<AgentOperatorsParams, AgentOperatorsResult> Type =
RequestType<AgentOperatorsParams, AgentOperatorsResult>.Create("agent/operators");
}
/// <summary>
/// SQL Agent create Operator params
/// </summary>
public class CreateAgentOperatorParams : GeneralRequestDetails
{
public string OwnerUri { get; set; }
public AgentOperatorInfo Operator { get; set; }
}
/// <summary>
/// SQL Agent create Operator result
/// </summary>
public class CreateAgentOperatorResult
{
public bool Succeeded { get; set; }
public string ErrorMessage { get; set; }
}
/// <summary>
/// SQL Agent create Operator request type
/// </summary>
public class CreateAgentOperatorRequest
{
/// <summary>
/// Request definition
/// </summary>
public static readonly
RequestType<CreateAgentOperatorParams, CreateAgentOperatorResult> Type =
RequestType<CreateAgentOperatorParams, CreateAgentOperatorResult>.Create("agent/createoperator");
}
/// <summary>
/// SQL Agent delete Operator params
/// </summary>
public class DeleteAgentOperatorParams : GeneralRequestDetails
{
public string OwnerUri { get; set; }
public AgentOperatorInfo Operator { get; set; }
}
/// <summary>
/// SQL Agent delete Operator result
/// </summary>
public class DeleteAgentOperatorResult
{
public bool Succeeded { get; set; }
public string ErrorMessage { get; set; }
}
/// <summary>
/// SQL Agent delete Operator request type
/// </summary>
public class DeleteAgentOperatorRequest
{
/// <summary>
/// Request definition
/// </summary>
public static readonly
RequestType<DeleteAgentOperatorParams, DeleteAgentOperatorResult> Type =
RequestType<DeleteAgentOperatorParams, DeleteAgentOperatorResult>.Create("agent/deleteoperator");
}
/// <summary>
/// SQL Agent update Operator params
/// </summary>
public class UpdateAgentOperatorParams : GeneralRequestDetails
{
public string OwnerUri { get; set; }
public AgentOperatorInfo Operator { get; set; }
}
/// <summary>
/// SQL Agent update Operator result
/// </summary>
public class UpdateAgentOperatorResult
{
public bool Succeeded { get; set; }
public string ErrorMessage { get; set; }
}
/// <summary>
/// SQL Agent update Operator request type
/// </summary>
public class UpdateAgentOperatorRequest
{
/// <summary>
/// Request definition
/// </summary>
public static readonly
RequestType<UpdateAgentOperatorParams, UpdateAgentOperatorResult> Type =
RequestType<UpdateAgentOperatorParams, UpdateAgentOperatorResult>.Create("agent/updateoperator");
}
}

View File

@@ -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
{
/// <summary>
/// a class for storing various properties of agent proxy accounts
/// </summary>
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; }
}
}

View File

@@ -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
{
/// <summary>
/// SQL Agent proxy accounts parameters
/// </summary>
public class AgentProxiesParams : GeneralRequestDetails
{
public string OwnerUri { get; set; }
}
/// <summary>
/// SQL Agent proxy accounts result
/// </summary>
public class AgentProxiesResult
{
public bool Succeeded { get; set; }
public string ErrorMessage { get; set; }
public AgentProxyInfo[] Proxies { get; set; }
}
/// <summary>
/// SQL Agent Proxy Accounts request type
/// </summary>
public class AgentProxiesRequest
{
/// <summary>
/// Request definition
/// </summary>
public static readonly
RequestType<AgentProxiesParams, AgentProxiesResult> Type =
RequestType<AgentProxiesParams, AgentProxiesResult>.Create("agent/proxies");
}
/// <summary>
/// SQL Agent create Proxy Account params
/// </summary>
public class CreateAgentProxyParams : GeneralRequestDetails
{
public string OwnerUri { get; set; }
public AgentProxyInfo Proxy { get; set; }
}
/// <summary>
/// SQL Agent create Proxy result
/// </summary>
public class CreateAgentProxyResult
{
public bool Succeeded { get; set; }
public string ErrorMessage { get; set; }
}
/// <summary>
/// SQL Agent create Proxy request type
/// </summary>
public class CreateAgentProxyRequest
{
/// <summary>
/// Request definition
/// </summary>
public static readonly
RequestType<CreateAgentProxyParams, CreateAgentProxyResult> Type =
RequestType<CreateAgentProxyParams, CreateAgentProxyResult>.Create("agent/createproxy");
}
/// <summary>
/// SQL Agent delete Proxy params
/// </summary>
public class DeleteAgentProxyParams : GeneralRequestDetails
{
public string OwnerUri { get; set; }
public AgentProxyInfo Proxy { get; set; }
}
/// <summary>
/// SQL Agent delete Proxy result
/// </summary>
public class DeleteAgentProxyResult
{
public bool Succeeded { get; set; }
public string ErrorMessage { get; set; }
}
/// <summary>
/// SQL Agent delete Proxy request type
/// </summary>
public class DeleteAgentProxyRequest
{
/// <summary>
/// Request definition
/// </summary>
public static readonly
RequestType<DeleteAgentProxyParams, DeleteAgentProxyResult> Type =
RequestType<DeleteAgentProxyParams, DeleteAgentProxyResult>.Create("agent/deleteproxy");
}
/// <summary>
/// SQL Agent update Proxy params
/// </summary>
public class UpdateAgentProxyParams : GeneralRequestDetails
{
public string OwnerUri { get; set; }
public string OriginalProxyName { get; set; }
public AgentProxyInfo Proxy { get; set; }
}
/// <summary>
/// SQL Agent update Proxy result
/// </summary>
public class UpdateAgentProxyResult
{
public bool Succeeded { get; set; }
public string ErrorMessage { get; set; }
}
/// <summary>
/// SQL Agent update Proxy request type
/// </summary>
public class UpdateAgentProxyRequest
{
/// <summary>
/// Request definition
/// </summary>
public static readonly
RequestType<UpdateAgentProxyParams, UpdateAgentProxyResult> Type =
RequestType<UpdateAgentProxyParams, UpdateAgentProxyResult>.Create("agent/updateproxy");
}
}

View File

@@ -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
{
/// <summary>
/// AgentAlert class
/// </summary>
internal class AgentAlert : ManagementActionBase
{
/// <summary>
/// Agent alert info instance
/// </summary>
private AgentAlertInfo alertInfo = null;
/// <summary>
/// Default constructor that will be used to create dialog
/// </summary>
/// <param name="dataContainer"></param>
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;
}
}
}
}
}

View File

@@ -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
{
/// <summary>
/// Agent Operators management class
/// </summary>
internal class AgentOperator : ManagementActionBase
{
private AgentOperatorInfo operatorInfo;
AgentOperatorsData operatorsData = null;
/// <summary>
/// Constructor
/// </summary>
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);
}
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose(bool disposing)
{
if(disposing)
{
}
base.Dispose(disposing);
}
public bool CreateOrUpdate()
{
this.operatorsData.ApplyChanges(this.operatorInfo);
return true;
}
}
#region internal structures
/// <summary>
/// Provides data to be consumed in the job notification grid
/// </summary>
internal struct AgentJobNotificationHelper
{
/// <summary>
/// constructor
/// </summary>
/// <param name="name">job name</param>
/// <param name="notifyEmail"></param>
/// <param name="notifyPager"></param>
public AgentJobNotificationHelper(string name, CompletionAction notifyEmail, CompletionAction notifyPager)
{
this.Name = name;
this.NotifyEmail = notifyEmail;
this.NotifyPager = notifyPager;
}
/// <summary>
/// Name of the job
/// </summary>
public string Name;
/// <summary>
/// job email notification action
/// </summary>
public CompletionAction NotifyEmail;
/// <summary>
/// job pager notification action
/// </summary>
public CompletionAction NotifyPager;
}
/// <summary>
/// Provides data to be consumed in the alert notification grid
/// </summary>
internal struct AgentAlertNotificationHelper
{
/// <summary>
/// constructor
/// </summary>
/// <param name="name">Name of the alert</param>
/// <param name="notifyEmail"></param>
/// <param name="notifyPager"></param>
/// <param name="alert">Alert object</param>
public AgentAlertNotificationHelper(string name, bool notifyEmail, bool notifyPager, Alert alert)
{
this.Name = name;
this.NotifyEmail = notifyEmail;
this.NotifyPager = notifyPager;
this.Alert = alert;
}
/// <summary>
/// Alert name
/// </summary>
public string Name;
/// <summary>
/// Indicates whether the alert will notify the operator through email
/// </summary>
public bool NotifyEmail;
/// <summary>
/// Indicates whether the alert will notify the operator through pager
/// </summary>
public bool NotifyPager;
/// <summary>
/// Alert object. optimisation to stop us having to lookup the alert object when needed
/// </summary>
public Alert Alert;
}
#endregion
/// <summary>
/// Proxy class for the AgentOperators dialog and property pages.
/// Performs lazy instantiation of groups of data based around the operators dialog property pages
/// </summary>
internal class AgentOperatorsData
{
#region members
/// <summary>
/// Data container
/// </summary>
CDataContainer dataContainer;
/// <summary>
/// Original operator name. Empty if we are creating a new operator
/// </summary>
string originalOperatorName = String.Empty;
/// <summary>
/// Indicates whether we are creating an operator or not
/// </summary>
bool createMode;
/// <summary>
/// Has then data for the general page been initialised
/// </summary>
bool generalInitialized = false;
/// <summary>
/// has the data for the history page been initialised
/// </summary>
bool historyInitialized = false;
/// <summary>
/// True if this operator cannot be modified
/// </summary>
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
/// <summary>
/// will be null if the alert notifications have not been initialised
/// </summary>
IList<AgentAlertNotificationHelper> alertNotifications;
/// <summary>
/// will be null if the job notifications have not been initialised
/// </summary>
IList<AgentJobNotificationHelper> jobNotifications;
#endregion
#region history items
DateTime lastEmailDate;
DateTime lastPagerDate;
#endregion
#endregion
#region properties
/// <summary>
/// indicates if the data is in create mode
/// </summary>
public bool Creating
{
get
{
return this.createMode;
}
}
/// <summary>
/// name of the object
/// </summary>
public string Name
{
get
{
LoadGeneralData();
return name;
}
set
{
LoadGeneralData();
name = value;
}
}
/// <summary>
/// Indicates if the dataobject is readonly
/// </summary>
public bool ReadOnly
{
get
{
return this.readOnly;
}
}
#region general items
/// <summary>
/// indicates whether or not the operator is enabled
/// </summary>
public bool Enabled
{
get
{
LoadGeneralData();
return enabled;
}
set
{
LoadGeneralData();
enabled = value;
}
}
/// <summary>
/// email address of this operator
/// </summary>
public string EmailAddress
{
get
{
LoadGeneralData();
return this.emailAddress;
}
set
{
LoadGeneralData();
this.emailAddress = value;
}
}
/// <summary>
/// pager address of this operator
/// </summary>
public string PagerAddress
{
get
{
LoadGeneralData();
return this.pagerAddress;
}
set
{
LoadGeneralData();
this.pagerAddress = value;
}
}
/// <summary>
/// the days of the week the operator is active
/// </summary>
public SqlServer.Management.Smo.Agent.WeekDays PagerDays
{
get
{
LoadGeneralData();
return this.pagerDays;
}
set
{
LoadGeneralData();
this.pagerDays = value;
}
}
/// <summary>
/// Weekday start time for this operator to be active
/// </summary>
public DateTime WeekdayStartTime
{
get
{
LoadGeneralData();
return this.weekdayStartTime;
}
set
{
LoadGeneralData();
this.weekdayStartTime = value;
}
}
/// <summary>
/// Weekday end time for this operator to be active
/// </summary>
public DateTime WeekdayEndTime
{
get
{
LoadGeneralData();
return this.weekdayEndTime;
}
set
{
LoadGeneralData();
this.weekdayEndTime = value;
}
}
/// <summary>
/// Saturday start time for this operator to be active
/// </summary>
public DateTime SaturdayStartTime
{
get
{
LoadGeneralData();
return this.saturdayStartTime;
}
set
{
LoadGeneralData();
this.saturdayStartTime = value;
}
}
/// <summary>
/// Saturday end time for this operator to be active
/// </summary>
public DateTime SaturdayEndTime
{
get
{
LoadGeneralData();
return this.saturdayEndTime;
}
set
{
LoadGeneralData();
this.saturdayEndTime = value;
}
}
/// <summary>
/// Sunday start time for this operator to be active
/// </summary>
public DateTime SundayStartTime
{
get
{
LoadGeneralData();
return this.sundayStartTime;
}
set
{
LoadGeneralData();
this.sundayStartTime = value;
}
}
/// <summary>
/// Saturday end time for this operator to be active
/// </summary>
public DateTime SundayEndTime
{
get
{
LoadGeneralData();
return this.sundayEndTime;
}
set
{
LoadGeneralData();
this.sundayEndTime = value;
}
}
#endregion
#region notification items
/// <summary>
/// Alerts that notify this operator
/// </summary>
public IList<AgentAlertNotificationHelper> AlertNotifications
{
get
{
LoadAlertNotificationData();
return this.alertNotifications;
}
set
{
this.alertNotifications = value;
}
}
/// <summary>
/// Jobs that notify this operator. This has to be set through the jobs dialog and is read only
/// </summary>
public IList<AgentJobNotificationHelper> JobNotifications
{
get
{
LoadJobNotificationData();
return this.jobNotifications;
}
}
#endregion
#region history items
/// <summary>
/// Date this operator was last emailed
/// </summary>
public DateTime LastEmailDate
{
get
{
LoadHistoryData();
return this.lastEmailDate;
}
}
/// <summary>
/// Date this operator was last paged
/// </summary>
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
/// <summary>
/// load data for the general tab. This can be called multiple times but will only load the data once intially
/// or after a reset
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
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<AgentJobNotificationHelper>();
// 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);
}
}
}
/// <summary>
/// Load alerts that notify this operator
/// </summary>
private void LoadAlertNotificationData()
{
if (this.alertNotifications != null)
{
return;
}
// defaults in create ode
if (createMode)
{
LoadAlertNotificationDefaults();
return;
}
this.alertNotifications = new List<AgentAlertNotificationHelper>();
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);
}
}
/// <summary>
/// load the notifiaction history for the operator
/// </summary>
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
/// <summary>
/// apply any changes to the operator. If the operator does not exist create it.
/// </summary>
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
/// <summary>
/// Reset the object to it's original state / reload any data from the erver
/// </summary>
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
/// <summary>
/// set general tab defaults
/// </summary>
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;
}
/// <summary>
/// Set job notification defaults. This is just an empty list
/// </summary>
private void LoadJobNotificationDefaults()
{
this.jobNotifications = new List<AgentJobNotificationHelper>();
}
/// <summary>
/// set the alert notification defaults. This list will contain all of the alerts
/// </summary>
private void LoadAlertNotificationDefaults()
{
this.alertNotifications = new List<AgentAlertNotificationHelper>();
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);
}
}
/// <summary>
/// load defaults for the history page
/// </summary>
private void LoadHistoryDefaults()
{
this.lastEmailDate = DateTime.MinValue;
this.lastPagerDate = DateTime.MinValue;
this.historyInitialized = true;
}
#endregion
#region helpers
/// <summary>
/// Get the job server. Will throw if it is not available
/// </summary>
/// <returns>Job server object</returns>
private JobServer GetJobServer()
{
JobServer jobServer = this.dataContainer.Server.JobServer;
if(jobServer == null)
{
throw new ApplicationException("AgentOperatorsSR.JobServerIsNotAvailable");
}
return jobServer;
}
/// <summary>
/// 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.
/// </summary>
/// <returns>Operator object</returns>
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;
}
/// <summary>
///
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
static public TimeSpan ConvertAgentTime(DateTime dateTime)
{
return new TimeSpan(dateTime.Hour, dateTime.Minute, dateTime.Second);
}
/// <summary>
///
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
static public DateTime ConvertAgentTime(TimeSpan dateTime)
{
return new DateTime(2000, 1, 1, dateTime.Hours, dateTime.Minutes, dateTime.Seconds);
}
/// <summary>
///
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
static public int ConvertAgentTimeToInt(DateTime dateTime)
{
return dateTime.Hour * 10000 + dateTime.Minute * 100 + dateTime.Second;
}
/// <summary>
///
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
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
}
}

View File

@@ -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"; }
}
/// <summary>
/// Main constructor. Creates all pages and adds them
/// to the tree control.
/// </summary>
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);
}
/// <summary>
/// It creates a new ProxyAccount or gets an existing
/// one from JobServer and updates all properties.
/// </summary>
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;
}
/// <summary>
/// Called to update the proxy object with properties
/// from this page.
/// </summary>
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);
}
}
}
/// <summary>
/// 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.
/// </summary>
/// <returns>true if there are any changes between existingPermissions and newPermissions lists</returns>
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
/// <summary>
/// Retrieves an instance of ProxyAccount from job server using name provided.
/// If proxy does not exist it throws an exception.
/// </summary>
/// <param name="proxyAccountName">Name of the proxy to get</param>
/// <param name="jobServer">Job server to get the proxy from</param>
/// <returns>A valid proxy account.</returns>
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;
}
/// <summary>
/// Retrieves a proxy account name from shared data containter.
/// </summary>
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;
}
}
/// <summary>
/// Uses enumerator to list names of all proxy accounts that use specified Agent SubSystem.
/// </summary>
/// <param name="serverConnection">Connection to use.</param>
/// <param name="subsystemName">Requested SubSystem name</param>
/// <param name="includeSysadmin">If set to true, 'sysadmin' account is added as a first entry in
/// the list of proxy accounts.</param>
/// <returns>An array containing names of proxy accounts</returns>
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
}
}

View File

@@ -403,153 +403,4 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
return foundJobs;
}
}
/// <summary>
/// a class for storing various properties of agent jobs,
/// used by the Job Activity Monitor
/// </summary>
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;
}
}
}
}

View File

@@ -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
{
/// <summary>
/// a class for storing various properties of agent jobs,
/// used by the Job Activity Monitor
/// </summary>
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;
}
}
}
}

View File

@@ -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;

View File

@@ -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()
{ }

View File

@@ -2023,4 +2023,405 @@
<value>Connection not found</value>
<comment></comment>
</data>
<data name="EnableAlertsTitle" xml:space="preserve">
<value>Enable Alerts - {0}</value>
<comment>.
Parameters: 0 - serverName (String) </comment>
</data>
<data name="EnableAlertDescription" xml:space="preserve">
<value>Enable Alert '{0}'</value>
<comment>.
Parameters: 0 - alertName (String) </comment>
</data>
<data name="EnablingAlert" xml:space="preserve">
<value>Enabling Alert '{0}'</value>
<comment>.
Parameters: 0 - alertName (String) </comment>
</data>
<data name="EnabledAlert" xml:space="preserve">
<value>Enabled Alert '{0}'</value>
<comment>.
Parameters: 0 - alertName (String) </comment>
</data>
<data name="DisableAlertsTitle" xml:space="preserve">
<value>Disable Alerts - {0}</value>
<comment>.
Parameters: 0 - serverName (String) </comment>
</data>
<data name="DisableAlertDescription" xml:space="preserve">
<value>Disable Alert '{0}'</value>
<comment>.
Parameters: 0 - alertName (String) </comment>
</data>
<data name="DisablingAlert" xml:space="preserve">
<value>Disabling Alert '{0}'</value>
<comment>.
Parameters: 0 - alertName (String) </comment>
</data>
<data name="DisabledAlert" xml:space="preserve">
<value>Disabled Alert '{0}'</value>
<comment>.
Parameters: 0 - alertName (String) </comment>
</data>
<data name="EnableJobsTitle" xml:space="preserve">
<value>Enable Jobs - {0}</value>
<comment>.
Parameters: 0 - serverName (String) </comment>
</data>
<data name="EnableJobDescription" xml:space="preserve">
<value>Enable Job '{0}'</value>
<comment>.
Parameters: 0 - jobName (String) </comment>
</data>
<data name="EnablingJob" xml:space="preserve">
<value>Enabling Job '{0}'</value>
<comment>.
Parameters: 0 - jobName (String) </comment>
</data>
<data name="EnabledJob" xml:space="preserve">
<value>Enabled Job '{0}'</value>
<comment>.
Parameters: 0 - jobName (String) </comment>
</data>
<data name="DisableJobsTitle" xml:space="preserve">
<value>Disable Jobs - {0}</value>
<comment>.
Parameters: 0 - serverName (String) </comment>
</data>
<data name="DisableJobDescription" xml:space="preserve">
<value>Disable Job '{0}'</value>
<comment>.
Parameters: 0 - jobName (String) </comment>
</data>
<data name="DisablingJob" xml:space="preserve">
<value>Disabling Job '{0}'</value>
<comment>.
Parameters: 0 - jobName (String) </comment>
</data>
<data name="DisabledJob" xml:space="preserve">
<value>Disabled Job '{0}'</value>
<comment>.
Parameters: 0 - jobName (String) </comment>
</data>
<data name="StartJobsTitle" xml:space="preserve">
<value>Start Jobs - {0}</value>
<comment>.
Parameters: 0 - serverName (String) </comment>
</data>
<data name="StartJobDescription" xml:space="preserve">
<value>Start Job '{0}'</value>
<comment>.
Parameters: 0 - jobName (String) </comment>
</data>
<data name="GettingStartStep" xml:space="preserve">
<value>Job '{0}' has more than one step. Getting step to start</value>
<comment>.
Parameters: 0 - jobName (String) </comment>
</data>
<data name="UserCancelledSelectStep" xml:space="preserve">
<value>User canceled select step. Job will not be started</value>
<comment></comment>
</data>
<data name="StartingJob" xml:space="preserve">
<value>Starting Job '{0}'</value>
<comment>.
Parameters: 0 - jobName (String) </comment>
</data>
<data name="StartJobWithStep" xml:space="preserve">
<value>Start Job '{0}' with step '{1}'</value>
<comment>.
Parameters: 0 - jobName (String), 1 - stepName (String) </comment>
</data>
<data name="RequestPostedToTargetServers" xml:space="preserve">
<value>Posted remote job execution request</value>
<comment></comment>
</data>
<data name="ExecuteJob" xml:space="preserve">
<value>Execute job '{0}'</value>
<comment>.
Parameters: 0 - jobName (string) </comment>
</data>
<data name="JobFailed" xml:space="preserve">
<value>Execution of job '{0}' failed. See the history log for details.</value>
<comment>.
Parameters: 0 - jobName (string) </comment>
</data>
<data name="Executing" xml:space="preserve">
<value>Executing</value>
<comment></comment>
</data>
<data name="BetweenRetries" xml:space="preserve">
<value>Between retries</value>
<comment></comment>
</data>
<data name="Suspended" xml:space="preserve">
<value>Suspended</value>
<comment></comment>
</data>
<data name="PerformingCompletionAction" xml:space="preserve">
<value>Performing completion action</value>
<comment></comment>
</data>
<data name="WaitingForStepToFinish" xml:space="preserve">
<value>Waiting for step to finish</value>
<comment></comment>
</data>
<data name="WaitingForWorkerThread" xml:space="preserve">
<value>Waiting for worker thread</value>
<comment></comment>
</data>
<data name="StopJobsTitle" xml:space="preserve">
<value>Stop Jobs - {0}</value>
<comment>.
Parameters: 0 - serverName (String) </comment>
</data>
<data name="StopJobDescription" xml:space="preserve">
<value>Stop Job '{0}'</value>
<comment>.
Parameters: 0 - jobName (String) </comment>
</data>
<data name="StoppingJob" xml:space="preserve">
<value>Stopping Job '{0}'</value>
<comment>.
Parameters: 0 - jobName (String) </comment>
</data>
<data name="StoppedJob" xml:space="preserve">
<value>Stopped Job '{0}'</value>
<comment>.
Parameters: 0 - jobName (String) </comment>
</data>
<data name="AllDatabases" xml:space="preserve">
<value>&lt;all databases&gt;</value>
<comment> First item in database name drop down list</comment>
</data>
<data name="UnknownSeverity" xml:space="preserve">
<value>Unknown severity: {0}</value>
<comment> Exception thrown when agent alert has unknown severity level.
Parameters: 0 - severity (int) </comment>
</data>
<data name="Severity001" xml:space="preserve">
<value>001 - Miscellaneous System Information</value>
<comment></comment>
</data>
<data name="Severity002" xml:space="preserve">
<value>002 - Reserved</value>
<comment></comment>
</data>
<data name="Severity003" xml:space="preserve">
<value>003 - Reserved</value>
<comment></comment>
</data>
<data name="Severity004" xml:space="preserve">
<value>004 - Reserved</value>
<comment></comment>
</data>
<data name="Severity005" xml:space="preserve">
<value>005 - Reserved</value>
<comment></comment>
</data>
<data name="Severity006" xml:space="preserve">
<value>006 - Reserved</value>
<comment></comment>
</data>
<data name="Severity007" xml:space="preserve">
<value>007 - Notification: Status Information</value>
<comment></comment>
</data>
<data name="Severity008" xml:space="preserve">
<value>008 - Notification: User Intervention Required</value>
<comment></comment>
</data>
<data name="Severity009" xml:space="preserve">
<value>009 - User Defined</value>
<comment></comment>
</data>
<data name="Severity010" xml:space="preserve">
<value>010 - Information</value>
<comment></comment>
</data>
<data name="Severity011" xml:space="preserve">
<value>011 - Specified Database Object Not Found</value>
<comment></comment>
</data>
<data name="Severity012" xml:space="preserve">
<value>012 - Unused</value>
<comment></comment>
</data>
<data name="Severity013" xml:space="preserve">
<value>013 - User Transaction Syntax Error</value>
<comment></comment>
</data>
<data name="Severity014" xml:space="preserve">
<value>014 - Insufficient Permission</value>
<comment></comment>
</data>
<data name="Severity015" xml:space="preserve">
<value>015 - Syntax Error in SQL Statements</value>
<comment></comment>
</data>
<data name="Severity016" xml:space="preserve">
<value>016 - Miscellaneous User Error</value>
<comment></comment>
</data>
<data name="Severity017" xml:space="preserve">
<value>017 - Insufficient Resources</value>
<comment></comment>
</data>
<data name="Severity018" xml:space="preserve">
<value>018 - Nonfatal Internal Error</value>
<comment></comment>
</data>
<data name="Severity019" xml:space="preserve">
<value>019 - Fatal Error in Resource</value>
<comment></comment>
</data>
<data name="Severity020" xml:space="preserve">
<value>020 - Fatal Error in Current Process</value>
<comment></comment>
</data>
<data name="Severity021" xml:space="preserve">
<value>021 - Fatal Error in Database Processes</value>
<comment></comment>
</data>
<data name="Severity022" xml:space="preserve">
<value>022 - Fatal Error: Table Integrity Suspect</value>
<comment></comment>
</data>
<data name="Severity023" xml:space="preserve">
<value>023 - Fatal Error: Database Integrity Suspect</value>
<comment></comment>
</data>
<data name="Severity024" xml:space="preserve">
<value>024 - Fatal Error: Hardware Error</value>
<comment></comment>
</data>
<data name="Severity025" xml:space="preserve">
<value>025 - Fatal Error</value>
<comment></comment>
</data>
<data name="PagerScheduleMonFri" xml:space="preserve">
<value>Pager schedule end time is earlier than start time on Mon-Fri. Do you want to continue?</value>
<comment> Message box that displayed if start time is more than end time on Mon-Fri</comment>
</data>
<data name="PagerScheduleSatSun" xml:space="preserve">
<value>Pager schedule end time is earlier than start time on Sat-Sun. Do you want to continue?</value>
<comment> Message box that displayed if start time is more than end time on Sat-Sun</comment>
</data>
<data name="PagerScheduleWarning" xml:space="preserve">
<value>Pager schedule warning</value>
<comment> Message box caption</comment>
</data>
<data name="General" xml:space="preserve">
<value>General</value>
<comment> Tree node name</comment>
</data>
<data name="Notifications" xml:space="preserve">
<value>Notifications</value>
<comment> Tree node name</comment>
</data>
<data name="History" xml:space="preserve">
<value>History</value>
<comment> Tree node name</comment>
</data>
<data name="Day" xml:space="preserve">
<value>Day</value>
<comment> Pager schedule grid column name</comment>
</data>
<data name="StartTime" xml:space="preserve">
<value>Start Time</value>
<comment> Pager schedule grid column name</comment>
</data>
<data name="EndTime" xml:space="preserve">
<value>End Time</value>
<comment> Pager schedule grid column name</comment>
</data>
<data name="ColumnIndexIsInvalid" xml:space="preserve">
<value>Column index is invalid.</value>
<comment> Exception thrown when column index is invalid</comment>
</data>
<data name="RowIndexIsInvalid" xml:space="preserve">
<value>Row index is invalid.</value>
<comment> Exception thrown when row index is invalid</comment>
</data>
<data name="NewOperatorProperties" xml:space="preserve">
<value>New Operator</value>
<comment> Name of the operator dialog in create new operator mode</comment>
</data>
<data name="OperatorProperties" xml:space="preserve">
<value>{0} Properties</value>
<comment> Name of the operator dialog in modify operator mode.
Parameters: 0 - operatorName (string) </comment>
</data>
<data name="FailedToCreateInitializeAgentOperatorDialog" xml:space="preserve">
<value>Unable to create/initialize Agent Operator dialog.</value>
<comment> Exception thrown when dialog cannot be created/intialized.</comment>
</data>
<data name="JobServerIsNotAvailable" xml:space="preserve">
<value>Job server is not available.</value>
<comment> Exception thrown when job server is not available</comment>
</data>
<data name="CannotCreateInitializeGeneralPage" xml:space="preserve">
<value>Cannot create/initialize General page.</value>
<comment> Exception thrown when we cannot create/initialize agent operators general page</comment>
</data>
<data name="CannotCreateInitializeNotificationsPage" xml:space="preserve">
<value>Cannot create/initialize Notifications page.</value>
<comment> Exception thrown when we cannot create/initialize agent operators notifications page</comment>
</data>
<data name="CannotCreateInitializeHistoryPage" xml:space="preserve">
<value>Cannot create/initialize History page.</value>
<comment> Exception thrown when we cannot create/initialize agent operators history page</comment>
</data>
<data name="CannotResetOperator" xml:space="preserve">
<value>Cannot reset operator.</value>
<comment> Exception throw when dialog cannot refresh operator</comment>
</data>
<data name="AlertList" xml:space="preserve">
<value>Alert list:</value>
<comment> Name of label on notifications page</comment>
</data>
<data name="JobList" xml:space="preserve">
<value>Job list:</value>
<comment> Name of label on notifications page</comment>
</data>
<data name="Email" xml:space="preserve">
<value>E-mail</value>
<comment> Name of column on notifications page</comment>
</data>
<data name="Pager" xml:space="preserve">
<value>Pager</value>
<comment> Name of column on notifications page</comment>
</data>
<data name="AlertName" xml:space="preserve">
<value>Alert name</value>
<comment> Name of column on notifications page</comment>
</data>
<data name="JobName" xml:space="preserve">
<value>Job name</value>
<comment> Name of column on notifications page</comment>
</data>
<data name="Always" xml:space="preserve">
<value>Always</value>
<comment> Completion Action</comment>
</data>
<data name="Never" xml:space="preserve">
<value>Never</value>
<comment> Completion Action</comment>
</data>
<data name="OnFailure" xml:space="preserve">
<value>On failure</value>
<comment> Completion Action</comment>
</data>
<data name="OnSuccess" xml:space="preserve">
<value>On success</value>
<comment> Completion Action</comment>
</data>
<data name="CannotModifyAlerts" xml:space="preserve">
<value>Cannot modify alerts.</value>
<comment> Exception thrown when we cannot modify alerts</comment>
</data>
<data name="CannotCreateScriptForModifyAlerts" xml:space="preserve">
<value>Cannot create script for modify alerts.</value>
<comment> Exception thrown when we cannot create script that modify alerts</comment>
</data>
</root>

View File

@@ -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 = <all databases>
; 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.

View File

@@ -2379,6 +2379,500 @@
<note>.
Parameters: 0 - tableName (string) </note>
</trans-unit>
<trans-unit id="EnableAlertsTitle">
<source>Enable Alerts - {0}</source>
<target state="new">Enable Alerts - {0}</target>
<note>.
Parameters: 0 - serverName (String) </note>
</trans-unit>
<trans-unit id="EnableAlertDescription">
<source>Enable Alert '{0}'</source>
<target state="new">Enable Alert '{0}'</target>
<note>.
Parameters: 0 - alertName (String) </note>
</trans-unit>
<trans-unit id="EnablingAlert">
<source>Enabling Alert '{0}'</source>
<target state="new">Enabling Alert '{0}'</target>
<note>.
Parameters: 0 - alertName (String) </note>
</trans-unit>
<trans-unit id="EnabledAlert">
<source>Enabled Alert '{0}'</source>
<target state="new">Enabled Alert '{0}'</target>
<note>.
Parameters: 0 - alertName (String) </note>
</trans-unit>
<trans-unit id="DisableAlertsTitle">
<source>Disable Alerts - {0}</source>
<target state="new">Disable Alerts - {0}</target>
<note>.
Parameters: 0 - serverName (String) </note>
</trans-unit>
<trans-unit id="DisableAlertDescription">
<source>Disable Alert '{0}'</source>
<target state="new">Disable Alert '{0}'</target>
<note>.
Parameters: 0 - alertName (String) </note>
</trans-unit>
<trans-unit id="DisablingAlert">
<source>Disabling Alert '{0}'</source>
<target state="new">Disabling Alert '{0}'</target>
<note>.
Parameters: 0 - alertName (String) </note>
</trans-unit>
<trans-unit id="DisabledAlert">
<source>Disabled Alert '{0}'</source>
<target state="new">Disabled Alert '{0}'</target>
<note>.
Parameters: 0 - alertName (String) </note>
</trans-unit>
<trans-unit id="EnableJobsTitle">
<source>Enable Jobs - {0}</source>
<target state="new">Enable Jobs - {0}</target>
<note>.
Parameters: 0 - serverName (String) </note>
</trans-unit>
<trans-unit id="EnableJobDescription">
<source>Enable Job '{0}'</source>
<target state="new">Enable Job '{0}'</target>
<note>.
Parameters: 0 - jobName (String) </note>
</trans-unit>
<trans-unit id="EnablingJob">
<source>Enabling Job '{0}'</source>
<target state="new">Enabling Job '{0}'</target>
<note>.
Parameters: 0 - jobName (String) </note>
</trans-unit>
<trans-unit id="EnabledJob">
<source>Enabled Job '{0}'</source>
<target state="new">Enabled Job '{0}'</target>
<note>.
Parameters: 0 - jobName (String) </note>
</trans-unit>
<trans-unit id="DisableJobsTitle">
<source>Disable Jobs - {0}</source>
<target state="new">Disable Jobs - {0}</target>
<note>.
Parameters: 0 - serverName (String) </note>
</trans-unit>
<trans-unit id="DisableJobDescription">
<source>Disable Job '{0}'</source>
<target state="new">Disable Job '{0}'</target>
<note>.
Parameters: 0 - jobName (String) </note>
</trans-unit>
<trans-unit id="DisablingJob">
<source>Disabling Job '{0}'</source>
<target state="new">Disabling Job '{0}'</target>
<note>.
Parameters: 0 - jobName (String) </note>
</trans-unit>
<trans-unit id="DisabledJob">
<source>Disabled Job '{0}'</source>
<target state="new">Disabled Job '{0}'</target>
<note>.
Parameters: 0 - jobName (String) </note>
</trans-unit>
<trans-unit id="StartJobsTitle">
<source>Start Jobs - {0}</source>
<target state="new">Start Jobs - {0}</target>
<note>.
Parameters: 0 - serverName (String) </note>
</trans-unit>
<trans-unit id="StartJobDescription">
<source>Start Job '{0}'</source>
<target state="new">Start Job '{0}'</target>
<note>.
Parameters: 0 - jobName (String) </note>
</trans-unit>
<trans-unit id="GettingStartStep">
<source>Job '{0}' has more than one step. Getting step to start</source>
<target state="new">Job '{0}' has more than one step. Getting step to start</target>
<note>.
Parameters: 0 - jobName (String) </note>
</trans-unit>
<trans-unit id="UserCancelledSelectStep">
<source>User canceled select step. Job will not be started</source>
<target state="new">User canceled select step. Job will not be started</target>
<note></note>
</trans-unit>
<trans-unit id="StartingJob">
<source>Starting Job '{0}'</source>
<target state="new">Starting Job '{0}'</target>
<note>.
Parameters: 0 - jobName (String) </note>
</trans-unit>
<trans-unit id="StartJobWithStep">
<source>Start Job '{0}' with step '{1}'</source>
<target state="new">Start Job '{0}' with step '{1}'</target>
<note>.
Parameters: 0 - jobName (String), 1 - stepName (String) </note>
</trans-unit>
<trans-unit id="RequestPostedToTargetServers">
<source>Posted remote job execution request</source>
<target state="new">Posted remote job execution request</target>
<note></note>
</trans-unit>
<trans-unit id="ExecuteJob">
<source>Execute job '{0}'</source>
<target state="new">Execute job '{0}'</target>
<note>.
Parameters: 0 - jobName (string) </note>
</trans-unit>
<trans-unit id="JobFailed">
<source>Execution of job '{0}' failed. See the history log for details.</source>
<target state="new">Execution of job '{0}' failed. See the history log for details.</target>
<note>.
Parameters: 0 - jobName (string) </note>
</trans-unit>
<trans-unit id="Executing">
<source>Executing</source>
<target state="new">Executing</target>
<note></note>
</trans-unit>
<trans-unit id="BetweenRetries">
<source>Between retries</source>
<target state="new">Between retries</target>
<note></note>
</trans-unit>
<trans-unit id="Suspended">
<source>Suspended</source>
<target state="new">Suspended</target>
<note></note>
</trans-unit>
<trans-unit id="PerformingCompletionAction">
<source>Performing completion action</source>
<target state="new">Performing completion action</target>
<note></note>
</trans-unit>
<trans-unit id="WaitingForStepToFinish">
<source>Waiting for step to finish</source>
<target state="new">Waiting for step to finish</target>
<note></note>
</trans-unit>
<trans-unit id="WaitingForWorkerThread">
<source>Waiting for worker thread</source>
<target state="new">Waiting for worker thread</target>
<note></note>
</trans-unit>
<trans-unit id="StopJobsTitle">
<source>Stop Jobs - {0}</source>
<target state="new">Stop Jobs - {0}</target>
<note>.
Parameters: 0 - serverName (String) </note>
</trans-unit>
<trans-unit id="StopJobDescription">
<source>Stop Job '{0}'</source>
<target state="new">Stop Job '{0}'</target>
<note>.
Parameters: 0 - jobName (String) </note>
</trans-unit>
<trans-unit id="StoppingJob">
<source>Stopping Job '{0}'</source>
<target state="new">Stopping Job '{0}'</target>
<note>.
Parameters: 0 - jobName (String) </note>
</trans-unit>
<trans-unit id="StoppedJob">
<source>Stopped Job '{0}'</source>
<target state="new">Stopped Job '{0}'</target>
<note>.
Parameters: 0 - jobName (String) </note>
</trans-unit>
<trans-unit id="AllDatabases">
<source>&lt;all databases&gt;</source>
<target state="new">&lt;all databases&gt;</target>
<note> First item in database name drop down list</note>
</trans-unit>
<trans-unit id="UnknownSeverity">
<source>Unknown severity: {0}</source>
<target state="new">Unknown severity: {0}</target>
<note> Exception thrown when agent alert has unknown severity level.
Parameters: 0 - severity (int) </note>
</trans-unit>
<trans-unit id="Severity001">
<source>001 - Miscellaneous System Information</source>
<target state="new">001 - Miscellaneous System Information</target>
<note></note>
</trans-unit>
<trans-unit id="Severity002">
<source>002 - Reserved</source>
<target state="new">002 - Reserved</target>
<note></note>
</trans-unit>
<trans-unit id="Severity003">
<source>003 - Reserved</source>
<target state="new">003 - Reserved</target>
<note></note>
</trans-unit>
<trans-unit id="Severity004">
<source>004 - Reserved</source>
<target state="new">004 - Reserved</target>
<note></note>
</trans-unit>
<trans-unit id="Severity005">
<source>005 - Reserved</source>
<target state="new">005 - Reserved</target>
<note></note>
</trans-unit>
<trans-unit id="Severity006">
<source>006 - Reserved</source>
<target state="new">006 - Reserved</target>
<note></note>
</trans-unit>
<trans-unit id="Severity007">
<source>007 - Notification: Status Information</source>
<target state="new">007 - Notification: Status Information</target>
<note></note>
</trans-unit>
<trans-unit id="Severity008">
<source>008 - Notification: User Intervention Required</source>
<target state="new">008 - Notification: User Intervention Required</target>
<note></note>
</trans-unit>
<trans-unit id="Severity009">
<source>009 - User Defined</source>
<target state="new">009 - User Defined</target>
<note></note>
</trans-unit>
<trans-unit id="Severity010">
<source>010 - Information</source>
<target state="new">010 - Information</target>
<note></note>
</trans-unit>
<trans-unit id="Severity011">
<source>011 - Specified Database Object Not Found</source>
<target state="new">011 - Specified Database Object Not Found</target>
<note></note>
</trans-unit>
<trans-unit id="Severity012">
<source>012 - Unused</source>
<target state="new">012 - Unused</target>
<note></note>
</trans-unit>
<trans-unit id="Severity013">
<source>013 - User Transaction Syntax Error</source>
<target state="new">013 - User Transaction Syntax Error</target>
<note></note>
</trans-unit>
<trans-unit id="Severity014">
<source>014 - Insufficient Permission</source>
<target state="new">014 - Insufficient Permission</target>
<note></note>
</trans-unit>
<trans-unit id="Severity015">
<source>015 - Syntax Error in SQL Statements</source>
<target state="new">015 - Syntax Error in SQL Statements</target>
<note></note>
</trans-unit>
<trans-unit id="Severity016">
<source>016 - Miscellaneous User Error</source>
<target state="new">016 - Miscellaneous User Error</target>
<note></note>
</trans-unit>
<trans-unit id="Severity017">
<source>017 - Insufficient Resources</source>
<target state="new">017 - Insufficient Resources</target>
<note></note>
</trans-unit>
<trans-unit id="Severity018">
<source>018 - Nonfatal Internal Error</source>
<target state="new">018 - Nonfatal Internal Error</target>
<note></note>
</trans-unit>
<trans-unit id="Severity019">
<source>019 - Fatal Error in Resource</source>
<target state="new">019 - Fatal Error in Resource</target>
<note></note>
</trans-unit>
<trans-unit id="Severity020">
<source>020 - Fatal Error in Current Process</source>
<target state="new">020 - Fatal Error in Current Process</target>
<note></note>
</trans-unit>
<trans-unit id="Severity021">
<source>021 - Fatal Error in Database Processes</source>
<target state="new">021 - Fatal Error in Database Processes</target>
<note></note>
</trans-unit>
<trans-unit id="Severity022">
<source>022 - Fatal Error: Table Integrity Suspect</source>
<target state="new">022 - Fatal Error: Table Integrity Suspect</target>
<note></note>
</trans-unit>
<trans-unit id="Severity023">
<source>023 - Fatal Error: Database Integrity Suspect</source>
<target state="new">023 - Fatal Error: Database Integrity Suspect</target>
<note></note>
</trans-unit>
<trans-unit id="Severity024">
<source>024 - Fatal Error: Hardware Error</source>
<target state="new">024 - Fatal Error: Hardware Error</target>
<note></note>
</trans-unit>
<trans-unit id="Severity025">
<source>025 - Fatal Error</source>
<target state="new">025 - Fatal Error</target>
<note></note>
</trans-unit>
<trans-unit id="PagerScheduleMonFri">
<source>Pager schedule end time is earlier than start time on Mon-Fri. Do you want to continue?</source>
<target state="new">Pager schedule end time is earlier than start time on Mon-Fri. Do you want to continue?</target>
<note> Message box that displayed if start time is more than end time on Mon-Fri</note>
</trans-unit>
<trans-unit id="PagerScheduleSatSun">
<source>Pager schedule end time is earlier than start time on Sat-Sun. Do you want to continue?</source>
<target state="new">Pager schedule end time is earlier than start time on Sat-Sun. Do you want to continue?</target>
<note> Message box that displayed if start time is more than end time on Sat-Sun</note>
</trans-unit>
<trans-unit id="PagerScheduleWarning">
<source>Pager schedule warning</source>
<target state="new">Pager schedule warning</target>
<note> Message box caption</note>
</trans-unit>
<trans-unit id="General">
<source>General</source>
<target state="new">General</target>
<note> Tree node name</note>
</trans-unit>
<trans-unit id="Notifications">
<source>Notifications</source>
<target state="new">Notifications</target>
<note> Tree node name</note>
</trans-unit>
<trans-unit id="History">
<source>History</source>
<target state="new">History</target>
<note> Tree node name</note>
</trans-unit>
<trans-unit id="Day">
<source>Day</source>
<target state="new">Day</target>
<note> Pager schedule grid column name</note>
</trans-unit>
<trans-unit id="StartTime">
<source>Start Time</source>
<target state="new">Start Time</target>
<note> Pager schedule grid column name</note>
</trans-unit>
<trans-unit id="EndTime">
<source>End Time</source>
<target state="new">End Time</target>
<note> Pager schedule grid column name</note>
</trans-unit>
<trans-unit id="ColumnIndexIsInvalid">
<source>Column index is invalid.</source>
<target state="new">Column index is invalid.</target>
<note> Exception thrown when column index is invalid</note>
</trans-unit>
<trans-unit id="RowIndexIsInvalid">
<source>Row index is invalid.</source>
<target state="new">Row index is invalid.</target>
<note> Exception thrown when row index is invalid</note>
</trans-unit>
<trans-unit id="NewOperatorProperties">
<source>New Operator</source>
<target state="new">New Operator</target>
<note> Name of the operator dialog in create new operator mode</note>
</trans-unit>
<trans-unit id="OperatorProperties">
<source>{0} Properties</source>
<target state="new">{0} Properties</target>
<note> Name of the operator dialog in modify operator mode.
Parameters: 0 - operatorName (string) </note>
</trans-unit>
<trans-unit id="FailedToCreateInitializeAgentOperatorDialog">
<source>Unable to create/initialize Agent Operator dialog.</source>
<target state="new">Unable to create/initialize Agent Operator dialog.</target>
<note> Exception thrown when dialog cannot be created/intialized.</note>
</trans-unit>
<trans-unit id="JobServerIsNotAvailable">
<source>Job server is not available.</source>
<target state="new">Job server is not available.</target>
<note> Exception thrown when job server is not available</note>
</trans-unit>
<trans-unit id="CannotCreateInitializeGeneralPage">
<source>Cannot create/initialize General page.</source>
<target state="new">Cannot create/initialize General page.</target>
<note> Exception thrown when we cannot create/initialize agent operators general page</note>
</trans-unit>
<trans-unit id="CannotCreateInitializeNotificationsPage">
<source>Cannot create/initialize Notifications page.</source>
<target state="new">Cannot create/initialize Notifications page.</target>
<note> Exception thrown when we cannot create/initialize agent operators notifications page</note>
</trans-unit>
<trans-unit id="CannotCreateInitializeHistoryPage">
<source>Cannot create/initialize History page.</source>
<target state="new">Cannot create/initialize History page.</target>
<note> Exception thrown when we cannot create/initialize agent operators history page</note>
</trans-unit>
<trans-unit id="CannotResetOperator">
<source>Cannot reset operator.</source>
<target state="new">Cannot reset operator.</target>
<note> Exception throw when dialog cannot refresh operator</note>
</trans-unit>
<trans-unit id="AlertList">
<source>Alert list:</source>
<target state="new">Alert list:</target>
<note> Name of label on notifications page</note>
</trans-unit>
<trans-unit id="JobList">
<source>Job list:</source>
<target state="new">Job list:</target>
<note> Name of label on notifications page</note>
</trans-unit>
<trans-unit id="Email">
<source>E-mail</source>
<target state="new">E-mail</target>
<note> Name of column on notifications page</note>
</trans-unit>
<trans-unit id="Pager">
<source>Pager</source>
<target state="new">Pager</target>
<note> Name of column on notifications page</note>
</trans-unit>
<trans-unit id="AlertName">
<source>Alert name</source>
<target state="new">Alert name</target>
<note> Name of column on notifications page</note>
</trans-unit>
<trans-unit id="JobName">
<source>Job name</source>
<target state="new">Job name</target>
<note> Name of column on notifications page</note>
</trans-unit>
<trans-unit id="Always">
<source>Always</source>
<target state="new">Always</target>
<note> Completion Action</note>
</trans-unit>
<trans-unit id="Never">
<source>Never</source>
<target state="new">Never</target>
<note> Completion Action</note>
</trans-unit>
<trans-unit id="OnFailure">
<source>On failure</source>
<target state="new">On failure</target>
<note> Completion Action</note>
</trans-unit>
<trans-unit id="OnSuccess">
<source>On success</source>
<target state="new">On success</target>
<note> Completion Action</note>
</trans-unit>
<trans-unit id="CannotModifyAlerts">
<source>Cannot modify alerts.</source>
<target state="new">Cannot modify alerts.</target>
<note> Exception thrown when we cannot modify alerts</note>
</trans-unit>
<trans-unit id="CannotCreateScriptForModifyAlerts">
<source>Cannot create script for modify alerts.</source>
<target state="new">Cannot create script for modify alerts.</target>
<note> Exception thrown when we cannot create script that modify alerts</note>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -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;
using System.Collections.Generic;
using Microsoft.SqlTools.ServiceLayer.Agent;
namespace Microsoft.SqlTools.ServiceLayer.Security.Contracts
{
/// <summary>
/// a class for storing various credential properties
/// </summary>
public class CredentialInfo
{
public string Identity { get; set; }
public int Id { get; }
public DateTime DateLastModified { get; }
public DateTime CreateDate { get; }
public string ProviderName { get; set; }
}
}

View File

@@ -0,0 +1,45 @@
//
// 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.Security.Contracts
{
/// <summary>
/// Create Credential parameters
/// </summary>
public class CreateCredentialParams : GeneralRequestDetails
{
public string OwnerUri { get; set; }
public CredentialInfo Credential { get; set; }
}
/// <summary>
/// Create Credential result
/// </summary>
public class CreateCredentialResult
{
public bool Succeeded { get; set; }
public string ErrorMessage { get; set; }
}
/// <summary>
/// Create Credential request type
/// </summary>
public class CreateCredentialRequest
{
/// <summary>
/// Request definition
/// </summary>
public static readonly
RequestType<CreateCredentialParams, CreateCredentialResult> Type =
RequestType<CreateCredentialParams, CreateCredentialResult>.Create("security/createcredential");
}
}

View File

@@ -0,0 +1,114 @@
//
// 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.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Xml;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Diagnostics;
using Microsoft.SqlTools.ServiceLayer.Management;
using Microsoft.SqlTools.ServiceLayer.Admin;
namespace Microsoft.SqlTools.ServiceLayer.Security
{
internal class Credential : ManagementActionBase
{
// #region Trace support
// private const string componentName = "Credential";
// public string ComponentName
// {
// get
// {
// return componentName;
// }
// }
// #endregion
#region Constants
private const int MAX_SQL_SYS_NAME_LENGTH = 128; // max sql sys name length
private const string PASSWORD_MASK_STRING = "**********";
#endregion
#region Variables
private CredentialData credentialData = null;
#endregion
#region Constructors / Dispose
/// <summary>
/// required when loading from Object Explorer context
/// </summary>
/// <param name="context"></param>
public Credential(CDataContainer context)
{
this.DataContainer = context;
this.credentialData = new CredentialData(context);
this.credentialData.Initialize();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
if (disposing == true)
{
if (this.credentialData != null)
{
this.credentialData.Dispose();
}
}
}
#endregion
#region Overrides SqlManagementUserControl
/// <summary>
/// called on background thread by the framework to execute the action
/// </summary>
/// <param name="node"></param>
public void OnRunNow(object sender)
{
this.credentialData.SendDataToServer();
}
#endregion
/// <summary>
/// update logic layer based on content of user interface
/// </summary>
private void UpdateLogicLayer()
{
this.credentialData.CredentialName = "this.textBoxCredentialName.Text";
this.credentialData.CredentialIdentity = "this.textBoxIdentity.Text";
this.credentialData.SecurePassword = AdminService.BuildSecureStringFromPassword("password");
this.credentialData.SecurePasswordConfirm = AdminService.BuildSecureStringFromPassword("password");
if (this.ServerConnection.ServerVersion.Major >= 10)
{
// need to update only during create time
this.credentialData.IsEncryptionByProvider = true; //this.checkBoxUseProvider.Checked;
if (this.credentialData.IsEncryptionByProvider)
{
this.credentialData.ProviderName = "this.comboBoxProviderName.SelectedItem.ToString()";
}
else
{
this.credentialData.ProviderName = string.Empty;
}
}
}
}
}

View File

@@ -0,0 +1,256 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Security;
using System.Text;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Diagnostics;
using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.Admin;
namespace Microsoft.SqlTools.ServiceLayer.Security
{
internal class CredentialException : Exception
{
public CredentialException(string message)
: base(message)
{
}
}
internal class CredentialData : IDisposable
{
#region Properties
private string credentialName = string.Empty;
public string CredentialName
{
get { return credentialName; }
set { credentialName = value; }
}
private string credentialIdentity = string.Empty;
public string CredentialIdentity
{
get { return credentialIdentity; }
set { credentialIdentity = value; }
}
private SecureString securePassword;
public SecureString SecurePassword
{
get { return securePassword; }
set
{
securePassword = value;
PasswordWasChanged = true;
}
}
private SecureString securePasswordConfirm;
public SecureString SecurePasswordConfirm
{
get { return securePasswordConfirm; }
set
{
securePasswordConfirm = value;
PasswordWasChanged = true;
}
}
private bool isPropertiesMode = false;
public bool IsPropertiesMode
{
get
{
return isPropertiesMode;
}
}
private bool passwordWasChanged = false;
public bool PasswordWasChanged
{
get { return passwordWasChanged; }
set { passwordWasChanged = value; }
}
private bool isEncryptionByProvider = false;
public bool IsEncryptionByProvider
{
get { return isEncryptionByProvider; }
set { isEncryptionByProvider = value; }
}
private string providerName = string.Empty;
public string ProviderName
{
get { return providerName; }
set { providerName = value; }
}
#endregion
private const string ENUMERATOR_FIELD_IDENTITY = "Identity";
private const string ENUMERATOR_FIELD_PROVIDER_NAME = "ProviderName";
#region Constructor
private CDataContainer context = null;
private CDataContainer Context { get { return context; } set { context = value; } }
public CredentialData(CDataContainer ctxt)
{
this.Context = ctxt;
LoadDataFromXmlContext();
}
#endregion
#region Implementation: LoadDataFromXmlContext(), LoadDataFromServer(), SendDataToServer()
public void Initialize()
{
LoadDataFromXmlContext();
LoadDataFromServer();
}
/// <summary>
/// LoadDataFromXmlContext
///
/// loads context information from xml - e.g. name of object
/// </summary>
private void LoadDataFromXmlContext()
{
this.CredentialName = this.Context.GetDocumentPropertyString("credential");
this.isPropertiesMode = (this.CredentialName != null) && (this.CredentialName.Length > 0);
}
/// <summary>
/// LoadDataFromServer
///
/// talks with enumerator an retrieves info that is not available in the xml context but on server
/// </summary>
private void LoadDataFromServer()
{
if (this.IsPropertiesMode == true)
{
bool isKatmaiAndNotMatrix = (this.context.Server.Version.Major >= 10);
Urn urn = new Urn("Server/Credential[@Name='" + Urn.EscapeString(this.CredentialName) + "']");
string [] fields;
if (isKatmaiAndNotMatrix)
{
fields = new string[] { ENUMERATOR_FIELD_IDENTITY, ENUMERATOR_FIELD_PROVIDER_NAME };
}
else
{
fields = new string[] { ENUMERATOR_FIELD_IDENTITY };
}
Request r = new Request(urn, fields);
System.Data.DataTable dataTable = Enumerator.GetData(this.Context.ConnectionInfo, r);
if (dataTable.Rows.Count == 0)
{
throw new Exception("SRError.CredentialNoLongerExists");
}
System.Data.DataRow dataRow = dataTable.Rows[0];
this.CredentialIdentity = Convert.ToString(dataRow[ENUMERATOR_FIELD_IDENTITY], System.Globalization.CultureInfo.InvariantCulture);
if (isKatmaiAndNotMatrix)
{
this.providerName = Convert.ToString(dataRow[ENUMERATOR_FIELD_PROVIDER_NAME], System.Globalization.CultureInfo.InvariantCulture);
this.isEncryptionByProvider = !string.IsNullOrEmpty(providerName);
}
}
else
{
this.CredentialName = string.Empty;
this.CredentialIdentity = string.Empty;
this.providerName = string.Empty;
this.isEncryptionByProvider = false;
}
this.SecurePassword = new SecureString();
this.SecurePasswordConfirm = new SecureString();
this.PasswordWasChanged = false;
}
/// <summary>
/// SendDataToServer
///
/// here we talk with server via smo and do the actual data changing
/// </summary>
public void SendDataToServer()
{
if (this.IsPropertiesMode == true)
{
SendToServerAlterCredential();
}
else
{
SendToServerCreateCredential();
}
}
/// <summary>
/// create credential - create mode
/// </summary>
private void SendToServerCreateCredential()
{
Microsoft.SqlServer.Management.Smo.Credential smoCredential = new Microsoft.SqlServer.Management.Smo.Credential (
this.Context.Server,
this.CredentialName);
if (this.isEncryptionByProvider)
{
smoCredential.MappedClassType = MappedClassType.CryptographicProvider;
smoCredential.ProviderName = this.providerName;
}
smoCredential.Create(this.CredentialIdentity, this.SecurePassword.ToString());
GC.Collect(); // this.SecurePassword.ToString() just created an immutable string that lives in memory
}
/// <summary>
/// alter credential - properties mode
/// </summary>
private void SendToServerAlterCredential()
{
Microsoft.SqlServer.Management.Smo.Credential smoCredential = this.Context.Server.Credentials[this.CredentialName];
if (smoCredential != null)
{
if (this.PasswordWasChanged == false)
{
if (smoCredential.Identity != this.CredentialIdentity)
{
smoCredential.Alter(this.CredentialIdentity);
}
}
else
{
smoCredential.Alter(this.CredentialIdentity, this.SecurePassword.ToString());
GC.Collect(); // this.SecurePassword.ToString() just created an immutable string that lives in memory
}
}
else
{
throw new Exception("SRError.CredentialNoLongerExists");
}
}
#endregion
#region IDisposable Members
public void Dispose()
{
this.SecurePassword.Dispose();
this.SecurePasswordConfirm.Dispose();
}
#endregion
}
}

View File

@@ -0,0 +1,116 @@
//
// 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.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Admin;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.Security.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.Security
{
/// <summary>
/// Main class for Security Service functionality
/// </summary>
public sealed class SecurityService : IDisposable
{
private bool disposed;
private ConnectionService connectionService = null;
private static readonly Lazy<SecurityService> instance = new Lazy<SecurityService>(() => new SecurityService());
/// <summary>
/// Construct a new SecurityService instance with default parameters
/// </summary>
public SecurityService()
{
}
/// <summary>
/// Gets the singleton instance object
/// </summary>
public static SecurityService Instance
{
get { return instance.Value; }
}
/// <summary>
/// Internal for testing purposes only
/// </summary>
internal ConnectionService ConnectionServiceInstance
{
get
{
if (connectionService == null)
{
connectionService = ConnectionService.Instance;
}
return connectionService;
}
set
{
connectionService = value;
}
}
/// <summary>
/// Service host object for sending/receiving requests/events.
/// Internal for testing purposes.
/// </summary>
internal IProtocolEndpoint ServiceHost
{
get;
set;
}
/// <summary>
/// Initializes the Security Service instance
/// </summary>
public void InitializeService(ServiceHost serviceHost)
{
this.ServiceHost = serviceHost;
this.ServiceHost.SetRequestHandler(CreateCredentialRequest.Type, HandleCreateCredentialRequest);
}
/// <summary>
/// Handle request to create a credential
/// </summary>
internal async Task HandleCreateCredentialRequest(CreateCredentialParams parameters, RequestContext<CreateCredentialResult> requestContext)
{
try
{
var result = new CreateCredentialResult();
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(
parameters.OwnerUri,
out connInfo);
CDataContainer dataContainer = AdminService.CreateDataContainer(connInfo, databaseExists: true);
Credential credential = new Credential(dataContainer);
await requestContext.SendResult(result);
}
catch (Exception e)
{
await requestContext.SendError(e);
}
}
/// <summary>
/// Disposes the service
/// </summary>
public void Dispose()
{
if (!disposed)
{
disposed = true;
}
}
}
}

View File

@@ -0,0 +1,15 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.Utility
{
public class ReturnResult
{
public bool Succeeded { get; set; }
public string ErrorMessage { get; set; }
}
}

View File

@@ -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 System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.XEvent;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Agent;
using Microsoft.SqlTools.ServiceLayer.Agent.Contracts;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
using Microsoft.SqlTools.ServiceLayer.Profiler;
using Microsoft.SqlTools.ServiceLayer.Profiler.Contracts;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Moq;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
{
public class AgentAlertTests
{
/// <summary>
/// Verify default agent/alerts handlers
/// </summary>
[Fact]
public async Task TestHandleAgentAlertsRequest()
{
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
{
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
var requestParams = new AgentAlertsParams()
{
OwnerUri = connectionResult.ConnectionInfo.OwnerUri
};
var requestContext = new Mock<RequestContext<AgentAlertsResult>>();
AgentService service = new AgentService();
await service.HandleAgentAlertsRequest(requestParams, requestContext.Object);
}
}
/// <summary>
/// Verify the default "create agent alert" request handler with valid parameters
/// </summary>
[Fact]
public async Task TestHandleCreateAgentAlertsRequest()
{
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
{
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
var requestParams = new CreateAgentAlertParams
{
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
Alert = new AgentAlertInfo()
{
JobName = "Test Job Name"
}
};
var requestContext = new Mock<RequestContext<CreateAgentAlertResult>>();
AgentService service = new AgentService();
await service.HandleCreateAgentAlertRequest(requestParams, requestContext.Object);
// var agentAlerts = await AgentTestUtils.GetAgentAlerts(connectionResult.ConnectionInfo.OwnerUri);
// if (agentAlerts != null && agentAlerts.Length > 0)
// {
// foreach (var agentAlert in agentAlerts)
// {
// if (agentAlert.JobName.Equals(alert.JobName))
// {
// await service.HandleDeleteAgentAlertRequest(new DeleteAgentAlertParams()
// {
// OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
// Alert = alert
// }, deleteContext.Object);
// }
// }
// }
}
}
/// <summary>
/// Verify the default "update agent alert" request handler with valid parameters
/// </summary>
[Fact]
public async Task TestHandleUpdateAgentAlertsRequest()
{
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
{
var createContext = new Mock<RequestContext<CreateAgentAlertResult>>();
var updateContext = new Mock<RequestContext<UpdateAgentAlertResult>>();
var deleteContext = new Mock<RequestContext<DeleteAgentAlertResult>>();
var service = new AgentService();
var alert = new AgentAlertInfo()
{
JobName = "test_update_job",
AlertType = AlertType.SqlServerEvent,
Severity = 1
};
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
await service.HandleDeleteAgentAlertRequest(new DeleteAgentAlertParams()
{
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
Alert = alert
}, deleteContext.Object);
await service.HandleCreateAgentAlertRequest(new CreateAgentAlertParams
{
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
Alert = alert
}, createContext.Object);
await service.HandleUpdateAgentAlertRequest(new UpdateAgentAlertParams()
{
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
Alert = alert
}, updateContext.Object);
await service.HandleDeleteAgentAlertRequest(new DeleteAgentAlertParams()
{
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
Alert = alert
}, deleteContext.Object);
createContext.VerifyAll();
updateContext.VerifyAll();
deleteContext.VerifyAll();
}
}
}
}

View File

@@ -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.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Agent;
using Microsoft.SqlTools.ServiceLayer.Agent.Contracts;
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Moq;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
{
public class AgentOperatorTests
{
/// <summary>
/// Verify the default "update agent alert" request handler with valid parameters
/// </summary>
[Fact]
public async Task TestHandleUpdateAgentOperatorRequest()
{
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
{
var createContext = new Mock<RequestContext<CreateAgentOperatorResult>>();
var service = new AgentService();
var operatorInfo = new AgentOperatorInfo()
{
Id = 10,
Name = "Joe DBA",
EmailAddress = "test@aol.com"
};
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
await service.HandleCreateAgentOperatorRequest(new CreateAgentOperatorParams
{
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
Operator = operatorInfo
}, createContext.Object);
createContext.VerifyAll();
}
}
}
}

View File

@@ -0,0 +1,79 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Agent;
using Microsoft.SqlTools.ServiceLayer.Agent.Contracts;
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Moq;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
{
public class AgentProxyTests
{
/// <summary>
/// Verify the default "update agent alert" request handler with valid parameters
/// </summary>
[Fact]
public async Task TestHandleUpdateAgentProxyRequest()
{
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
{
var createContext = new Mock<RequestContext<CreateAgentProxyResult>>();
var updateContext = new Mock<RequestContext<UpdateAgentProxyResult>>();
var deleteContext = new Mock<RequestContext<DeleteAgentProxyResult>>();
var service = new AgentService();
var proxy = new AgentProxyInfo()
{
Id = 10,
AccountName = "Test Proxy 2",
CredentialName = "User",
Description = "",
IsEnabled = true
};
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
await service.HandleDeleteAgentProxyRequest(new DeleteAgentProxyParams()
{
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
Proxy = proxy
}, deleteContext.Object);
deleteContext.VerifyAll();
await service.HandleCreateAgentProxyRequest(new CreateAgentProxyParams
{
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
Proxy = proxy
}, createContext.Object);
createContext.VerifyAll();
string originalProxyName = proxy.AccountName;
proxy.AccountName = proxy.AccountName + " Updated";
await service.HandleUpdateAgentProxyRequest(new UpdateAgentProxyParams()
{
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
OriginalProxyName = originalProxyName,
Proxy = proxy
}, updateContext.Object);
updateContext.VerifyAll();
await service.HandleDeleteAgentProxyRequest(new DeleteAgentProxyParams()
{
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
Proxy = proxy
}, deleteContext.Object);
deleteContext.VerifyAll();
}
}
}
}

View File

@@ -46,7 +46,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
AgentService service = new AgentService();
await service.HandleAgentJobsRequest(requestParams, requestContext.Object);
requestContext.VerifyAll();
}
}
}
/// <summary>

View File

@@ -0,0 +1,34 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Agent;
using Microsoft.SqlTools.ServiceLayer.Agent.Contracts;
using Moq;
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
{
public static class AgentTestUtils
{
internal static async Task<AgentAlertInfo[]> GetAgentAlerts(string connectionUri)
{
var requestParams = new AgentAlertsParams()
{
OwnerUri = connectionUri
};
var requestContext = new Mock<RequestContext<AgentAlertsResult>>();
AgentAlertInfo[] agentAlerts = null;
requestContext.Setup(x => x.SendResult(It.IsAny<AgentAlertsResult>()))
.Callback<AgentAlertsResult>(r => agentAlerts = r.Alerts);
AgentService service = new AgentService();
await service.HandleAgentAlertsRequest(requestParams, requestContext.Object);
return agentAlerts;
}
}
}

View File

@@ -0,0 +1,54 @@
//
// 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;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
using Microsoft.SqlTools.ServiceLayer.Security;
using Microsoft.SqlTools.ServiceLayer.Security.Contracts;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
using Moq;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
using static Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility.LiveConnectionHelper;
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Security
{
/// <summary>
/// Tests for the security service component
/// </summary>
public class SecuritygServiceTests
{
/// <summary>
/// Verify the script object request
/// </summary>
[Fact]
public async Task TestHandleCreateCredentialRequest()
{
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
{
var createContext = new Mock<RequestContext<CreateCredentialResult>>();
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
var service = new SecurityService();
var credential = new CredentialInfo()
{
};
await service.HandleCreateCredentialRequest(new CreateCredentialParams
{
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
Credential = credential
}, createContext.Object);
createContext.VerifyAll();
}
}
}
}