Agent/improve steps (#710)

* added stepInfo for editing jobs/steps

* added schedules to history requests

* added alerts and schedules to history

* formatting

* code review comments

* removed smo import

* changed and optimized histories, steps, alerts and operators
This commit is contained in:
Aditya Bist
2018-10-16 13:24:32 -07:00
committed by GitHub
parent 8fe3d06068
commit 0efed221ee
7 changed files with 180 additions and 93 deletions

View File

@@ -27,7 +27,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
/// </summary> /// </summary>
public class AgentService public class AgentService
{ {
private Dictionary<Guid, JobProperties> jobs = null;
private ConnectionService connectionService = null; private ConnectionService connectionService = null;
private static readonly Lazy<AgentService> instance = new Lazy<AgentService>(() => new AgentService()); private static readonly Lazy<AgentService> instance = new Lazy<AgentService>(() => new AgentService());
@@ -148,11 +147,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
var serverConnection = new ServerConnection(sqlConnection); var serverConnection = new ServerConnection(sqlConnection);
var fetcher = new JobFetcher(serverConnection); var fetcher = new JobFetcher(serverConnection);
var filter = new JobActivityFilter(); var filter = new JobActivityFilter();
this.jobs = fetcher.FetchJobs(filter); var jobs = fetcher.FetchJobs(filter);
var agentJobs = new List<AgentJobInfo>(); var agentJobs = new List<AgentJobInfo>();
if (this.jobs != null) if (jobs != null)
{ {
foreach (var job in this.jobs.Values) foreach (var job in jobs.Values)
{ {
agentJobs.Add(AgentUtilities.ConvertToAgentJobInfo(job)); agentJobs.Add(AgentUtilities.ConvertToAgentJobInfo(job));
} }
@@ -186,42 +185,63 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
out connInfo); out connInfo);
if (connInfo != null) if (connInfo != null)
{ {
ConnectionServiceInstance.TryFindConnection(parameters.OwnerUri, out connInfo);
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true); CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
var jobs = dataContainer.Server.JobServer.Jobs; var jobs = dataContainer.Server.JobServer.Jobs;
Tuple<SqlConnectionInfo, DataTable, ServerConnection> tuple = CreateSqlConnection(connInfo, parameters.JobId); Tuple<SqlConnectionInfo, DataTable, ServerConnection> tuple = CreateSqlConnection(connInfo, parameters.JobId);
SqlConnectionInfo sqlConnInfo = tuple.Item1; SqlConnectionInfo sqlConnInfo = tuple.Item1;
DataTable dt = tuple.Item2; DataTable dt = tuple.Item2;
ServerConnection connection = tuple.Item3; ServerConnection connection = tuple.Item3;
// Send Steps, Alerts and Schedules with job history in background
// Add steps to the job if any
JobStepCollection steps = jobs[parameters.JobName].JobSteps;
var jobSteps = new List<AgentJobStepInfo>();
foreach (JobStep step in steps)
{
jobSteps.Add(AgentUtilities.ConvertToAgentJobStepInfo(step, parameters.JobId, parameters.JobName));
}
result.Steps = jobSteps.ToArray();
// Add schedules to the job if any
JobScheduleCollection schedules = jobs[parameters.JobName].JobSchedules;
var jobSchedules = new List<AgentScheduleInfo>();
foreach (JobSchedule schedule in schedules)
{
jobSchedules.Add(AgentUtilities.ConvertToAgentScheduleInfo(schedule));
}
result.Schedules = jobSchedules.ToArray();
// Alerts
AlertCollection alerts = dataContainer.Server.JobServer.Alerts;
var jobAlerts = new List<Alert>();
foreach (Alert alert in alerts)
{
if (alert.JobName == parameters.JobName)
{
jobAlerts.Add(alert);
}
}
result.Alerts = AgentUtilities.ConvertToAgentAlertInfo(jobAlerts);
// Add histories
int count = dt.Rows.Count; int count = dt.Rows.Count;
List<AgentJobHistoryInfo> jobHistories = new List<AgentJobHistoryInfo>(); List<AgentJobHistoryInfo> jobHistories = new List<AgentJobHistoryInfo>();
if (count > 0) if (count > 0)
{ {
var job = dt.Rows[0]; var job = dt.Rows[0];
string jobName = Convert.ToString(job[AgentUtilities.UrnJobName], System.Globalization.CultureInfo.InvariantCulture); Guid jobId = (Guid)job[AgentUtilities.UrnJobId];
Guid jobId = (Guid) job[AgentUtilities.UrnJobId];
int runStatus = Convert.ToInt32(job[AgentUtilities.UrnRunStatus], System.Globalization.CultureInfo.InvariantCulture); int runStatus = Convert.ToInt32(job[AgentUtilities.UrnRunStatus], System.Globalization.CultureInfo.InvariantCulture);
var t = new LogSourceJobHistory(jobName, sqlConnInfo, null, runStatus, jobId, null); var t = new LogSourceJobHistory(parameters.JobName, sqlConnInfo, null, runStatus, jobId, null);
var tlog = t as ILogSource; var tlog = t as ILogSource;
tlog.Initialize(); tlog.Initialize();
var logEntries = t.LogEntries; var logEntries = t.LogEntries;
// Send Steps, Alerts and Schedules with job history in background // Finally add the job histories
JobStepCollection steps = jobs[jobName].JobSteps; jobHistories = AgentUtilities.ConvertToAgentJobHistoryInfo(logEntries, job, steps);
JobScheduleCollection schedules = jobs[jobName].JobSchedules; result.Histories = jobHistories.ToArray();
List<Alert> alerts = new List<Alert>(); result.Success = true;
foreach (Alert alert in dataContainer.Server.JobServer.Alerts)
{
if (alert.JobID == jobId && alert.JobName == jobName)
{
alerts.Add(alert);
}
}
jobHistories = AgentUtilities.ConvertToAgentJobHistoryInfo(logEntries, job, steps, schedules, alerts);
tlog.CloseReader(); tlog.CloseReader();
} }
result.Jobs = jobHistories.ToArray();
result.Success = true;
connection.Disconnect(); connection.Disconnect();
await requestContext.SendResult(result); await requestContext.SendResult(result);
} }

View File

@@ -40,8 +40,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
{ {
Name = job.Name, Name = job.Name,
Description = job.Description, Description = job.Description,
CurrentExecutionStatus = job.CurrentExecutionStatus, CurrentExecutionStatus = (Contracts.JobExecutionStatus) job.CurrentExecutionStatus,
LastRunOutcome = job.LastRunOutcome, LastRunOutcome = (Contracts.CompletionResult) job.LastRunOutcome,
CurrentExecutionStep = job.CurrentExecutionStep, CurrentExecutionStep = job.CurrentExecutionStep,
Enabled = job.Enabled, Enabled = job.Enabled,
HasTarget = job.HasTarget, HasTarget = job.HasTarget,
@@ -57,7 +57,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
}; };
} }
internal static AgentJobStep ConvertToAgentJobStepInfo(JobStep step, LogSourceJobHistory.LogEntryJobHistory logEntry, string jobId) internal static AgentJobStep ConvertToAgentJobStep(JobStep step, LogSourceJobHistory.LogEntryJobHistory logEntry, string jobId)
{ {
AgentJobStepInfo stepInfo = new AgentJobStepInfo(); AgentJobStepInfo stepInfo = new AgentJobStepInfo();
stepInfo.JobId = jobId; stepInfo.JobId = jobId;
@@ -88,6 +88,30 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
return jobStep; return jobStep;
} }
internal static AgentJobStepInfo ConvertToAgentJobStepInfo(JobStep step, string jobId, string jobName)
{
AgentJobStepInfo stepInfo = new AgentJobStepInfo();
stepInfo.JobId = jobId;
stepInfo.JobName = jobName;
stepInfo.StepName = step.Name;
stepInfo.SubSystem = step.SubSystem.ToString();
stepInfo.Id = step.ID;
stepInfo.FailureAction = step.OnFailAction.ToString();
stepInfo.SuccessAction = step.OnSuccessAction.ToString();
stepInfo.FailStepId = step.OnFailStep;
stepInfo.SuccessStepId = step.OnSuccessStep;
stepInfo.Command = step.Command;
stepInfo.CommandExecutionSuccessCode = step.CommandExecutionSuccessCode;
stepInfo.DatabaseName = step.DatabaseName;
stepInfo.DatabaseUserName = step.DatabaseUserName;
stepInfo.Server = step.Server;
stepInfo.OutputFileName = step.OutputFileName;
stepInfo.RetryAttempts = step.RetryAttempts;
stepInfo.RetryInterval = step.RetryInterval;
stepInfo.ProxyName = step.ProxyName;
return stepInfo;
}
internal static AgentScheduleInfo ConvertToAgentScheduleInfo(JobSchedule schedule) internal static AgentScheduleInfo ConvertToAgentScheduleInfo(JobSchedule schedule)
{ {
AgentScheduleInfo scheduleInfo = new AgentScheduleInfo(); AgentScheduleInfo scheduleInfo = new AgentScheduleInfo();
@@ -113,7 +137,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
return scheduleInfo; return scheduleInfo;
} }
internal static AgentAlertInfo ConvertToAgentAlertInfo(Alert alert) internal static AgentAlertInfo[] ConvertToAgentAlertInfo(List<Alert> alerts)
{
var result = new List<AgentAlertInfo>();
foreach(Alert alert in alerts)
{ {
AgentAlertInfo alertInfo = new AgentAlertInfo(); AgentAlertInfo alertInfo = new AgentAlertInfo();
alertInfo.Id = alert.ID; alertInfo.Id = alert.ID;
@@ -139,11 +166,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
alertInfo.AlertType = (Contracts.AlertType) alert.AlertType; alertInfo.AlertType = (Contracts.AlertType) alert.AlertType;
alertInfo.WmiEventNamespace = alert.WmiEventNamespace; alertInfo.WmiEventNamespace = alert.WmiEventNamespace;
alertInfo.WmiEventQuery = alert.WmiEventQuery; alertInfo.WmiEventQuery = alert.WmiEventQuery;
return alertInfo; result.Add(alertInfo);
}
return result.ToArray();
} }
public static List<AgentJobHistoryInfo> ConvertToAgentJobHistoryInfo(List<ILogEntry> logEntries, public static List<AgentJobHistoryInfo> ConvertToAgentJobHistoryInfo(List<ILogEntry> logEntries, DataRow jobRow, JobStepCollection steps)
DataRow jobRow, JobStepCollection steps, JobScheduleCollection schedules, List<Alert> alerts)
{ {
List<AgentJobHistoryInfo> jobs = new List<AgentJobHistoryInfo>(); List<AgentJobHistoryInfo> jobs = new List<AgentJobHistoryInfo>();
// get all the values for a job history // get all the values for a job history
@@ -174,26 +202,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
foreach (JobStep step in steps) foreach (JobStep step in steps)
{ {
var jobId = jobRow[UrnJobId].ToString(); var jobId = jobRow[UrnJobId].ToString();
jobSteps.Add(AgentUtilities.ConvertToAgentJobStepInfo(step, logEntry, jobId)); jobSteps.Add(AgentUtilities.ConvertToAgentJobStep(step, logEntry, jobId));
} }
jobHistoryInfo.Steps = jobSteps.ToArray(); jobHistoryInfo.Steps = jobSteps.ToArray();
// Add schedules to the job if any
var jobSchedules = new List<AgentScheduleInfo>();
foreach (JobSchedule schedule in schedules)
{
jobSchedules.Add(AgentUtilities.ConvertToAgentScheduleInfo(schedule));
}
jobHistoryInfo.Schedules = jobSchedules.ToArray();
// Add alerts to the job if any
var jobAlerts = new List<AgentAlertInfo>();
foreach (Alert alert in alerts)
{
jobAlerts.Add(AgentUtilities.ConvertToAgentAlertInfo(alert));
}
jobHistoryInfo.Alerts = jobAlerts.ToArray();
jobs.Add(jobHistoryInfo); jobs.Add(jobHistoryInfo);
} }
return jobs; return jobs;

