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

@@ -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"
}
}