object save optimization - object properties divided into groups and anly modified...
[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 if (m_modified & MODIFY_OTHER)
170 {
171 pszEscScript = EncodeSQLString(CHECK_NULL_EX(m_scriptSource));
172 size_t qlen = _tcslen(pszEscScript) + 1024;
173 pszQuery = (TCHAR *)malloc(sizeof(TCHAR) * qlen);
174
175 // Check for object's existence in database
176 _sntprintf(pszQuery, qlen, _T("SELECT id FROM conditions WHERE id=%d"), m_id);
177 hResult = DBSelect(hdb, pszQuery);
178 if (hResult != NULL)
179 {
180 if (DBGetNumRows(hResult) > 0)
181 bNewObject = FALSE;
182 DBFreeResult(hResult);
183 }
184
185 // Form and execute INSERT or UPDATE query
186 if (bNewObject)
187 {
188 _sntprintf(pszQuery, qlen,
189 _T("INSERT INTO conditions (id,activation_event,")
190 _T("deactivation_event,source_object,active_status,")
191 _T("inactive_status,script) VALUES (%d,%d,%d,%d,%d,%d,'%s')"),
192 m_id, m_activationEventCode, m_deactivationEventCode,
193 m_sourceObject, m_activeStatus, m_inactiveStatus, pszEscScript);
194 }
195 else
196 {
197 _sntprintf(pszQuery, qlen,
198 _T("UPDATE conditions SET activation_event=%d,")
199 _T("deactivation_event=%d,source_object=%d,active_status=%d,")
200 _T("inactive_status=%d,script='%s' WHERE id=%d"),
201 m_activationEventCode, m_deactivationEventCode, m_sourceObject,
202 m_activeStatus, m_inactiveStatus, pszEscScript, m_id);
203 }
204 free(pszEscScript);
205 DBQuery(hdb, pszQuery);
206
207 // Save DCI mapping
208 _sntprintf(pszQuery, qlen, _T("DELETE FROM cond_dci_map WHERE condition_id=%d"), m_id);
209 DBQuery(hdb, pszQuery);
210 for(i = 0; i < m_dciCount; i++)
211 {
212 _sntprintf(pszQuery, qlen, _T("INSERT INTO cond_dci_map (condition_id,sequence_number,dci_id,node_id,")
213 _T("dci_func,num_polls) VALUES (%d,%d,%d,%d,%d,%d)"),
214 m_id, i, m_dciList[i].id, m_dciList[i].nodeId,
215 m_dciList[i].function, m_dciList[i].polls);
216 DBQuery(hdb, pszQuery);
217 }
218 free(pszQuery);
219 }
220
221 // Save access list
222 saveACLToDB(hdb);
223
224 // Unlock object and clear modification flag
225 m_modified = 0;
226 unlockProperties();
227 return true;
228 }
229
230 /**
231 * Delete object from database
232 */
233 bool ConditionObject::deleteFromDatabase(DB_HANDLE hdb)
234 {
235 bool success = NetObj::deleteFromDatabase(hdb);
236 if (success)
237 success = executeQueryOnObject(hdb, _T("DELETE FROM conditions WHERE id=?"));
238 if (success)
239 success = executeQueryOnObject(hdb, _T("DELETE FROM cond_dci_map WHERE condition_id=?"));
240 return success;
241 }
242
243 /**
244 * Create NXCP message from object
245 */
246 void ConditionObject::fillMessageInternal(NXCPMessage *pMsg)
247 {
248 NetObj::fillMessageInternal(pMsg);
249
250 pMsg->setField(VID_SCRIPT, CHECK_NULL_EX(m_scriptSource));
251 pMsg->setField(VID_ACTIVATION_EVENT, m_activationEventCode);
252 pMsg->setField(VID_DEACTIVATION_EVENT, m_deactivationEventCode);
253 pMsg->setField(VID_SOURCE_OBJECT, m_sourceObject);
254 pMsg->setField(VID_ACTIVE_STATUS, (WORD)m_activeStatus);
255 pMsg->setField(VID_INACTIVE_STATUS, (WORD)m_inactiveStatus);
256 pMsg->setField(VID_NUM_ITEMS, m_dciCount);
257 for(UINT32 i = 0, dwId = VID_DCI_LIST_BASE; (i < m_dciCount) && (dwId < (VID_DCI_LIST_LAST + 1)); i++)
258 {
259 pMsg->setField(dwId++, m_dciList[i].id);
260 pMsg->setField(dwId++, m_dciList[i].nodeId);
261 pMsg->setField(dwId++, (WORD)m_dciList[i].function);
262 pMsg->setField(dwId++, (WORD)m_dciList[i].polls);
263 pMsg->setField(dwId++, (WORD)GetDCObjectType(m_dciList[i].nodeId, m_dciList[i].id));
264 dwId += 5;
265 }
266 }
267
268 /**
269 * Modify object from NXCP message
270 */
271 UINT32 ConditionObject::modifyFromMessageInternal(NXCPMessage *pRequest)
272 {
273 UINT32 i, dwId;
274 NetObj *pObject;
275
276 // Change script
277 if (pRequest->isFieldExist(VID_SCRIPT))
278 {
279 TCHAR szError[1024];
280
281 safe_free(m_scriptSource);
282 delete m_script;
283 m_scriptSource = pRequest->getFieldAsString(VID_SCRIPT);
284 NXSL_Program *p = (NXSL_Program *)NXSLCompile(m_scriptSource, szError, 1024, NULL);
285 if (p != NULL)
286 {
287 m_script = new NXSL_VM(new NXSL_ServerEnv);
288 if (!m_script->load(p))
289 {
290 nxlog_write(MSG_COND_SCRIPT_COMPILATION_ERROR, EVENTLOG_ERROR_TYPE, "dss", m_id, m_name, m_script->getErrorText());
291 delete_and_null(m_script);
292 }
293 delete p;
294 }
295 else
296 {
297 m_script = NULL;
298 nxlog_write(MSG_COND_SCRIPT_COMPILATION_ERROR, EVENTLOG_ERROR_TYPE, "dss", m_id, m_name, szError);
299 }
300 }
301
302 // Change activation event
303 if (pRequest->isFieldExist(VID_ACTIVATION_EVENT))
304 m_activationEventCode = pRequest->getFieldAsUInt32(VID_ACTIVATION_EVENT);
305
306 // Change deactivation event
307 if (pRequest->isFieldExist(VID_DEACTIVATION_EVENT))
308 m_deactivationEventCode = pRequest->getFieldAsUInt32(VID_DEACTIVATION_EVENT);
309
310 // Change source object
311 if (pRequest->isFieldExist(VID_SOURCE_OBJECT))
312 m_sourceObject = pRequest->getFieldAsUInt32(VID_SOURCE_OBJECT);
313
314 // Change active status
315 if (pRequest->isFieldExist(VID_ACTIVE_STATUS))
316 m_activeStatus = pRequest->getFieldAsUInt16(VID_ACTIVE_STATUS);
317
318 // Change inactive status
319 if (pRequest->isFieldExist(VID_INACTIVE_STATUS))
320 m_inactiveStatus = pRequest->getFieldAsUInt16(VID_INACTIVE_STATUS);
321
322 // Change DCI list
323 if (pRequest->isFieldExist(VID_NUM_ITEMS))
324 {
325 safe_free(m_dciList);
326 m_dciCount = pRequest->getFieldAsUInt32(VID_NUM_ITEMS);
327 if (m_dciCount > 0)
328 {
329 m_dciList = (INPUT_DCI *)malloc(sizeof(INPUT_DCI) * m_dciCount);
330 for(i = 0, dwId = VID_DCI_LIST_BASE; (i < m_dciCount) && (dwId < (VID_DCI_LIST_LAST + 1)); i++)
331 {
332 m_dciList[i].id = pRequest->getFieldAsUInt32(dwId++);
333 m_dciList[i].nodeId = pRequest->getFieldAsUInt32(dwId++);
334 m_dciList[i].function = pRequest->getFieldAsUInt16(dwId++);
335 m_dciList[i].polls = pRequest->getFieldAsUInt16(dwId++);
336 dwId += 6;
337 }
338
339 // Update cache size of DCIs
340 for(i = 0; i < m_dciCount; i++)
341 {
342 pObject = FindObjectById(m_dciList[i].nodeId);
343 if (pObject != NULL)
344 {
345 if ((pObject->getObjectClass() == OBJECT_NODE) ||
346 (pObject->getObjectClass() == OBJECT_CLUSTER) ||
347 (pObject->getObjectClass() == OBJECT_MOBILEDEVICE) ||
348 (pObject->getObjectClass() == OBJECT_SENSOR))
349 {
350 ((DataCollectionTarget *)pObject)->updateDCItemCacheSize(m_dciList[i].id, m_id);
351 }
352 }
353 }
354 }
355 else
356 {
357 m_dciList = NULL;
358 }
359 }
360
361 return NetObj::modifyFromMessageInternal(pRequest);
362 }
363
364 /**
365 * Lock for polling
366 */
367 void ConditionObject::lockForPoll()
368 {
369 m_queuedForPolling = TRUE;
370 }
371
372 /**
373 * Poller entry point
374 */
375 void ConditionObject::doPoll(PollerInfo *poller)
376 {
377 poller->startExecution();
378 check();
379 lockProperties();
380 m_queuedForPolling = FALSE;
381 m_lastPoll = time(NULL);
382 unlockProperties();
383 delete poller;
384 }
385
386 /**
387 * Check condition
388 */
389 void ConditionObject::check()
390 {
391 NXSL_Value **ppValueList, *pValue;
392 NetObj *pObject;
393 UINT32 i, dwNumValues;
394 int iOldStatus = m_status;
395
396 if ((m_script == NULL) || (m_status == STATUS_UNMANAGED) || IsShutdownInProgress())
397 return;
398
399 lockProperties();
400 ppValueList = (NXSL_Value **)malloc(sizeof(NXSL_Value *) * m_dciCount);
401 memset(ppValueList, 0, sizeof(NXSL_Value *) * m_dciCount);
402 for(i = 0; i < m_dciCount; i++)
403 {
404 pObject = FindObjectById(m_dciList[i].nodeId);
405 if (pObject != NULL)
406 {
407 if (pObject->getObjectClass() == OBJECT_NODE)
408 {
409 DCObject *pItem = ((Node *)pObject)->getDCObjectById(m_dciList[i].id);
410 if (pItem != NULL)
411 {
412 if (pItem->getType() == DCO_TYPE_ITEM)
413 {
414 ppValueList[i] = ((DCItem *)pItem)->getValueForNXSL(m_dciList[i].function, m_dciList[i].polls);
415 }
416 else if (pItem->getType() == DCO_TYPE_TABLE)
417 {
418 Table *t = ((DCTable *)pItem)->getLastValue();
419 if (t != NULL)
420 {
421 ppValueList[i] = new NXSL_Value(new NXSL_Object(&g_nxslTableClass, t));
422 }
423 }
424 }
425 }
426 }
427 if (ppValueList[i] == NULL)
428 ppValueList[i] = new NXSL_Value;
429 }
430 dwNumValues = m_dciCount;
431 unlockProperties();
432
433 // Create array from values
434 NXSL_Array *array = new NXSL_Array;
435 for(i = 0; i < dwNumValues; i++)
436 {
437 array->set(i + 1, new NXSL_Value(ppValueList[i]));
438 }
439 m_script->setGlobalVariable(_T("$values"), new NXSL_Value(array));
440
441 DbgPrintf(6, _T("Running evaluation script for condition %d \"%s\""), m_id, m_name);
442 if (m_script->run(dwNumValues, ppValueList))
443 {
444 pValue = m_script->getResult();
445 if (pValue->getValueAsInt32() == 0)
446 {
447 if (m_isActive)
448 {
449 // Deactivate condition
450 lockProperties();
451 m_status = m_inactiveStatus;
452 m_isActive = FALSE;
453 setModified(MODIFY_RUNTIME);
454 unlockProperties();
455
456 PostEvent(m_deactivationEventCode,
457 (m_sourceObject == 0) ? g_dwMgmtNode : m_sourceObject,
458 "dsdd", m_id, m_name, iOldStatus, m_status);
459
460 DbgPrintf(6, _T("Condition %d \"%s\" deactivated"),
461 m_id, m_name);
462 }
463 else
464 {
465 DbgPrintf(6, _T("Condition %d \"%s\" still inactive"),
466 m_id, m_name);
467 lockProperties();
468 if (m_status != m_inactiveStatus)
469 {
470 m_status = m_inactiveStatus;
471 setModified(MODIFY_RUNTIME);
472 }
473 unlockProperties();
474 }
475 }
476 else
477 {
478 if (!m_isActive)
479 {
480 // Activate condition
481 lockProperties();
482 m_status = m_activeStatus;
483 m_isActive = TRUE;
484 setModified(MODIFY_RUNTIME);
485 unlockProperties();
486
487 PostEvent(m_activationEventCode,
488 (m_sourceObject == 0) ? g_dwMgmtNode : m_sourceObject,
489 "dsdd", m_id, m_name, iOldStatus, m_status);
490
491 DbgPrintf(6, _T("Condition %d \"%s\" activated"),
492 m_id, m_name);
493 }
494 else
495 {
496 DbgPrintf(6, _T("Condition %d \"%s\" still active"),
497 m_id, m_name);
498 lockProperties();
499 if (m_status != m_activeStatus)
500 {
501 m_status = m_activeStatus;
502 setModified(MODIFY_RUNTIME);
503 }
504 unlockProperties();
505 }
506 }
507 }
508 else
509 {
510 nxlog_write(MSG_COND_SCRIPT_EXECUTION_ERROR, EVENTLOG_ERROR_TYPE,
511 "dss", m_id, m_name, m_script->getErrorText());
512
513 lockProperties();
514 if (m_status != STATUS_UNKNOWN)
515 {
516 m_status = STATUS_UNKNOWN;
517 setModified(MODIFY_RUNTIME);
518 }
519 unlockProperties();
520 }
521 free(ppValueList);
522
523 // Cause parent object(s) to recalculate it's status
524 if (iOldStatus != m_status)
525 {
526 lockParentList(false);
527 for(int i = 0; i < m_parentList->size(); i++)
528 m_parentList->get(i)->calculateCompoundStatus();
529 unlockParentList();
530 }
531 }
532
533 /**
534 * Determine DCI cache size required by condition object
535 */
536 int ConditionObject::getCacheSizeForDCI(UINT32 itemId, bool noLock)
537 {
538 UINT32 i;
539 int nSize = 0;
540
541 if (!noLock)
542 lockProperties();
543 for(i = 0; i < m_dciCount; i++)
544 {
545 if (m_dciList[i].id == itemId)
546 {
547 switch(m_dciList[i].function)
548 {
549 case F_LAST:
550 nSize = 1;
551 break;
552 case F_DIFF:
553 nSize = 2;
554 break;
555 default:
556 nSize = m_dciList[i].polls;
557 break;
558 }
559 break;
560 }
561 }
562 if (!noLock)
563 unlockProperties();
564 return nSize;
565 }
566
567 /**
568 * Serialize object to JSON
569 */
570 json_t *ConditionObject::toJson()
571 {
572 json_t *root = NetObj::toJson();
573 json_t *inputs = json_array();
574 for(int i = 0; i < m_dciCount; i++)
575 {
576 json_t *dci = json_object();
577 json_object_set_new(dci, "id", json_integer(m_dciList[i].id));
578 json_object_set_new(dci, "nodeId", json_integer(m_dciList[i].nodeId));
579 json_object_set_new(dci, "function", json_integer(m_dciList[i].function));
580 json_object_set_new(dci, "polls", json_integer(m_dciList[i].polls));
581 json_array_append_new(inputs, dci);
582 }
583 json_object_set_new(root, "inputs", inputs);
584 json_object_set_new(root, "script", json_string_t(m_scriptSource));
585 json_object_set_new(root, "activationEventCode", json_integer(m_activationEventCode));
586 json_object_set_new(root, "deactivationEventCode", json_integer(m_deactivationEventCode));
587 json_object_set_new(root, "sourceObject", json_integer(m_sourceObject));
588 json_object_set_new(root, "activeStatus", json_integer(m_activeStatus));
589 json_object_set_new(root, "inactiveStatus", json_integer(m_inactiveStatus));
590 json_object_set_new(root, "isActive", json_boolean(m_isActive));
591 json_object_set_new(root, "lastPoll", json_integer(m_lastPoll));
592 return root;
593 }