Add Agent Job and Job Step request handlers (#635)

* Stage changes

* Fix update job request handler

* WIP

* Additional agent handler updates

* Setup agent job step create test

* Fix Step update handler
This commit is contained in:
Karl Burtram
2018-06-13 10:02:25 -07:00
committed by GitHub
parent 7c1f78a678
commit 8dda34c95a
52 changed files with 705 additions and 619 deletions

View File

@@ -6,9 +6,11 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Smo.Agent;
@@ -326,7 +328,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
Tuple<bool, string> result = await ConfigureAgentJobStep(
parameters.OwnerUri,
parameters.Step,
ConfigAction.Create);
ConfigAction.Create,
RunType.RunNow);
await requestContext.SendResult(new CreateAgentJobStepResult()
{
@@ -337,14 +340,32 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
internal async Task HandleUpdateAgentJobStepRequest(UpdateAgentJobStepParams parameters, RequestContext<UpdateAgentJobStepResult> requestContext)
{
UpdateAgentJobStepResult result = new UpdateAgentJobStepResult();
await requestContext.SendResult(result);
Tuple<bool, string> result = await ConfigureAgentJobStep(
parameters.OwnerUri,
parameters.Step,
ConfigAction.Update,
RunType.RunNow);
await requestContext.SendResult(new UpdateAgentJobStepResult()
{
Success = result.Item1,
ErrorMessage = result.Item2
});
}
internal async Task HandleDeleteAgentJobStepRequest(DeleteAgentJobStepParams parameters, RequestContext<ResultStatus> requestContext)
{
ResultStatus result = new ResultStatus();
await requestContext.SendResult(result);
Tuple<bool, string> result = await ConfigureAgentJobStep(
parameters.OwnerUri,
parameters.Step,
ConfigAction.Drop,
RunType.RunNow);
await requestContext.SendResult(new ResultStatus()
{
Success = result.Item1,
ErrorMessage = result.Item2
});
}
internal async Task<Tuple<bool, string>> ConfigureAgentJob(
@@ -362,10 +383,19 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
ownerUri,
out connInfo);
CDataContainer dataContainer = AdminService.CreateDataContainer(connInfo, databaseExists: true);
CDataContainer dataContainer = CDataContainer.CreateDataContainer(
connInfo,
databaseExists: true);
XmlDocument jobDoc = CreateJobXmlDocument(
dataContainer.Server.Name.ToUpper(),
jobInfo.Name);
dataContainer.Init(jobDoc.InnerXml);
STParameters param = new STParameters(dataContainer.Document);
param.SetParam("job", string.Empty);
param.SetParam("jobid", jobInfo.JobId);
param.SetParam("jobid", string.Empty);
var jobData = new JobData(dataContainer, jobInfo);
using (JobActions jobActions = new JobActions(dataContainer, jobData, configAction))
@@ -386,13 +416,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
internal async Task<Tuple<bool, string>> ConfigureAgentJobStep(
string ownerUri,
AgentJobStepInfo stepInfo,
ConfigAction configAction)
ConfigAction configAction,
RunType runType)
{
return await Task<Tuple<bool, string>>.Run(() =>
{
try
{
if (string.IsNullOrWhiteSpace(stepInfo.JobId))
if (string.IsNullOrWhiteSpace(stepInfo.JobName))
{
return new Tuple<bool, string>(false, "JobId cannot be null");
}
@@ -402,17 +433,25 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
ownerUri,
out connInfo);
CDataContainer dataContainer = AdminService.CreateDataContainer(connInfo, databaseExists: true);
CDataContainer dataContainer = CDataContainer.CreateDataContainer(
connInfo,
databaseExists: true);
XmlDocument jobDoc = CreateJobXmlDocument(
dataContainer.Server.Name.ToUpper(),
stepInfo.JobName);
dataContainer.Init(jobDoc.InnerXml);
STParameters param = new STParameters(dataContainer.Document);
param.SetParam("job", string.Empty);
param.SetParam("jobid", stepInfo.JobId);
param.SetParam("script", stepInfo.Script);
param.SetParam("scriptName", stepInfo.ScriptName);
param.SetParam("jobid", string.Empty);
var jobData = new JobData(dataContainer);
using (var jobStep = new JobStepsActions(dataContainer, jobData))
using (var jobStep = new JobStepsActions(dataContainer, jobData, stepInfo, configAction))
{
jobStep.CreateJobStep();
var executionHandler = new ExecutonHandler(jobStep);
executionHandler.RunNow(runType, this);
}
return new Tuple<bool, string>(true, string.Empty);
@@ -425,6 +464,38 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
});
}
public XmlDocument CreateJobXmlDocument(string svrName, string jobName)
{
// XML element strings
const string XmlFormDescElementName = "formdescription";
const string XmlParamsElementName = "params";
const string XmlJobElementName = "job";
const string XmlUrnElementName = "urn";
const string UrnFormatStr = "Server[@Name='{0}']/JobServer[@Name='{0}']/Job[@Name='{1}']";
// Write out XML.
StringWriter textWriter = new StringWriter();
XmlTextWriter xmlWriter = new XmlTextWriter(textWriter);
xmlWriter.WriteStartElement(XmlFormDescElementName);
xmlWriter.WriteStartElement(XmlParamsElementName);
xmlWriter.WriteElementString(XmlJobElementName, jobName);
xmlWriter.WriteElementString(XmlUrnElementName, string.Format(UrnFormatStr, svrName, jobName));
xmlWriter.WriteEndElement();
xmlWriter.WriteEndElement();
xmlWriter.Close();
// Create an XML document.
XmlDocument doc = new XmlDocument();
XmlTextReader rdr = new XmlTextReader(new System.IO.StringReader(textWriter.ToString()));
rdr.MoveToContent();
doc.LoadXml(rdr.ReadOuterXml());
return doc;
}
#endregion // "Jobs Handlers"
#region "Alert Handlers"
@@ -446,7 +517,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
if (connInfo != null)
{
CDataContainer dataContainer = AdminService.CreateDataContainer(connInfo, databaseExists: true);
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
AlertCollection alerts = dataContainer.Server.JobServer.Alerts;
}
@@ -502,7 +573,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
{
if (connInfo != null && ValidateAgentAlertInfo(alert))
{
CDataContainer dataContainer = AdminService.CreateDataContainer(connInfo, databaseExists: true);
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
STParameters param = new STParameters(dataContainer.Document);
param.SetParam("alert", alert.JobName);
@@ -531,7 +602,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
AgentAlertInfo alert = parameters.Alert;
if (connInfo != null && ValidateAgentAlertInfo(alert))
{
CDataContainer dataContainer = AdminService.CreateDataContainer(connInfo, databaseExists: true);
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
STParameters param = new STParameters(dataContainer.Document);
param.SetParam("alert", alert.JobName);
@@ -572,7 +643,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
out connInfo);
AgentOperatorInfo operatorInfo = parameters.Operator;
CDataContainer dataContainer = AdminService.CreateDataContainer(connInfo, databaseExists: true);
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
STParameters param = new STParameters(dataContainer.Document);
param.SetParam("operator", operatorInfo.Name);
@@ -662,7 +733,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
ownerUri,
out connInfo);
CDataContainer dataContainer = AdminService.CreateDataContainer(connInfo, databaseExists: true);
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
STParameters param = new STParameters(dataContainer.Document);
param.SetParam("proxyaccount", accountName);

View File

@@ -13,6 +13,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts
{
public string JobId { get; set; }
public string JobName { get; set; }
public string Script { get; set; }
public string ScriptName { get; set; }

View File

@@ -49,13 +49,23 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
protected override bool DoPreProcessExecution(RunType runType, out ExecutionMode executionResult)
{
base.DoPreProcessExecution(runType, out executionResult);
this.data.ApplyChanges(creating: true);
if (!IsScripting(runType))
{
this.DataContainer.SqlDialogSubject = this.data.Job;
}
if (this.configAction == ConfigAction.Drop)
{
if (this.data.Job != null)
{
this.data.Job.DropIfExists();
}
}
else
{
this.data.ApplyChanges(creating: this.configAction == ConfigAction.Create);
if (!IsScripting(runType))
{
this.DataContainer.SqlDialogSubject = this.data.Job;
}
}
return false;
}
}
}
}

