all pollers converted to single thread pool
[public/netxms.git] / src / server / core / condition.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2013 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 Condition::Condition() : 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 Condition::Condition(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 Condition::~Condition()
68 {
69 safe_free(m_dciList);
70 safe_free(m_scriptSource);
71 delete m_script;
72 }
73
74 /**
75 * Load object from database
76 */
77 BOOL Condition::loadFromDatabase(UINT32 dwId)
78 {
79 TCHAR szQuery[512];
80 DB_RESULT hResult;
81 UINT32 i;
82
83 m_id = dwId;
84
85 if (!loadCommonProperties())
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(g_hCoreDB, 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);
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(g_hCoreDB, 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();
153 }
154
155 /**
156 * Save object to database
157 */
158 BOOL Condition::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 Condition::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 Condition::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 Condition::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);
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 {
344 DCObject *pItem = ((Node *)pObject)->getDCObjectById(m_dciList[i].id);
345 if ((pItem != NULL) && (pItem->getType() == DCO_TYPE_ITEM))
346 {
347 ((DCItem *)pItem)->updateCacheSize(m_id);
348 }
349 }
350 }
351 }
352 }
353 else
354 {
355 m_dciList = NULL;
356 }
357 }
358
359 return NetObj::modifyFromMessageInternal(pRequest);
360 }
361
362 /**
363 * Lock for polling
364 */
365 void Condition::lockForPoll()
366 {
367 m_queuedForPolling = TRUE;
368 }
369
370 /**
371 * Poller entry point
372 */
373 void Condition::doPoll(PollerInfo *poller)
374 {
375 poller->startExecution();
376 check();
377 lockProperties();
378 m_queuedForPolling = FALSE;
379 m_lastPoll = time(NULL);
380 unlockProperties();
381 delete poller;
382 }
383
384 /**
385 * Check condition
386 */
387 void Condition::check()
388 {
389 NXSL_Value **ppValueList, *pValue;
390 NetObj *pObject;
391 UINT32 i, dwNumValues;
392 int iOldStatus = m_iStatus;
393
394 if ((m_script == NULL) || (m_iStatus == STATUS_UNMANAGED))
395 return;
396
397 lockProperties();
398 ppValueList = (NXSL_Value **)malloc(sizeof(NXSL_Value *) * m_dciCount);
399 memset(ppValueList, 0, sizeof(NXSL_Value *) * m_dciCount);
400 for(i = 0; i < m_dciCount; i++)
401 {
402 pObject = FindObjectById(m_dciList[i].nodeId);
403 if (pObject != NULL)
404 {
405 if (pObject->getObjectClass() == OBJECT_NODE)
406 {
407 DCObject *pItem = ((Node *)pObject)->getDCObjectById(m_dciList[i].id);
408 if (pItem != NULL)
409 {
410 if (pItem->getType() == DCO_TYPE_ITEM)
411 {
412 ppValueList[i] = ((DCItem *)pItem)->getValueForNXSL(m_dciList[i].function, m_dciList[i].polls);
413 }
414 else if (pItem->getType() == DCO_TYPE_TABLE)
415 {
416 Table *t = ((DCTable *)pItem)->getLastValue();
417 if (t != NULL)
418 {
419 ppValueList[i] = new NXSL_Value(new NXSL_Object(&g_nxslTableClass, t));
420 }
421 }
422 }
423 }
424 }
425 if (ppValueList[i] == NULL)
426 ppValueList[i] = new NXSL_Value;
427 }
428 dwNumValues = m_dciCount;
429 unlockProperties();
430
431 // Create array from values
432 NXSL_Array *array = new NXSL_Array;
433 for(i = 0; i < dwNumValues; i++)
434 {
435 array->set(i + 1, new NXSL_Value(ppValueList[i]));
436 }
437 m_script->setGlobalVariable(_T("$values"), new NXSL_Value(array));
438
439 DbgPrintf(6, _T("Running evaluation script for condition %d \"%s\""), m_id, m_name);
440 if (m_script->run(dwNumValues, ppValueList))
441 {
442 pValue = m_script->getResult();
443 if (pValue->getValueAsInt32() == 0)
444 {
445 if (m_isActive)
446 {
447 // Deactivate condition
448 lockProperties();
449 m_iStatus = m_inactiveStatus;
450 m_isActive = FALSE;
451 setModified();
452 unlockProperties();
453
454 PostEvent(m_deactivationEventCode,
455 (m_sourceObject == 0) ? g_dwMgmtNode : m_sourceObject,
456 "dsdd", m_id, m_name, iOldStatus, m_iStatus);
457
458 DbgPrintf(6, _T("Condition %d \"%s\" deactivated"),
459 m_id, m_name);
460 }
461 else
462 {
463 DbgPrintf(6, _T("Condition %d \"%s\" still inactive"),
464 m_id, m_name);
465 lockProperties();
466 if (m_iStatus != m_inactiveStatus)
467 {
468 m_iStatus = m_inactiveStatus;
469 setModified();
470 }
471 unlockProperties();
472 }
473 }
474 else
475 {
476 if (!m_isActive)
477 {
478 // Activate condition
479 lockProperties();
480 m_iStatus = m_activeStatus;
481 m_isActive = TRUE;
482 setModified();
483 unlockProperties();
484
485 PostEvent(m_activationEventCode,
486 (m_sourceObject == 0) ? g_dwMgmtNode : m_sourceObject,
487 "dsdd", m_id, m_name, iOldStatus, m_iStatus);
488
489 DbgPrintf(6, _T("Condition %d \"%s\" activated"),
490 m_id, m_name);
491 }
492 else
493 {
494 DbgPrintf(6, _T("Condition %d \"%s\" still active"),
495 m_id, m_name);
496 lockProperties();
497 if (m_iStatus != m_activeStatus)
498 {
499 m_iStatus = m_activeStatus;
500 setModified();
501 }
502 unlockProperties();
503 }
504 }
505 }
506 else
507 {
508 nxlog_write(MSG_COND_SCRIPT_EXECUTION_ERROR, EVENTLOG_ERROR_TYPE,
509 "dss", m_id, m_name, m_script->getErrorText());
510
511 lockProperties();
512 if (m_iStatus != STATUS_UNKNOWN)
513 {
514 m_iStatus = STATUS_UNKNOWN;
515 setModified();
516 }
517 unlockProperties();
518 }
519 free(ppValueList);
520
521 // Cause parent object(s) to recalculate it's status
522 if (iOldStatus != m_iStatus)
523 {
524 LockParentList(FALSE);
525 for(i = 0; i < m_dwParentCount; i++)
526 m_pParentList[i]->calculateCompoundStatus();
527 UnlockParentList();
528 }
529 }
530
531 /**
532 * Determine DCI cache size required by condition object
533 */
534 int Condition::getCacheSizeForDCI(UINT32 itemId, bool noLock)
535 {
536 UINT32 i;
537 int nSize = 0;
538
539 if (!noLock)
540 lockProperties();
541 for(i = 0; i < m_dciCount; i++)
542 {
543 if (m_dciList[i].id == itemId)
544 {
545 switch(m_dciList[i].function)
546 {
547 case F_LAST:
548 nSize = 1;
549 break;
550 case F_DIFF:
551 nSize = 2;
552 break;
553 default:
554 nSize = m_dciList[i].polls;
555 break;
556 }
557 break;
558 }
559 }
560 if (!noLock)
561 unlockProperties();
562 return nSize;
563 }