0504aeffa1716138f75ccfc67662a23022a0b5fb
[public/netxms.git] / src / server / core / dctthreshold.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2015 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: dctthreshold.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Create new condition
27 */
28 DCTableCondition::DCTableCondition(const TCHAR *column, int operation, const TCHAR *value)
29 {
30 m_column = _tcsdup(CHECK_NULL_EX(column));
31 m_operation = operation;
32 m_value = value;
33 }
34
35 /**
36 * Copy constructor
37 */
38 DCTableCondition::DCTableCondition(DCTableCondition *src)
39 {
40 m_column = _tcsdup(src->m_column);
41 m_operation = src->m_operation;
42 m_value = src->m_value;
43 }
44
45 /**
46 * Condition destructor
47 */
48 DCTableCondition::~DCTableCondition()
49 {
50 free(m_column);
51 }
52
53 /**
54 * Check if condition is true
55 */
56 bool DCTableCondition::check(Table *value, int row)
57 {
58 int col = value->getColumnIndex(m_column);
59 if (col == -1)
60 return false;
61
62 int dt = value->getColumnDataType(col);
63 bool result = false;
64 switch(m_operation)
65 {
66 case OP_LE: // Less
67 switch(dt)
68 {
69 case DCI_DT_INT:
70 result = (value->getAsInt(row, col) < (INT32)m_value);
71 break;
72 case DCI_DT_UINT:
73 result = (value->getAsUInt(row, col) < (UINT32)m_value);
74 break;
75 case DCI_DT_INT64:
76 result = (value->getAsInt64(row, col) < (INT64)m_value);
77 break;
78 case DCI_DT_UINT64:
79 result = (value->getAsUInt64(row, col) < (UINT64)m_value);
80 break;
81 case DCI_DT_FLOAT:
82 result = (value->getAsDouble(row, col) < (double)m_value);
83 break;
84 }
85 break;
86 case OP_LE_EQ: // Less or equal
87 switch(dt)
88 {
89 case DCI_DT_INT:
90 result = (value->getAsInt(row, col) <= (INT32)m_value);
91 break;
92 case DCI_DT_UINT:
93 result = (value->getAsUInt(row, col) <= (UINT32)m_value);
94 break;
95 case DCI_DT_INT64:
96 result = (value->getAsInt64(row, col) <= (INT64)m_value);
97 break;
98 case DCI_DT_UINT64:
99 result = (value->getAsUInt64(row, col) <= (UINT64)m_value);
100 break;
101 case DCI_DT_FLOAT:
102 result = (value->getAsDouble(row, col) <= (double)m_value);
103 break;
104 }
105 break;
106 case OP_EQ: // Equal
107 switch(dt)
108 {
109 case DCI_DT_INT:
110 result = (value->getAsInt(row, col) == (INT32)m_value);
111 break;
112 case DCI_DT_UINT:
113 result = (value->getAsUInt(row, col) == (UINT32)m_value);
114 break;
115 case DCI_DT_INT64:
116 result = (value->getAsInt64(row, col) == (INT64)m_value);
117 break;
118 case DCI_DT_UINT64:
119 result = (value->getAsUInt64(row, col) == (UINT64)m_value);
120 break;
121 case DCI_DT_FLOAT:
122 result = (value->getAsDouble(row, col) == (double)m_value);
123 break;
124 case DCI_DT_STRING:
125 result = !_tcscmp(value->getAsString(row, col, _T("")), m_value.getString());
126 break;
127 }
128 break;
129 case OP_GT_EQ: // Greater or equal
130 switch(dt)
131 {
132 case DCI_DT_INT:
133 result = (value->getAsInt(row, col) >= (INT32)m_value);
134 break;
135 case DCI_DT_UINT:
136 result = (value->getAsUInt(row, col) >= (UINT32)m_value);
137 break;
138 case DCI_DT_INT64:
139 result = (value->getAsInt64(row, col) >= (INT64)m_value);
140 break;
141 case DCI_DT_UINT64:
142 result = (value->getAsUInt64(row, col) >= (UINT64)m_value);
143 break;
144 case DCI_DT_FLOAT:
145 result = (value->getAsDouble(row, col) >= (double)m_value);
146 break;
147 }
148 break;
149 case OP_GT: // Greater
150 switch(dt)
151 {
152 case DCI_DT_INT:
153 result = (value->getAsInt(row, col) > (INT32)m_value);
154 break;
155 case DCI_DT_UINT:
156 result = (value->getAsUInt(row, col) > (UINT32)m_value);
157 break;
158 case DCI_DT_INT64:
159 result = (value->getAsInt64(row, col) > (INT64)m_value);
160 break;
161 case DCI_DT_UINT64:
162 result = (value->getAsUInt64(row, col) > (UINT64)m_value);
163 break;
164 case DCI_DT_FLOAT:
165 result = (value->getAsDouble(row, col) > (double)m_value);
166 break;
167 }
168 break;
169 case OP_NE: // Not equal
170 switch(dt)
171 {
172 case DCI_DT_INT:
173 result = (value->getAsInt(row, col) != (INT32)m_value);
174 break;
175 case DCI_DT_UINT:
176 result = (value->getAsUInt(row, col) != (UINT32)m_value);
177 break;
178 case DCI_DT_INT64:
179 result = (value->getAsInt64(row, col) != (INT64)m_value);
180 break;
181 case DCI_DT_UINT64:
182 result = (value->getAsUInt64(row, col) != (UINT64)m_value);
183 break;
184 case DCI_DT_FLOAT:
185 result = (value->getAsDouble(row, col) != (double)m_value);
186 break;
187 case DCI_DT_STRING:
188 result = _tcscmp(value->getAsString(row, col, _T("")), m_value.getString()) ? true : false;
189 break;
190 }
191 break;
192 case OP_LIKE:
193 result = MatchString(m_value.getString(), value->getAsString(row, col, _T("")), true);
194 break;
195 case OP_NOTLIKE:
196 result = !MatchString(m_value.getString(), value->getAsString(row, col, _T("")), true);
197 break;
198 default:
199 break;
200 }
201 return result;
202 }
203
204 /**
205 * Condition group constructor
206 */
207 DCTableConditionGroup::DCTableConditionGroup()
208 {
209 m_conditions = new ObjectArray<DCTableCondition>(8, 8, true);
210 }
211
212 /**
213 * Condition group copy constructor
214 */
215 DCTableConditionGroup::DCTableConditionGroup(DCTableConditionGroup *src)
216 {
217 m_conditions = new ObjectArray<DCTableCondition>(src->m_conditions->size(), 8, true);
218 for(int i = 0; i < src->m_conditions->size(); i++)
219 m_conditions->add(new DCTableCondition(src->m_conditions->get(i)));
220 }
221
222 /**
223 * Create condition group from NXCP message
224 */
225 DCTableConditionGroup::DCTableConditionGroup(NXCPMessage *msg, UINT32 *baseId)
226 {
227 UINT32 varId = *baseId;
228 int count = msg->getFieldAsUInt32(varId++);
229 m_conditions = new ObjectArray<DCTableCondition>(count, 8, true);
230 for(int i = 0; i < count; i++)
231 {
232 TCHAR column[MAX_COLUMN_NAME], value[MAX_RESULT_LENGTH];
233 msg->getFieldAsString(varId++, column, MAX_COLUMN_NAME);
234 int op = (int)msg->getFieldAsUInt16(varId++);
235 msg->getFieldAsString(varId++, value, MAX_RESULT_LENGTH);
236 m_conditions->add(new DCTableCondition(column, op, value));
237 }
238 *baseId = varId;
239 }
240
241 /**
242 * Create condition group from NXMP record
243 */
244 DCTableConditionGroup::DCTableConditionGroup(ConfigEntry *e)
245 {
246 ConfigEntry *root = e->findEntry(_T("conditions"));
247 if (root != NULL)
248 {
249 ObjectArray<ConfigEntry> *conditions = root->getSubEntries(_T("condition#*"));
250 m_conditions = new ObjectArray<DCTableCondition>(conditions->size(), 4, true);
251 for(int i = 0; i < conditions->size(); i++)
252 {
253 ConfigEntry *c = conditions->get(i);
254 const TCHAR *column = c->getSubEntryValue(_T("column"), 0, _T(""));
255 const TCHAR *value = c->getSubEntryValue(_T("value"), 0, _T(""));
256 int op = c->getSubEntryValueAsInt(_T("operation"));
257 m_conditions->add(new DCTableCondition(column, op, value));
258 }
259 delete conditions;
260 }
261 else
262 {
263 m_conditions = new ObjectArray<DCTableCondition>(8, 8, true);
264 }
265 }
266
267 /**
268 * Condition group destructor
269 */
270 DCTableConditionGroup::~DCTableConditionGroup()
271 {
272 delete m_conditions;
273 }
274
275 /**
276 * Fill NXCP mesage
277 */
278 UINT32 DCTableConditionGroup::fillMessage(NXCPMessage *msg, UINT32 baseId)
279 {
280 UINT32 varId = baseId;
281 msg->setField(varId++, (UINT32)m_conditions->size());
282 for(int i = 0; i < m_conditions->size(); i++)
283 {
284 DCTableCondition *c = m_conditions->get(i);
285 msg->setField(varId++, c->getColumn());
286 msg->setField(varId++, (UINT16)c->getOperation());
287 msg->setField(varId++, c->getValue());
288 }
289 return varId;
290 }
291
292 /**
293 * Check if condition group is true
294 */
295 bool DCTableConditionGroup::check(Table *value, int row)
296 {
297 for(int i = 0; i < m_conditions->size(); i++)
298 if (!m_conditions->get(i)->check(value, row))
299 return false;
300 return true;
301 }
302
303 /**
304 * Table threshold constructor
305 */
306 DCTableThreshold::DCTableThreshold()
307 {
308 m_id = CreateUniqueId(IDG_THRESHOLD);
309 m_groups = new ObjectArray<DCTableConditionGroup>(4, 4, true);
310 m_activationEvent = EVENT_TABLE_THRESHOLD_ACTIVATED;
311 m_deactivationEvent = EVENT_TABLE_THRESHOLD_DEACTIVATED;
312 m_activeKeys = new StringSet;
313 }
314
315 /**
316 * Table threshold copy constructor
317 */
318 DCTableThreshold::DCTableThreshold(DCTableThreshold *src)
319 {
320 m_id = CreateUniqueId(IDG_THRESHOLD);
321 m_groups = new ObjectArray<DCTableConditionGroup>(src->m_groups->size(), 4, true);
322 for(int i = 0; i < src->m_groups->size(); i++)
323 m_groups->add(new DCTableConditionGroup(src->m_groups->get(i)));
324 m_activationEvent = src->m_activationEvent;
325 m_deactivationEvent = src->m_deactivationEvent;
326 m_activeKeys = new StringSet;
327 }
328
329 /**
330 * Create table threshold from database
331 * Expected column order: id,activation_event,deactivation_event
332 */
333 DCTableThreshold::DCTableThreshold(DB_HANDLE hdb, DB_RESULT hResult, int row)
334 {
335 m_id = DBGetFieldLong(hResult, row, 0);
336 m_activationEvent = DBGetFieldULong(hResult, row, 1);
337 m_deactivationEvent = DBGetFieldULong(hResult, row, 2);
338 m_groups = new ObjectArray<DCTableConditionGroup>(4, 4, true);
339 loadConditions(hdb);
340 m_activeKeys = new StringSet;
341 }
342
343 /**
344 * Create table threshold from NXCP message
345 */
346 DCTableThreshold::DCTableThreshold(NXCPMessage *msg, UINT32 *baseId)
347 {
348 UINT32 varId = *baseId;
349 m_id = msg->getFieldAsUInt32(varId++);
350 if (m_id == 0)
351 m_id = CreateUniqueId(IDG_THRESHOLD);
352 m_activationEvent = msg->getFieldAsUInt32(varId++);
353 m_deactivationEvent = msg->getFieldAsUInt32(varId++);
354 int count = (int)msg->getFieldAsUInt32(varId++);
355 m_groups = new ObjectArray<DCTableConditionGroup>(count, 4, true);
356 *baseId = varId;
357 for(int i = 0; i < count; i++)
358 m_groups->add(new DCTableConditionGroup(msg, baseId));
359 m_activeKeys = new StringSet;
360 }
361
362 /**
363 * Create from NXMP record
364 */
365 DCTableThreshold::DCTableThreshold(ConfigEntry *e)
366 {
367 m_id = CreateUniqueId(IDG_THRESHOLD);
368 m_activationEvent = EventCodeFromName(e->getSubEntryValue(_T("activationEvent"), 0, _T("SYS_TABLE_THRESHOLD_ACTIVATED")));
369 m_deactivationEvent = EventCodeFromName(e->getSubEntryValue(_T("deactivationEvent"), 0, _T("SYS_TABLE_THRESHOLD_DEACTIVATED")));
370
371 ConfigEntry *groupsRoot = e->findEntry(_T("groups"));
372 if (groupsRoot != NULL)
373 {
374 ObjectArray<ConfigEntry> *groups = groupsRoot->getSubEntries(_T("group#*"));
375 m_groups = new ObjectArray<DCTableConditionGroup>(groups->size(), 4, true);
376 for(int i = 0; i < groups->size(); i++)
377 {
378 m_groups->add(new DCTableConditionGroup(groups->get(i)));
379 }
380 delete groups;
381 }
382 else
383 {
384 m_groups = new ObjectArray<DCTableConditionGroup>(4, 4, true);
385 }
386 m_activeKeys = new StringSet;
387 }
388
389 /**
390 * Load conditions from database
391 */
392 void DCTableThreshold::loadConditions(DB_HANDLE hdb)
393 {
394 DB_STATEMENT hStmt = DBPrepare(hdb, _T("SELECT group_id,column_name,check_operation,check_value FROM dct_threshold_conditions WHERE threshold_id=? ORDER BY group_id,sequence_number"));
395 if (hStmt == NULL)
396 return;
397
398 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
399 DB_RESULT hResult = DBSelectPrepared(hStmt);
400 if (hResult != NULL)
401 {
402 int count = DBGetNumRows(hResult);
403 if (count > 0)
404 {
405 DCTableConditionGroup *group = NULL;
406 int groupId = -1;
407 for(int i = 0; i < count; i++)
408 {
409 if ((DBGetFieldLong(hResult, i, 0) != groupId) || (group == NULL))
410 {
411 groupId = DBGetFieldLong(hResult, i, 0);
412 group = new DCTableConditionGroup();
413 m_groups->add(group);
414 }
415 TCHAR column[MAX_COLUMN_NAME], value[MAX_RESULT_LENGTH];
416 group->getConditions()->add(
417 new DCTableCondition(
418 DBGetField(hResult, i, 1, column, MAX_COLUMN_NAME),
419 DBGetFieldLong(hResult, i, 2),
420 DBGetField(hResult, i, 3, value, MAX_RESULT_LENGTH)));
421 }
422 }
423 DBFreeResult(hResult);
424 }
425 DBFreeStatement(hStmt);
426 }
427
428 /**
429 * Table threshold destructor
430 */
431 DCTableThreshold::~DCTableThreshold()
432 {
433 delete m_groups;
434 delete m_activeKeys;
435 }
436
437 /**
438 * Save threshold to database
439 */
440 bool DCTableThreshold::saveToDatabase(DB_HANDLE hdb, UINT32 tableId, int seq)
441 {
442 DB_STATEMENT hStmt = DBPrepare(hdb, _T("INSERT INTO dct_thresholds (id,table_id,sequence_number,activation_event,deactivation_event) VALUES (?,?,?,?,?)"));
443 if (hStmt == NULL)
444 return false;
445
446 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
447 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, tableId);
448 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (INT32)seq);
449 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, m_activationEvent);
450 DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, m_deactivationEvent);
451 DBExecute(hStmt);
452 DBFreeStatement(hStmt);
453
454 if (m_groups->size() > 0)
455 {
456 hStmt = DBPrepare(hdb, _T("INSERT INTO dct_threshold_conditions (threshold_id,group_id,sequence_number,column_name,check_operation,check_value) VALUES (?,?,?,?,?,?)"));
457 if (hStmt == NULL)
458 return false;
459 for(int i = 0; i < m_groups->size(); i++)
460 {
461 DCTableConditionGroup *group = m_groups->get(i);
462 ObjectArray<DCTableCondition> *conditions = group->getConditions();
463 for(int j = 0; j < conditions->size(); j++)
464 {
465 DCTableCondition *c = conditions->get(j);
466 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
467 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, (INT32)i);
468 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (INT32)j);
469 DBBind(hStmt, 4, DB_SQLTYPE_VARCHAR, c->getColumn(), DB_BIND_STATIC);
470 DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, (INT32)c->getOperation());
471 DBBind(hStmt, 6, DB_SQLTYPE_VARCHAR, c->getValue(), DB_BIND_STATIC);
472 DBExecute(hStmt);
473 }
474 }
475 DBFreeStatement(hStmt);
476 }
477 return true;
478 }
479
480 /**
481 * Fill NXCP message with threshold data
482 */
483 UINT32 DCTableThreshold::fillMessage(NXCPMessage *msg, UINT32 baseId)
484 {
485 UINT32 varId = baseId;
486 msg->setField(varId++, m_id);
487 msg->setField(varId++, m_activationEvent);
488 msg->setField(varId++, m_deactivationEvent);
489 msg->setField(varId++, (UINT32)m_groups->size());
490 for(int i = 0; i < m_groups->size(); i++)
491 {
492 varId = m_groups->get(i)->fillMessage(msg, varId);
493 }
494 return varId;
495 }
496
497 /**
498 * Check threshold
499 * Method will return the following codes:
500 * THRESHOLD_REACHED - when value match the threshold condition while previous check doesn't
501 * THRESHOLD_REARMED - when value doesn't match the threshold condition while previous check do
502 * NO_ACTION - when there are no changes in value match to threshold's condition
503 */
504 ThresholdCheckResult DCTableThreshold::check(Table *value, int row, const TCHAR *instance)
505 {
506 for(int i = 0; i < m_groups->size(); i++)
507 {
508 if (m_groups->get(i)->check(value, row))
509 {
510 if (m_activeKeys->contains(instance))
511 {
512 return ALREADY_ACTIVE;
513 }
514 m_activeKeys->add(instance);
515 return ACTIVATED;
516 }
517 }
518
519 // no match
520 if (m_activeKeys->contains(instance))
521 {
522 m_activeKeys->remove(instance);
523 return DEACTIVATED;
524 }
525 return ALREADY_INACTIVE;
526 }
527
528 /**
529 * Create NXMP record for threshold
530 */
531 void DCTableThreshold::createNXMPRecord(String &str, int id)
532 {
533 TCHAR activationEvent[MAX_EVENT_NAME], deactivationEvent[MAX_EVENT_NAME];
534
535 EventNameFromCode(m_activationEvent, activationEvent);
536 EventNameFromCode(m_deactivationEvent, deactivationEvent);
537 str.appendFormattedString(_T("\t\t\t\t\t\t<threshold id=\"%d\">\n")
538 _T("\t\t\t\t\t\t\t<activationEvent>%s</activationEvent>\n")
539 _T("\t\t\t\t\t\t\t<deactivationEvent>%s</deactivationEvent>\n")
540 _T("\t\t\t\t\t\t\t<groups>\n"),
541 id, (const TCHAR *)EscapeStringForXML2(activationEvent),
542 (const TCHAR *)EscapeStringForXML2(deactivationEvent));
543 for(int i = 0; i < m_groups->size(); i++)
544 {
545 str.appendFormattedString(_T("\t\t\t\t\t\t\t\t<group id=\"%d\">\n\t\t\t\t\t\t\t\t\t<conditions>\n"), i + 1);
546 ObjectArray<DCTableCondition> *conditions = m_groups->get(i)->getConditions();
547 for(int j = 0; j < conditions->size(); j++)
548 {
549 DCTableCondition *c = conditions->get(j);
550 str.appendFormattedString(_T("\t\t\t\t\t\t\t\t\t\t<condition id=\"%d\">\n")
551 _T("\t\t\t\t\t\t\t\t\t\t\t<column>%s</column>\n")
552 _T("\t\t\t\t\t\t\t\t\t\t\t<operation>%d</operation>\n")
553 _T("\t\t\t\t\t\t\t\t\t\t\t<value>%s</value>\n")
554 _T("\t\t\t\t\t\t\t\t\t\t</condition>\n"),
555 j + 1, (const TCHAR *)EscapeStringForXML2(c->getColumn()),
556 c->getOperation(), (const TCHAR *)EscapeStringForXML2(c->getValue()));
557 }
558 str += _T("\t\t\t\t\t\t\t\t\t</conditions>\n\t\t\t\t\t\t\t\t</group>\n");
559 }
560 str += _T("\t\t\t\t\t\t\t</groups>\n\t\t\t\t\t\t</threshold>\n");
561 }
562
563 /**
564 * Copy threshold state
565 */
566 void DCTableThreshold::copyState(DCTableThreshold *src)
567 {
568 m_activeKeys->clear();
569 m_activeKeys->addAll(src->m_activeKeys);
570 }