Manage the maintenance state of alarms
In this example, we will present a way to manage the maintenance state of alarms.
Configure a manual enable/disable action with parameters for device, node or site scope.
Add an automatic disable action that scans expired maintenances and resets severity and journal.
Create a pre-insert hook to check active maintenance when a new alarm arrives.
Expose maintenance controls via a right-click menu on the alarm table.
git checkout origin/osp-alarms-configuration .
git checkout origin/osp-web-configuration .
git checkout origin/osp-scripts-configuration .
git checkout origin/osp-variables-configuration .
git checkout origin/example-alarms-maintenance-management .
Prerequisite
Steps
Note
To simplify scripting and share helper functions, all the logic is inside the root/maintenance/maintenance.lua.
The following code snippet does not detail the contents of the helper function.
1. Create the configuration to enable/disable maintenance
This action is used for enabling/disabling the maintenance manually. It takes as parameter :
An actionType to define if the activation is for a device, node or site.
An optional location if the activation is for a site.
An optional node if the activation is for a node.
An optional serial if the activation is for a device.
The script determines which alarms need to be put in maintenance depending on those parameters.
root/maintenance/manual-enable-disable/action.alarms
{
"moduleId": "modules.alarms.alarms-1",
"scriptFile": "root/maintenance/maintenance.lua",
"accessedValues": [],
"beginBatch": "begin_batch",
"criteria": "criteria",
"execute": "execute",
"endBatch": "end_batch"
}
root/maintenance/maintenance.lua
156---------------------------------------------------------------------
157-- Enable maintenance upon a user action.
158-- used by root.maintenance.manual-enable-disable
159function begin_batch()
160 batch_execution_data = {}
161end
162
163function criteria(data)
164 local actionType = data[ACTION_TYPE]
165 local location = data[ACTION_TYPE_SITE]
166 local node = data[ACTION_TYPE_NODE]
167 local serial = data[ACTION_TYPE_DEVICE]
168
169 if type(location) ~= "table" then
170 location = {location}
171 end
172
173 if type(node) ~= "table" then
174 node = {node}
175 end
176
177 if type(serial) ~= "table" then
178 serial = {serial}
179 end
180
181 if actionType == ACTION_TYPE_DEVICE and next(serial) ~= nil then
182 local filter = FILTER_BEGIN
183 local filter_middle = [[{'_id': '%s'},]]
184 for _, v in pairs(serial) do
185 filter = filter .. string.format(filter_middle, v)
186 end
187 return filter .. FILTER_END
188 elseif actionType == ACTION_TYPE_NODE and next(node) ~= nil and next(location) ~= nil then
189 local filter = FILTER_BEGIN
190 local filter_middle = [[
191 {
192 '$and': [
193 {'additionalData.%s': '%s'},
194 {'%s': '%s'},
195 {'summary':
196 {
197 '$ne': '%s'
198 }
199 }
200 ]
201 }
202 ]]
203
204 for i, n in pairs(node) do
205 filter = filter .. string.format(filter_middle, NODE_FIELD, n, SITE_FIELD, location[i], DEVICE_MAINTENANCE_SUMMARY)
206 end
207 return filter .. FILTER_END
208 elseif actionType == ACTION_TYPE_SITE and next(location) ~= nil then
209 local filter = FILTER_BEGIN
210 local filter_middle = [[
211 {
212 '$and': [
213 {'%s': '%s'},
214 {'summary':
215 {
216 '$ne': '%s'
217 }
218 }
219 ]
220 }
221 ]]
222 for _, v in pairs(location) do
223 filter = filter .. string.format(filter_middle, SITE_FIELD, v, SITE_MAINTENANCE_SUMMARY)
224 end
225
226 return filter .. FILTER_END
227 else
228 return FILTER_EMPTY
229 end
230end
231
232function execute(alarm, data, actions)
233 local maintenanceUntil = data["until"]
234 if data["hours"] ~= nil then
235 local now = Timestamp.now()
236 now:plusHours(data["hours"])
237 maintenanceUntil = now:getValue()
238 end
239 local type = data[ACTION_TYPE]
240
241 if type == ACTION_TYPE_DEVICE then
242 store.set(MAINTENANCE_STORE_PREFIX .. "-" .. alarm.id, maintenanceUntil)
243 elseif type == ACTION_TYPE_NODE then
244 store.set(MAINTENANCE_STORE_PREFIX .. "-" .. alarm.location .. "-" .. alarm.additionalData[NODE_FIELD], maintenanceUntil)
245 elseif type == ACTION_TYPE_SITE then
246 store.set(MAINTENANCE_STORE_PREFIX .. "-" .. alarm.location, maintenanceUntil)
247 end
248
249 if alarm.tags[SITE_STATUS_TAG] ~= nil then
250 return
251 end
252
253 update_alarm(alarm, data, actions, maintenanceUntil)
254end
255
256function end_batch()
257 for k, v in pairs(batch_execution_data) do
258 if v[ACTION_TYPE] == ACTION_TYPE_NODE then
259 alarms:insert(
260 severity.get("root.alarms.severities.minor"),
261 DEVICE_MAINTENANCE_SUMMARY,
262 v[SITE_FIELD],
263 SCRIPT_NAME,
264 k,
265 {DEVICE_STATUS_TAG},
266 {node=v[NODE_FIELD], maintenance="In maintenance", monitored=true, region=v[REGION_FIELD], assigned=ASSIGNED_OCC, customer=v[ALARM_CUSTOMER]}
267 )
268 elseif v[ACTION_TYPE] == ACTION_TYPE_SITE then
269 alarms:insert(
270 severity.get("root.alarms.severities.minor"),
271 SITE_MAINTENANCE_SUMMARY,
272 v[SITE_FIELD],
273 SCRIPT_NAME,
274 k,
275 {SITE_STATUS_TAG},
276 {maintenance= "In maintenance", monitored=true, region=v[REGION_FIELD], assigned=ASSIGNED_OCC, customer=v[ALARM_CUSTOMER]}
277 )
278 end
279 end
280end
2. Create the configuration to disable the site/node maintenance automatically
This action scans the alarms in maintenance and if the maintenance has expired, performs the following actions:
Set the severity to clear
Set the maintenance field to Normal
Add a message in the journal
root/maintenance/auto-disable/action.alarms
{
"moduleId": "modules.alarms.alarms-1",
"period": {
"unit": "SECONDS",
"value": 5
},
"scriptFile": "root/maintenance/maintenance.lua",
"criteria": "criteria_disable",
"execute": "execute_disable"
}
root/maintenance/maintenance.lua
77---------------------------------------------------------------------
78-- Check regularly to disable maintenance mode when nessecary.
79-- used by root.maintenance.disable
80function criteria_disable(unused)
81 local sev = 100 --Replace with the severity to use by default when the maintenance is disable.
82 if severity.has("root.alarms.severities.clear") then
83 sev = severity.get("root.alarms.severities.clear")
84 end
85
86 local filter = [[
87 {
88 '$and': [
89 {'additionalData.%s': {'$exists': true}},
90 {
91 '$or': [
92 {'additionalData.%s': {'$ne': '%s'}},
93 {
94 '$and': [
95 {'summary': '%s'},
96 {'severity': {'$ne': %s}}
97 ]
98 },
99 {
100 '$and': [
101 {'summary': '%s'},
102 {'severity': {'$ne': %s}}
103 ]
104 }
105 ]
106 }
107 ]
108 }
109 ]]
110
111 return string.format(filter, MAINTENANCE_FIELD, MAINTENANCE_FIELD, NORMAL_MODE, SITE_MAINTENANCE_SUMMARY, sev, DEVICE_MAINTENANCE_SUMMARY, sev)
112end
113
114function execute_disable(alarm, unused, actions)
115 local now = Timestamp.now()
116 local sev = 100 --Replace with the severity to use by default when the maintenance is disable.
117 if severity.has("root.alarms.severities.clear") then
118 sev = severity.get("root.alarms.severities.clear")
119 end
120
121 if alarm.summary == SITE_MAINTENANCE_SUMMARY then
122 local locationMaintenance = nil
123 if alarm.location ~= nil then
124 locationMaintenance = getSiteMaintenance(alarm.location)
125 end
126
127 -- check if location maintenance is done
128 if locationMaintenance ~= nil and now:isNewerThan(Timestamp.from(locationMaintenance)) then
129 actions:escalate(sev)
130 actions:edit(nil, nil, nil, nil, nil, {maintenance = NORMAL_MODE})
131 actions:journal(MAINTENANCE_DISABLE_MESSAGE, SCRIPT_NAME)
132 end
133 elseif alarm.summary == DEVICE_MAINTENANCE_SUMMARY then
134 local deviceMaintenance = nil
135 if alarm.additionalData[NODE_FIELD] ~= nil and alarm.location ~= nil then
136 deviceMaintenance = getDeviceMaintenance(alarm.location, alarm.additionalData[NODE_FIELD])
137 end
138
139 -- check if device maintenance is done
140 if deviceMaintenance ~= nil and now:isNewerThan(Timestamp.from(deviceMaintenance)) then
141 actions:escalate(sev)
142 actions:edit(nil, nil, nil, nil, nil, {maintenance = NORMAL_MODE})
143 actions:journal(MAINTENANCE_DISABLE_MESSAGE, SCRIPT_NAME)
144 end
145 else
146 local maintenanceUntil = Timestamp.from(alarm.additionalData[MAINTENANCE_UNTIL_FIELD])
147
148 if now:isNewerThan(maintenanceUntil) then
149 actions:edit(nil, nil, nil, nil, nil, {maintenance = NORMAL_MODE})
150 actions:journal(MAINTENANCE_DISABLE_MESSAGE, SCRIPT_NAME)
151 end
152 end
153end
3. Create the configuration to process new alarms
When a new alarm arrives, we must check if the maintenance is enabled for it.
root/maintenance/insert/pre-insert.alarms
{
"moduleId": "modules.alarms.alarms-1",
"priority": 5,
"scriptFile": "root/maintenance/maintenance.lua",
"accessedValues": [],
"for": "match_insertion",
"thenExecute": "then_execute_insertion"
}
root/maintenance/maintenance.lua
52---------------------------------------------------------------------
53-- Check if a new alarm need to be set with the maintenance mode
54-- used by root.maintenance.insertion
55function match_insertion(alarm)
56 if alarm.tags[SITE_STATUS_TAG] ~= nil then
57 return false
58 end
59 return true
60end
61
62function then_execute_insertion(alarm, data, operations)
63 local olderMaintenance = getOlderMaintenanceUntil(alarm)
64 local now = Timestamp.now()
65 local newUntil = Timestamp.from(olderMaintenance)
66
67 local newAlarm = operations:forward()
68 newAlarm.additionalData = alarm.additionalData
69 if newUntil:isNewerThan(now) then
70 newAlarm.additionalData[MAINTENANCE_FIELD] = MAINTENANCE_MODE
71 else
72 newAlarm.additionalData[MAINTENANCE_FIELD] = NORMAL_MODE
73 end
74 newAlarm.additionalData[MAINTENANCE_UNTIL_FIELD] = olderMaintenance
75 newAlarm.hideUntil = olderMaintenance
76end