View File

@@ -16,6 +16,7 @@ using Microsoft.SqlServer.Management.Smo.Agent;
using Microsoft.SqlServer.Management.Diagnostics;
using Microsoft.SqlTools.ServiceLayer.Admin;
using SMO = Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.Management;
namespace Microsoft.SqlTools.ServiceLayer.Agent
{

View File

@@ -14,6 +14,7 @@ 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;
using SMO = Microsoft.SqlServer.Management.Smo;
namespace Microsoft.SqlTools.ServiceLayer.Agent
@@ -1139,9 +1140,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
else
{
// just lookup the original object
STParameters parameters = new STParameters(this.context.Document);
string urn = string.Empty;
job = this.context.Server.GetSmoObject(this.urn) as Job;
job = this.Job;
}
if (!this.IsReadOnly)

View File

@@ -15,8 +15,10 @@ using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Smo.Agent;
using Microsoft.SqlServer.Management.Diagnostics;
using Microsoft.SqlTools.ServiceLayer.Admin;
using Microsoft.SqlTools.ServiceLayer.Management;
using SMO = Microsoft.SqlServer.Management.Smo;
namespace Microsoft.SqlTools.ServiceLayer.Agent
{
internal class JobSchedulesData

View File

@@ -17,6 +17,7 @@ using System.Text;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Smo.Agent;
using Microsoft.SqlTools.ServiceLayer.Admin;
using Microsoft.SqlTools.ServiceLayer.Management;
namespace Microsoft.SqlTools.ServiceLayer.Agent
{

View File

@@ -15,7 +15,6 @@ using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Smo.Agent;
using SMO = Microsoft.SqlServer.Management.Smo;
namespace Microsoft.SqlTools.ServiceLayer.Agent
{
internal class JobStepData
@@ -45,26 +44,32 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
/// Original name of this step
/// </summary>
string originalName;
/// <summary>
/// Current name
/// </summary>
string currentName;
/// <summary>
/// Current step id
/// </summary>
int id;
/// <summary>
/// Original step id
/// </summary>
int originalId;
/// <summary>
/// Subsystem that will execute this step
/// </summary>
AgentSubSystem subSystem;
/// <summary>
/// action to take if the step fails
/// </summary>
StepCompletionAction failureAction;
/// <summary>
/// Action to take if the step succeeds
/// </summary>
@@ -175,6 +180,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
return jobStep;
}
}
/// <summary>
/// Server version
/// </summary>
@@ -185,6 +191,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
return this.parent.Version;
}
}
/// <summary>
/// indicates whether the job exists on the server
/// </summary>
@@ -237,7 +244,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
#endregion
#region Properties for Job Step
public String Name
public string Name
{
get
{
@@ -248,7 +255,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
this.currentName = value.Trim();
}
}
public String Command
public string Command
{
get
{
@@ -274,7 +282,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
this.commandExecutionSuccessCode = value;
}
}
public String DatabaseName
public string DatabaseName
{
get
{
@@ -287,7 +295,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
this.databaseName = value;
}
}
public String DatabaseUserName
public string DatabaseUserName
{
get
{
@@ -300,7 +308,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
this.databaseUserName = value;
}
}
public String Server
public string Server
{
get
{
@@ -706,7 +714,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
this.successStep = null;
this.successStepId = -1;
this.priority = OSRunPriority.Normal;
this.outputFileName = String.Empty;
this.outputFileName = string.Empty;
this.appendToLogFile = false;
this.appendToStepHist = false;
this.writeLogToTable = false;
@@ -1027,15 +1035,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
this.failStep = step;
}
#endregion
}
}

