Skip to content

Commit 3ba78fb

Browse files
authored
Create business rule to enforce CI maintenance window
1 parent 5aa4aa8 commit 3ba78fb

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Business Rule: Enforce CI maintenance window on Change schedule
2+
// Table: change_request | When: before insert, before update
3+
4+
(function executeRule(current, previous /*null*/) {
5+
// ===== Configuration =====
6+
var BLOCK_WHEN_NO_SCHEDULE = false; // if true, a CI without maintenance_schedule causes failure
7+
var REQUIRE_BOTH_BOUNDARIES = true; // if true, both planned start and end must be inside maintenance window
8+
var TIMEZONE = 'Europe/London'; // optional; '' to use schedule/instance default
9+
// =========================
10+
11+
try {
12+
// Only run when dates are meaningful
13+
if (!current.planned_start_date || !current.planned_end_date) return;
14+
15+
// Build GDTs once
16+
var psd = new GlideDateTime(current.planned_start_date.getDisplayValue());
17+
var ped = new GlideDateTime(current.planned_end_date.getDisplayValue());
18+
if (psd.after(ped)) {
19+
gs.addErrorMessage('Planned start is after planned end. Please correct the schedule.');
20+
current.setAbortAction(true);
21+
return;
22+
}
23+
24+
// Collect related CIs for this Change
25+
var ciIds = [];
26+
var tci = new GlideRecord('task_ci');
27+
tci.addQuery('task', current.getUniqueValue());
28+
tci.query();
29+
while (tci.next()) ciIds.push(String(tci.getValue('ci_item')));
30+
31+
if (ciIds.length === 0) {
32+
// No CIs; nothing to validate
33+
return;
34+
}
35+
36+
var anyPass = false;
37+
var missingScheduleCount = 0;
38+
var evaluated = 0;
39+
40+
// Evaluate each CI's maintenance schedule
41+
var ci = new GlideRecord('cmdb_ci');
42+
ci.addQuery('sys_id', 'IN', ciIds.join(','));
43+
ci.query();
44+
45+
while (ci.next()) {
46+
evaluated++;
47+
48+
var schedRef = ci.getValue('maintenance_schedule');
49+
if (!schedRef) {
50+
missingScheduleCount++;
51+
continue;
52+
}
53+
54+
var sched = new GlideSchedule(schedRef, TIMEZONE || '');
55+
var startOK = sched.isInSchedule(psd);
56+
var endOK = sched.isInSchedule(ped);
57+
58+
var pass = REQUIRE_BOTH_BOUNDARIES ? (startOK && endOK) : (startOK || endOK);
59+
if (pass) {
60+
anyPass = true;
61+
break; // at least one CI permits this window
62+
}
63+
}
64+
65+
// Handle missing schedules according to policy
66+
if (!anyPass) {
67+
var hasBlockingNoSchedule = BLOCK_WHEN_NO_SCHEDULE && missingScheduleCount > 0;
68+
if (hasBlockingNoSchedule || evaluated > 0) {
69+
gs.addErrorMessage(buildMessage());
70+
current.setAbortAction(true);
71+
}
72+
}
73+
74+
function buildMessage() {
75+
var parts = [];
76+
parts.push('Planned window does not fall inside any related CI maintenance schedules.');
77+
if (REQUIRE_BOTH_BOUNDARIES) parts.push('Both start and end must be inside a permitted window.');
78+
if (missingScheduleCount > 0) {
79+
parts.push((BLOCK_WHEN_NO_SCHEDULE ? 'Blocking' : 'Ignoring') + ' ' + missingScheduleCount + ' CI(s) with no maintenance schedule.');
80+
}
81+
return parts.join(' ');
82+
}
83+
} catch (e) {
84+
gs.error('Maintenance window validation failed: ' + e.message);
85+
// Be safe: do not block due to a runtime error
86+
}
87+
})(current, previous);

0 commit comments

Comments
 (0)