- Fixed bugs number 195 and 203
[public/netxms.git] / src / server / core / condition.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003, 2004, 2005, 2006 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 //
27 // Constructor
28 //
29
30 Condition::Condition()
31 :NetObj()
32 {
33 m_pszScript = NULL;
34 m_pDCIList = NULL;
35 m_dwDCICount = 0;
36 m_pszScript = 0;
37 m_dwSourceObject = 0;
38 m_nActiveStatus = STATUS_MAJOR;
39 m_nInactiveStatus = STATUS_NORMAL;
40 m_pCompiledScript = NULL;
41 m_bIsActive = FALSE;
42 m_tmLastPoll = 0;
43 m_bQueuedForPolling = FALSE;
44 m_dwActivationEventCode = EVENT_CONDITION_ACTIVATED;
45 m_dwDeactivationEventCode = EVENT_CONDITION_DEACTIVATED;
46 }
47
48
49 //
50 // Constructor for new objects
51 //
52
53 Condition::Condition(BOOL bHidden)
54 :NetObj()
55 {
56 m_pszScript = NULL;
57 m_pDCIList = NULL;
58 m_dwDCICount = 0;
59 m_pszScript = 0;
60 m_dwSourceObject = 0;
61 m_nActiveStatus = STATUS_MAJOR;
62 m_nInactiveStatus = STATUS_NORMAL;
63 m_bIsHidden = bHidden;
64 m_pCompiledScript = NULL;
65 m_bIsActive = FALSE;
66 m_tmLastPoll = 0;
67 m_bQueuedForPolling = FALSE;
68 m_dwActivationEventCode = EVENT_CONDITION_ACTIVATED;
69 m_dwDeactivationEventCode = EVENT_CONDITION_DEACTIVATED;
70 }
71
72
73 //
74 // Destructor
75 //
76
77 Condition::~Condition()
78 {
79 safe_free(m_pDCIList);
80 safe_free(m_pszScript);
81 delete m_pCompiledScript;
82 }
83
84
85 //
86 // Load object from database
87 //
88
89 BOOL Condition::CreateFromDB(DWORD dwId)
90 {
91 TCHAR szQuery[512];
92 DB_RESULT hResult;
93 DWORD i;
94
95 m_dwId = dwId;
96
97 if (!LoadCommonProperties())
98 return FALSE;
99
100 // Load properties
101 _sntprintf(szQuery, 512, _T("SELECT activation_event,deactivation_event,")
102 _T("source_object,active_status,inactive_status,")
103 _T("script FROM conditions WHERE id=%d"), dwId);
104 hResult = DBSelect(g_hCoreDB, szQuery);
105 if (hResult == NULL)
106 return FALSE; // Query failed
107
108 if (DBGetNumRows(hResult) == 0)
109 {
110 // No object with given ID in database
111 DBFreeResult(hResult);
112 return FALSE;
113 }
114
115 m_dwActivationEventCode = DBGetFieldULong(hResult, 0, 0);
116 m_dwDeactivationEventCode = DBGetFieldULong(hResult, 0, 1);
117 m_dwSourceObject = DBGetFieldULong(hResult, 0, 2);
118 m_nActiveStatus = DBGetFieldLong(hResult, 0, 3);
119 m_nInactiveStatus = DBGetFieldLong(hResult, 0, 4);
120 m_pszScript = DBGetField(hResult, 0, 5, NULL, 0);
121 DecodeSQLString(m_pszScript);
122
123 DBFreeResult(hResult);
124
125 // Compile script
126 m_pCompiledScript = (NXSL_Program *)NXSLCompile(m_pszScript, szQuery, 512);
127 if (m_pCompiledScript == NULL)
128 WriteLog(MSG_COND_SCRIPT_COMPILATION_ERROR, EVENTLOG_ERROR_TYPE,
129 "dss", m_dwId, m_szName, szQuery);
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_dwDCICount = DBGetNumRows(hResult);
139 if (m_dwDCICount > 0)
140 {
141 m_pDCIList = (INPUT_DCI *)malloc(sizeof(INPUT_DCI) * m_dwDCICount);
142 for(i = 0; i < m_dwDCICount; i++)
143 {
144 m_pDCIList[i].dwId = DBGetFieldULong(hResult, i, 0);
145 m_pDCIList[i].dwNodeId = DBGetFieldULong(hResult, i, 1);
146 m_pDCIList[i].nFunction = DBGetFieldLong(hResult, i, 2);
147 m_pDCIList[i].nPolls = DBGetFieldLong(hResult, i, 3);
148 }
149 }
150 DBFreeResult(hResult);
151
152 return LoadACLFromDB();
153 }
154
155
156 //
157 // Save object to database
158 //
159
160 BOOL Condition::SaveToDB(DB_HANDLE hdb)
161 {
162 TCHAR *pszEscScript, *pszQuery;
163 DB_RESULT hResult;
164 BOOL bNewObject = TRUE;
165 DWORD i;
166
167 LockData();
168
169 SaveCommonProperties(hdb);
170
171 pszEscScript = EncodeSQLString(CHECK_NULL_EX(m_pszScript));
172 pszQuery = (TCHAR *)malloc(sizeof(TCHAR) * (_tcslen(pszEscScript) + 1024));
173
174 // Check for object's existence in database
175 _stprintf(pszQuery, _T("SELECT id FROM conditions WHERE id=%d"), m_dwId);
176 hResult = DBSelect(hdb, pszQuery);
177 if (hResult != NULL)
178 {
179 if (DBGetNumRows(hResult) > 0)
180 bNewObject = FALSE;
181 DBFreeResult(hResult);
182 }
183
184 // Form and execute INSERT or UPDATE query
185 if (bNewObject)
186 {
187 _stprintf(pszQuery, _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_dwId, m_dwActivationEventCode, m_dwDeactivationEventCode,
191 m_dwSourceObject, m_nActiveStatus, m_nInactiveStatus, pszEscScript);
192 }
193 else
194 {
195 _stprintf(pszQuery, _T("UPDATE conditions SET activation_event=%d,")
196 _T("deactivation_event=%d,source_object=%d,active_status=%d,")
197 _T("inactive_status=%d,script='%s' WHERE id=%d"),
198 m_dwActivationEventCode, m_dwDeactivationEventCode, m_dwSourceObject,
199 m_nActiveStatus, m_nInactiveStatus, pszEscScript, m_dwId);
200 }
201 free(pszEscScript);
202 DBQuery(hdb, pszQuery);
203
204 // Save DCI mapping
205 _stprintf(pszQuery, _T("DELETE FROM cond_dci_map WHERE condition_id=%d"), m_dwId);
206 DBQuery(hdb, pszQuery);
207 for(i = 0; i < m_dwDCICount; i++)
208 {
209 _stprintf(pszQuery, _T("INSERT INTO cond_dci_map (condition_id,sequence_number,dci_id,node_id,")
210 _T("dci_func,num_polls) VALUES (%d,%d,%d,%d,%d,%d)"),
211 m_dwId, i, m_pDCIList[i].dwId, m_pDCIList[i].dwNodeId,
212 m_pDCIList[i].nFunction, m_pDCIList[i].nPolls);
213 DBQuery(hdb, pszQuery);
214 }
215 free(pszQuery);
216
217 // Save access list
218 SaveACLToDB(hdb);
219
220 // Unlock object and clear modification flag
221 m_bIsModified = FALSE;
222 UnlockData();
223 return TRUE;
224 }
225
226
227 //
228 // Delete object from database
229 //
230
231 BOOL Condition::DeleteFromDB(void)
232 {
233 TCHAR szQuery[128];
234 BOOL bSuccess;
235
236 bSuccess = NetObj::DeleteFromDB();
237 if (bSuccess)
238 {
239 _sntprintf(szQuery, 128, _T("DELETE FROM conditions WHERE id=%d"), m_dwId);
240 QueueSQLRequest(szQuery);
241 _sntprintf(szQuery, 128, _T("DELETE FROM cond_dci_map WHERE condition_id=%d"), m_dwId);
242 QueueSQLRequest(szQuery);
243 }
244 return bSuccess;
245 }
246
247
248 //
249 // Create NXCP message from object
250 //
251
252 void Condition::CreateMessage(CSCPMessage *pMsg)
253 {
254 DWORD i, dwId;
255
256 NetObj::CreateMessage(pMsg);
257 pMsg->SetVariable(VID_SCRIPT, CHECK_NULL_EX(m_pszScript));
258 pMsg->SetVariable(VID_ACTIVATION_EVENT, m_dwActivationEventCode);
259 pMsg->SetVariable(VID_DEACTIVATION_EVENT, m_dwDeactivationEventCode);
260 pMsg->SetVariable(VID_SOURCE_OBJECT, m_dwSourceObject);
261 pMsg->SetVariable(VID_ACTIVE_STATUS, (WORD)m_nActiveStatus);
262 pMsg->SetVariable(VID_INACTIVE_STATUS, (WORD)m_nInactiveStatus);
263 pMsg->SetVariable(VID_NUM_ITEMS, m_dwDCICount);
264 for(i = 0, dwId = VID_DCI_LIST_BASE; (i < m_dwDCICount) && (dwId < (VID_DCI_LIST_LAST + 1)); i++)
265 {
266 pMsg->SetVariable(dwId++, m_pDCIList[i].dwId);
267 pMsg->SetVariable(dwId++, m_pDCIList[i].dwNodeId);
268 pMsg->SetVariable(dwId++, (WORD)m_pDCIList[i].nFunction);
269 pMsg->SetVariable(dwId++, (WORD)m_pDCIList[i].nPolls);
270 dwId += 6;
271 }
272 }
273
274
275 //
276 // Modify object from NXCP message
277 //
278
279 DWORD Condition::ModifyFromMessage(CSCPMessage *pRequest, BOOL bAlreadyLocked)
280 {
281 DWORD i, dwId;
282 NetObj *pObject;
283 DCItem *pItem;
284
285 if (!bAlreadyLocked)
286 LockData();
287
288 // Change script
289 if (pRequest->IsVariableExist(VID_SCRIPT))
290 {
291 TCHAR szError[1024];
292
293 safe_free(m_pszScript);
294 delete m_pCompiledScript;
295 m_pszScript = pRequest->GetVariableStr(VID_SCRIPT);
296 m_pCompiledScript = (NXSL_Program *)NXSLCompile(m_pszScript, szError, 1024);
297 if (m_pCompiledScript == NULL)
298 WriteLog(MSG_COND_SCRIPT_COMPILATION_ERROR, EVENTLOG_ERROR_TYPE,
299 "dss", m_dwId, m_szName, szError);
300 }
301
302 // Change activation event
303 if (pRequest->IsVariableExist(VID_ACTIVATION_EVENT))
304 m_dwActivationEventCode = pRequest->GetVariableLong(VID_ACTIVATION_EVENT);
305
306 // Change deactivation event
307 if (pRequest->IsVariableExist(VID_DEACTIVATION_EVENT))
308 m_dwDeactivationEventCode = pRequest->GetVariableLong(VID_DEACTIVATION_EVENT);
309
310 // Change source object
311 if (pRequest->IsVariableExist(VID_SOURCE_OBJECT))
312 m_dwSourceObject = pRequest->GetVariableLong(VID_SOURCE_OBJECT);
313
314 // Change active status
315 if (pRequest->IsVariableExist(VID_ACTIVE_STATUS))
316 m_nActiveStatus = pRequest->GetVariableShort(VID_ACTIVE_STATUS);
317
318 // Change inactive status
319 if (pRequest->IsVariableExist(VID_INACTIVE_STATUS))
320 m_nInactiveStatus = pRequest->GetVariableShort(VID_INACTIVE_STATUS);
321
322 // Change DCI list
323 if (pRequest->IsVariableExist(VID_NUM_ITEMS))
324 {
325 safe_free(m_pDCIList);
326 m_dwDCICount = pRequest->GetVariableLong(VID_NUM_ITEMS);
327 if (m_dwDCICount > 0)
328 {
329 m_pDCIList = (INPUT_DCI *)malloc(sizeof(INPUT_DCI) * m_dwDCICount);
330 for(i = 0, dwId = VID_DCI_LIST_BASE; (i < m_dwDCICount) && (dwId < (VID_DCI_LIST_LAST + 1)); i++)
331 {
332 m_pDCIList[i].dwId = pRequest->GetVariableLong(dwId++);
333 m_pDCIList[i].dwNodeId = pRequest->GetVariableLong(dwId++);
334 m_pDCIList[i].nFunction = pRequest->GetVariableShort(dwId++);
335 m_pDCIList[i].nPolls = pRequest->GetVariableShort(dwId++);
336 dwId += 6;
337 }
338
339 // Update cache size of DCIs
340 for(i = 0; i < m_dwDCICount; i++)
341 {
342 pObject = FindObjectById(m_pDCIList[i].dwNodeId);
343 if (pObject != NULL)
344 {
345 if (pObject->Type() == OBJECT_NODE)
346 {
347 pItem = ((Node *)pObject)->GetItemById(m_pDCIList[i].dwId);
348 if (pItem != NULL)
349 {
350 pItem->UpdateCacheSize(m_dwId);
351 }
352 }
353 }
354 }
355 }
356 else
357 {
358 m_pDCIList = NULL;
359 }
360 }
361
362 return NetObj::ModifyFromMessage(pRequest, TRUE);
363 }
364
365
366 //
367 // Lock for polling
368 //
369
370 void Condition::LockForPoll(void)
371 {
372 IncRefCount();
373 m_bQueuedForPolling = TRUE;
374 }
375
376
377 //
378 // This method should be callsed by poller thread when poll finish
379 //
380
381 void Condition::EndPoll(void)
382 {
383 LockData();
384 m_bQueuedForPolling = FALSE;
385 m_tmLastPoll = time(NULL);
386 UnlockData();
387 DecRefCount();
388 }
389
390
391 //
392 // Check condition
393 //
394
395 void Condition::Check(void)
396 {
397 NXSL_Environment *pEnv;
398 NXSL_Value **ppValueList, *pValue;
399 NetObj *pObject;
400 DCItem *pItem;
401 DWORD i, dwNumValues;
402 int iOldStatus = m_iStatus;
403
404 if ((m_pCompiledScript == NULL) || (m_iStatus == STATUS_UNMANAGED))
405 return;
406
407 pEnv = new NXSL_Environment;
408 pEnv->SetLibrary(g_pScriptLibrary);
409
410 LockData();
411 ppValueList = (NXSL_Value **)malloc(sizeof(NXSL_Value *) * m_dwDCICount);
412 memset(ppValueList, 0, sizeof(NXSL_Value *) * m_dwDCICount);
413 for(i = 0; i < m_dwDCICount; i++)
414 {
415 pObject = FindObjectById(m_pDCIList[i].dwNodeId);
416 if (pObject != NULL)
417 {
418 if (pObject->Type() == OBJECT_NODE)
419 {
420 pItem = ((Node *)pObject)->GetItemById(m_pDCIList[i].dwId);
421 if (pItem != NULL)
422 {
423 ppValueList[i] = pItem->GetValueForNXSL(m_pDCIList[i].nFunction, m_pDCIList[i].nPolls);
424 }
425 }
426 }
427 if (ppValueList[i] == NULL)
428 ppValueList[i] = new NXSL_Value;
429 }
430 dwNumValues = m_dwDCICount;
431 UnlockData();
432
433 DbgPrintf(6, _T("Running evaluation script for condition %d \"%s\""),
434 m_dwId, m_szName);
435 if (m_pCompiledScript->Run(pEnv, dwNumValues, ppValueList) == 0)
436 {
437 pValue = m_pCompiledScript->GetResult();
438 if (pValue->GetValueAsInt32() == 0)
439 {
440 if (m_bIsActive)
441 {
442 // Deactivate condition
443 LockData();
444 m_iStatus = m_nInactiveStatus;
445 m_bIsActive = FALSE;
446 Modify();
447 UnlockData();
448
449 PostEvent(m_dwDeactivationEventCode,
450 (m_dwSourceObject == 0) ? g_dwMgmtNode : m_dwSourceObject,
451 "dsdd", m_dwId, m_szName, iOldStatus, m_iStatus);
452
453 DbgPrintf(6, _T("Condition %d \"%s\" deactivated"),
454 m_dwId, m_szName);
455 }
456 else
457 {
458 DbgPrintf(6, _T("Condition %d \"%s\" still inactive"),
459 m_dwId, m_szName);
460 LockData();
461 if (m_iStatus != m_nInactiveStatus)
462 {
463 m_iStatus = m_nInactiveStatus;
464 Modify();
465 }
466 UnlockData();
467 }
468 }
469 else
470 {
471 if (!m_bIsActive)
472 {
473 // Activate condition
474 LockData();
475 m_iStatus = m_nActiveStatus;
476 m_bIsActive = TRUE;
477 Modify();
478 UnlockData();
479
480 PostEvent(m_dwActivationEventCode,
481 (m_dwSourceObject == 0) ? g_dwMgmtNode : m_dwSourceObject,
482 "dsdd", m_dwId, m_szName, iOldStatus, m_iStatus);
483
484 DbgPrintf(6, _T("Condition %d \"%s\" activated"),
485 m_dwId, m_szName);
486 }
487 else
488 {
489 DbgPrintf(6, _T("Condition %d \"%s\" still active"),
490 m_dwId, m_szName);
491 LockData();
492 if (m_iStatus != m_nActiveStatus)
493 {
494 m_iStatus = m_nActiveStatus;
495 Modify();
496 }
497 UnlockData();
498 }
499 }
500 }
501 else
502 {
503 WriteLog(MSG_COND_SCRIPT_EXECUTION_ERROR, EVENTLOG_ERROR_TYPE,
504 "dss", m_dwId, m_szName, m_pCompiledScript->GetErrorText());
505
506 LockData();
507 if (m_iStatus != STATUS_UNKNOWN)
508 {
509 m_iStatus = STATUS_UNKNOWN;
510 Modify();
511 }
512 UnlockData();
513 }
514 free(ppValueList);
515
516 // Cause parent object(s) to recalculate it's status
517 if (iOldStatus != m_iStatus)
518 {
519 LockParentList(FALSE);
520 for(i = 0; i < m_dwParentCount; i++)
521 m_pParentList[i]->CalculateCompoundStatus();
522 UnlockParentList();
523 }
524 }
525
526
527 //
528 // Determine DCI cache size required by condition object
529 //
530
531 int Condition::GetCacheSizeForDCI(DWORD dwItemId, BOOL bNoLock)
532 {
533 DWORD i;
534 int nSize = 0;
535
536 if (!bNoLock)
537 LockData();
538 for(i = 0; i < m_dwDCICount; i++)
539 {
540 if (m_pDCIList[i].dwId == dwItemId)
541 {
542 switch(m_pDCIList[i].nFunction)
543 {
544 case F_LAST:
545 nSize = 1;
546 break;
547 case F_DIFF:
548 nSize = 2;
549 break;
550 default:
551 nSize = m_pDCIList[i].nPolls;
552 break;
553 }
554 break;
555 }
556 }
557 if (!bNoLock)
558 UnlockData();
559 return nSize;
560 }