2bedf8329c74609f328515d9750547e86fa689f0
[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.appendFormattedString(_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.appendFormattedString(_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.appendFormattedString(_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.appendFormattedString(_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_Value **ppValueList, *pValue;
384 NXSL_VariableSystem *pLocals, *pGlobals = NULL;
385 bool bRet = true;
386 UINT32 i;
387 NetObj *pObject;
388
389 if (m_pScript == NULL)
390 return true;
391
392 // Pass event's parameters as arguments and
393 // other information as variables
394 ppValueList = (NXSL_Value **)malloc(sizeof(NXSL_Value *) * pEvent->getParametersCount());
395 memset(ppValueList, 0, sizeof(NXSL_Value *) * pEvent->getParametersCount());
396 for(i = 0; i < pEvent->getParametersCount(); i++)
397 ppValueList[i] = new NXSL_Value(pEvent->getParameter(i));
398
399 pLocals = new NXSL_VariableSystem;
400 pLocals->create(_T("EVENT_CODE"), new NXSL_Value(pEvent->getCode()));
401 pLocals->create(_T("SEVERITY"), new NXSL_Value(pEvent->getSeverity()));
402 pLocals->create(_T("SEVERITY_TEXT"), new NXSL_Value(GetStatusAsText(pEvent->getSeverity(), true)));
403 pLocals->create(_T("OBJECT_ID"), new NXSL_Value(pEvent->getSourceId()));
404 pLocals->create(_T("EVENT_TEXT"), new NXSL_Value((TCHAR *)pEvent->getMessage()));
405 pLocals->create(_T("USER_TAG"), new NXSL_Value((TCHAR *)pEvent->getUserTag()));
406 pObject = FindObjectById(pEvent->getSourceId());
407 if (pObject != NULL)
408 {
409 if (pObject->getObjectClass() == OBJECT_NODE)
410 m_pScript->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, pObject)));
411 }
412 m_pScript->setGlobalVariable(_T("$event"), new NXSL_Value(new NXSL_Object(&g_nxslEventClass, pEvent)));
413 m_pScript->setGlobalVariable(_T("CUSTOM_MESSAGE"), new NXSL_Value);
414
415 // Run script
416 if (m_pScript->run(pEvent->getParametersCount(), ppValueList, pLocals, &pGlobals))
417 {
418 pValue = m_pScript->getResult();
419 if (pValue != NULL)
420 {
421 bRet = pValue->getValueAsInt32() ? true : false;
422 if (bRet)
423 {
424 NXSL_Variable *var;
425
426 var = pGlobals->find(_T("CUSTOM_MESSAGE"));
427 if (var != NULL)
428 {
429 // Update custom message in event
430 pEvent->setCustomMessage(CHECK_NULL_EX(var->getValue()->getValueAsCString()));
431 }
432 }
433 }
434 }
435 else
436 {
437 nxlog_write(MSG_EPRULE_SCRIPT_EXECUTION_ERROR, EVENTLOG_ERROR_TYPE, "ds", m_id + 1, m_pScript->getErrorText());
438 }
439 free(ppValueList);
440 delete pGlobals;
441
442 return bRet;
443 }
444
445 /**
446 * Situation update callback data
447 */
448 struct SituationUpdateCallbackData
449 {
450 Situation *s;
451 TCHAR *text;
452 Event *evt;
453 };
454
455 /**
456 * Situation update callback
457 */
458 static EnumerationCallbackResult SituationUpdateCallback(const TCHAR *key, const void *value, void *data)
459 {
460 TCHAR *attrName = ((SituationUpdateCallbackData *)data)->evt->expandText(key);
461 TCHAR *attrValue = ((SituationUpdateCallbackData *)data)->evt->expandText((const TCHAR *)value);
462 ((SituationUpdateCallbackData *)data)->s->UpdateSituation(((SituationUpdateCallbackData *)data)->text, attrName, attrValue);
463 free(attrName);
464 free(attrValue);
465 return _CONTINUE;
466 }
467
468 /**
469 * Check if event match to rule and perform required actions if yes
470 * Method will return TRUE if event matched and RF_STOP_PROCESSING flag is set
471 */
472 bool EPRule::processEvent(Event *pEvent)
473 {
474 bool bStopProcessing = false;
475
476 // Check disable flag
477 if (!(m_dwFlags & RF_DISABLED))
478 {
479 // Check if event match
480 if (matchSource(pEvent->getSourceId()) && matchEvent(pEvent->getCode()) &&
481 matchSeverity(pEvent->getSeverity()) && matchScript(pEvent))
482 {
483 DbgPrintf(6, _T("Event ") UINT64_FMT _T(" match EPP rule %d"), pEvent->getId(), (int)m_id);
484
485 // Generate alarm if requested
486 if (m_dwFlags & RF_GENERATE_ALARM)
487 generateAlarm(pEvent);
488
489 // Event matched, perform actions
490 if (m_dwNumActions > 0)
491 {
492 TCHAR *alarmMessage = pEvent->expandText(m_szAlarmMessage);
493 TCHAR *alarmKey = pEvent->expandText(m_szAlarmKey);
494 for(UINT32 i = 0; i < m_dwNumActions; i++)
495 ExecuteAction(m_pdwActionList[i], pEvent, alarmMessage, alarmKey);
496 free(alarmMessage);
497 free(alarmKey);
498 }
499
500 // Update situation of needed
501 if (m_dwSituationId != 0)
502 {
503 Situation *pSituation = FindSituationById(m_dwSituationId);
504 if (pSituation != NULL)
505 {
506 SituationUpdateCallbackData data;
507 data.text = pEvent->expandText(m_szSituationInstance);
508 data.s = pSituation;
509 data.evt = pEvent;
510 m_situationAttrList.forEach(SituationUpdateCallback, &data);
511 free(data.text);
512 }
513 else
514 {
515 DbgPrintf(3, _T("Event Policy: unable to find situation with ID=%d"), m_dwSituationId);
516 }
517 }
518
519 bStopProcessing = (m_dwFlags & RF_STOP_PROCESSING) ? true : false;
520 }
521 }
522
523 return bStopProcessing;
524 }
525
526 /**
527 * Generate alarm from event
528 */
529 void EPRule::generateAlarm(Event *pEvent)
530 {
531 // Terminate alarms with key == our ack_key
532 if ((m_iAlarmSeverity == SEVERITY_RESOLVE) || (m_iAlarmSeverity == SEVERITY_TERMINATE))
533 {
534 TCHAR *pszAckKey = pEvent->expandText(m_szAlarmKey);
535 if (pszAckKey[0] != 0)
536 ResolveAlarmByKey(pszAckKey, (m_dwFlags & RF_TERMINATE_BY_REGEXP) ? true : false, m_iAlarmSeverity == SEVERITY_TERMINATE, pEvent);
537 free(pszAckKey);
538 }
539 else // Generate new alarm
540 {
541 CreateNewAlarm(m_szAlarmMessage, m_szAlarmKey, ALARM_STATE_OUTSTANDING,
542 (m_iAlarmSeverity == SEVERITY_FROM_EVENT) ? pEvent->getSeverity() : m_iAlarmSeverity,
543 m_dwAlarmTimeout, m_dwAlarmTimeoutEvent, pEvent, 0);
544 }
545 }
546
547 /**
548 * Load rule from database
549 */
550 bool EPRule::loadFromDB()
551 {
552 DB_RESULT hResult;
553 TCHAR szQuery[256], name[MAX_DB_STRING], value[MAX_DB_STRING];
554 bool bSuccess = true;
555 UINT32 i, count;
556
557 // Load rule's sources
558 _sntprintf(szQuery, 256, _T("SELECT object_id FROM policy_source_list WHERE rule_id=%d"), m_id);
559 hResult = DBSelect(g_hCoreDB, szQuery);
560 if (hResult != NULL)
561 {
562 m_dwNumSources = DBGetNumRows(hResult);
563 m_pdwSourceList = (UINT32 *)malloc(sizeof(UINT32) * m_dwNumSources);
564 for(i = 0; i < m_dwNumSources; i++)
565 m_pdwSourceList[i] = DBGetFieldULong(hResult, i, 0);
566 DBFreeResult(hResult);
567 }
568 else
569 {
570 bSuccess = false;
571 }
572
573 // Load rule's events
574 _sntprintf(szQuery, 256, _T("SELECT event_code FROM policy_event_list WHERE rule_id=%d"), m_id);
575 hResult = DBSelect(g_hCoreDB, szQuery);
576 if (hResult != NULL)
577 {
578 m_dwNumEvents = DBGetNumRows(hResult);
579 m_pdwEventList = (UINT32 *)malloc(sizeof(UINT32) * m_dwNumEvents);
580 for(i = 0; i < m_dwNumEvents; i++)
581 m_pdwEventList[i] = DBGetFieldULong(hResult, i, 0);
582 DBFreeResult(hResult);
583 }
584 else
585 {
586 bSuccess = false;
587 }
588
589 // Load rule's actions
590 _sntprintf(szQuery, 256, _T("SELECT action_id FROM policy_action_list WHERE rule_id=%d"), m_id);
591 hResult = DBSelect(g_hCoreDB, szQuery);
592 if (hResult != NULL)
593 {
594 m_dwNumActions = DBGetNumRows(hResult);
595 m_pdwActionList = (UINT32 *)malloc(sizeof(UINT32) * m_dwNumActions);
596 for(i = 0; i < m_dwNumActions; i++)
597 m_pdwActionList[i] = DBGetFieldULong(hResult, i, 0);
598 DBFreeResult(hResult);
599 }
600 else
601 {
602 bSuccess = false;
603 }
604
605 // Load situation attributes
606 _sntprintf(szQuery, 256, _T("SELECT attr_name,attr_value FROM policy_situation_attr_list WHERE rule_id=%d"), m_id);
607 hResult = DBSelect(g_hCoreDB, szQuery);
608 if (hResult != NULL)
609 {
610 count = DBGetNumRows(hResult);
611 for(i = 0; i < count; i++)
612 {
613 DBGetField(hResult, i, 0, name, MAX_DB_STRING);
614 DBGetField(hResult, i, 1, value, MAX_DB_STRING);
615 m_situationAttrList.set(name, value);
616 }
617 DBFreeResult(hResult);
618 }
619 else
620 {
621 bSuccess = false;
622 }
623
624 return bSuccess;
625 }
626
627 /**
628 * Callback for saving situation attributes
629 */
630 static EnumerationCallbackResult SaveSituationAttribute(const TCHAR *key, const void *value, void *data)
631 {
632 DB_STATEMENT hStmt = (DB_STATEMENT)data;
633 DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, key, DB_BIND_STATIC);
634 DBBind(hStmt, 4, DB_SQLTYPE_VARCHAR, (const TCHAR *)value, DB_BIND_STATIC);
635 DBExecute(hStmt);
636 return _CONTINUE;
637 }
638
639 /**
640 * Save rule to database
641 */
642 void EPRule::saveToDB(DB_HANDLE hdb)
643 {
644 UINT32 len = (UINT32)(_tcslen(CHECK_NULL(m_pszComment)) + _tcslen(CHECK_NULL(m_pszScript)) + 4096);
645 TCHAR *pszQuery = (TCHAR *)malloc(len * sizeof(TCHAR));
646
647 // General attributes
648 TCHAR guidText[128];
649 _sntprintf(pszQuery, len, _T("INSERT INTO event_policy (rule_id,rule_guid,flags,comments,alarm_message,")
650 _T("alarm_severity,alarm_key,script,alarm_timeout,alarm_timeout_event,")
651 _T("situation_id,situation_instance) ")
652 _T("VALUES (%d,'%s',%d,%s,%s,%d,%s,%s,%d,%d,%d,%s)"),
653 m_id, uuid_to_string(m_guid, guidText),m_dwFlags, (const TCHAR *)DBPrepareString(hdb, m_pszComment),
654 (const TCHAR *)DBPrepareString(hdb, m_szAlarmMessage), m_iAlarmSeverity,
655 (const TCHAR *)DBPrepareString(hdb, m_szAlarmKey),
656 (const TCHAR *)DBPrepareString(hdb, m_pszScript), m_dwAlarmTimeout, m_dwAlarmTimeoutEvent,
657 m_dwSituationId, (const TCHAR *)DBPrepareString(hdb, m_szSituationInstance));
658 DBQuery(hdb, pszQuery);
659
660 // Actions
661 int i;
662 for(i = 0; i < (int)m_dwNumActions; i++)
663 {
664 _sntprintf(pszQuery, len, _T("INSERT INTO policy_action_list (rule_id,action_id) VALUES (%d,%d)"),
665 m_id, m_pdwActionList[i]);
666 DBQuery(hdb, pszQuery);
667 }
668
669 // Events
670 for(i = 0; i < (int)m_dwNumEvents; i++)
671 {
672 _sntprintf(pszQuery, len, _T("INSERT INTO policy_event_list (rule_id,event_code) VALUES (%d,%d)"),
673 m_id, m_pdwEventList[i]);
674 DBQuery(hdb, pszQuery);
675 }
676
677 // Sources
678 for(i = 0; i < (int)m_dwNumSources; i++)
679 {
680 _sntprintf(pszQuery, len, _T("INSERT INTO policy_source_list (rule_id,object_id) VALUES (%d,%d)"),
681 m_id, m_pdwSourceList[i]);
682 DBQuery(hdb, pszQuery);
683 }
684
685 // Situation attributes
686 if (m_situationAttrList.size() > 0)
687 {
688 DB_STATEMENT hStmt = DBPrepare(hdb, _T("INSERT INTO policy_situation_attr_list (rule_id,situation_id,attr_name,attr_value) VALUES (?,?,?,?)"));
689 if (hStmt != NULL)
690 {
691 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
692 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_dwSituationId);
693 m_situationAttrList.forEach(SaveSituationAttribute, hStmt);
694 DBFreeStatement(hStmt);
695 }
696 }
697
698 free(pszQuery);
699 }
700
701 /**
702 * Create NXCP message with rule's data
703 */
704 void EPRule::createMessage(NXCPMessage *msg)
705 {
706 msg->setField(VID_FLAGS, m_dwFlags);
707 msg->setField(VID_RULE_ID, m_id);
708 msg->setField(VID_GUID, m_guid, UUID_LENGTH);
709 msg->setField(VID_ALARM_SEVERITY, (WORD)m_iAlarmSeverity);
710 msg->setField(VID_ALARM_KEY, m_szAlarmKey);
711 msg->setField(VID_ALARM_MESSAGE, m_szAlarmMessage);
712 msg->setField(VID_ALARM_TIMEOUT, m_dwAlarmTimeout);
713 msg->setField(VID_ALARM_TIMEOUT_EVENT, m_dwAlarmTimeoutEvent);
714 msg->setField(VID_COMMENTS, CHECK_NULL_EX(m_pszComment));
715 msg->setField(VID_NUM_ACTIONS, m_dwNumActions);
716 msg->setFieldFromInt32Array(VID_RULE_ACTIONS, m_dwNumActions, m_pdwActionList);
717 msg->setField(VID_NUM_EVENTS, m_dwNumEvents);
718 msg->setFieldFromInt32Array(VID_RULE_EVENTS, m_dwNumEvents, m_pdwEventList);
719 msg->setField(VID_NUM_SOURCES, m_dwNumSources);
720 msg->setFieldFromInt32Array(VID_RULE_SOURCES, m_dwNumSources, m_pdwSourceList);
721 msg->setField(VID_SCRIPT, CHECK_NULL_EX(m_pszScript));
722 msg->setField(VID_SITUATION_ID, m_dwSituationId);
723 msg->setField(VID_SITUATION_INSTANCE, m_szSituationInstance);
724 m_situationAttrList.fillMessage(msg, VID_SITUATION_NUM_ATTRS, VID_SITUATION_ATTR_LIST_BASE);
725 }
726
727 /**
728 * Check if given action is used within rule
729 */
730 bool EPRule::isActionInUse(UINT32 dwActionId)
731 {
732 UINT32 i;
733
734 for(i = 0; i < m_dwNumActions; i++)
735 if (m_pdwActionList[i] == dwActionId)
736 return true;
737 return false;
738 }
739
740 /**
741 * Event processing policy constructor
742 */
743 EventPolicy::EventPolicy()
744 {
745 m_dwNumRules = 0;
746 m_ppRuleList = NULL;
747 m_rwlock = RWLockCreate();
748 }
749
750 /**
751 * Event processing policy destructor
752 */
753 EventPolicy::~EventPolicy()
754 {
755 clear();
756 RWLockDestroy(m_rwlock);
757 }
758
759 /**
760 * Clear existing policy
761 */
762 void EventPolicy::clear()
763 {
764 UINT32 i;
765
766 for(i = 0; i < m_dwNumRules; i++)
767 delete m_ppRuleList[i];
768 safe_free(m_ppRuleList);
769 m_ppRuleList = NULL;
770 }
771
772 /**
773 * Load event processing policy from database
774 */
775 bool EventPolicy::loadFromDB()
776 {
777 DB_RESULT hResult;
778 bool bSuccess = false;
779
780 hResult = DBSelect(g_hCoreDB, _T("SELECT rule_id,rule_guid,flags,comments,alarm_message,")
781 _T("alarm_severity,alarm_key,script,alarm_timeout,alarm_timeout_event,")
782 _T("situation_id,situation_instance ")
783 _T("FROM event_policy ORDER BY rule_id"));
784 if (hResult != NULL)
785 {
786 UINT32 i;
787
788 bSuccess = true;
789 m_dwNumRules = DBGetNumRows(hResult);
790 m_ppRuleList = (EPRule **)malloc(sizeof(EPRule *) * m_dwNumRules);
791 for(i = 0; i < m_dwNumRules; i++)
792 {
793 m_ppRuleList[i] = new EPRule(hResult, i);
794 bSuccess = bSuccess && m_ppRuleList[i]->loadFromDB();
795 }
796 DBFreeResult(hResult);
797 }
798
799 return bSuccess;
800 }
801
802 /**
803 * Save event processing policy to database
804 */
805 void EventPolicy::saveToDB()
806 {
807 UINT32 i;
808
809 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
810
811 readLock();
812 DBBegin(hdb);
813 DBQuery(hdb, _T("DELETE FROM event_policy"));
814 DBQuery(hdb, _T("DELETE FROM policy_action_list"));
815 DBQuery(hdb, _T("DELETE FROM policy_event_list"));
816 DBQuery(hdb, _T("DELETE FROM policy_source_list"));
817 DBQuery(hdb, _T("DELETE FROM policy_situation_attr_list"));
818 for(i = 0; i < m_dwNumRules; i++)
819 m_ppRuleList[i]->saveToDB(hdb);
820 DBCommit(hdb);
821 unlock();
822 DBConnectionPoolReleaseConnection(hdb);
823 }
824
825 /**
826 * Pass event through policy
827 */
828 void EventPolicy::processEvent(Event *pEvent)
829 {
830 UINT32 i;
831
832 DbgPrintf(7, _T("EPP: processing event ") UINT64_FMT, pEvent->getId());
833 readLock();
834 for(i = 0; i < m_dwNumRules; i++)
835 if (m_ppRuleList[i]->processEvent(pEvent))
836 {
837 DbgPrintf(7, _T("EPP: got \"stop processing\" flag for event ") UINT64_FMT _T(" at rule %d"), pEvent->getId(), i + 1);
838 break; // EPRule::ProcessEvent() return TRUE if we should stop processing this event
839 }
840 unlock();
841 }
842
843 /**
844 * Send event policy to client
845 */
846 void EventPolicy::sendToClient(ClientSession *pSession, UINT32 dwRqId)
847 {
848 UINT32 i;
849 NXCPMessage msg;
850
851 readLock();
852 msg.setCode(CMD_EPP_RECORD);
853 msg.setId(dwRqId);
854 for(i = 0; i < m_dwNumRules; i++)
855 {
856 m_ppRuleList[i]->createMessage(&msg);
857 pSession->sendMessage(&msg);
858 msg.deleteAllFields();
859 }
860 unlock();
861 }
862
863 /**
864 * Replace policy with new one
865 */
866 void EventPolicy::replacePolicy(UINT32 dwNumRules, EPRule **ppRuleList)
867 {
868 UINT32 i;
869
870 writeLock();
871
872 // Replace rule list
873 clear();
874 m_dwNumRules = dwNumRules;
875 m_ppRuleList = ppRuleList;
876
877 // Replace id's in rules
878 for(i = 0; i < m_dwNumRules; i++)
879 m_ppRuleList[i]->setId(i);
880
881 unlock();
882 }
883
884 /**
885 * Check if given action is used in policy
886 */
887 bool EventPolicy::isActionInUse(UINT32 dwActionId)
888 {
889 bool bResult = false;
890
891 readLock();
892
893 for(UINT32 i = 0; i < m_dwNumRules; i++)
894 if (m_ppRuleList[i]->isActionInUse(dwActionId))
895 {
896 bResult = true;
897 break;
898 }
899
900 unlock();
901 return bResult;
902 }
903
904 /**
905 * Export rule
906 */
907 void EventPolicy::exportRule(String &str, uuid_t guid)
908 {
909 readLock();
910 for(UINT32 i = 0; i < m_dwNumRules; i++)
911 {
912 if (!uuid_compare(guid, m_ppRuleList[i]->getGuid()))
913 {
914 m_ppRuleList[i]->createNXMPRecord(str);
915 break;
916 }
917 }
918 unlock();
919 }
920
921 /**
922 * Import rule
923 */
924 void EventPolicy::importRule(EPRule *rule)
925 {
926 writeLock();
927
928 // Find rule with same GUID and replace it if found
929 bool newRule = true;
930 for(UINT32 i = 0; i < m_dwNumRules; i++)
931 {
932 if (!uuid_compare(rule->getGuid(), m_ppRuleList[i]->getGuid()))
933 {
934 delete m_ppRuleList[i];
935 m_ppRuleList[i] = rule;
936 rule->setId(i);
937 newRule = false;
938 break;
939 }
940 }
941
942 // Add new rule at the end
943 if (newRule)
944 {
945 rule->setId(m_dwNumRules);
946 m_dwNumRules++;
947 m_ppRuleList = (EPRule **)realloc(m_ppRuleList, sizeof(EPRule *) * m_dwNumRules);
948 m_ppRuleList[m_dwNumRules - 1] = rule;
949 }
950
951 unlock();
952 }