View File

@@ -8,6 +8,25 @@ using Microsoft.SqlTools.ServiceLayer.Agent;
namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts
{ {
public enum JobCompletionActionCondition
{
Never = 0,
OnSuccess = 1,
OnFailure = 2,
Always = 3
}
public enum JobExecutionStatus
{
Executing = 1,
WaitingForWorkerThread = 2,
BetweenRetries = 3,
Idle = 4,
Suspended = 5,
WaitingForStepToFinish = 6,
PerformingCompletionAction = 7
}
/// <summary> /// <summary>
/// a class for storing various properties of agent jobs, /// a class for storing various properties of agent jobs,
/// used by the Job Activity Monitor /// used by the Job Activity Monitor
@@ -17,8 +36,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts
public string Name { get; set; } public string Name { get; set; }
public string Owner { get; set; } public string Owner { get; set; }
public string Description { get; set; } public string Description { get; set; }
public int CurrentExecutionStatus { get; set; } public JobExecutionStatus CurrentExecutionStatus { get; set; }
public int LastRunOutcome { get; set; } public CompletionResult LastRunOutcome { get; set; }
public string CurrentExecutionStep { get; set; } public string CurrentExecutionStep { get; set; }
public bool Enabled { get; set; } public bool Enabled { get; set; }
public bool HasTarget { get; set; } public bool HasTarget { get; set; }
@@ -31,5 +50,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts
public string LastRun { get; set; } public string LastRun { get; set; }
public string NextRun { get; set; } public string NextRun { get; set; }
public string JobId { get; set; } public string JobId { get; set; }
public string OperatorToEmail { get; set; }
public string OperatorToPage { get; set; }
public int StartStepID { get; set; }
public JobCompletionActionCondition EmailLevel { get; set; }
public JobCompletionActionCondition PageLevel { get; set; }
public JobCompletionActionCondition EventLogLevel { get; set; }
public JobCompletionActionCondition DeleteLevel { get; set; }
public AgentJobStepInfo[] JobSteps { get; set; }
public AgentScheduleInfo[] JobSchedules { get; set; }
public AgentAlertInfo[] Alerts { get; set; }
} }
} }

