2aebcc29b63c7ef62d132c5f529ef7174e5dc208
[public/netxms.git] / src / server / core / epp.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2014 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: epp.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Default event policy rule constructor
27 */
28 EPRule::EPRule(UINT32 id)
29 {
30 m_id = id;
31 uuid_generate(m_guid);
32 m_dwFlags = 0;
33 m_dwNumSources = 0;
34 m_pdwSourceList = NULL;
35 m_dwNumEvents = 0;
36 m_pdwEventList = NULL;
37 m_dwNumActions = 0;
38 m_pdwActionList = NULL;
39 m_pszComment = NULL;
40 m_iAlarmSeverity = 0;
41 m_szAlarmKey[0] = 0;
42 m_szAlarmMessage[0] = 0;
43 m_pszScript = NULL;
44 m_pScript = NULL;
45 m_dwAlarmTimeout = 0;
46 m_dwAlarmTimeoutEvent = EVENT_ALARM_TIMEOUT;
47 m_dwSituationId = 0;
48 m_szSituationInstance[0] = 0;
49 }
50
51 /**
52 * Create rule from config entry
53 */
54 EPRule::EPRule(ConfigEntry *config)
55 {
56 m_id = 0;
57 config->getSubEntryValueAsUUID(_T("guid"), m_guid);
58 m_dwFlags = config->getSubEntryValueAsUInt(_T("flags"));
59 m_dwNumSources = 0;
60 m_pdwSourceList = NULL;
61 m_dwNumActions = 0;
62 m_pdwActionList = NULL;
63
64 ConfigEntry *eventsRoot = config->findEntry(_T("events"));
65 if (eventsRoot != NULL)
66 {
67 ObjectArray<ConfigEntry> *events = eventsRoot->getSubEntries(_T("event#*"));
68 m_dwNumEvents = 0;
69 m_pdwEventList = (UINT32 *)malloc(sizeof(UINT32) * events->size());
70 for(int i = 0; i < events->size(); i++)
71 {
72 EVENT_TEMPLATE *e = FindEventTemplateByName(events->get(i)->getSubEntryValue(_T("name"), 0, _T("<unknown>")));
73 if (e != NULL)
74 {
75 m_pdwEventList[m_dwNumEvents++] = e->dwCode;
76 }
77 }
78 }
79
80 m_pszComment = _tcsdup(config->getSubEntryValue(_T("comments"), 0, _T("")));
81 m_iAlarmSeverity = config->getSubEntryValueAsInt(_T("alarmSeverity"));
82 m_dwAlarmTimeout = config->getSubEntryValueAsUInt(_T("alarmTimeout"));
83 m_dwAlarmTimeoutEvent = config->getSubEntryValueAsUInt(_T("alarmTimeout"), 0, EVENT_ALARM_TIMEOUT);
84 nx_strncpy(m_szAlarmKey, config->getSubEntryValue(_T("alarmKey"), 0, _T("")), MAX_DB_STRING);
85 nx_strncpy(m_szAlarmMessage, config->getSubEntryValue(_T("alarmMessage"), 0, _T("")), MAX_DB_STRING);
86
87 m_dwSituationId = 0;
88 m_szSituationInstance[0] = 0;
89
90 m_pszScript = _tcsdup(config->getSubEntryValue(_T("script"), 0, _T("")));
91 if ((m_pszScript != NULL) && (*m_pszScript != 0))
92 {
93 TCHAR szError[256];
94
95 m_pScript = NXSLCompileAndCreateVM(m_pszScript, szError, 256, new NXSL_ServerEnv);
96 if (m_pScript != NULL)
97 {
98 m_pScript->setGlobalVariable(_T("CUSTOM_MESSAGE"), new NXSL_Value(_T("")));
99 }
100 else
101 {
102 nxlog_write(MSG_EPRULE_SCRIPT_COMPILATION_ERROR, EVENTLOG_ERROR_TYPE, "ds", m_id, szError);
103 }
104 }
105 else
106 {
107 m_pScript = NULL;
108 }
109 }
110
111 /**
112 * Construct event policy rule from database record
113 * Assuming the following field order:
114 * rule_id,rule_guid,flags,comments,alarm_message,alarm_severity,alarm_key,script,
115 * alarm_timeout,alarm_timeout_event,situation_id,situation_instance
116 */
117 EPRule::EPRule(DB_RESULT hResult, int row)
118 {
119 m_id = DBGetFieldULong(hResult, row, 0);
120 DBGetFieldGUID(hResult, row, 1, m_guid);
121 m_dwFlags = DBGetFieldULong(hResult, row, 2);
122 m_pszComment = DBGetField(hResult, row, 3, NULL, 0);
123 DBGetField(hResult, row, 4, m_szAlarmMessage, MAX_EVENT_MSG_LENGTH);
124 m_iAlarmSeverity = DBGetFieldLong(hResult, row, 5);
125 DBGetField(hResult, row, 6, m_szAlarmKey, MAX_DB_STRING);
126 m_pszScript = DBGetField(hResult, row, 7, NULL, 0);
127 if ((m_pszScript != NULL) && (*m_pszScript != 0))
128 {
129 TCHAR szError[256];
130
131 m_pScript = NXSLCompileAndCreateVM(m_pszScript, szError, 256, new NXSL_ServerEnv);
132 if (m_pScript != NULL)
133 {
134 m_pScript->setGlobalVariable(_T("CUSTOM_MESSAGE"), new NXSL_Value(_T("")));
135 }
136 else
137 {
138 nxlog_write(MSG_EPRULE_SCRIPT_COMPILATION_ERROR, EVENTLOG_ERROR_TYPE,
139 "ds", m_id, szError);
140 }
141 }
142 else
143 {
144 m_pScript = NULL;
145 }
146 m_dwAlarmTimeout = DBGetFieldULong(hResult, row, 8);
147 m_dwAlarmTimeoutEvent = DBGetFieldULong(hResult, row, 9);
148 m_dwSituationId = DBGetFieldULong(hResult, row, 10);
149 DBGetField(hResult, row, 11, m_szSituationInstance, MAX_DB_STRING);
150 }
151
152 /**
153 * Construct event policy rule from NXCP message
154 */
155 EPRule::EPRule(NXCPMessage *msg)
156 {
157 UINT32 i, id, count;
158 TCHAR *name, *value;
159
160 m_dwFlags = msg->getFieldAsUInt32(VID_FLAGS);
161 m_id = msg->getFieldAsUInt32(VID_RULE_ID);
162 msg->getFieldAsBinary(VID_GUID, m_guid, UUID_LENGTH);
163 m_pszComment = msg->getFieldAsString(VID_COMMENTS);
164
165 m_dwNumActions = msg->getFieldAsUInt32(VID_NUM_ACTIONS);
166 m_pdwActionList = (UINT32 *)malloc(sizeof(UINT32) * m_dwNumActions);
167 msg->getFieldAsInt32Array(VID_RULE_ACTIONS, m_dwNumActions, m_pdwActionList);
168
169 m_dwNumEvents = msg->getFieldAsUInt32(VID_NUM_EVENTS);
170 m_pdwEventList = (UINT32 *)malloc(sizeof(UINT32) * m_dwNumEvents);
171 msg->getFieldAsInt32Array(VID_RULE_EVENTS, m_dwNumEvents, m_pdwEventList);
172
173 m_dwNumSources = msg->getFieldAsUInt32(VID_NUM_SOURCES);
174 m_pdwSourceList = (UINT32 *)malloc(sizeof(UINT32) * m_dwNumSources);
175 msg->getFieldAsInt32Array(VID_RULE_SOURCES, m_dwNumSources, m_pdwSourceList);
176
177 msg->getFieldAsString(VID_ALARM_KEY, m_szAlarmKey, MAX_DB_STRING);
178 msg->getFieldAsString(VID_ALARM_MESSAGE, m_szAlarmMessage, MAX_DB_STRING);
179 m_iAlarmSeverity = msg->getFieldAsUInt16(VID_ALARM_SEVERITY);
180 m_dwAlarmTimeout = msg->getFieldAsUInt32(VID_ALARM_TIMEOUT);
181 m_dwAlarmTimeoutEvent = msg->getFieldAsUInt32(VID_ALARM_TIMEOUT_EVENT);
182
183 m_dwSituationId = msg->getFieldAsUInt32(VID_SITUATION_ID);
184 msg->getFieldAsString(VID_SITUATION_INSTANCE, m_szSituationInstance, MAX_DB_STRING);
185 count = msg->getFieldAsUInt32(VID_SITUATION_NUM_ATTRS);
186 for(i = 0, id = VID_SITUATION_ATTR_LIST_BASE; i < count; i++)
187 {
188 name = msg->getFieldAsString(id++);
189 value = msg->getFieldAsString(id++);
190 m_situationAttrList.setPreallocated(name, value);
191 }
192
193 m_pszScript = msg->getFieldAsString(VID_SCRIPT);
194 if ((m_pszScript != NULL) && (*m_pszScript != 0))
195 {
196 TCHAR szError[256];
197
198 m_pScript = NXSLCompileAndCreateVM(m_pszScript, szError, 256, new NXSL_ServerEnv);
199 if (m_pScript != NULL)
200 {
201 m_pScript->setGlobalVariable(_T("CUSTOM_MESSAGE"), new NXSL_Value(_T("")));
202 }
203 else
204 {
205 nxlog_write(MSG_EPRULE_SCRIPT_COMPILATION_ERROR, EVENTLOG_ERROR_TYPE, "ds", m_id, szError);
206 }
207 }
208 else
209 {
210 m_pScript = NULL;
211 }
212 }
213
214 /**
215 * Event policy rule destructor
216 */
217 EPRule::~EPRule()
218 {
219 safe_free(m_pdwSourceList);
220 safe_free(m_pdwEventList);
221 safe_free(m_pdwActionList);
222 safe_free(m_pszComment);
223 safe_free(m_pszScript);
224 delete m_pScript;
225 }
226
227 /**
228 * Create management pack record
229 */
230 void EPRule::createNXMPRecord(String &str)
231 {
232 str.addFormattedString(_T("\t\t<rule id=\"%d\">\n")
233 _T("\t\t\t<flags>%d</flags>\n")
234 _T("\t\t\t<alarmMessage>%s</alarmMessage>\n")
235 _T("\t\t\t<alarmKey>%s</alarmKey>\n")
236 _T("\t\t\t<alarmSeverity>%d</alarmSeverity>\n")
237 _T("\t\t\t<alarmTimeout>%d</alarmTimeout>\n")
238 _T("\t\t\t<alarmTimeoutEvent>%d</alarmTimeoutEvent>\n")
239 _T("\t\t\t<situation>%d</situation>\n")
240 _T("\t\t\t<situationInstance>%s</situationInstance>\n")
241 _T("\t\t\t<script>%s</script>\n")
242 _T("\t\t\t<comments>%s</comments>\n")
243 _T("\t\t\t<sources>\n"),
244 m_id, m_dwFlags,
245 (const TCHAR *)EscapeStringForXML2(m_szAlarmMessage),
246 (const TCHAR *)EscapeStringForXML2(m_szAlarmKey),
247 m_iAlarmSeverity, m_dwAlarmTimeout, m_dwAlarmTimeoutEvent,
248 m_dwSituationId, (const TCHAR *)EscapeStringForXML2(m_szSituationInstance),
249 (const TCHAR *)EscapeStringForXML2(m_pszScript),
250 (const TCHAR *)EscapeStringForXML2(m_pszComment));
251
252 for(UINT32 i = 0; i < m_dwNumSources; i++)
253 {
254 NetObj *object = FindObjectById(m_pdwSourceList[i]);
255 if (object != NULL)
256 {
257 uuid_t guid;
258 object->getGuid(guid);
259 TCHAR guidText[128];
260 str.addFormattedString(_T("\t\t\t\t<source id=\"%d\">\n")
261 _T("\t\t\t\t\t<name>%s</name>\n")
262 _T("\t\t\t\t\t<guid>%s</guid>\n")
263 _T("\t\t\t\t\t<class>%d</class>\n")
264 _T("\t\t\t\t</source>\n"),
265 object->getId(),
266 (const TCHAR *)EscapeStringForXML2(object->getName()),
267 uuid_to_string(guid, guidText), object->getObjectClass());
268 }
269 }
270
271 str += _T("\t\t\t</sources>\n\t\t\t<events>\n");
272
273 for(UINT32 i = 0; i < m_dwNumEvents; i++)
274 {
275 TCHAR eventName[MAX_EVENT_NAME];
276 EventNameFromCode(m_pdwEventList[i], eventName);
277 str.addFormattedString(_T("\t\t\t\t<event id=\"%d\">\n")
278 _T("\t\t\t\t\t<name>%s</name>\n")
279 _T("\t\t\t\t</event>\n"),
280 m_pdwEventList[i], (const TCHAR *)EscapeStringForXML2(eventName));
281 }
282
283 str += _T("\t\t\t</events>\n\t\t\t<actions>\n");
284
285 for(UINT32 i = 0; i < m_dwNumActions; i++)
286 {
287 str.addFormattedString(_T("\t\t\t\t<action id=\"%d\">\n")
288 _T("\t\t\t\t</action>\n"),
289 m_pdwActionList[i]);
290 }
291
292 str += _T("\t\t\t</actions>\n\t\t</rule>\n");
293 }
294
295 /**
296 * Check if source object's id match to the rule
297 */
298 bool EPRule::matchSource(UINT32 dwObjectId)
299 {
300 UINT32 i;
301 NetObj *pObject;
302 bool bMatch = FALSE;
303
304 if (m_dwNumSources == 0) // No sources in list means "any"
305 {
306 bMatch = true;
307 }
308 else
309 {
310 for(i = 0; i < m_dwNumSources; i++)
311 if (m_pdwSourceList[i] == dwObjectId)
312 {
313 bMatch = true;
314 break;
315 }
316 else
317 {
318 pObject = FindObjectById(m_pdwSourceList[i]);
319 if (pObject != NULL)
320 {
321 if (pObject->isChild(dwObjectId))
322 {
323 bMatch = true;
324 break;
325 }
326 }
327 else
328 {
329 nxlog_write(MSG_INVALID_EPP_OBJECT, EVENTLOG_ERROR_TYPE, "d", m_pdwSourceList[i]);
330 }
331 }
332 }
333 return (m_dwFlags & RF_NEGATED_SOURCE) ? !bMatch : bMatch;
334 }
335
336 /**
337 * Check if event's id match to the rule
338 */
339 bool EPRule::matchEvent(UINT32 dwEventCode)
340 {
341 UINT32 i;
342 bool bMatch = false;
343
344 if (m_dwNumEvents == 0) // No sources in list means "any"
345 {
346 bMatch = true;
347 }
348 else
349 {
350 for(i = 0; i < m_dwNumEvents; i++)
351 if (m_pdwEventList[i] & GROUP_FLAG_BIT)
352 {
353 /* TODO: check group membership */
354 }
355 else
356 {
357 if (m_pdwEventList[i] == dwEventCode)
358 {
359 bMatch = true;
360 break;
361 }
362 }
363 }
364 return (m_dwFlags & RF_NEGATED_EVENTS) ? !bMatch : bMatch;
365 }
366
367 /**
368 * Check if event's severity match to the rule
369 */
370 bool EPRule::matchSeverity(UINT32 dwSeverity)
371 {
372 static UINT32 dwSeverityFlag[] = { RF_SEVERITY_INFO, RF_SEVERITY_WARNING,
373 RF_SEVERITY_MINOR, RF_SEVERITY_MAJOR,
374 RF_SEVERITY_CRITICAL };
375 return (dwSeverityFlag[dwSeverity] & m_dwFlags) ? true : false;
376 }
377
378 /**
379 * Check if event match to the script
380 */
381 bool EPRule::matchScript(Event *pEvent)
382 {
383 NXSL_ServerEnv *pEnv;
384 NXSL_Value **ppValueList, *pValue;
385 NXSL_VariableSystem *pLocals, *pGlobals = NULL;
386 bool bRet = true;
387 UINT32 i;
388 NetObj *pObject;
389
390 if (m_pScript == NULL)
391 return true;
392
393 pEnv = new NXSL_ServerEnv;
394
395 // Pass event's parameters as arguments and
396 // other information as variables
397 ppValueList = (NXSL_Value **)malloc(sizeof(NXSL_Value *) * pEvent->getParametersCount());
398 memset(ppValueList, 0, sizeof(NXSL_Value *) * pEvent->getParametersCount());
399 for(i = 0; i < pEvent->getParametersCount(); i++)
400 ppValueList[i] = new NXSL_Value(pEvent->getParameter(i));
401
402 pLocals = new NXSL_VariableSystem;
403 pLocals->create(_T("EVENT_CODE"), new NXSL_Value(pEvent->getCode()));
404 pLocals->create(_T("SEVERITY"), new NXSL_Value(pEvent->getSeverity()));
405 pLocals->create(_T("SEVERITY_TEXT"), new NXSL_Value(GetStatusAsText(pEvent->getSeverity(), true)));
406 pLocals->create(_T("OBJECT_ID"), new NXSL_Value(pEvent->getSourceId()));
407 pLocals->create(_T("EVENT_TEXT"), new NXSL_Value((TCHAR *)pEvent->getMessage()));
408 pLocals->create(_T("USER_TAG"), new NXSL_Value((TCHAR *)pEvent->getUserTag()));
409 pObject = FindObjectById(pEvent->getSourceId());
410 if (pObject != NULL)
411 {
412 if (pObject->getObjectClass() == OBJECT_NODE)
413 m_pScript->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, pObject)));
414 }
415 m_pScript->setGlobalVariable(_T("$event"), new NXSL_Value(new NXSL_Object(&g_nxslEventClass, pEvent)));
416 m_pScript->setGlobalVariable(_T("CUSTOM_MESSAGE"), new NXSL_Value);
417
418 // Run script
419 if (m_pScript->run(pEvent->getParametersCount(), ppValueList, pLocals, &pGlobals))
420 {
421 pValue = m_pScript->getResult();
422 if (pValue != NULL)
423 {
424 bRet = pValue->getValueAsInt32() ? true : false;
425 if (bRet)
426 {
427 NXSL_Variable *var;
428
429 var = pGlobals->find(_T("CUSTOM_MESSAGE"));
430 if (var != NULL)
431 {
432 // Update custom message in event
433 pEvent->setCustomMessage(CHECK_NULL_EX(var->getValue()->getValueAsCString()));
434 }
435 }
436 }
437 }
438 else
439 {
440 nxlog_write(MSG_EPRULE_SCRIPT_EXECUTION_ERROR, EVENTLOG_ERROR_TYPE, "ds", m_id + 1, m_pScript->getErrorText());
441 }
442 free(ppValueList);
443 delete pGlobals;
444
445 return bRet;
446 }
447
448 /**
449 * Situation update callback data
450 */
451 struct SituationUpdateCallbackData
452 {
453 Situation *s;
454 TCHAR *text;
455 Event *evt;
456 };
457
458 /**
459 * Situation update callback
460 */
461 static bool SituationUpdateCallback(const TCHAR *key, const void *value, void *data)
462 {
463 TCHAR *attrName = ((SituationUpdateCallbackData *)data)->evt->expandText(key);
464 TCHAR *attrValue = ((SituationUpdateCallbackData *)data)->evt->expandText((const TCHAR *)value);
465 ((SituationUpdateCallbackData *)data)->s->UpdateSituation(((SituationUpdateCallbackData *)data)->text, attrName, attrValue);
466 free(attrName);
467 free(attrValue);
468 return true;
469 }
470
471 /**
472 * Check if event match to rule and perform required actions if yes
473 * Method will return TRUE if event matched and RF_STOP_PROCESSING flag is set
474 */
475 bool EPRule::processEvent(Event *pEvent)
476 {
477 bool bStopProcessing = false;
478
479 // Check disable flag
480 if (!(m_dwFlags & RF_DISABLED))
481 {
482 // Check if event match
483 if (matchSource(pEvent->getSourceId()) && matchEvent(pEvent->getCode()) &&
484 matchSeverity(pEvent->getSeverity()) && matchScript(pEvent))
485 {
486 DbgPrintf(6, _T("Event ") UINT64_FMT _T(" match EPP rule %d"), pEvent->getId(), (int)m_id);
487
488 // Generate alarm if requested
489 if (m_dwFlags & RF_GENERATE_ALARM)
490 generateAlarm(pEvent);
491
492 // Event matched, perform actions
493 if (m_dwNumActions > 0)
494 {
495 TCHAR *alarmMessage = pEvent->expandText(m_szAlarmMessage);
496 TCHAR *alarmKey = pEvent->expandText(m_szAlarmKey);
497 for(UINT32 i = 0; i < m_dwNumActions; i++)
498 ExecuteAction(m_pdwActionList[i], pEvent, alarmMessage, alarmKey);
499 free(alarmMessage);
500 free(alarmKey);
501 }
502
503 // Update situation of needed
504 if (m_dwSituationId != 0)
505 {
506 Situation *pSituation = FindSituationById(m_dwSituationId);
507 if (pSituation != NULL)
508 {
509 SituationUpdateCallbackData data;
510 data.text = pEvent->expandText(m_szSituationInstance);
511 data.s = pSituation;
512 data.evt = pEvent;
513 m_situationAttrList.forEach(SituationUpdateCallback, &data);
514 free(data.text);
515 }
516 else
517 {
518 DbgPrintf(3, _T("Event Policy: unable to find situation with ID=%d"), m_dwSituationId);
519 }
520 }
521
522 bStopProcessing = (m_dwFlags & RF_STOP_PROCESSING) ? true : false;
523 }
524 }
525
526 return bStopProcessing;
527 }
528
529 /**
530 * Generate alarm from event
531 */
532 void EPRule::generateAlarm(Event *pEvent)
533 {
534 // Terminate alarms with key == our ack_key
535 if ((m_iAlarmSeverity == SEVERITY_RESOLVE) || (m_iAlarmSeverity == SEVERITY_TERMINATE))
536 {
537 TCHAR *pszAckKey = pEvent->expandText(m_szAlarmKey);
538 if (pszAckKey[0] != 0)
539 ResolveAlarmByKey(pszAckKey, (m_dwFlags & RF_TERMINATE_BY_REGEXP) ? true : false, m_iAlarmSeverity == SEVERITY_TERMINATE, pEvent);
540 free(pszAckKey);
541 }
542 else // Generate new alarm
543 {
544 CreateNewAlarm(m_szAlarmMessage, m_szAlarmKey, ALARM_STATE_OUTSTANDING,
545 (m_iAlarmSeverity == SEVERITY_FROM_EVENT) ? pEvent->getSeverity() : m_iAlarmSeverity,
546 m_dwAlarmTimeout, m_dwAlarmTimeoutEvent, pEvent, 0);
547 }
548 }
549
550 /**
551 * Load rule from database
552 */
553 bool EPRule::loadFromDB()
554 {
555 DB_RESULT hResult;
556 TCHAR szQuery[256], name[MAX_DB_STRING], value[MAX_DB_STRING];
557 bool bSuccess = true;
558 UINT32 i, count;
559
560 // Load rule's sources
561 _sntprintf(szQuery, 256, _T("SELECT object_id FROM policy_source_list WHERE rule_id=%d"), m_id);
562 hResult = DBSelect(g_hCoreDB, szQuery);
563 if (hResult != NULL)
564 {
565 m_dwNumSources = DBGetNumRows(hResult);
566 m_pdwSourceList = (UINT32 *)malloc(sizeof(UINT32) * m_dwNumSources);
567 for(i = 0; i < m_dwNumSources; i++)
568 m_pdwSourceList[i] = DBGetFieldULong(hResult, i, 0);
569 DBFreeResult(hResult);
570 }
571 else
572 {
573 bSuccess = false;
574 }
575
576 // Load rule's events
577 _sntprintf(szQuery, 256, _T("SELECT event_code FROM policy_event_list WHERE rule_id=%d"), m_id);
578 hResult = DBSelect(g_hCoreDB, szQuery);
579 if (hResult != NULL)
580 {
581 m_dwNumEvents = DBGetNumRows(hResult);
582 m_pdwEventList = (UINT32 *)malloc(sizeof(UINT32) * m_dwNumEvents);
583 for(i = 0; i < m_dwNumEvents; i++)
584 m_pdwEventList[i] = DBGetFieldULong(hResult, i, 0);
585 DBFreeResult(hResult);
586 }
587 else
588 {
589 bSuccess = false;
590 }
591
592 // Load rule's actions
593 _sntprintf(szQuery, 256, _T("SELECT action_id FROM policy_action_list WHERE rule_id=%d"), m_id);
594 hResult = DBSelect(g_hCoreDB, szQuery);
595 if (hResult != NULL)
596 {
597 m_dwNumActions = DBGetNumRows(hResult);
598 m_pdwActionList = (UINT32 *)malloc(sizeof(UINT32) * m_dwNumActions);
599 for(i = 0; i < m_dwNumActions; i++)
600 m_pdwActionList[i] = DBGetFieldULong(hResult, i, 0);
601 DBFreeResult(hResult);
602 }
603 else
604 {
605 bSuccess = false;
606 }
607
608 // Load situation attributes
609 _sntprintf(szQuery, 256, _T("SELECT attr_name,attr_value FROM policy_situation_attr_list WHERE rule_id=%d"), m_id);
610 hResult = DBSelect(g_hCoreDB, szQuery);
611 if (hResult != NULL)
612 {
613 count = DBGetNumRows(hResult);
614 for(i = 0; i < count; i++)
615 {
616 DBGetField(hResult, i, 0, name, MAX_DB_STRING);
617 DBGetField(hResult, i, 1, value, MAX_DB_STRING);
618 m_situationAttrList.set(name, value);
619 }
620 DBFreeResult(hResult);
621 }
622 else
623 {
624 bSuccess = false;
625 }
626
627 return bSuccess;
628 }
629
630 /**
631 * Callback for saving situation attributes
632 */
633 static bool SaveSituationAttribute(const TCHAR *key, const void *value, void *data)
634 {
635 DB_STATEMENT hStmt = (DB_STATEMENT)data;
636 DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, key, DB_BIND_STATIC);
637 DBBind(hStmt, 4, DB_SQLTYPE_VARCHAR, (const TCHAR *)value, DB_BIND_STATIC);
638 DBExecute(hStmt);
639 return true;
640 }
641
642 /**
643 * Save rule to database
644 */
645 void EPRule::saveToDB(DB_HANDLE hdb)
646 {
647 UINT32 len = (UINT32)(_tcslen(CHECK_NULL(m_pszComment)) + _tcslen(CHECK_NULL(m_pszScript)) + 4096);
648 TCHAR *pszQuery = (TCHAR *)malloc(len * sizeof(TCHAR));
649
650 // General attributes
651 TCHAR guidText[128];
652 _sntprintf(pszQuery, len, _T("INSERT INTO event_policy (rule_id,rule_guid,flags,comments,alarm_message,")
653 _T("alarm_severity,alarm_key,script,alarm_timeout,alarm_timeout_event,")
654 _T("situation_id,situation_instance) ")
655 _T("VALUES (%d,'%s',%d,%s,%s,%d,%s,%s,%d,%d,%d,%s)"),
656 m_id, uuid_to_string(m_guid, guidText),m_dwFlags, (const TCHAR *)DBPrepareString(hdb, m_pszComment),
657 (const TCHAR *)DBPrepareString(hdb, m_szAlarmMessage), m_iAlarmSeverity,
658 (const TCHAR *)DBPrepareString(hdb, m_szAlarmKey),
659 (const TCHAR *)DBPrepareString(hdb, m_pszScript), m_dwAlarmTimeout, m_dwAlarmTimeoutEvent,
660 m_dwSituationId, (const TCHAR *)DBPrepareString(hdb, m_szSituationInstance));
661 DBQuery(hdb, pszQuery);
662
663 // Actions
664 int i;
665 for(i = 0; i < (int)m_dwNumActions; i++)
666 {
667 _sntprintf(pszQuery, len, _T("INSERT INTO policy_action_list (rule_id,action_id) VALUES (%d,%d)"),
668 m_id, m_pdwActionList[i]);
669 DBQuery(hdb, pszQuery);
670 }
671
672 // Events
673 for(i = 0; i < (int)m_dwNumEvents; i++)
674 {
675 _sntprintf(pszQuery, len, _T("INSERT INTO policy_event_list (rule_id,event_code) VALUES (%d,%d)"),
676 m_id, m_pdwEventList[i]);
677 DBQuery(hdb, pszQuery);
678 }
679
680 // Sources
681 for(i = 0; i < (int)m_dwNumSources; i++)
682 {
683 _sntprintf(pszQuery, len, _T("INSERT INTO policy_source_list (rule_id,object_id) VALUES (%d,%d)"),
684 m_id, m_pdwSourceList[i]);
685 DBQuery(hdb, pszQuery);
686 }
687
688 // Situation attributes
689 if (m_situationAttrList.size() > 0)
690 {
691 DB_STATEMENT hStmt = DBPrepare(hdb, _T("INSERT INTO policy_situation_attr_list (rule_id,situation_id,attr_name,attr_value) VALUES (?,?,?,?)"));
692 if (hStmt != NULL)
693 {
694 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
695 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_dwSituationId);
696 m_situationAttrList.forEach(SaveSituationAttribute, hStmt);
697 DBFreeStatement(hStmt);
698 }
699 }
700
701 free(pszQuery);
702 }
703
704 /**
705 * Create NXCP message with rule's data
706 */
707 void EPRule::createMessage(NXCPMessage *msg)
708 {
709 msg->setField(VID_FLAGS, m_dwFlags);
710 msg->setField(VID_RULE_ID, m_id);
711 msg->setField(VID_GUID, m_guid, UUID_LENGTH);
712 msg->setField(VID_ALARM_SEVERITY, (WORD)m_iAlarmSeverity);
713 msg->setField(VID_ALARM_KEY, m_szAlarmKey);
714 msg->setField(VID_ALARM_MESSAGE, m_szAlarmMessage);
715 msg->setField(VID_ALARM_TIMEOUT, m_dwAlarmTimeout);
716 msg->setField(VID_ALARM_TIMEOUT_EVENT, m_dwAlarmTimeoutEvent);
717 msg->setField(VID_COMMENTS, CHECK_NULL_EX(m_pszComment));
718 msg->setField(VID_NUM_ACTIONS, m_dwNumActions);
719 msg->setFieldFromInt32Array(VID_RULE_ACTIONS, m_dwNumActions, m_pdwActionList);
720 msg->setField(VID_NUM_EVENTS, m_dwNumEvents);
721 msg->setFieldFromInt32Array(VID_RULE_EVENTS, m_dwNumEvents, m_pdwEventList);
722 msg->setField(VID_NUM_SOURCES, m_dwNumSources);
723 msg->setFieldFromInt32Array(VID_RULE_SOURCES, m_dwNumSources, m_pdwSourceList);
724 msg->setField(VID_SCRIPT, CHECK_NULL_EX(m_pszScript));
725 msg->setField(VID_SITUATION_ID, m_dwSituationId);
726 msg->setField(VID_SITUATION_INSTANCE, m_szSituationInstance);
727 m_situationAttrList.fillMessage(msg, VID_SITUATION_NUM_ATTRS, VID_SITUATION_ATTR_LIST_BASE);
728 }
729
730 /**
731 * Check if given action is used within rule
732 */
733 bool EPRule::isActionInUse(UINT32 dwActionId)
734 {
735 UINT32 i;
736
737 for(i = 0; i < m_dwNumActions; i++)
738 if (m_pdwActionList[i] == dwActionId)
739 return true;
740 return false;
741 }
742
743 /**
744 * Event processing policy constructor
745 */
746 EventPolicy::EventPolicy()
747 {
748 m_dwNumRules = 0;
749 m_ppRuleList = NULL;
750 m_rwlock = RWLockCreate();
751 }
752
753 /**
754 * Event processing policy destructor
755 */
756 EventPolicy::~EventPolicy()
757 {
758 clear();
759 RWLockDestroy(m_rwlock);
760 }
761
762 /**
763 * Clear existing policy
764 */
765 void EventPolicy::clear()
766 {
767 UINT32 i;
768
769 for(i = 0; i < m_dwNumRules; i++)
770 delete m_ppRuleList[i];
771 safe_free(m_ppRuleList);
772 m_ppRuleList = NULL;
773 }
774
775 /**
776 * Load event processing policy from database
777 */
778 bool EventPolicy::loadFromDB()
779 {
780 DB_RESULT hResult;
781 bool bSuccess = false;
782
783 hResult = DBSelect(g_hCoreDB, _T("SELECT rule_id,rule_guid,flags,comments,alarm_message,")
784 _T("alarm_severity,alarm_key,script,alarm_timeout,alarm_timeout_event,")
785 _T("situation_id,situation_instance ")
786 _T("FROM event_policy ORDER BY rule_id"));
787 if (hResult != NULL)
788 {
789 UINT32 i;
790
791 bSuccess = true;
792 m_dwNumRules = DBGetNumRows(hResult);
793 m_ppRuleList = (EPRule **)malloc(sizeof(EPRule *) * m_dwNumRules);
794 for(i = 0; i < m_dwNumRules; i++)
795 {
796 m_ppRuleList[i] = new EPRule(hResult, i);
797 bSuccess = bSuccess && m_ppRuleList[i]->loadFromDB();
798 }
799 DBFreeResult(hResult);
800 }
801
802 return bSuccess;
803 }
804
805 /**
806 * Save event processing policy to database
807 */
808 void EventPolicy::saveToDB()
809 {
810 UINT32 i;
811
812 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
813
814 readLock();
815 DBBegin(hdb);
816 DBQuery(hdb, _T("DELETE FROM event_policy"));
817 DBQuery(hdb, _T("DELETE FROM policy_action_list"));
818 DBQuery(hdb, _T("DELETE FROM policy_event_list"));
819 DBQuery(hdb, _T("DELETE FROM policy_source_list"));
820 DBQuery(hdb, _T("DELETE FROM policy_situation_attr_list"));
821 for(i = 0; i < m_dwNumRules; i++)
822 m_ppRuleList[i]->saveToDB(hdb);
823 DBCommit(hdb);
824 unlock();
825 DBConnectionPoolReleaseConnection(hdb);
826 }
827
828 /**
829 * Pass event through policy
830 */
831 void EventPolicy::processEvent(Event *pEvent)
832 {
833 UINT32 i;
834
835 DbgPrintf(7, _T("EPP: processing event ") UINT64_FMT, pEvent->getId());
836 readLock();
837 for(i = 0; i < m_dwNumRules; i++)
838 if (m_ppRuleList[i]->processEvent(pEvent))
839 {
840 DbgPrintf(7, _T("EPP: got \"stop processing\" flag for event ") UINT64_FMT _T(" at rule %d"), pEvent->getId(), i + 1);
841 break; // EPRule::ProcessEvent() return TRUE if we should stop processing this event
842 }
843 unlock();
844 }
845
846 /**
847 * Send event policy to client
848 */
849 void EventPolicy::sendToClient(ClientSession *pSession, UINT32 dwRqId)
850 {
851 UINT32 i;
852 NXCPMessage msg;
853
854 readLock();
855 msg.setCode(CMD_EPP_RECORD);
856 msg.setId(dwRqId);
857 for(i = 0; i < m_dwNumRules; i++)
858 {
859 m_ppRuleList[i]->createMessage(&msg);
860 pSession->sendMessage(&msg);
861 msg.deleteAllFields();
862 }
863 unlock();
864 }
865
866 /**
867 * Replace policy with new one
868 */
869 void EventPolicy::replacePolicy(UINT32 dwNumRules, EPRule **ppRuleList)
870 {
871 UINT32 i;
872
873 writeLock();
874
875 // Replace rule list
876 clear();
877 m_dwNumRules = dwNumRules;
878 m_ppRuleList = ppRuleList;
879
880 // Replace id's in rules
881 for(i = 0; i < m_dwNumRules; i++)
882 m_ppRuleList[i]->setId(i);
883
884 unlock();
885 }
886
887 /**
888 * Check if given action is used in policy
889 */
890 bool EventPolicy::isActionInUse(UINT32 dwActionId)
891 {
892 bool bResult = false;
893
894 readLock();
895
896 for(UINT32 i = 0; i < m_dwNumRules; i++)
897 if (m_ppRuleList[i]->isActionInUse(dwActionId))
898 {
899 bResult = true;
900 break;
901 }
902
903 unlock();
904 return bResult;
905 }
906
907 /**
908 * Export rule
909 */
910 void EventPolicy::exportRule(String &str, uuid_t guid)
911 {
912 readLock();
913 for(UINT32 i = 0; i < m_dwNumRules; i++)
914 {
915 if (!uuid_compare(guid, m_ppRuleList[i]->getGuid()))
916 {
917 m_ppRuleList[i]->createNXMPRecord(str);
918 break;
919 }
920 }
921 unlock();
922 }
923
924 /**
925 * Import rule
926 */
927 void EventPolicy::importRule(EPRule *rule)
928 {
929 writeLock();
930
931 // Find rule with same GUID and replace it if found
932 bool newRule = true;
933 for(UINT32 i = 0; i < m_dwNumRules; i++)
934 {
935 if (!uuid_compare(rule->getGuid(), m_ppRuleList[i]->getGuid()))
936 {
937 delete m_ppRuleList[i];
938 m_ppRuleList[i] = rule;
939 rule->setId(i);
940 newRule = false;
941 break;
942 }
943 }
944
945 // Add new rule at the end
946 if (newRule)
947 {
948 rule->setId(m_dwNumRules);
949 m_dwNumRules++;
950 m_ppRuleList = (EPRule **)realloc(m_ppRuleList, sizeof(EPRule *) * m_dwNumRules);
951 m_ppRuleList[m_dwNumRules - 1] = rule;
952 }
953
954 unlock();
955 }