aff4328f5e53eb936d1304ad2041d24d99faed6b
[public/netxms.git] / src / server / core / condition.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2016 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: container.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Default constructor
27 */
28 ConditionObject::ConditionObject() : NetObj()
29 {
30 m_scriptSource = NULL;
31 m_script = NULL;
32 m_dciList = NULL;
33 m_dciCount = 0;
34 m_sourceObject = 0;
35 m_activeStatus = STATUS_MAJOR;
36 m_inactiveStatus = STATUS_NORMAL;
37 m_isActive = false;
38 m_lastPoll = 0;
39 m_queuedForPolling = false;
40 m_activationEventCode = EVENT_CONDITION_ACTIVATED;
41 m_deactivationEventCode = EVENT_CONDITION_DEACTIVATED;
42 }
43
44 /**
45 * Constructor for new objects
46 */
47 ConditionObject::ConditionObject(bool hidden) : NetObj()
48 {
49 m_scriptSource = NULL;
50 m_script = NULL;
51 m_dciList = NULL;
52 m_dciCount = 0;
53 m_sourceObject = 0;
54 m_activeStatus = STATUS_MAJOR;
55 m_inactiveStatus = STATUS_NORMAL;
56 m_isHidden = hidden;
57 m_isActive = false;
58 m_lastPoll = 0;
59 m_queuedForPolling = false;
60 m_activationEventCode = EVENT_CONDITION_ACTIVATED;
61 m_deactivationEventCode = EVENT_CONDITION_DEACTIVATED;
62 }
63
64 /**
65 * Destructor
66 */
67 ConditionObject::~ConditionObject()
68 {
69 free(m_dciList);
70 free(m_scriptSource);
71 delete m_script;
72 }
73
74 /**
75 * Load object from database
76 */
77 bool ConditionObject::loadFromDatabase(DB_HANDLE hdb, UINT32 dwId)
78 {
79 TCHAR szQuery[512];
80 DB_RESULT hResult;
81 UINT32 i;
82
83 m_id = dwId;
84
85 if (!loadCommonProperties(hdb))
86 return false;
87
88 // Load properties
89 _sntprintf(szQuery, 512, _T("SELECT activation_event,deactivation_event,")
90 _T("source_object,active_status,inactive_status,")
91 _T("script FROM conditions WHERE id=%d"), dwId);
92 hResult = DBSelect(hdb, szQuery);
93 if (hResult == NULL)
94 return false; // Query failed
95
96 if (DBGetNumRows(hResult) == 0)
97 {
98 // No object with given ID in database
99 DBFreeResult(hResult);
100 return false;
101 }
102
103 m_activationEventCode = DBGetFieldULong(hResult, 0, 0);
104 m_deactivationEventCode = DBGetFieldULong(hResult, 0, 1);
105 m_sourceObject = DBGetFieldULong(hResult, 0, 2);
106 m_activeStatus = DBGetFieldLong(hResult, 0, 3);
107 m_inactiveStatus = DBGetFieldLong(hResult, 0, 4);
108 m_scriptSource = DBGetField(hResult, 0, 5, NULL, 0);
109 DecodeSQLString(m_scriptSource);
110
111 DBFreeResult(hResult);
112
113 // Compile script
114 NXSL_Program *p = (NXSL_Program *)NXSLCompile(m_scriptSource, szQuery, 512, NULL);
115 if (p != NULL)
116 {
117 m_script = new NXSL_VM(new NXSL_ServerEnv);
118 if (!m_script->load(p))
119 {
120 nxlog_write(MSG_COND_SCRIPT_COMPILATION_ERROR, EVENTLOG_ERROR_TYPE, "dss", m_id, m_name, m_script->getErrorText());
121 delete_and_null(m_script);
122 }
123 delete p;
124 }
125 else
126 {
127 m_script = NULL;
128 nxlog_write(MSG_COND_SCRIPT_COMPILATION_ERROR, EVENTLOG_ERROR_TYPE, "dss", m_id, m_name, szQuery);
129 }
130
131 // Load DCI map
132 _sntprintf(szQuery, 512, _T("SELECT dci_id,node_id,dci_func,num_polls ")
133 _T("FROM cond_dci_map WHERE condition_id=%d ORDER BY sequence_number"), dwId);
134 hResult = DBSelect(hdb, szQuery);
135 if (hResult == NULL)
136 return false; // Query failed
137
138 m_dciCount = DBGetNumRows(hResult);
139 if (m_dciCount > 0)
140 {
141 m_dciList = (INPUT_DCI *)malloc(sizeof(INPUT_DCI) * m_dciCount);
142 for(i = 0; i < m_dciCount; i++)
143 {
144 m_dciList[i].id = DBGetFieldULong(hResult, i, 0);
145 m_dciList[i].nodeId = DBGetFieldULong(hResult, i, 1);
146 m_dciList[i].function = DBGetFieldLong(hResult, i, 2);
147 m_dciList[i].polls = DBGetFieldLong(hResult, i, 3);
148 }
149 }
150 DBFreeResult(hResult);
151
152 return loadACLFromDB(hdb);
153 }
154
155 /**
156 * Save object to database
157 */
158 bool ConditionObject::saveToDatabase(DB_HANDLE hdb)
159 {
160 TCHAR *pszEscScript, *pszQuery;
161 DB_RESULT hResult;
162 BOOL bNewObject = TRUE;
163 UINT32 i;
164
165 lockProperties();
166
167 saveCommonProperties(hdb);
168
169 pszEscScript = EncodeSQLString(CHECK_NULL_EX(m_scriptSource));
170 size_t qlen = _tcslen(pszEscScript) + 1024;
171 pszQuery = (TCHAR *)malloc(sizeof(TCHAR) * qlen);
172
173 // Check for object's existence in database
174 _sntprintf(pszQuery, qlen, _T("SELECT id FROM conditions WHERE id=%d"), m_id);
175 hResult = DBSelect(hdb, pszQuery);
176 if (hResult != NULL)
177 {
178 if (DBGetNumRows(hResult) > 0)
179 bNewObject = FALSE;
180 DBFreeResult(hResult);
181 }
182
183 // Form and execute INSERT or UPDATE query
184 if (bNewObject)
185 {
186 _sntprintf(pszQuery, qlen,
187 _T("INSERT INTO conditions (id,activation_event,")
188 _T("deactivation_event,source_object,active_status,")
189 _T("inactive_status,script) VALUES (%d,%d,%d,%d,%d,%d,'%s')"),
190 m_id, m_activationEventCode, m_deactivationEventCode,
191 m_sourceObject, m_activeStatus, m_inactiveStatus, pszEscScript);
192 }
193 else
194 {
195 _sntprintf(pszQuery, qlen,
196 _T("UPDATE conditions SET activation_event=%d,")
197 _T("deactivation_event=%d,source_object=%d,active_status=%d,")
198 _T("inactive_status=%d,script='%s' WHERE id=%d"),
199 m_activationEventCode, m_deactivationEventCode, m_sourceObject,
200 m_activeStatus, m_inactiveStatus, pszEscScript, m_id);
201 }
202 free(pszEscScript);
203 DBQuery(hdb, pszQuery);
204
205 // Save DCI mapping
206 _sntprintf(pszQuery, qlen, _T("DELETE FROM cond_dci_map WHERE condition_id=%d"), m_id);
207 DBQuery(hdb, pszQuery);
208 for(i = 0; i < m_dciCount; i++)
209 {
210 _sntprintf(pszQuery, qlen, _T("INSERT INTO cond_dci_map (condition_id,sequence_number,dci_id,node_id,")
211 _T("dci_func,num_polls) VALUES (%d,%d,%d,%d,%d,%d)"),
212 m_id, i, m_dciList[i].id, m_dciList[i].nodeId,
213 m_dciList[i].function, m_dciList[i].polls);
214 DBQuery(hdb, pszQuery);
215 }
216 free(pszQuery);
217
218 // Save access list
219 saveACLToDB(hdb);
220
221 // Unlock object and clear modification flag
222 m_isModified = false;
223 unlockProperties();
224 return true;
225 }
226
227 /**
228 * Delete object from database
229 */
230 bool ConditionObject::deleteFromDatabase(DB_HANDLE hdb)
231 {
232 bool success = NetObj::deleteFromDatabase(hdb);
233 if (success)
234 success = executeQueryOnObject(hdb, _T("DELETE FROM conditions WHERE id=?"));
235 if (success)
236 success = executeQueryOnObject(hdb, _T("DELETE FROM cond_dci_map WHERE condition_id=?"));
237 return success;
238 }
239
240 /**
241 * Create NXCP message from object
242 */
243 void ConditionObject::fillMessageInternal(NXCPMessage *pMsg)
244 {
245 NetObj::fillMessageInternal(pMsg);
246
247 pMsg->setField(VID_SCRIPT, CHECK_NULL_EX(m_scriptSource));
248 pMsg->setField(VID_ACTIVATION_EVENT, m_activationEventCode);
249 pMsg->setField(VID_DEACTIVATION_EVENT, m_deactivationEventCode);
250 pMsg->setField(VID_SOURCE_OBJECT, m_sourceObject);
251 pMsg->setField(VID_ACTIVE_STATUS, (WORD)m_activeStatus);
252 pMsg->setField(VID_INACTIVE_STATUS, (WORD)m_inactiveStatus);
253 pMsg->setField(VID_NUM_ITEMS, m_dciCount);
254 for(UINT32 i = 0, dwId = VID_DCI_LIST_BASE; (i < m_dciCount) && (dwId < (VID_DCI_LIST_LAST + 1)); i++)
255 {
256 pMsg->setField(dwId++, m_dciList[i].id);
257 pMsg->setField(dwId++, m_dciList[i].nodeId);
258 pMsg->setField(dwId++, (WORD)m_dciList[i].function);
259 pMsg->setField(dwId++, (WORD)m_dciList[i].polls);
260 pMsg->setField(dwId++, (WORD)GetDCObjectType(m_dciList[i].nodeId, m_dciList[i].id));
261 dwId += 5;
262 }
263 }
264
265 /**
266 * Modify object from NXCP message
267 */
268 UINT32 ConditionObject::modifyFromMessageInternal(NXCPMessage *pRequest)
269 {
270 UINT32 i, dwId;
271 NetObj *pObject;
272
273 // Change script
274 if (pRequest->isFieldExist(VID_SCRIPT))
275 {
276 TCHAR szError[1024];
277
278 safe_free(m_scriptSource);
279 delete m_script;
280 m_scriptSource = pRequest->getFieldAsString(VID_SCRIPT);
281 NXSL_Program *p = (NXSL_Program *)NXSLCompile(m_scriptSource, szError, 1024, NULL);
282 if (p != NULL)
283 {
284 m_script = new NXSL_VM(new NXSL_ServerEnv);
285 if (!m_script->load(p))
286 {
287 nxlog_write(MSG_COND_SCRIPT_COMPILATION_ERROR, EVENTLOG_ERROR_TYPE, "dss", m_id, m_name, m_script->getErrorText());
288 delete_and_null(m_script);
289 }
290 delete p;
291 }
292 else
293 {
294 m_script = NULL;
295 nxlog_write(MSG_COND_SCRIPT_COMPILATION_ERROR, EVENTLOG_ERROR_TYPE, "dss", m_id, m_name, szError);
296 }
297 }
298
299 // Change activation event
300 if (pRequest->isFieldExist(VID_ACTIVATION_EVENT))
301 m_activationEventCode = pRequest->getFieldAsUInt32(VID_ACTIVATION_EVENT);
302
303 // Change deactivation event
304 if (pRequest->isFieldExist(VID_DEACTIVATION_EVENT))
305 m_deactivationEventCode = pRequest->getFieldAsUInt32(VID_DEACTIVATION_EVENT);
306
307 // Change source object
308 if (pRequest->isFieldExist(VID_SOURCE_OBJECT))
309 m_sourceObject = pRequest->getFieldAsUInt32(VID_SOURCE_OBJECT);
310
311 // Change active status
312 if (pRequest->isFieldExist(VID_ACTIVE_STATUS))
313 m_activeStatus = pRequest->getFieldAsUInt16(VID_ACTIVE_STATUS);
314
315 // Change inactive status
316 if (pRequest->isFieldExist(VID_INACTIVE_STATUS))
317 m_inactiveStatus = pRequest->getFieldAsUInt16(VID_INACTIVE_STATUS);
318
319 // Change DCI list
320 if (pRequest->isFieldExist(VID_NUM_ITEMS))
321 {
322 safe_free(m_dciList);
323 m_dciCount = pRequest->getFieldAsUInt32(VID_NUM_ITEMS);
324 if (m_dciCount > 0)
325 {
326 m_dciList = (INPUT_DCI *)malloc(sizeof(INPUT_DCI) * m_dciCount);
327 for(i = 0, dwId = VID_DCI_LIST_BASE; (i < m_dciCount) && (dwId < (VID_DCI_LIST_LAST + 1)); i++)
328 {
329 m_dciList[i].id = pRequest->getFieldAsUInt32(dwId++);
330 m_dciList[i].nodeId = pRequest->getFieldAsUInt32(dwId++);
331 m_dciList[i].function = pRequest->getFieldAsUInt16(dwId++);
332 m_dciList[i].polls = pRequest->getFieldAsUInt16(dwId++);
333 dwId += 6;
334 }
335
336 // Update cache size of DCIs
337 for(i = 0; i < m_dciCount; i++)
338 {
339 pObject = FindObjectById(m_dciList[i].nodeId);
340 if (pObject != NULL)
341 {
342 if ((pObject->getObjectClass() == OBJECT_NODE) ||
343 (pObject->getObjectClass() == OBJECT_CLUSTER) ||
344 (pObject->getObjectClass() == OBJECT_MOBILEDEVICE) ||
345 (pObject->getObjectClass() == OBJECT_SENSOR))
346 {
347 ((DataCollectionTarget *)pObject)->updateDCItemCacheSize(m_dciList[i].id, m_id);
348 }
349 }
350 }
351 }
352 else
353 {
354 m_dciList = NULL;
355 }
356 }
357
358 return NetObj::modifyFromMessageInternal(pRequest);
359 }
360
361 /**
362 * Lock for polling
363 */
364 void ConditionObject::lockForPoll()
365 {
366 m_queuedForPolling = TRUE;
367 }
368
369 /**
370 * Poller entry point
371 */
372 void ConditionObject::doPoll(PollerInfo *poller)
373 {
374 poller->startExecution();
375 check();
376 lockProperties();
377 m_queuedForPolling = FALSE;
378 m_lastPoll = time(NULL);
379 unlockProperties();
380 delete poller;
381 }
382
383 /**
384 * Check condition
385 */
386 void ConditionObject::check()
387 {
388 NXSL_Value **ppValueList, *pValue;
389 NetObj *pObject;
390 UINT32 i, dwNumValues;
391 int iOldStatus = m_status;
392
393 if ((m_script == NULL) || (m_status == STATUS_UNMANAGED) || IsShutdownInProgress())
394 return;
395
396 lockProperties();
397 ppValueList = (NXSL_Value **)malloc(sizeof(NXSL_Value *) * m_dciCount);
398 memset(ppValueList, 0, sizeof(NXSL_Value *) * m_dciCount);
399 for(i = 0; i < m_dciCount; i++)
400 {
401 pObject = FindObjectById(m_dciList[i].nodeId);
402 if (pObject != NULL)
403 {
404 if (pObject->getObjectClass() == OBJECT_NODE)
405 {
406 DCObject *pItem = ((Node *)pObject)->getDCObjectById(m_dciList[i].id);
407 if (pItem != NULL)
408 {
409 if (pItem->getType() == DCO_TYPE_ITEM)
410 {
411 ppValueList[i] = ((DCItem *)pItem)->getValueForNXSL(m_dciList[i].function, m_dciList[i].polls);
412 }
413 else if (pItem->getType() == DCO_TYPE_TABLE)
414 {
415 Table *t = ((DCTable *)pItem)->getLastValue();
416 if (t != NULL)
417 {
418 ppValueList[i] = new NXSL_Value(new NXSL_Object(&g_nxslTableClass, t));
419 }
420 }
421 }
422 }
423 }
424 if (ppValueList[i] == NULL)
425 ppValueList[i] = new NXSL_Value;
426 }
427 dwNumValues = m_dciCount;
428 unlockProperties();
429
430 // Create array from values
431 NXSL_Array *array = new NXSL_Array;
432 for(i = 0; i < dwNumValues; i++)
433 {
434 array->set(i + 1, new NXSL_Value(ppValueList[i]));
435 }
436 m_script->setGlobalVariable(_T("$values"), new NXSL_Value(array));
437
438 DbgPrintf(6, _T("Running evaluation script for condition %d \"%s\""), m_id, m_name);
439 if (m_script->run(dwNumValues, ppValueList))
440 {
441 pValue = m_script->getResult();
442 if (pValue->getValueAsInt32() == 0)
443 {
444 if (m_isActive)
445 {
446 // Deactivate condition
447 lockProperties();
448 m_status = m_inactiveStatus;
449 m_isActive = FALSE;
450 setModified();
451 unlockProperties();
452
453 PostEvent(m_deactivationEventCode,
454 (m_sourceObject == 0) ? g_dwMgmtNode : m_sourceObject,
455 "dsdd", m_id, m_name, iOldStatus, m_status);
456
457 DbgPrintf(6, _T("Condition %d \"%s\" deactivated"),
458 m_id, m_name);
459 }
460 else
461 {
462 DbgPrintf(6, _T("Condition %d \"%s\" still inactive"),
463 m_id, m_name);
464 lockProperties();
465 if (m_status != m_inactiveStatus)
466 {
467 m_status = m_inactiveStatus;
468 setModified();
469 }
470 unlockProperties();
471 }
472 }
473 else
474 {
475 if (!m_isActive)
476 {
477 // Activate condition
478 lockProperties();
479 m_status = m_activeStatus;
480 m_isActive = TRUE;
481 setModified();
482 unlockProperties();
483
484 PostEvent(m_activationEventCode,
485 (m_sourceObject == 0) ? g_dwMgmtNode : m_sourceObject,
486 "dsdd", m_id, m_name, iOldStatus, m_status);
487
488 DbgPrintf(6, _T("Condition %d \"%s\" activated"),
489 m_id, m_name);
490 }
491 else
492 {
493 DbgPrintf(6, _T("Condition %d \"%s\" still active"),
494 m_id, m_name);
495 lockProperties();
496 if (m_status != m_activeStatus)
497 {
498 m_status = m_activeStatus;
499 setModified();
500 }
501 unlockProperties();
502 }
503 }
504 }
505 else
506 {
507 nxlog_write(MSG_COND_SCRIPT_EXECUTION_ERROR, EVENTLOG_ERROR_TYPE,
508 "dss", m_id, m_name, m_script->getErrorText());
509
510 lockProperties();
511 if (m_status != STATUS_UNKNOWN)
512 {
513 m_status = STATUS_UNKNOWN;
514 setModified();
515 }
516 unlockProperties();
517 }
518 free(ppValueList);
519
520 // Cause parent object(s) to recalculate it's status
521 if (iOldStatus != m_status)
522 {
523 lockParentList(false);
524 for(int i = 0; i < m_parentList->size(); i++)
525 m_parentList->get(i)->calculateCompoundStatus();
526 unlockParentList();
527 }
528 }
529
530 /**
531 * Determine DCI cache size required by condition object
532 */
533 int ConditionObject::getCacheSizeForDCI(UINT32 itemId, bool noLock)
534 {
535 UINT32 i;
536 int nSize = 0;
537
538 if (!noLock)
539 lockProperties();
540 for(i = 0; i < m_dciCount; i++)
541 {
542 if (m_dciList[i].id == itemId)
543 {
544 switch(m_dciList[i].function)
545 {
546 case F_LAST:
547 nSize = 1;
548 break;
549 case F_DIFF:
550 nSize = 2;
551 break;
552 default:
553 nSize = m_dciList[i].polls;
554 break;
555 }
556 break;
557 }
558 }
559 if (!noLock)
560 unlockProperties();
561 return nSize;
562 }
563
564 /**
565 * Serialize object to JSON
566 */
567 json_t *ConditionObject::toJson()
568 {
569 json_t *root = NetObj::toJson();
570 json_t *inputs = json_array();
571 for(int i = 0; i < m_dciCount; i++)
572 {
573 json_t *dci = json_object();
574 json_object_set_new(dci, "id", json_integer(m_dciList[i].id));
575 json_object_set_new(dci, "nodeId", json_integer(m_dciList[i].nodeId));
576 json_object_set_new(dci, "function", json_integer(m_dciList[i].function));
577 json_object_set_new(dci, "polls", json_integer(m_dciList[i].polls));
578 json_array_append_new(inputs, dci);
579 }
580 json_object_set_new(root, "inputs", inputs);
581 json_object_set_new(root, "script", json_string_t(m_scriptSource));
582 json_object_set_new(root, "activationEventCode", json_integer(m_activationEventCode));
583 json_object_set_new(root, "deactivationEventCode", json_integer(m_deactivationEventCode));
584 json_object_set_new(root, "sourceObject", json_integer(m_sourceObject));
585 json_object_set_new(root, "activeStatus", json_integer(m_activeStatus));
586 json_object_set_new(root, "inactiveStatus", json_integer(m_inactiveStatus));
587 json_object_set_new(root, "isActive", json_boolean(m_isActive));
588 json_object_set_new(root, "lastPoll", json_integer(m_lastPoll));
589 return root;
590 }