/// <inheritdoc/> public async Task OnJobCreatingAsync(IJobService manager, JobInfoModel job) { var jobDeviceId = GetJobDeviceId(job); try { var deviceTwin = await _ioTHubTwinServices.GetAsync(jobDeviceId); if (deviceTwin == null) { deviceTwin = new DeviceTwinModel { Id = jobDeviceId }; await _ioTHubTwinServices.CreateAsync(deviceTwin); } var cs = await _ioTHubTwinServices.GetConnectionStringAsync(deviceTwin.Id); if (job.JobConfiguration?.Type == JTokenType.Object && job.JobConfiguration is JObject o) { var connectionString = JToken.FromObject(cs.ToString()); if (o.ContainsKey(TwinProperties.ConnectionString)) { o[TwinProperties.ConnectionString] = connectionString; } else { o.Add(TwinProperties.ConnectionString, connectionString); } _logger.Debug("Added connection string to job {id}", jobDeviceId); } } catch (Exception ex) { _logger.Error(ex, "Error while creating IoT Device."); } }
/// <summary> /// Deserialize the monitored item job /// </summary> /// <param name="job"></param> /// <returns></returns> private WriterGroupJobModel AsJob(JobInfoModel job) { if (job.JobConfiguration != null) { var publishJob = (WriterGroupJobModel)_serializer.DeserializeJobConfiguration( job.JobConfiguration, job.JobConfigurationType); if (publishJob != null) { return(publishJob); } } return(new WriterGroupJobModel { MessagingMode = MessagingMode.Samples, WriterGroup = new WriterGroupModel { WriterGroupId = job.Id, MessageSettings = new WriterGroupMessageSettingsModel() { NetworkMessageContentMask = NetworkMessageContentMask.PublisherId | NetworkMessageContentMask.WriterGroupId | NetworkMessageContentMask.SequenceNumber | NetworkMessageContentMask.PayloadHeader | NetworkMessageContentMask.NetworkMessageHeader | NetworkMessageContentMask.Timestamp | NetworkMessageContentMask.DataSetMessageHeader }, // ... DataSetWriters = new List <DataSetWriterModel>() }, Engine = null, ConnectionString = null }); }
public ActionResult Create(TenantScheduledJobViewModel vm) { if (ModelState.IsValid) { var jobInfo = new JobInfoModel { JobType = "Scheduled", CronExpression = vm.CronExpression, Url = vm.Url }; var newScheduledJob = new TenantScheduledJob { TenantId = vm.TenantId, Name = vm.Name, JobInfo = JsonConvert.SerializeObject(jobInfo) }; db.TenantScheduledJobs.Add(newScheduledJob); db.SaveChanges(); // Create Hangfire Job var selectedTenant = db.Tenants.FirstOrDefault(t => t.Id == vm.TenantId); if (selectedTenant != null) { RecurringJob.AddOrUpdate(vm.Name, () => WebClientHelper.InvokeUrl(vm.Url), vm.CronExpression); } return(RedirectToAction("Details", "Tenants", new { id = vm.TenantId })); } return(View(vm)); }
public ActionResult Create(ScheduledJobViewModel vm) { if (ModelState.IsValid) { var jobInfo = new JobInfoModel { JobType = "Scheduled", CronExpression = vm.CronExpression, Url = vm.Url }; var newScheduledJob = new ScheduledJob { Name = vm.Name, JobInfo = JsonConvert.SerializeObject(jobInfo) }; _db.ScheduledJobs.Add(newScheduledJob); _db.SaveChanges(); try { // Create Hangfire Job RecurringJob.AddOrUpdate(vm.Name, () => WebClientHelper.InvokeUrl(vm.Url), vm.CronExpression); return(RedirectToAction("Index")); } catch (Exception ex) { ViewBag.jobException = "Invalid cron expression"; } } return(View(vm)); }
/// <inheritdoc/> public async Task OnJobCreatingAsync(IJobService manager, JobInfoModel job) { if (job.JobConfiguration?.IsObject != true) { return; } try { var jobDeviceId = GetJobDeviceId(job); var deviceTwin = await _ioTHubTwinServices.FindAsync(jobDeviceId); if (deviceTwin == null) { deviceTwin = new DeviceTwinModel { Id = jobDeviceId }; await _ioTHubTwinServices.CreateOrUpdateAsync(deviceTwin, true); } var cs = await _ioTHubTwinServices.GetConnectionStringAsync(deviceTwin.Id); job.JobConfiguration[TwinProperties.ConnectionString].AssignValue(cs.ToString()); _logger.Debug("Added connection string to job {id}", jobDeviceId); } catch (Exception ex) { _logger.Error(ex, "Error while creating IoT Device."); } }
/// <inheritdoc/> public async Task <JobInfoModel> AddAsync(JobInfoModel job, CancellationToken ct) { if (job == null) { throw new ArgumentNullException(nameof(job)); } while (true) { var document = await _documents.FindAsync <JobDocument>(job.Id, ct); if (document != null) { throw new ConflictingResourceException($"Job {job.Id} already exists."); } job.LifetimeData.Created = job.LifetimeData.Updated = DateTime.UtcNow; try { var result = await _documents.AddAsync(job.ToDocumentModel(), ct); return(result.Value.ToFrameworkModel()); } catch (ConflictingResourceException) { // Try again continue; } catch { throw; } } }
/// <summary> /// Convert to storage /// </summary> /// <param name="job"></param> /// <param name="etag"></param> /// <returns></returns> public static JobDocument ToDocumentModel(this JobInfoModel job, string etag = null) { if (job?.LifetimeData == null) { return(null); } return(new JobDocument { ETag = etag, Id = job.Id, JobId = job.Id, Name = job.Name, JobConfiguration = new JobConfigDocument { JobId = job.Id, Job = job.JobConfiguration.Copy() }, Type = job.JobConfigurationType, Demands = job.Demands?.Select(d => d.ToDocumentModel(job.Id)).ToList(), DesiredActiveAgents = job.RedundancyConfig?.DesiredActiveAgents ?? 1, DesiredPassiveAgents = job.RedundancyConfig?.DesiredPassiveAgents ?? 0, Created = job.LifetimeData.Created, ProcessingStatus = job.LifetimeData.ProcessingStatus? .ToDictionary(k => k.Key, v => v.Value.ToDocumentModel(job.Id)), Status = job.LifetimeData.Status, Updated = job.LifetimeData.Updated }); }
/// <summary> /// Deserialize the monitored item job /// </summary> /// <param name="job"></param> /// <returns></returns> private WriterGroupJobModel AsJob(JobInfoModel job) { if (job.JobConfiguration != null) { var publishJob = (WriterGroupJobModel)_serializer.DeserializeJobConfiguration( job.JobConfiguration, job.JobConfigurationType); if (publishJob != null) { if (publishJob.Engine == null) { publishJob.Engine = new EngineConfigurationModel { BatchSize = _config.DefaultBatchSize, BatchTriggerInterval = _config.DefaultBatchTriggerInterval, DiagnosticsInterval = TimeSpan.FromSeconds(60), MaxMessageSize = 0, MaxEgressMessageQueue = _config.DefaultMaxEgressMessageQueue }; } else { publishJob.Engine.BatchTriggerInterval = _config.DefaultBatchTriggerInterval; publishJob.Engine.BatchSize = _config.DefaultBatchSize; publishJob.Engine.MaxEgressMessageQueue = _config.DefaultMaxEgressMessageQueue; } return(publishJob); } } return(new WriterGroupJobModel { MessagingMode = _config.DefaultMessagingMode, WriterGroup = new WriterGroupModel { MessageType = _config.DefaultMessageEncoding, WriterGroupId = job.Id, DataSetWriters = new List <DataSetWriterModel>(), MessageSettings = new WriterGroupMessageSettingsModel() { NetworkMessageContentMask = NetworkMessageContentMask.PublisherId | NetworkMessageContentMask.WriterGroupId | NetworkMessageContentMask.NetworkMessageNumber | NetworkMessageContentMask.SequenceNumber | NetworkMessageContentMask.PayloadHeader | NetworkMessageContentMask.Timestamp | NetworkMessageContentMask.DataSetClassId | NetworkMessageContentMask.NetworkMessageHeader | NetworkMessageContentMask.DataSetMessageHeader }, }, Engine = new EngineConfigurationModel { BatchSize = _config.DefaultBatchSize, BatchTriggerInterval = _config.DefaultBatchTriggerInterval, DiagnosticsInterval = TimeSpan.FromSeconds(60), MaxMessageSize = 0, MaxEgressMessageQueue = _config.DefaultMaxEgressMessageQueue }, ConnectionString = null }); }
/// <inheritdoc/> public async Task OnJobDeletedAsync(IJobService manager, JobInfoModel job) { var jobDeviceId = GetJobDeviceId(job); try { await _ioTHubTwinServices.DeleteAsync(jobDeviceId); } catch (Exception ex) { _logger.Error(ex, "Failed to delete device job {id}", jobDeviceId); } }
/// <inheritdoc/> public async Task <JobInfoModel> NewOrUpdateJobAsync(string jobId, Func <JobInfoModel, Task <bool> > predicate, CancellationToken ct) { var created = false; var job = await _jobRepository.AddOrUpdateAsync(jobId, async model => { if (model == null) { created = true; // Create new job model = new JobInfoModel { Id = jobId }; SetDefaultValues(model); if (!await predicate(model)) { return(null); } foreach (var jreh in _jobRepositoryEventHandlers) { await jreh.OnJobCreatingAsync(this, model); } return(model); } created = false; var updated = model.Clone(); if (!await predicate(updated)) { return(null); } if (!model.RedundancyConfig.Equals(updated.RedundancyConfig)) { updated.RedundancyConfig = updated.RedundancyConfig; updated.LifetimeData.ProcessingStatus.Clear(); } return(updated); }); if (created) { foreach (var jreh in _jobRepositoryEventHandlers) { await jreh.OnJobCreatedAsync(this, job); } } return(job); }
/// <inheritdoc/> public async Task OnJobAssignmentAsync(IJobService manager, JobInfoModel job, string workerId) { if (job.JobConfiguration?.IsObject != true) { return; } if (string.IsNullOrEmpty(workerId)) { throw new ArgumentNullException("empty WorkerId provided"); } try { var edgeDeviceTwin = await _ioTHubTwinServices.FindAsync(workerId.Split("_publisher")[0]); if (edgeDeviceTwin == null) { _logger.Error("IoT Edge Device not found."); return; } var jobDeviceId = GetJobDeviceId(job); var deviceTwin = await _ioTHubTwinServices.FindAsync(jobDeviceId); if (deviceTwin == null) { deviceTwin = new DeviceTwinModel { Id = jobDeviceId, DeviceScope = edgeDeviceTwin.DeviceScope }; await _ioTHubTwinServices.CreateOrUpdateAsync(deviceTwin, true); } else { if (deviceTwin.DeviceScope != edgeDeviceTwin.DeviceScope) { deviceTwin.DeviceScope = edgeDeviceTwin.DeviceScope; await _ioTHubTwinServices.CreateOrUpdateAsync(deviceTwin, true); } } var cs = await _ioTHubTwinServices.GetConnectionStringAsync(deviceTwin.Id); job.JobConfiguration[TwinProperties.ConnectionString].AssignValue(cs.ToString()); _logger.Debug("Added connection string to job {id}", jobDeviceId); } catch (Exception ex) { _logger.Error(ex, "Error while assigning the Job's IoT Device."); } }
/// <summary> /// Create model /// </summary> /// <param name="model"></param> /// <returns></returns> public static JobInfoApiModel ToApiModel( this JobInfoModel model) { if (model == null) { return(null); } return(new JobInfoApiModel { LifetimeData = model.LifetimeData.ToApiModel(), RedundancyConfig = model.RedundancyConfig.ToApiModel(), Demands = model.Demands? .Select(d => d.ToApiModel()).ToList(), JobConfiguration = model.JobConfiguration?.DeepClone(), JobConfigurationType = model.JobConfigurationType, Name = model.Name, Id = model.Id }); }
/// <inheritdoc/> public async Task <JobInfoModel> AddAsync(JobInfoModel job, CancellationToken ct) { await _lock.WaitAsync(); try { if (_jobs.Any(j => j.Id == job.Id)) { throw new ConflictingResourceException($"{job.Id} already exists."); } job.LifetimeData.Created = job.LifetimeData.Updated = DateTime.UtcNow; _jobs.Add(job); await BufferedUpdate(); return(job); } finally { _lock.Release(); } }
/// <summary> /// Set default values /// </summary> /// <param name="model"></param> private static void SetDefaultValues(JobInfoModel model) { if (model.Id == null) { model.Id = Guid.NewGuid().ToString(); } model.LifetimeData = new JobLifetimeDataModel { Created = DateTime.UtcNow, Status = JobStatus.Active, Updated = DateTime.UtcNow, ProcessingStatus = new Dictionary <string, ProcessingStatusModel>() }; if (model.RedundancyConfig == null) { model.RedundancyConfig = new RedundancyConfigModel { DesiredActiveAgents = 1, DesiredPassiveAgents = 0 }; } }
/// <inheritdoc/> public async Task OnJobCreatingAsync(IJobService manager, JobInfoModel job) { if (job.JobConfiguration?.IsObject != true) { return; } try { var jobDeviceId = GetJobDeviceId(job); var deviceTwin = await _ioTHubTwinServices.FindAsync(jobDeviceId); if (deviceTwin == null) { deviceTwin = new DeviceTwinModel { Id = jobDeviceId }; await _ioTHubTwinServices.CreateOrUpdateAsync(deviceTwin, true); } } catch (Exception ex) { _logger.Error(ex, "Error while creating IoT Device."); } }
/// <summary> /// Calculate new processing instructions if possible /// </summary> /// <param name="job"></param> /// <param name="workerId"></param> /// <returns></returns> private JobProcessingInstructionModel CalculateInstructions(JobInfoModel job, string workerId) { var numberOfActiveAgents = job.LifetimeData.ProcessingStatus .Count(j => j.Value.ProcessMode == ProcessMode.Active && j.Value.LastKnownHeartbeat > DateTime.UtcNow.Subtract(_jobOrchestratorConfig.JobStaleTime) && j.Key != workerId); var numberOfPassiveAgents = job.LifetimeData.ProcessingStatus .Count(j => j.Value.ProcessMode == ProcessMode.Passive && j.Value.LastKnownHeartbeat > DateTime.UtcNow.Subtract(_jobOrchestratorConfig.JobStaleTime) && j.Key != workerId); if (numberOfActiveAgents < job.RedundancyConfig.DesiredActiveAgents) { job.LifetimeData.ProcessingStatus[workerId] = new ProcessingStatusModel { ProcessMode = ProcessMode.Active, LastKnownHeartbeat = DateTime.UtcNow }; return(new JobProcessingInstructionModel { Job = job, ProcessMode = ProcessMode.Active }); } if (numberOfPassiveAgents < job.RedundancyConfig.DesiredPassiveAgents) { job.LifetimeData.ProcessingStatus[workerId] = new ProcessingStatusModel { ProcessMode = ProcessMode.Passive, LastKnownHeartbeat = DateTime.UtcNow }; return(new JobProcessingInstructionModel { Job = job, ProcessMode = ProcessMode.Passive }); } return(null); }
/// <inheritdoc/> public async Task <JobInfoModel> NewJobAsync(JobInfoModel model, CancellationToken ct) { SetDefaultValues(model); foreach (var jreh in _jobRepositoryEventHandlers) { await jreh.OnJobCreatingAsync(this, model); } try { model = await _jobRepository.AddAsync(model, ct); } catch { foreach (var jreh in _jobRepositoryEventHandlers) { await jreh.OnJobDeletedAsync(this, model); } throw; } foreach (var jreh in _jobRepositoryEventHandlers) { await jreh.OnJobCreatedAsync(this, model); } return(model); }
/// <summary> /// Deserialize the monitored item job /// </summary> /// <param name="job"></param> /// <returns></returns> private WriterGroupJobModel AsJob(JobInfoModel job) { if (job.JobConfiguration != null) { var publishJob = (WriterGroupJobModel)_serializer.DeserializeJobConfiguration( job.JobConfiguration, job.JobConfigurationType); if (publishJob != null) { return(publishJob); } } return(new WriterGroupJobModel { MessagingMode = MessagingMode.Samples, WriterGroup = new WriterGroupModel { WriterGroupId = job.Id, // ... DataSetWriters = new List <DataSetWriterModel>() }, Engine = null, ConnectionString = null }); }
/// <summary> /// Create job device identifier /// </summary> /// <param name="job"></param> /// <returns></returns> private static string GetJobDeviceId(JobInfoModel job) { return($"{job.JobConfigurationType}_{job.Id}"); }
//Load Job public JsonResult LoadJobInfoData() { try { int index = 1; List <JobDetailImpl> jobList = new List <JobDetailImpl>(); List <CronTriggerImpl> triggerList = new List <CronTriggerImpl>(); List <JobInfoModel> jobInfoList = new List <JobInfoModel>(); var jobKeySet = scheduler.GetJobKeys(GroupMatcher <JobKey> .AnyGroup()); var triggerKeys = scheduler.GetTriggerKeys(GroupMatcher <TriggerKey> .AnyGroup()); foreach (var jobKey in jobKeySet) { var jobDetail = (JobDetailImpl)scheduler.GetJobDetail(jobKey); jobList.Add(jobDetail); } foreach (var triggerkey in triggerKeys) { var triggerDetail = (CronTriggerImpl)scheduler.GetTrigger(triggerkey); triggerList.Add(triggerDetail); } foreach (var job in jobList) { JobInfoModel jobInfo = new JobInfoModel(); jobInfo.JobId = index++; jobInfo.JobName = job.Name; jobInfo.JobGroupName = job.Group; //jobInfo.IsDurable = job.Durable; //jobInfo.JobDescription = job.Description; var trigger = string.Join(",", scheduler.GetTriggersOfJob(new JobKey(job.Name, job.Group)).Select(m => m.Key.ToString())); var triggerInfo = triggerList.Where(i => i.FullName.Equals(trigger.ToString()) && i.FullJobName.Equals($"{job.Group}.{job.Name}")).Select(i => new JobInfoModel { TriggerName = i.Name, TriggerGroupName = i.Group, //TriggerDescription = i.Description, StartTime = i.StartTimeUtc.LocalDateTime.ToString("yyyy-MM-dd HH:mm:ss"), PrevFireTime = i.GetPreviousFireTimeUtc()?.LocalDateTime.ToString("yyyy-MM-dd HH:mm:ss"), NextFireTime = i.GetNextFireTimeUtc()?.LocalDateTime.ToString("yyyy-MM-dd HH:mm:ss"), Cron = i.CronExpressionString, //Priority = i.Priority, Status = scheduler.GetTriggerState(new TriggerKey(i.Name, i.Group)).ToString(), //JobClassName = scheduler.GetJobDetail(i.JobKey).JobType.FullName }).FirstOrDefault(); jobInfo.TriggerName = triggerInfo.TriggerName; jobInfo.TriggerGroupName = triggerInfo.TriggerGroupName; //jobInfo.TriggerDescription = triggerInfo.TriggerDescription; jobInfo.StartTime = triggerInfo.StartTime; jobInfo.PrevFireTime = triggerInfo.PrevFireTime; jobInfo.NextFireTime = triggerInfo.NextFireTime; jobInfo.Cron = triggerInfo.Cron; //jobInfo.Priority = triggerInfo.Priority; jobInfo.Status = GetState(triggerInfo.Status); //jobInfo.JobClassName = triggerInfo.JobClassName; jobInfoList.Add(jobInfo); } return(Json(jobInfoList, JsonRequestBehavior.AllowGet)); } catch (Exception ex) { throw new Exception(ex.Message); } }
/// <inheritdoc/> public Task OnJobDeletingAsync(IJobService manager, JobInfoModel job) { return(Task.CompletedTask); }