View File

@@ -13,6 +13,7 @@ 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.Management;
namespace Microsoft.SqlTools.ServiceLayer.Agent
{

View File

@@ -4,168 +4,88 @@
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
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.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>
/// Summary description for JobSteps.
/// JobStepsActions
/// </summary>
internal class JobStepsActions : ManagementActionBase
{
private bool validated = true;
private JobStepData data;
private JobData jobData;
private JobData data;
public JobStepsActions(CDataContainer dataContainer, JobData data)
public JobStepsActions(
CDataContainer dataContainer,
JobData jobData,
AgentJobStepInfo stepInfo,
ConfigAction configAction)
{
this.DataContainer = dataContainer;
this.data = data;
this.jobData = jobData;
if (configAction == ConfigAction.Update)
{
JobStep jobStep = GetJobStep(this.jobData, stepInfo.StepName);
this.data = new JobStepData(jobStep, jobData.JobSteps);
}
else
{
this.data = new JobStepData(jobData.JobSteps);
}
// load properties from AgentJobStepInfo
this.data.ID = stepInfo.Id;
this.data.Name = stepInfo.StepName;
this.data.Command = stepInfo.Script;
}
#region ISupportValidation
/// <summary>
/// Validate the Job step data.
/// This is limited to two checks
/// 1. See if all steps are reachable
/// 2. If we are editing rather than creating check to see if the last steps completion
/// action will change from GoToNext to QuitWithSuccess.
/// </summary>
/// <returns>true if the checks passed, or the user has ok the warning</returns>
public bool Validate()
protected override bool DoPreProcessExecution(RunType runType, out ExecutionMode executionResult)
{
// Only validate once after the user has made changes. Validate() is called when the
// user navigates away from the JobSteps page, and if they navigate back and then out
// again they'd be prompted twice. This annoys our users.
if (this.validated)
base.DoPreProcessExecution(runType, out executionResult);
// Make sure the job step name is not blank.
if (this.data.Name == null || this.data.Name.Length == 0)
{
return true;
throw new Exception(SR.JobStepNameCannotBeBlank);
}
bool valid = true;
// Get the unreachable steps
List<JobStepData> unreachableSteps = this.data.JobSteps.FindUnreachableJobSteps();
// see if the last step success completion action will change
bool lastStepWillChange = this.data.JobSteps.CheckIfLastStepCompletionActionWillChange();
// warning message
StringBuilder warningMessage = new StringBuilder();
// if there are unreachable steps, add the warning and each problematic step
if (unreachableSteps.Count > 0)
// Check to make sure that the user has not entered a job step name that already exists.
for (int stepIndex = 0; stepIndex < this.data.Parent.Steps.Count; ++stepIndex)
{
warningMessage.AppendLine("JobSR.UnreachableStepHeader");
foreach (JobStepData jobStep in unreachableSteps)
// don't compare if the id's are the same.
if (data.ID != ((JobStepData)this.data.Parent.Steps[stepIndex]).ID && data.Name == ((JobStepData)this.data.Parent.Steps[stepIndex]).Name)
{
warningMessage.AppendLine("JobSR.UnreachableStepFormat(jobStep.ID, jobStep.Name)");
// Throw an error if the job step name already exists
throw new Exception(SR.JobStepNameAlreadyExists(this.data.Name));
}
warningMessage.AppendLine(string.Empty);
}
// add a warning if the last step will change
if (lastStepWillChange)
{
warningMessage.AppendLine("JobSR.LastStepSuccessWillChange");
warningMessage.AppendLine(string.Empty);
if (runType == RunType.RunNow)
{
this.data.ApplyChanges(this.jobData.Job);
}
// if anything was wrong tell the user and see if they are ok with it
if (warningMessage.Length > 0)
{
warningMessage.Append("JobSR.AreYouSure");
}
this.validated = valid;
return valid;
// regular execution always takes place
return true;
}
#endregion
// private void PopulateGrid(JobStepsData steps)
// {
// for (int i = 0; i < steps.Steps.Count; i++)
// {
// JobStepData step = steps.Steps[i] as JobStepData;
// if (step != null)
// {
// // add rows to the grid
// GridCellCollection row = new GridCellCollection();
// GridCell cell;
// // id
// cell = new GridCell(step.ID.ToString(CultureInfo.InvariantCulture));
// row.Add(cell);
// // step name
// cell = new GridCell(step.Name);
// row.Add(cell);
// // subsystem
// cell = new GridCell(JobStepSubSystems.LookupFriendlyName(step.SubSystem));
// row.Add(cell);
// // on success
// cell = new GridCell(GetFriendlyNameForAction(step.SuccessAction, step.SuccessStep));
// row.Add(cell);
// // on failure
// cell = new GridCell(GetFriendlyNameForAction(step.FailureAction, step.FailStep));
// row.Add(cell);
// this.jobStepList.AddRow(row);
// }
// }
// }
/// <summary>
/// Convert an action and it's target step to a localizable user friendly name
/// </summary>
/// <param name="action"></param>
/// <param name="targetStep"></param>
/// <returns></returns>
// private static string GetFriendlyNameForAction(StepCompletionAction action, JobStepData targetStep)
// {
// String friendlyName = String.Empty;
// // switch (action)
// // {
// // case StepCompletionAction.GoToNextStep:
// // friendlyName = JobSR.GotoNextStep;
// // break;
// // case StepCompletionAction.QuitWithFailure:
// // friendlyName = JobSR.QuitWithFailure;
// // break;
// // case StepCompletionAction.QuitWithSuccess:
// // friendlyName = JobSR.QuitWithSuccess;
// // break;
// // case StepCompletionAction.GoToStep:
// // STrace.Assert(targetStep != null, "Action type is goto step, but the target step is null");
// // if (targetStep != null)
// // {
// // friendlyName = JobSR.GotoStep(targetStep.ID, targetStep.Name);
// // }
// // break;
// // default:
// // STrace.Assert(false, "Unknown jobstep completion action");
// // break;
// // }
// return friendlyName;
// }
public void CreateJobStep()
private JobStep GetJobStep(JobData jobData, string stepName)
{
//JobStepData data = new JobStepData(this.data.JobSteps);
JobStepData data = this.data.JobSteps.Steps[0] as JobStepData;
JobStepPropertySheet jsProp = new JobStepPropertySheet(this.DataContainer, data);
jsProp.Init();
jsProp.Create();
JobStep jobStep = null;
if (jobData.Job != null)
{
const string UrnFormatStr = "Server[@Name='{0}']/JobServer[@Name='{0}']/Job[@Name='{1}']/Step[@Name='{2}']";
string serverName = this.DataContainer.Server.Name.ToUpper();
string urn = string.Format(UrnFormatStr, serverName, jobData.Job.Name, stepName);
jobStep = jobData.Job.Parent.Parent.GetSmoObject(urn) as JobStep;
}
return jobStep;
}
}
}

View File

@@ -14,6 +14,7 @@ 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.Management;
using SMO = Microsoft.SqlServer.Management.Smo;
namespace Microsoft.SqlTools.ServiceLayer.Agent

View File

@@ -9,18 +9,14 @@ using System.Collections;
using System.ComponentModel;
using Microsoft.SqlServer.Management.Smo.Agent;
using Microsoft.SqlTools.ServiceLayer.Admin;
using Microsoft.SqlTools.ServiceLayer.Management;
namespace Microsoft.SqlServer.Management.SqlManagerUI
{
/// <summary>
/// Summary description for JobsRefrencingScheduleForm.
/// </summary>
#if DEBUG || EXPOSE_MANAGED_INTERNALS
public
#else
internal
#endif
class JobsReferencingScheduleForm
public class JobsReferencingScheduleForm
{
#region UI Variables