View File

@@ -135,6 +135,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts
public string OwnerUri { get; set; } public string OwnerUri { get; set; }
public string JobId { get; set; } public string JobId { get; set; }
public string JobName { get; set; }
} }
/// <summary> /// <summary>
@@ -142,7 +143,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts
/// </summary> /// </summary>
public class AgentJobHistoryResult : ResultStatus public class AgentJobHistoryResult : ResultStatus
{ {
public AgentJobHistoryInfo[] Jobs { get; set; } public AgentJobHistoryInfo[] Histories { get; set; }
public AgentJobStepInfo[] Steps { get; set; }
public AgentScheduleInfo[] Schedules { get; set; }
public AgentAlertInfo[] Alerts { get; set ;}
} }
/// <summary> /// <summary>

View File

@@ -379,15 +379,24 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
"HasSchedule", "HasSchedule",
"HasStep", "HasStep",
"HasServer", "HasServer",
"LastRunDate",
"NextRunDate",
"LastRunOutcome", "LastRunOutcome",
"JobID", "JobID",
"Description" "Description",
"LastRunDate",
"NextRunDate",
"OperatorToEmail",
"OperatorToNetSend",
"OperatorToPage",
"OwnerLoginName",
"PageLevel",
"StartStepID",
"NetSendLevel",
"EventLogLevel",
"EmailLevel",
"DeleteLevel"
}; };
DataTable dt = enumerator.Process(connection, request); DataTable dt = enumerator.Process(connection, request);
int numJobs = dt.Rows.Count; int numJobs = dt.Rows.Count;
if (numJobs == 0) if (numJobs == 0)
{ {
@@ -395,7 +404,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
} }
Dictionary<Guid, JobProperties> foundJobs = new Dictionary<Guid, JobProperties>(numJobs); Dictionary<Guid, JobProperties> foundJobs = new Dictionary<Guid, JobProperties>(numJobs);
for (int i = 0; i < numJobs; ++i) for (int i = 0; i < numJobs; ++i)
{ {
JobProperties jobProperties = new JobProperties(dt.Rows[i]); JobProperties jobProperties = new JobProperties(dt.Rows[i]);

View File

@@ -16,7 +16,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
public class JobProperties public class JobProperties
{ {
private string name; private string name;
private int currentExecutionStatus; private int currentExecutionStatus;
private int lastRunOutcome; private int lastRunOutcome;
private string currentExecutionStep; private string currentExecutionStep;
@@ -32,6 +31,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
private DateTime nextRun; private DateTime nextRun;
private Guid jobId; private Guid jobId;
private string description; private string description;
private string owner;
private string operatorToEmail;
private string operatorToPage;
private int startStepID;
private int emailLevel;
private int pageLevel;
private int eventLogLevel;
private int deleteLevel;
private JobProperties() private JobProperties()
{ {
@@ -65,6 +72,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
this.lastRunOutcome = Convert.ToInt32(row["LastRunOutcome"], CultureInfo.InvariantCulture); this.lastRunOutcome = Convert.ToInt32(row["LastRunOutcome"], CultureInfo.InvariantCulture);
this.jobId = Guid.Parse(row["JobID"].ToString()); this.jobId = Guid.Parse(row["JobID"].ToString());
this.description = row["Description"].ToString(); this.description = row["Description"].ToString();
this.owner = row["OwnerLoginName"].ToString();
this.operatorToEmail = row["OperatorToEmail"].ToString();
this.operatorToPage = row["OperatorToPage"].ToString();
this.startStepID = Convert.ToInt32(row["StartStepID"], CultureInfo.InvariantCulture);
this.emailLevel = Convert.ToInt32(row["EmailLevel"], CultureInfo.InvariantCulture);
this.pageLevel = Convert.ToInt32(row["PageLevel"], CultureInfo.InvariantCulture);
this.eventLogLevel = Convert.ToInt32(row["EventLogLevel"], CultureInfo.InvariantCulture);
this.deleteLevel = Convert.ToInt32(row["DeleteLevel"], CultureInfo.InvariantCulture);
// for a job to be runnable, it must: // for a job to be runnable, it must:
// 1. have a target server // 1. have a target server

View File

@@ -43,10 +43,9 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
return new AgentJobInfo() return new AgentJobInfo()
{ {
Name = TestJobName, Name = TestJobName,
Owner = "sa",
Description = "Test job description", Description = "Test job description",
CurrentExecutionStatus = 1, CurrentExecutionStatus = JobExecutionStatus.Executing,
LastRunOutcome = 1, LastRunOutcome = CompletionResult.InProgress,
CurrentExecutionStep = "Step 1", CurrentExecutionStep = "Step 1",
Enabled = false, Enabled = false,
HasTarget = false, HasTarget = false,