Rollback from r3608 to r3606
[public/netxms.git] / src / server / core / dcitem.cpp
1 /* $Id$ */
2 /*
3 ** NetXMS - Network Management System
4 ** Copyright (C) 2003, 2004, 2005, 2006, 2007 Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License as published by
8 ** the Free Software Foundation; either version 2 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** File: dcitem.cpp
21 **
22 **/
23
24 #include "nxcore.h"
25
26
27 //
28 // Get DCI value from within transformation script
29 // First argument is a node object (passed to script via $node variable),
30 // and second is DCI ID
31 //
32
33 static int F_GetDCIValue(int argc, NXSL_Value **argv, NXSL_Value **ppResult)
34 {
35 NXSL_Object *object;
36 Node *node;
37 DCItem *dci;
38
39 if (!argv[0]->IsObject())
40 return NXSL_ERR_NOT_OBJECT;
41
42 if (!argv[1]->IsInteger())
43 return NXSL_ERR_NOT_INTEGER;
44
45 object = argv[0]->GetValueAsObject();
46 if (_tcscmp(object->Class()->Name(), "NetXMS_Node"))
47 return NXSL_ERR_BAD_CLASS;
48
49 node = (Node *)object->Data();
50 dci = node->GetItemById(argv[1]->GetValueAsUInt32());
51 if (dci != NULL)
52 {
53 *ppResult = dci->GetValueForNXSL(F_LAST, 1);
54 }
55 else
56 {
57 *ppResult = new NXSL_Value; // Return NULL if DCI not found
58 }
59
60 return 0;
61 }
62
63
64 //
65 // Find DCI by name
66 //
67
68 static int F_FindDCIByName(int argc, NXSL_Value **argv, NXSL_Value **ppResult)
69 {
70 NXSL_Object *object;
71 Node *node;
72 DCItem *dci;
73
74 if (!argv[0]->IsObject())
75 return NXSL_ERR_NOT_OBJECT;
76
77 if (!argv[1]->IsString())
78 return NXSL_ERR_NOT_STRING;
79
80 object = argv[0]->GetValueAsObject();
81 if (_tcscmp(object->Class()->Name(), "NetXMS_Node"))
82 return NXSL_ERR_BAD_CLASS;
83
84 node = (Node *)object->Data();
85 dci = node->GetItemByName(argv[1]->GetValueAsCString());
86 *ppResult = (dci != NULL) ? new NXSL_Value(dci->Id()) : new NXSL_Value((DWORD)0);
87 return 0;
88 }
89
90
91 //
92 // Find DCI by description
93 //
94
95 static int F_FindDCIByDescription(int argc, NXSL_Value **argv, NXSL_Value **ppResult)
96 {
97 NXSL_Object *object;
98 Node *node;
99 DCItem *dci;
100
101 if (!argv[0]->IsObject())
102 return NXSL_ERR_NOT_OBJECT;
103
104 if (!argv[1]->IsString())
105 return NXSL_ERR_NOT_STRING;
106
107 object = argv[0]->GetValueAsObject();
108 if (_tcscmp(object->Class()->Name(), "NetXMS_Node"))
109 return NXSL_ERR_BAD_CLASS;
110
111 node = (Node *)object->Data();
112 dci = node->GetItemByDescription(argv[1]->GetValueAsCString());
113 *ppResult = (dci != NULL) ? new NXSL_Value(dci->Id()) : new NXSL_Value((DWORD)0);
114 return 0;
115 }
116
117
118 //
119 // Additional functions or use within transformation scripts
120 //
121
122 static NXSL_ExtFunction m_nxslDCIFunctions[] =
123 {
124 { "FindDCIByName", F_FindDCIByName, 2 },
125 { "FindDCIByDescription", F_FindDCIByDescription, 2 },
126 { "GetDCIValue", F_GetDCIValue, 2 }
127 };
128
129 void RegisterDCIFunctions(NXSL_Environment *pEnv)
130 {
131 pEnv->RegisterFunctionSet(sizeof(m_nxslDCIFunctions) / sizeof(NXSL_ExtFunction), m_nxslDCIFunctions);
132 }
133
134
135 //
136 // Default constructor for DCItem
137 //
138
139 DCItem::DCItem()
140 {
141 m_dwId = 0;
142 m_dwTemplateId = 0;
143 m_dwTemplateItemId = 0;
144 m_dwNumThresholds = 0;
145 m_ppThresholdList = NULL;
146 m_iBusy = 0;
147 m_iDataType = DCI_DT_INT;
148 m_iPollingInterval = 3600;
149 m_iRetentionTime = 0;
150 m_iDeltaCalculation = DCM_ORIGINAL_VALUE;
151 m_iSource = DS_INTERNAL;
152 m_iStatus = ITEM_STATUS_NOT_SUPPORTED;
153 m_szName[0] = 0;
154 m_szDescription[0] = 0;
155 m_szInstance[0] = 0;
156 m_tLastPoll = 0;
157 m_pszScript = NULL;
158 m_pScript = NULL;
159 m_pNode = NULL;
160 m_hMutex = MutexCreate();
161 m_dwCacheSize = 0;
162 m_ppValueCache = NULL;
163 m_tPrevValueTimeStamp = 0;
164 m_bCacheLoaded = FALSE;
165 m_iAdvSchedule = 0;
166 m_dwNumSchedules = 0;
167 m_ppScheduleList = NULL;
168 m_tLastCheck = 0;
169 m_iProcessAllThresholds = 0;
170 m_dwErrorCount = 0;
171 m_dwResourceId = 0;
172 m_dwProxyNode = 0;
173 }
174
175
176 //
177 // Create DCItem from another DCItem
178 //
179
180 DCItem::DCItem(const DCItem *pSrc)
181 {
182 DWORD i;
183
184 m_dwId = pSrc->m_dwId;
185 m_dwTemplateId = pSrc->m_dwTemplateId;
186 m_dwTemplateItemId = pSrc->m_dwTemplateItemId;
187 m_iBusy = 0;
188 m_iDataType = pSrc->m_iDataType;
189 m_iPollingInterval = pSrc->m_iPollingInterval;
190 m_iRetentionTime = pSrc->m_iRetentionTime;
191 m_iDeltaCalculation = pSrc->m_iDeltaCalculation;
192 m_iSource = pSrc->m_iSource;
193 m_iStatus = pSrc->m_iStatus;
194 m_tLastPoll = 0;
195 _tcscpy(m_szName, pSrc->m_szName);
196 _tcscpy(m_szDescription, pSrc->m_szDescription);
197 _tcscpy(m_szInstance, pSrc->m_szInstance);
198 m_pszScript = NULL;
199 m_pScript = NULL;
200 SetTransformationScript(pSrc->m_pszScript);
201 m_pNode = NULL;
202 m_hMutex = MutexCreate();
203 m_dwCacheSize = 0;
204 m_ppValueCache = NULL;
205 m_tPrevValueTimeStamp = 0;
206 m_bCacheLoaded = FALSE;
207 m_tLastCheck = 0;
208 m_dwErrorCount = 0;
209 m_iAdvSchedule = pSrc->m_iAdvSchedule;
210 m_dwResourceId = pSrc->m_dwResourceId;
211 m_dwProxyNode = pSrc->m_dwProxyNode;
212
213 // Copy schedules
214 m_dwNumSchedules = pSrc->m_dwNumSchedules;
215 m_ppScheduleList = (TCHAR **)malloc(sizeof(TCHAR *) * m_dwNumSchedules);
216 for(i = 0; i < m_dwNumSchedules; i++)
217 m_ppScheduleList[i] = _tcsdup(pSrc->m_ppScheduleList[i]);
218
219 // Copy thresholds
220 m_dwNumThresholds = pSrc->m_dwNumThresholds;
221 m_ppThresholdList = (Threshold **)malloc(sizeof(Threshold *) * m_dwNumThresholds);
222 for(i = 0; i < m_dwNumThresholds; i++)
223 {
224 m_ppThresholdList[i] = new Threshold(pSrc->m_ppThresholdList[i]);
225 m_ppThresholdList[i]->CreateId();
226 }
227
228 m_iProcessAllThresholds = pSrc->m_iProcessAllThresholds;
229 }
230
231
232 //
233 // Constructor for creating DCItem from database
234 // Assumes that fields in SELECT query are in following order:
235 // item_id,name,source,datatype,polling_interval,retention_time,status,
236 // delta_calculation,transformation,template_id,description,instance,
237 // template_item_id,adv_schedule,all_thresholds,resource_id,proxy_node
238 //
239
240 DCItem::DCItem(DB_RESULT hResult, int iRow, Template *pNode)
241 {
242 TCHAR *pszTmp, szQuery[256], szBuffer[MAX_DB_STRING];
243 DB_RESULT hTempResult;
244 DWORD i;
245
246 m_dwId = DBGetFieldULong(hResult, iRow, 0);
247 DBGetField(hResult, iRow, 1, m_szName, MAX_ITEM_NAME);
248 DecodeSQLString(m_szName);
249 m_iSource = (BYTE)DBGetFieldLong(hResult, iRow, 2);
250 m_iDataType = (BYTE)DBGetFieldLong(hResult, iRow, 3);
251 m_iPollingInterval = DBGetFieldLong(hResult, iRow, 4);
252 m_iRetentionTime = DBGetFieldLong(hResult, iRow, 5);
253 m_iStatus = (BYTE)DBGetFieldLong(hResult, iRow, 6);
254 m_iDeltaCalculation = (BYTE)DBGetFieldLong(hResult, iRow, 7);
255 m_pszScript = NULL;
256 m_pScript = NULL;
257 pszTmp = DBGetField(hResult, iRow, 8, NULL, 0);
258 DecodeSQLString(pszTmp);
259 SetTransformationScript(pszTmp);
260 free(pszTmp);
261 m_dwTemplateId = DBGetFieldULong(hResult, iRow, 9);
262 DBGetField(hResult, iRow, 10, m_szDescription, MAX_DB_STRING);
263 DecodeSQLString(m_szDescription);
264 DBGetField(hResult, iRow, 11, m_szInstance, MAX_DB_STRING);
265 DecodeSQLString(m_szInstance);
266 m_dwTemplateItemId = DBGetFieldULong(hResult, iRow, 12);
267 m_iBusy = 0;
268 m_tLastPoll = 0;
269 m_dwNumThresholds = 0;
270 m_ppThresholdList = NULL;
271 m_pNode = pNode;
272 m_hMutex = MutexCreate();
273 m_dwCacheSize = 0;
274 m_ppValueCache = NULL;
275 m_tPrevValueTimeStamp = 0;
276 m_bCacheLoaded = FALSE;
277 m_tLastCheck = 0;
278 m_dwErrorCount = 0;
279 m_iAdvSchedule = (BYTE)DBGetFieldLong(hResult, iRow, 13);
280 m_iProcessAllThresholds = (BYTE)DBGetFieldLong(hResult, iRow, 14);
281 m_dwResourceId = DBGetFieldULong(hResult, iRow, 15);
282 m_dwProxyNode = DBGetFieldULong(hResult, iRow, 16);
283
284 if (m_iAdvSchedule)
285 {
286 _sntprintf(szQuery, 256, _T("SELECT schedule FROM dci_schedules WHERE item_id=%d"), m_dwId);
287 hTempResult = DBSelect(g_hCoreDB, szQuery);
288 if (hTempResult != NULL)
289 {
290 m_dwNumSchedules = DBGetNumRows(hTempResult);
291 m_ppScheduleList = (TCHAR **)malloc(sizeof(TCHAR *) * m_dwNumSchedules);
292 for(i = 0; i < m_dwNumSchedules; i++)
293 {
294 m_ppScheduleList[i] = DBGetField(hTempResult, i, 0, NULL, 0);
295 DecodeSQLString(m_ppScheduleList[i]);
296 }
297 DBFreeResult(hTempResult);
298 }
299 else
300 {
301 m_dwNumSchedules = 0;
302 m_ppScheduleList = NULL;
303 }
304 }
305 else
306 {
307 m_dwNumSchedules = 0;
308 m_ppScheduleList = NULL;
309 }
310
311 // Load last raw value from database
312 _sntprintf(szQuery, 256, _T("SELECT raw_value,last_poll_time FROM raw_dci_values WHERE item_id=%d"), m_dwId);
313 hTempResult = DBSelect(g_hCoreDB, szQuery);
314 if (hTempResult != NULL)
315 {
316 if (DBGetNumRows(hTempResult) > 0)
317 {
318 m_prevRawValue = DBGetField(hTempResult, 0, 0, szBuffer, MAX_DB_STRING);
319 m_tPrevValueTimeStamp = DBGetFieldULong(hTempResult, 0, 1);
320 m_tLastPoll = m_tPrevValueTimeStamp;
321 }
322 DBFreeResult(hTempResult);
323 }
324 }
325
326
327 //
328 // Constructor for creating new DCItem from scratch
329 //
330
331 DCItem::DCItem(DWORD dwId, const TCHAR *szName, int iSource, int iDataType,
332 int iPollingInterval, int iRetentionTime, Template *pNode,
333 const TCHAR *pszDescription)
334 {
335 m_dwId = dwId;
336 m_dwTemplateId = 0;
337 m_dwTemplateItemId = 0;
338 nx_strncpy(m_szName, szName, MAX_ITEM_NAME);
339 if (pszDescription != NULL)
340 nx_strncpy(m_szDescription, pszDescription, MAX_DB_STRING);
341 else
342 strcpy(m_szDescription, m_szName);
343 m_szInstance[0] = 0;
344 m_iSource = iSource;
345 m_iDataType = iDataType;
346 m_iPollingInterval = iPollingInterval;
347 m_iRetentionTime = iRetentionTime;
348 m_iDeltaCalculation = DCM_ORIGINAL_VALUE;
349 m_iStatus = ITEM_STATUS_ACTIVE;
350 m_iBusy = 0;
351 m_iProcessAllThresholds = 0;
352 m_tLastPoll = 0;
353 m_pszScript = NULL;
354 m_pScript = NULL;
355 m_dwNumThresholds = 0;
356 m_ppThresholdList = NULL;
357 m_pNode = pNode;
358 m_hMutex = MutexCreate();
359 m_dwCacheSize = 0;
360 m_ppValueCache = NULL;
361 m_tPrevValueTimeStamp = 0;
362 m_bCacheLoaded = FALSE;
363 m_iAdvSchedule = 0;
364 m_dwNumSchedules = 0;
365 m_ppScheduleList = NULL;
366 m_tLastCheck = 0;
367 m_dwErrorCount = 0;
368 m_dwResourceId = 0;
369 m_dwProxyNode = 0;
370
371 UpdateCacheSize();
372 }
373
374
375 //
376 // Destructor for DCItem
377 //
378
379 DCItem::~DCItem()
380 {
381 DWORD i;
382
383 for(i = 0; i < m_dwNumThresholds; i++)
384 delete m_ppThresholdList[i];
385 safe_free(m_ppThresholdList);
386 for(i = 0; i < m_dwNumSchedules; i++)
387 free(m_ppScheduleList[i]);
388 safe_free(m_ppScheduleList);
389 safe_free(m_pszScript);
390 delete m_pScript;
391 ClearCache();
392 MutexDestroy(m_hMutex);
393 }
394
395
396 //
397 // Clear data cache
398 //
399
400 void DCItem::ClearCache(void)
401 {
402 DWORD i;
403
404 for(i = 0; i < m_dwCacheSize; i++)
405 delete m_ppValueCache[i];
406 safe_free(m_ppValueCache);
407 m_ppValueCache = NULL;
408 m_dwCacheSize = 0;
409 }
410
411
412 //
413 // Load data collection items thresholds from database
414 //
415
416 BOOL DCItem::LoadThresholdsFromDB(void)
417 {
418 DWORD i;
419 char szQuery[256];
420 DB_RESULT hResult;
421 BOOL bResult = FALSE;
422
423 sprintf(szQuery, "SELECT threshold_id,fire_value,rearm_value,check_function,"
424 "check_operation,parameter_1,parameter_2,event_code,current_state,"
425 "rearm_event_code,repeat_interval FROM thresholds WHERE item_id=%d "
426 "ORDER BY sequence_number", m_dwId);
427 hResult = DBSelect(g_hCoreDB, szQuery);
428 if (hResult != NULL)
429 {
430 m_dwNumThresholds = DBGetNumRows(hResult);
431 if (m_dwNumThresholds > 0)
432 {
433 m_ppThresholdList = (Threshold **)malloc(sizeof(Threshold *) * m_dwNumThresholds);
434 for(i = 0; i < m_dwNumThresholds; i++)
435 m_ppThresholdList[i] = new Threshold(hResult, i, this);
436 }
437 DBFreeResult(hResult);
438 bResult = TRUE;
439 }
440
441 //UpdateCacheSize();
442
443 return bResult;
444 }
445
446
447 //
448 // Save to database
449 //
450
451 BOOL DCItem::SaveToDB(DB_HANDLE hdb)
452 {
453 TCHAR *pszEscName, *pszEscScript, *pszEscDescr, *pszEscInstance, *pszQuery;
454 DB_RESULT hResult;
455 BOOL bNewObject = TRUE, bResult;
456
457 Lock();
458
459 pszEscName = EncodeSQLString(m_szName);
460 pszEscScript = EncodeSQLString(CHECK_NULL_EX(m_pszScript));
461 pszEscDescr = EncodeSQLString(m_szDescription);
462 pszEscInstance = EncodeSQLString(m_szInstance);
463 pszQuery = (TCHAR *)malloc(sizeof(TCHAR) * (_tcslen(pszEscScript) + 2048));
464
465 // Check for object's existence in database
466 sprintf(pszQuery, "SELECT item_id FROM items WHERE item_id=%d", m_dwId);
467 hResult = DBSelect(hdb, pszQuery);
468 if (hResult != 0)
469 {
470 if (DBGetNumRows(hResult) > 0)
471 bNewObject = FALSE;
472 DBFreeResult(hResult);
473 }
474
475 // Prepare and execute query
476 if (bNewObject)
477 sprintf(pszQuery, "INSERT INTO items (item_id,node_id,template_id,name,description,source,"
478 "datatype,polling_interval,retention_time,status,delta_calculation,"
479 "transformation,instance,template_item_id,adv_schedule,"
480 "all_thresholds,resource_id,proxy_node) VALUES "
481 "(%d,%d,%d,'%s','%s',%d,%d,%d,%d,%d,%d,'%s','%s',%d,%d,%d,%d,%d)",
482 m_dwId, (m_pNode == NULL) ? (DWORD)0 : m_pNode->Id(), m_dwTemplateId,
483 pszEscName, pszEscDescr, m_iSource, m_iDataType, m_iPollingInterval,
484 m_iRetentionTime, m_iStatus, m_iDeltaCalculation,
485 pszEscScript, pszEscInstance, m_dwTemplateItemId,
486 m_iAdvSchedule, m_iProcessAllThresholds, m_dwResourceId,
487 m_dwProxyNode);
488 else
489 sprintf(pszQuery, "UPDATE items SET node_id=%d,template_id=%d,name='%s',source=%d,"
490 "datatype=%d,polling_interval=%d,retention_time=%d,status=%d,"
491 "delta_calculation=%d,transformation='%s',description='%s',"
492 "instance='%s',template_item_id=%d,adv_schedule=%d,"
493 "all_thresholds=%d,resource_id=%d,proxy_node=%d WHERE item_id=%d",
494 (m_pNode == NULL) ? 0 : m_pNode->Id(), m_dwTemplateId,
495 pszEscName, m_iSource, m_iDataType, m_iPollingInterval,
496 m_iRetentionTime, m_iStatus, m_iDeltaCalculation, pszEscScript,
497 pszEscDescr, pszEscInstance, m_dwTemplateItemId,
498 m_iAdvSchedule, m_iProcessAllThresholds, m_dwResourceId,
499 m_dwProxyNode, m_dwId);
500 bResult = DBQuery(hdb, pszQuery);
501 free(pszEscName);
502 free(pszEscScript);
503 free(pszEscDescr);
504 free(pszEscInstance);
505
506 // Save thresholds
507 if (bResult)
508 {
509 DWORD i;
510
511 for(i = 0; i < m_dwNumThresholds; i++)
512 m_ppThresholdList[i]->SaveToDB(hdb, i);
513 }
514
515 // Delete non-existing thresholds
516 sprintf(pszQuery, "SELECT threshold_id FROM thresholds WHERE item_id=%d", m_dwId);
517 hResult = DBSelect(hdb, pszQuery);
518 if (hResult != NULL)
519 {
520 int i, iNumRows;
521 DWORD j, dwId;
522
523 iNumRows = DBGetNumRows(hResult);
524 for(i = 0; i < iNumRows; i++)
525 {
526 dwId = DBGetFieldULong(hResult, i, 0);
527 for(j = 0; j < m_dwNumThresholds; j++)
528 if (m_ppThresholdList[j]->Id() == dwId)
529 break;
530 if (j == m_dwNumThresholds)
531 {
532 sprintf(pszQuery, "DELETE FROM thresholds WHERE threshold_id=%d", dwId);
533 DBQuery(hdb, pszQuery);
534 }
535 }
536 DBFreeResult(hResult);
537 }
538
539 // Create record in raw_dci_values if needed
540 sprintf(pszQuery, "SELECT item_id FROM raw_dci_values WHERE item_id=%d", m_dwId);
541 hResult = DBSelect(hdb, pszQuery);
542 if (hResult != NULL)
543 {
544 if (DBGetNumRows(hResult) == 0)
545 {
546 char *pszEscValue;
547
548 pszEscValue = EncodeSQLString(m_prevRawValue.String());
549 sprintf(pszQuery, "INSERT INTO raw_dci_values (item_id,raw_value,last_poll_time)"
550 " VALUES (%d,'%s',%ld)",
551 m_dwId, pszEscValue, m_tPrevValueTimeStamp);
552 free(pszEscValue);
553 DBQuery(hdb, pszQuery);
554 }
555 DBFreeResult(hResult);
556 }
557
558 // Save schedules
559 sprintf(pszQuery, "DELETE FROM dci_schedules WHERE item_id=%d", m_dwId);
560 DBQuery(hdb, pszQuery);
561 if (m_iAdvSchedule)
562 {
563 TCHAR *pszEscSchedule;
564 DWORD i;
565
566 for(i = 0; i < m_dwNumSchedules; i++)
567 {
568 pszEscSchedule = EncodeSQLString(m_ppScheduleList[i]);
569 sprintf(pszQuery, "INSERT INTO dci_schedules (item_id,schedule_id,schedule) VALUES (%d,%d,'%s')",
570 m_dwId, i + 1, pszEscSchedule);
571 free(pszEscSchedule);
572 DBQuery(hdb, pszQuery);
573 }
574 }
575
576 Unlock();
577 free(pszQuery);
578 return bResult;
579 }
580
581
582 //
583 // Check last value for threshold breaches
584 //
585
586 void DCItem::CheckThresholds(ItemValue &value)
587 {
588 DWORD i, iResult, dwInterval;
589 ItemValue checkValue;
590 time_t now = time(NULL);
591
592 for(i = 0; i < m_dwNumThresholds; i++)
593 {
594 iResult = m_ppThresholdList[i]->Check(value, m_ppValueCache, checkValue);
595 switch(iResult)
596 {
597 case THRESHOLD_REACHED:
598 PostEvent(m_ppThresholdList[i]->EventCode(), m_pNode->Id(), "ssssisd", m_szName,
599 m_szDescription, m_ppThresholdList[i]->StringValue(),
600 (const char *)checkValue, m_dwId, m_szInstance, 0);
601 m_ppThresholdList[i]->SetLastEventTimestamp();
602 if (!m_iProcessAllThresholds)
603 i = m_dwNumThresholds; // Stop processing
604 break;
605 case THRESHOLD_REARMED:
606 PostEvent(m_ppThresholdList[i]->RearmEventCode(), m_pNode->Id(), "ssis", m_szName,
607 m_szDescription, m_dwId, m_szInstance);
608 break;
609 case NO_ACTION:
610 if (m_ppThresholdList[i]->IsReached())
611 {
612 // Check if we need to re-sent threshold violation event
613 if (m_ppThresholdList[i]->RepeatInterval() == -1)
614 dwInterval = g_dwThresholdRepeatInterval;
615 else
616 dwInterval = (DWORD)m_ppThresholdList[i]->RepeatInterval();
617 if ((dwInterval != 0) && (m_ppThresholdList[i]->GetLastEventTimestamp() + (time_t)dwInterval < now))
618 {
619 PostEvent(m_ppThresholdList[i]->EventCode(), m_pNode->Id(), "ssssisd", m_szName,
620 m_szDescription, m_ppThresholdList[i]->StringValue(),
621 (const char *)checkValue, m_dwId, m_szInstance, 1);
622 m_ppThresholdList[i]->SetLastEventTimestamp();
623 }
624
625 if (!m_iProcessAllThresholds)
626 {
627 i = m_dwNumThresholds; // Threshold condition still true, stop processing
628 }
629 }
630 break;
631 }
632 }
633 }
634
635
636 //
637 // Create CSCP message with item data
638 //
639
640 void DCItem::CreateMessage(CSCPMessage *pMsg)
641 {
642 DCI_THRESHOLD dct;
643 DWORD i, dwId;
644
645 Lock();
646 pMsg->SetVariable(VID_DCI_ID, m_dwId);
647 pMsg->SetVariable(VID_TEMPLATE_ID, m_dwTemplateId);
648 pMsg->SetVariable(VID_NAME, m_szName);
649 pMsg->SetVariable(VID_DESCRIPTION, m_szDescription);
650 pMsg->SetVariable(VID_INSTANCE, m_szInstance);
651 pMsg->SetVariable(VID_POLLING_INTERVAL, (DWORD)m_iPollingInterval);
652 pMsg->SetVariable(VID_RETENTION_TIME, (DWORD)m_iRetentionTime);
653 pMsg->SetVariable(VID_DCI_SOURCE_TYPE, (WORD)m_iSource);
654 pMsg->SetVariable(VID_DCI_DATA_TYPE, (WORD)m_iDataType);
655 pMsg->SetVariable(VID_DCI_STATUS, (WORD)m_iStatus);
656 pMsg->SetVariable(VID_DCI_DELTA_CALCULATION, (WORD)m_iDeltaCalculation);
657 pMsg->SetVariable(VID_DCI_FORMULA, CHECK_NULL_EX(m_pszScript));
658 pMsg->SetVariable(VID_ALL_THRESHOLDS, (WORD)m_iProcessAllThresholds);
659 pMsg->SetVariable(VID_RESOURCE_ID, m_dwResourceId);
660 pMsg->SetVariable(VID_PROXY_NODE, m_dwProxyNode);
661 pMsg->SetVariable(VID_NUM_THRESHOLDS, m_dwNumThresholds);
662 for(i = 0, dwId = VID_DCI_THRESHOLD_BASE; i < m_dwNumThresholds; i++, dwId++)
663 {
664 m_ppThresholdList[i]->CreateMessage(&dct);
665 pMsg->SetVariable(dwId, (BYTE *)&dct, sizeof(DCI_THRESHOLD));
666 }
667 pMsg->SetVariable(VID_ADV_SCHEDULE, (WORD)m_iAdvSchedule);
668 if (m_iAdvSchedule)
669 {
670 pMsg->SetVariable(VID_NUM_SCHEDULES, m_dwNumSchedules);
671 for(i = 0, dwId = VID_DCI_SCHEDULE_BASE; i < m_dwNumSchedules; i++, dwId++)
672 pMsg->SetVariable(dwId, m_ppScheduleList[i]);
673 }
674 Unlock();
675 }
676
677
678 //
679 // Delete item and collected data from database
680 //
681
682 void DCItem::DeleteFromDB(void)
683 {
684 char szQuery[256];
685
686 sprintf(szQuery, "DELETE FROM items WHERE item_id=%d", m_dwId);
687 QueueSQLRequest(szQuery);
688 sprintf(szQuery, "DELETE FROM idata_%d WHERE item_id=%d", m_pNode->Id(), m_dwId);
689 QueueSQLRequest(szQuery);
690 sprintf(szQuery, "DELETE FROM thresholds WHERE item_id=%d", m_dwId);
691 QueueSQLRequest(szQuery);
692 sprintf(szQuery, "DELETE FROM dci_schedules WHERE item_id=%d", m_dwId);
693 QueueSQLRequest(szQuery);
694 }
695
696
697 //
698 // Update item from CSCP message
699 //
700
701 void DCItem::UpdateFromMessage(CSCPMessage *pMsg, DWORD *pdwNumMaps,
702 DWORD **ppdwMapIndex, DWORD **ppdwMapId)
703 {
704 DWORD i, j, dwNum, dwId;
705 DCI_THRESHOLD *pNewThresholds;
706 Threshold **ppNewList;
707 TCHAR *pszStr;
708
709 Lock();
710
711 pMsg->GetVariableStr(VID_NAME, m_szName, MAX_ITEM_NAME);
712 pMsg->GetVariableStr(VID_DESCRIPTION, m_szDescription, MAX_DB_STRING);
713 pMsg->GetVariableStr(VID_INSTANCE, m_szInstance, MAX_DB_STRING);
714 m_iSource = (BYTE)pMsg->GetVariableShort(VID_DCI_SOURCE_TYPE);
715 m_iDataType = (BYTE)pMsg->GetVariableShort(VID_DCI_DATA_TYPE);
716 m_iPollingInterval = pMsg->GetVariableLong(VID_POLLING_INTERVAL);
717 m_iRetentionTime = pMsg->GetVariableLong(VID_RETENTION_TIME);
718 m_iStatus = (BYTE)pMsg->GetVariableShort(VID_DCI_STATUS);
719 m_iDeltaCalculation = (BYTE)pMsg->GetVariableShort(VID_DCI_DELTA_CALCULATION);
720 m_iProcessAllThresholds = (BYTE)pMsg->GetVariableShort(VID_ALL_THRESHOLDS);
721 m_dwResourceId = pMsg->GetVariableLong(VID_RESOURCE_ID);
722 m_dwProxyNode = pMsg->GetVariableLong(VID_PROXY_NODE);
723 pszStr = pMsg->GetVariableStr(VID_DCI_FORMULA);
724 SetTransformationScript(pszStr);
725 free(pszStr);
726
727 // Update schedules
728 for(i = 0; i < m_dwNumSchedules; i++)
729 free(m_ppScheduleList[i]);
730 m_iAdvSchedule = (BYTE)pMsg->GetVariableShort(VID_ADV_SCHEDULE);
731 if (m_iAdvSchedule)
732 {
733 m_dwNumSchedules = pMsg->GetVariableLong(VID_NUM_SCHEDULES);
734 m_ppScheduleList = (TCHAR **)realloc(m_ppScheduleList, sizeof(TCHAR *) * m_dwNumSchedules);
735 for(i = 0, dwId = VID_DCI_SCHEDULE_BASE; i < m_dwNumSchedules; i++, dwId++)
736 {
737 pszStr = pMsg->GetVariableStr(dwId);
738 if (pszStr != NULL)
739 {
740 m_ppScheduleList[i] = pszStr;
741 }
742 else
743 {
744 m_ppScheduleList[i] = _tcsdup(_T("(null)"));
745 }
746 }
747 }
748 else
749 {
750 if (m_ppScheduleList != NULL)
751 {
752 free(m_ppScheduleList);
753 m_ppScheduleList = NULL;
754 }
755 m_dwNumSchedules = 0;
756 }
757
758 // Update thresholds
759 dwNum = pMsg->GetVariableLong(VID_NUM_THRESHOLDS);
760 pNewThresholds = (DCI_THRESHOLD *)malloc(sizeof(DCI_THRESHOLD) * dwNum);
761 *ppdwMapIndex = (DWORD *)malloc(dwNum * sizeof(DWORD));
762 *ppdwMapId = (DWORD *)malloc(dwNum * sizeof(DWORD));
763 *pdwNumMaps = 0;
764
765 // Read all thresholds from message
766 for(i = 0, dwId = VID_DCI_THRESHOLD_BASE; i < dwNum; i++, dwId++)
767 {
768 pMsg->GetVariableBinary(dwId, (BYTE *)&pNewThresholds[i], sizeof(DCI_THRESHOLD));
769 pNewThresholds[i].dwId = ntohl(pNewThresholds[i].dwId);
770 }
771
772 // Check if some thresholds was deleted, and reposition others if needed
773 ppNewList = (Threshold **)malloc(sizeof(Threshold *) * dwNum);
774 for(i = 0; i < m_dwNumThresholds; i++)
775 {
776 for(j = 0; j < dwNum; j++)
777 if (m_ppThresholdList[i]->Id() == pNewThresholds[j].dwId)
778 break;
779 if (j == dwNum)
780 {
781 // No threshold with that id in new list, delete it
782 delete m_ppThresholdList[i];
783 m_dwNumThresholds--;
784 memmove(&m_ppThresholdList[i], &m_ppThresholdList[i + 1], sizeof(Threshold *) * (m_dwNumThresholds - i));
785 i--;
786 }
787 else
788 {
789 // Move existing thresholds to appropriate positions in new list
790 ppNewList[j] = m_ppThresholdList[i];
791 }
792 }
793 safe_free(m_ppThresholdList);
794 m_ppThresholdList = ppNewList;
795 m_dwNumThresholds = dwNum;
796
797 // Add or update thresholds
798 for(i = 0; i < dwNum; i++)
799 {
800 if (pNewThresholds[i].dwId == 0) // New threshold?
801 {
802 m_ppThresholdList[i] = new Threshold(this);
803 m_ppThresholdList[i]->CreateId();
804
805 // Add index -> id mapping
806 (*ppdwMapIndex)[*pdwNumMaps] = i;
807 (*ppdwMapId)[*pdwNumMaps] = m_ppThresholdList[i]->Id();
808 (*pdwNumMaps)++;
809 }
810 m_ppThresholdList[i]->UpdateFromMessage(&pNewThresholds[i]);
811 }
812
813 safe_free(pNewThresholds);
814 UpdateCacheSize();
815 Unlock();
816 }
817
818
819 //
820 // Process new value
821 //
822
823 void DCItem::NewValue(time_t tmTimeStamp, const TCHAR *pszOriginalValue)
824 {
825 TCHAR *pszEscValue, szQuery[MAX_LINE_SIZE + 128];
826 ItemValue rawValue, *pValue;
827
828 Lock();
829
830 // Normally m_pNode shouldn't be NULL for polled items, but who knows...
831 if (m_pNode == NULL)
832 {
833 Unlock();
834 return;
835 }
836
837 m_dwErrorCount = 0;
838
839 // Save raw value into database
840 pszEscValue = EncodeSQLString(pszOriginalValue);
841 _stprintf(szQuery, _T("UPDATE raw_dci_values SET raw_value='%s',last_poll_time=")
842 TIME_T_FMT _T(" WHERE item_id=%d"),
843 pszEscValue, tmTimeStamp, m_dwId);
844 free(pszEscValue);
845 QueueSQLRequest(szQuery);
846
847 // Create new ItemValue object and transform it as needed
848 pValue = new ItemValue(pszOriginalValue, (DWORD)tmTimeStamp);
849 if (m_tPrevValueTimeStamp == 0)
850 m_prevRawValue = *pValue; // Delta should be zero for first poll
851 rawValue = *pValue;
852 Transform(*pValue, tmTimeStamp - m_tPrevValueTimeStamp);
853 m_prevRawValue = rawValue;
854 m_tPrevValueTimeStamp = tmTimeStamp;
855
856 // Save transformed value to database
857 pszEscValue = EncodeSQLString(pValue->String());
858 _stprintf(szQuery, _T("INSERT INTO idata_%d (item_id,idata_timestamp,idata_value)")
859 _T(" VALUES (%d,") TIME_T_FMT _T(",'%s')"), m_pNode->Id(),
860 m_dwId, tmTimeStamp, pszEscValue);
861 free(pszEscValue);
862 QueueSQLRequest(szQuery);
863
864 // Check thresholds and add value to cache
865 CheckThresholds(*pValue);
866
867 if (m_dwCacheSize > 0)
868 {
869 delete m_ppValueCache[m_dwCacheSize - 1];
870 memmove(&m_ppValueCache[1], m_ppValueCache, sizeof(ItemValue *) * (m_dwCacheSize - 1));
871 m_ppValueCache[0] = pValue;
872 }
873 else
874 {
875 delete pValue;
876 }
877
878 Unlock();
879 }
880
881
882 //
883 // Process new data collection error
884 //
885
886 void DCItem::NewError(void)
887 {
888 DWORD i, iResult;
889
890 Lock();
891
892 // Normally m_pNode shouldn't be NULL for polled items, but who knows...
893 if (m_pNode == NULL)
894 {
895 Unlock();
896 return;
897 }
898
899 m_dwErrorCount++;
900
901 for(i = 0; i < m_dwNumThresholds; i++)
902 {
903 iResult = m_ppThresholdList[i]->CheckError(m_dwErrorCount);
904 switch(iResult)
905 {
906 case THRESHOLD_REACHED:
907 PostEvent(m_ppThresholdList[i]->EventCode(), m_pNode->Id(), "ssssis", m_szName,
908 m_szDescription, _T(""), _T(""), m_dwId, m_szInstance);
909 if (!m_iProcessAllThresholds)
910 i = m_dwNumThresholds; // Stop processing
911 break;
912 case THRESHOLD_REARMED:
913 PostEvent(m_ppThresholdList[i]->RearmEventCode(), m_pNode->Id(), "ssis", m_szName,
914 m_szDescription, m_dwId, m_szInstance);
915 break;
916 case NO_ACTION:
917 if ((m_ppThresholdList[i]->IsReached()) &&
918 (!m_iProcessAllThresholds))
919 i = m_dwNumThresholds; // Threshold condition still true, stop processing
920 break;
921 }
922 }
923
924 Unlock();
925 }
926
927
928 //
929 // Transform received value
930 //
931
932 void DCItem::Transform(ItemValue &value, time_t nElapsedTime)
933 {
934 switch(m_iDeltaCalculation)
935 {
936 case DCM_SIMPLE:
937 switch(m_iDataType)
938 {
939 case DCI_DT_INT:
940 value = (LONG)value - (LONG)m_prevRawValue;
941 break;
942 case DCI_DT_UINT:
943 value = (DWORD)value - (DWORD)m_prevRawValue;
944 break;
945 case DCI_DT_INT64:
946 value = (INT64)value - (INT64)m_prevRawValue;
947 break;
948 case DCI_DT_UINT64:
949 value = (QWORD)value - (QWORD)m_prevRawValue;
950 break;
951 case DCI_DT_FLOAT:
952 value = (double)value - (double)m_prevRawValue;
953 break;
954 case DCI_DT_STRING:
955 value = (LONG)((_tcscmp((const TCHAR *)value, (const TCHAR *)m_prevRawValue) == 0) ? 0 : 1);
956 break;
957 default:
958 // Delta calculation is not supported for other types
959 break;
960 }
961 break;
962 case DCM_AVERAGE_PER_MINUTE:
963 nElapsedTime /= 60; // Convert to minutes
964 case DCM_AVERAGE_PER_SECOND:
965 // Check elapsed time to prevent divide-by-zero exception
966 if (nElapsedTime == 0)
967 nElapsedTime++;
968
969 switch(m_iDataType)
970 {
971 case DCI_DT_INT:
972 value = ((LONG)value - (LONG)m_prevRawValue) / (LONG)nElapsedTime;
973 break;
974 case DCI_DT_UINT:
975 value = ((DWORD)value - (DWORD)m_prevRawValue) / (DWORD)nElapsedTime;
976 break;
977 case DCI_DT_INT64:
978 value = ((INT64)value - (INT64)m_prevRawValue) / (INT64)nElapsedTime;
979 break;
980 case DCI_DT_UINT64:
981 value = ((QWORD)value - (QWORD)m_prevRawValue) / (QWORD)nElapsedTime;
982 break;
983 case DCI_DT_FLOAT:
984 value = ((double)value - (double)m_prevRawValue) / (double)nElapsedTime;
985 break;
986 case DCI_DT_STRING:
987 // I don't see any meaning in "average delta per second (minute)" for string
988 // values, so result will be 0 if there are no difference between
989 // current and previous values, and 1 otherwise
990 value = (LONG)((strcmp((const TCHAR *)value, (const TCHAR *)m_prevRawValue) == 0) ? 0 : 1);
991 break;
992 default:
993 // Delta calculation is not supported for other types
994 break;
995 }
996 break;
997 default: // Default is no transformation
998 break;
999 }
1000
1001 if (m_pScript != NULL)
1002 {
1003 NXSL_Value *pValue;
1004 NXSL_ServerEnv *pEnv;
1005
1006 switch(m_iDataType)
1007 {
1008 case DCI_DT_INT:
1009 pValue = new NXSL_Value((LONG)value);
1010 break;
1011 case DCI_DT_UINT:
1012 pValue = new NXSL_Value((DWORD)value);
1013 break;
1014 case DCI_DT_INT64:
1015 pValue = new NXSL_Value((INT64)value);
1016 break;
1017 case DCI_DT_UINT64:
1018 pValue = new NXSL_Value((QWORD)value);
1019 break;
1020 case DCI_DT_FLOAT:
1021 pValue = new NXSL_Value((double)value);
1022 break;
1023 case DCI_DT_STRING:
1024 pValue = new NXSL_Value((char *)((const char *)value));
1025 break;
1026 default:
1027 pValue = new NXSL_Value;
1028 break;
1029 }
1030
1031 pEnv = new NXSL_ServerEnv;
1032 m_pScript->SetGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, m_pNode)));
1033
1034 if (m_pScript->Run(pEnv, 1, &pValue) == 0)
1035 {
1036 pValue = m_pScript->GetResult();
1037 if (pValue != NULL)
1038 {
1039 switch(m_iDataType)
1040 {
1041 case DCI_DT_INT:
1042 value = pValue->GetValueAsInt32();
1043 break;
1044 case DCI_DT_UINT:
1045 value = pValue->GetValueAsUInt32();
1046 break;
1047 case DCI_DT_INT64:
1048 value = pValue->GetValueAsInt64();
1049 break;
1050 case DCI_DT_UINT64:
1051 value = pValue->GetValueAsUInt64();
1052 break;
1053 case DCI_DT_FLOAT:
1054 value = pValue->GetValueAsReal();
1055 break;
1056 case DCI_DT_STRING:
1057 value = pValue->GetValueAsCString();
1058 break;
1059 default:
1060 break;
1061 }
1062 }
1063 }
1064 else
1065 {
1066 TCHAR szBuffer[1024];
1067
1068 _sntprintf(szBuffer, 1024, _T("DCI::%s::%d"),
1069 (m_pNode != NULL) ? m_pNode->Name() : _T("(null)"), m_dwId);
1070 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, _T("ssd"), szBuffer,
1071 m_pScript->GetErrorText(), m_dwId);
1072 }
1073 }
1074 }
1075
1076
1077 //
1078 // Set new ID and node/template association
1079 //
1080
1081 void DCItem::ChangeBinding(DWORD dwNewId, Template *pNewNode, BOOL doMacroExpansion)
1082 {
1083 DWORD i;
1084
1085 Lock();
1086 m_pNode = pNewNode;
1087 if (dwNewId != 0)
1088 {
1089 m_dwId = dwNewId;
1090 for(i = 0; i < m_dwNumThresholds; i++)
1091 m_ppThresholdList[i]->BindToItem(m_dwId);
1092 }
1093
1094 if (doMacroExpansion)
1095 {
1096 ExpandMacros(m_szName, m_szName, MAX_ITEM_NAME);
1097 ExpandMacros(m_szDescription, m_szDescription, MAX_DB_STRING);
1098 ExpandMacros(m_szInstance, m_szInstance, MAX_DB_STRING);
1099 }
1100
1101 ClearCache();
1102 UpdateCacheSize();
1103 Unlock();
1104 }
1105
1106
1107 //
1108 // Update required cache size depending on thresholds
1109 // dwCondId is an identifier of calling condition object id. If it is not 0,
1110 // GetCacheSizeForDCI should be called with bNoLock == TRUE for appropriate
1111 // condition object
1112 //
1113
1114 void DCItem::UpdateCacheSize(DWORD dwCondId)
1115 {
1116 DWORD i, dwSize, dwRequiredSize;
1117
1118 // Sanity check
1119 if (m_pNode == NULL)
1120 {
1121 DbgPrintf(3, _T("DCItem::UpdateCacheSize() called for DCI %d when m_pNode == NULL"), m_dwId);
1122 return;
1123 }
1124
1125 // Minimum cache size is 1 for nodes (so GetLastValue can work)
1126 // and it is always 0 for templates
1127 if (m_pNode->Type() == OBJECT_NODE)
1128 {
1129 dwRequiredSize = 1;
1130
1131 // Calculate required cache size
1132 for(i = 0; i < m_dwNumThresholds; i++)
1133 if (dwRequiredSize < m_ppThresholdList[i]->RequiredCacheSize())
1134 dwRequiredSize = m_ppThresholdList[i]->RequiredCacheSize();
1135
1136 RWLockReadLock(g_rwlockConditionIndex, INFINITE);
1137 for(i = 0; i < g_dwConditionIndexSize; i++)
1138 {
1139 dwSize = ((Condition *)g_pConditionIndex[i].pObject)->GetCacheSizeForDCI(m_dwId, dwCondId == g_pConditionIndex[i].dwKey);
1140 if (dwSize > dwRequiredSize)
1141 dwRequiredSize = dwSize;
1142 }
1143 RWLockUnlock(g_rwlockConditionIndex);
1144 }
1145 else
1146 {
1147 dwRequiredSize = 0;
1148 }
1149
1150 // Update cache if needed
1151 if (dwRequiredSize < m_dwCacheSize)
1152 {
1153 // Destroy unneeded values
1154 if (m_dwCacheSize > 0)
1155 for(i = dwRequiredSize; i < m_dwCacheSize; i++)
1156 delete m_ppValueCache[i];
1157
1158 m_dwCacheSize = dwRequiredSize;
1159 if (m_dwCacheSize > 0)
1160 {
1161 m_ppValueCache = (ItemValue **)realloc(m_ppValueCache, sizeof(ItemValue *) * m_dwCacheSize);
1162 }
1163 else
1164 {
1165 safe_free(m_ppValueCache);
1166 m_ppValueCache = NULL;
1167 }
1168 }
1169 else if (dwRequiredSize > m_dwCacheSize)
1170 {
1171 // Expand cache
1172 m_ppValueCache = (ItemValue **)realloc(m_ppValueCache, sizeof(ItemValue *) * dwRequiredSize);
1173 for(i = m_dwCacheSize; i < dwRequiredSize; i++)
1174 m_ppValueCache[i] = NULL;
1175
1176 // Load missing values from database
1177 if (m_pNode != NULL)
1178 {
1179 DB_ASYNC_RESULT hResult;
1180 char szBuffer[MAX_DB_STRING];
1181 BOOL bHasData;
1182
1183 switch(g_dwDBSyntax)
1184 {
1185 case DB_SYNTAX_MSSQL:
1186 sprintf(szBuffer, "SELECT TOP %d idata_value,idata_timestamp FROM idata_%d "
1187 "WHERE item_id=%d ORDER BY idata_timestamp DESC",
1188 dwRequiredSize, m_pNode->Id(), m_dwId);
1189 break;
1190 case DB_SYNTAX_ORACLE:
1191 sprintf(szBuffer, "SELECT idata_value,idata_timestamp FROM idata_%d "
1192 "WHERE item_id=%d AND ROWNUM <= %d ORDER BY idata_timestamp DESC",
1193 m_pNode->Id(), m_dwId, dwRequiredSize);
1194 break;
1195 case DB_SYNTAX_MYSQL:
1196 case DB_SYNTAX_PGSQL:
1197 case DB_SYNTAX_SQLITE:
1198 sprintf(szBuffer, "SELECT idata_value,idata_timestamp FROM idata_%d "
1199 "WHERE item_id=%d ORDER BY idata_timestamp DESC LIMIT %d",
1200 m_pNode->Id(), m_dwId, dwRequiredSize);
1201 break;
1202 default:
1203 sprintf(szBuffer, "SELECT idata_value,idata_timestamp FROM idata_%d "
1204 "WHERE item_id=%d ORDER BY idata_timestamp DESC",
1205 m_pNode->Id(), m_dwId);
1206 break;
1207 }
1208 hResult = DBAsyncSelect(g_hCoreDB, szBuffer);
1209 if (hResult != NULL)
1210 {
1211 // Skip already cached values
1212 for(i = 0, bHasData = TRUE; i < m_dwCacheSize; i++)
1213 bHasData = DBFetch(hResult);
1214
1215 // Create new cache entries
1216 for(; (i < dwRequiredSize) && bHasData; i++)
1217 {
1218 bHasData = DBFetch(hResult);
1219 if (bHasData)
1220 {
1221 DBGetFieldAsync(hResult, 0, szBuffer, MAX_DB_STRING);
1222 DecodeSQLString(szBuffer);
1223 m_ppValueCache[i] =
1224 new ItemValue(szBuffer, DBGetFieldAsyncULong(hResult, 1));
1225 }
1226 else
1227 {
1228 m_ppValueCache[i] = new ItemValue(_T(""), 1); // Empty value
1229 }
1230 }
1231
1232 // Fill up cache with empty values if we don't have enough values in database
1233 for(; i < dwRequiredSize; i++)
1234 m_ppValueCache[i] = new ItemValue(_T(""), 1);
1235
1236 DBFreeAsyncResult(g_hCoreDB, hResult);
1237 }
1238 else
1239 {
1240 // Error reading data from database, fill cache with empty values
1241 for(i = m_dwCacheSize; i < dwRequiredSize; i++)
1242 m_ppValueCache[i] = new ItemValue(_T(""), 1);
1243 }
1244 }
1245 m_dwCacheSize = dwRequiredSize;
1246 }
1247 m_bCacheLoaded = TRUE;
1248 }
1249
1250
1251 //
1252 // Put last value into CSCP message
1253 //
1254
1255 void DCItem::GetLastValue(CSCPMessage *pMsg, DWORD dwId)
1256 {
1257 pMsg->SetVariable(dwId++, m_dwId);
1258 pMsg->SetVariable(dwId++, m_szName);
1259 pMsg->SetVariable(dwId++, m_szDescription);
1260 pMsg->SetVariable(dwId++, (WORD)m_iSource);
1261 if (m_dwCacheSize > 0)
1262 {
1263 pMsg->SetVariable(dwId++, (WORD)m_iDataType);
1264 pMsg->SetVariable(dwId++, (TCHAR *)m_ppValueCache[0]->String());
1265 pMsg->SetVariable(dwId++, m_ppValueCache[0]->GetTimeStamp());
1266 }
1267 else
1268 {
1269 pMsg->SetVariable(dwId++, (WORD)DCI_DT_NULL);
1270 pMsg->SetVariable(dwId++, _T(""));
1271 pMsg->SetVariable(dwId++, (DWORD)0);
1272 }
1273 pMsg->SetVariable(dwId++, (WORD)m_iStatus);
1274 }
1275
1276
1277 //
1278 // Get item's last value for use in NXSL
1279 //
1280
1281 NXSL_Value *DCItem::GetValueForNXSL(int nFunction, int nPolls)
1282 {
1283 NXSL_Value *pValue;
1284
1285 switch(nFunction)
1286 {
1287 case F_LAST:
1288 pValue = (m_dwCacheSize > 0) ? new NXSL_Value((TCHAR *)m_ppValueCache[0]->String()) : new NXSL_Value;
1289 break;
1290 case F_DIFF:
1291 if (m_dwCacheSize >= 2)
1292 {
1293 ItemValue result;
1294
1295 CalculateItemValueDiff(result, m_iDataType, *m_ppValueCache[0], *m_ppValueCache[1]);
1296 pValue = new NXSL_Value((TCHAR *)result.String());
1297 }
1298 else
1299 {
1300 pValue = new NXSL_Value;
1301 }
1302 break;
1303 case F_AVERAGE:
1304 if (m_dwCacheSize > 0)
1305 {
1306 ItemValue result;
1307
1308 CalculateItemValueAverage(result, m_iDataType,
1309 min(m_dwCacheSize, (DWORD)nPolls), m_ppValueCache);
1310 pValue = new NXSL_Value((TCHAR *)result.String());
1311 }
1312 else
1313 {
1314 pValue = new NXSL_Value;
1315 }
1316 break;
1317 case F_DEVIATION:
1318 if (m_dwCacheSize > 0)
1319 {
1320 ItemValue result;
1321
1322 CalculateItemValueMD(result, m_iDataType,
1323 min(m_dwCacheSize, (DWORD)nPolls), m_ppValueCache);
1324 pValue = new NXSL_Value((TCHAR *)result.String());
1325 }
1326 else
1327 {
1328 pValue = new NXSL_Value;
1329 }
1330 break;
1331 case F_ERROR:
1332 pValue = new NXSL_Value((LONG)((m_dwErrorCount >= (DWORD)nPolls) ? 1 : 0));
1333 break;
1334 default:
1335 pValue = new NXSL_Value;
1336 break;
1337 }
1338 return pValue;
1339 }
1340
1341
1342 //
1343 // Clean expired data
1344 //
1345
1346 void DCItem::DeleteExpiredData(void)
1347 {
1348 TCHAR szQuery[256];
1349 time_t now;
1350
1351 now = time(NULL);
1352 Lock();
1353 _sntprintf(szQuery, 256, _T("DELETE FROM idata_%d WHERE (item_id=%d) AND (idata_timestamp<%ld)"),
1354 m_pNode->Id(), m_dwId, now - (time_t)m_iRetentionTime * 86400);
1355 Unlock();
1356 QueueSQLRequest(szQuery);
1357 }
1358
1359
1360 //
1361 // Delete all collected data
1362 //
1363
1364 BOOL DCItem::DeleteAllData(void)
1365 {
1366 TCHAR szQuery[256];
1367 BOOL success;
1368
1369 Lock();
1370 _sntprintf(szQuery, 256, _T("DELETE FROM idata_%d WHERE item_id=%d"), m_pNode->Id(), m_dwId);
1371 success = DBQuery(g_hCoreDB, szQuery);
1372 ClearCache();
1373 UpdateCacheSize();
1374 Unlock();
1375 return success;
1376 }
1377
1378
1379 //
1380 // Prepare item for deletion
1381 //
1382
1383 void DCItem::PrepareForDeletion(void)
1384 {
1385 Lock();
1386
1387 m_iStatus = ITEM_STATUS_DISABLED; // Prevent future polls
1388
1389 // Wait until current poll ends, if any
1390 // while() is not a very good solution, and probably need to be
1391 // rewrited using conditions
1392 while(m_iBusy)
1393 {
1394 Unlock();
1395 ThreadSleepMs(100);
1396 Lock();
1397 }
1398
1399 Unlock();
1400 }
1401
1402
1403 //
1404 // Match schedule element
1405 // NOTE: We assume that pattern can be modified during processing
1406 //
1407
1408 static BOOL MatchScheduleElement(TCHAR *pszPattern, int nValue)
1409 {
1410 TCHAR *ptr, *curr;
1411 int nStep, nCurr, nPrev;
1412 BOOL bRun = TRUE, bRange = FALSE;
1413
1414 // Check if step was specified
1415 ptr = _tcschr(pszPattern, _T('/'));
1416 if (ptr != NULL)
1417 {
1418 *ptr = 0;
1419 ptr++;
1420 nStep = atoi(ptr);
1421 }
1422 else
1423 {
1424 nStep = 1;
1425 }
1426
1427 if (*pszPattern == _T('*'))
1428 goto check_step;
1429
1430 for(curr = pszPattern; bRun; curr = ptr + 1)
1431 {
1432 for(ptr = curr; (*ptr != 0) && (*ptr != '-') && (*ptr != ','); ptr++);
1433 switch(*ptr)
1434 {
1435 case '-':
1436 if (bRange)
1437 return FALSE; // Form like 1-2-3 is invalid
1438 bRange = TRUE;
1439 *ptr = 0;
1440 nPrev = atoi(curr);
1441 break;
1442 case 0:
1443 bRun = FALSE;
1444 case ',':
1445 *ptr = 0;
1446 nCurr = atoi(curr);
1447 if (bRange)
1448 {
1449 if ((nValue >= nPrev) && (nValue <= nCurr))
1450 goto check_step;
1451 bRange = FALSE;
1452 }
1453 else
1454 {
1455 if (nValue == nCurr)
1456 return TRUE;
1457 }
1458 break;
1459 }
1460 }
1461
1462 return FALSE;
1463
1464 check_step:
1465 return (nValue % nStep) == 0;
1466 }
1467
1468
1469 //
1470 // Match schedule to current time
1471 //
1472
1473 static BOOL MatchSchedule(struct tm *pCurrTime, TCHAR *pszSchedule)
1474 {
1475 TCHAR *pszCurr, szValue[256];
1476
1477 // Minute
1478 pszCurr = ExtractWord(pszSchedule, szValue);
1479 if (!MatchScheduleElement(szValue, pCurrTime->tm_min))
1480 return FALSE;
1481
1482 // Hour
1483 pszCurr = ExtractWord(pszCurr, szValue);
1484 if (!MatchScheduleElement(szValue, pCurrTime->tm_hour))
1485 return FALSE;
1486
1487 // Day of month
1488 pszCurr = ExtractWord(pszCurr, szValue);
1489 if (!MatchScheduleElement(szValue, pCurrTime->tm_mday))
1490 return FALSE;
1491
1492 // Month
1493 pszCurr = ExtractWord(pszCurr, szValue);
1494 if (!MatchScheduleElement(szValue, pCurrTime->tm_mon + 1))
1495 return FALSE;
1496
1497 // Day of week
1498 ExtractWord(pszCurr, szValue);
1499 TranslateStr(szValue, _T("7"), _T("0"));
1500 return MatchScheduleElement(szValue, pCurrTime->tm_wday);
1501 }
1502
1503
1504 //
1505 // Check if associated cluster resource is active. Returns TRUE also if
1506 // DCI has no resource association
1507 //
1508
1509 BOOL DCItem::MatchClusterResource(void)
1510 {
1511 Cluster *pCluster;
1512
1513 if (m_dwResourceId == 0)
1514 return TRUE;
1515
1516 pCluster = ((Node *)m_pNode)->GetMyCluster();
1517 if (pCluster == NULL)
1518 return FALSE; // Has association, but cluster object cannot be found
1519
1520 return pCluster->IsResourceOnNode(m_dwResourceId, m_pNode->Id());
1521 }
1522
1523
1524 //
1525 // Check if DCI have to be polled
1526 //
1527
1528 BOOL DCItem::ReadyForPolling(time_t currTime)
1529 {
1530 BOOL bResult;
1531
1532 Lock();
1533 if ((m_iStatus != ITEM_STATUS_DISABLED) && (!m_iBusy) &&
1534 m_bCacheLoaded && (m_iSource != DS_PUSH_AGENT) &&
1535 (MatchClusterResource()))
1536 {
1537 if (m_iAdvSchedule)
1538 {
1539 DWORD i;
1540 struct tm tmCurrLocal, tmLastLocal;
1541
1542 memcpy(&tmCurrLocal, localtime(&currTime), sizeof(struct tm));
1543 memcpy(&tmLastLocal, localtime(&m_tLastCheck), sizeof(struct tm));
1544 for(i = 0, bResult = FALSE; i < m_dwNumSchedules; i++)
1545 {
1546 if (MatchSchedule(&tmCurrLocal, m_ppScheduleList[i]))
1547 {
1548 if ((currTime - m_tLastCheck >= 60) ||
1549 (tmCurrLocal.tm_min != tmLastLocal.tm_min))
1550 {
1551 bResult = TRUE;
1552 break;
1553 }
1554 }
1555 }
1556 m_tLastCheck = currTime;
1557 }
1558 else
1559 {
1560 if (m_iStatus == ITEM_STATUS_NOT_SUPPORTED)
1561 bResult = (m_tLastPoll + m_iPollingInterval * 10 <= currTime);
1562 else
1563 bResult = (m_tLastPoll + m_iPollingInterval <= currTime);
1564 }
1565 }
1566 else
1567 {
1568 bResult = FALSE;
1569 }
1570 Unlock();
1571 return bResult;
1572 }
1573
1574
1575 //
1576 // Update from template item
1577 //
1578
1579 void DCItem::UpdateFromTemplate(DCItem *pItem)
1580 {
1581 DWORD i, dwCount;
1582
1583 Lock();
1584
1585 m_iDataType = pItem->m_iDataType;
1586 m_iPollingInterval = pItem->m_iPollingInterval;
1587 m_iRetentionTime = pItem->m_iRetentionTime;
1588 m_iDeltaCalculation = pItem->m_iDeltaCalculation;
1589 m_iSource = pItem->m_iSource;
1590 m_iStatus = pItem->m_iStatus;
1591 m_iProcessAllThresholds = pItem->m_iProcessAllThresholds;
1592 m_dwProxyNode = pItem->m_dwProxyNode;
1593 SetTransformationScript(pItem->m_pszScript);
1594 m_iAdvSchedule = pItem->m_iAdvSchedule;
1595
1596 // Copy schedules
1597 for(i = 0; i < m_dwNumSchedules; i++)
1598 safe_free(m_ppScheduleList[i]);
1599 safe_free(m_ppScheduleList);
1600 m_dwNumSchedules = pItem->m_dwNumSchedules;
1601 m_ppScheduleList = (TCHAR **)malloc(sizeof(TCHAR *) * m_dwNumSchedules);
1602 for(i = 0; i < m_dwNumSchedules; i++)
1603 m_ppScheduleList[i] = _tcsdup(pItem->m_ppScheduleList[i]);
1604
1605 // Copy thresholds
1606 // ***************************
1607 // First, skip matching thresholds
1608 dwCount = min(m_dwNumThresholds, pItem->m_dwNumThresholds);
1609 for(i = 0; i < dwCount; i++)
1610 if (!m_ppThresholdList[i]->Compare(pItem->m_ppThresholdList[i]))
1611 break;
1612 dwCount = i; // First unmatched threshold's position
1613
1614 // Delete all original thresholds starting from first unmatched
1615 for(; i < m_dwNumThresholds; i++)
1616 delete m_ppThresholdList[i];
1617
1618 // (Re)create thresholds starting from first unmatched
1619 m_dwNumThresholds = pItem->m_dwNumThresholds;
1620 m_ppThresholdList = (Threshold **)realloc(m_ppThresholdList, sizeof(Threshold *) * m_dwNumThresholds);
1621 for(i = dwCount; i < m_dwNumThresholds; i++)
1622 {
1623 m_ppThresholdList[i] = new Threshold(pItem->m_ppThresholdList[i]);
1624 m_ppThresholdList[i]->CreateId();
1625 m_ppThresholdList[i]->BindToItem(m_dwId);
1626 }
1627
1628 ExpandMacros(pItem->m_szName, m_szName, MAX_ITEM_NAME);
1629 ExpandMacros(pItem->m_szDescription, m_szDescription, MAX_DB_STRING);
1630 ExpandMacros(pItem->m_szInstance, m_szInstance, MAX_DB_STRING);
1631
1632 UpdateCacheSize();
1633
1634 Unlock();
1635 }
1636
1637
1638 //
1639 // Set new formula
1640 //
1641
1642 void DCItem::SetTransformationScript(TCHAR *pszScript)
1643 {
1644 safe_free(m_pszScript);
1645 delete m_pScript;
1646 if (pszScript != NULL)
1647 {
1648 m_pszScript = _tcsdup(pszScript);
1649 StrStrip(m_pszScript);
1650 if (m_pszScript[0] != 0)
1651 {
1652 /* TODO: add compilation error handling */
1653 m_pScript = (NXSL_Program *)NXSLCompile(m_pszScript, NULL, 0);
1654 }
1655 else
1656 {
1657 m_pScript = NULL;
1658 }
1659 }
1660 else
1661 {
1662 m_pszScript = NULL;
1663 m_pScript = NULL;
1664 }
1665 }
1666
1667
1668 //
1669 // Get list of used events
1670 //
1671
1672 void DCItem::GetEventList(DWORD **ppdwList, DWORD *pdwSize)
1673 {
1674 DWORD i, j;
1675
1676 Lock();
1677
1678 if (m_dwNumThresholds > 0)
1679 {
1680 *ppdwList = (DWORD *)realloc(*ppdwList, sizeof(DWORD) * (*pdwSize + m_dwNumThresholds));
1681 j = *pdwSize;
1682 *pdwSize += m_dwNumThresholds;
1683 for(i = 0; i < m_dwNumThresholds; i++, j++)
1684 {
1685 (*ppdwList)[j] = m_ppThresholdList[i]->EventCode();
1686 }
1687 }
1688
1689 Unlock();
1690 }
1691
1692
1693 //
1694 // Create management pack record
1695 //
1696
1697 void DCItem::CreateNXMPRecord(String &str)
1698 {
1699 String strName, strDescr, strInstance, strTemp;
1700 DWORD i;
1701
1702 Lock();
1703
1704 strName = m_szName;
1705 EscapeString(strName);
1706
1707 strDescr = m_szDescription;
1708 EscapeString(strDescr);
1709
1710 strInstance = m_szInstance;
1711 EscapeString(strInstance);
1712
1713 str.AddFormattedString(_T("\t\t\t@DCI\n\t\t\t{\n")
1714 _T("\t\t\t\tNAME=\"%s\";\n")
1715 _T("\t\t\t\tDESCRIPTION=\"%s\";\n")
1716 _T("\t\t\t\tDATATYPE=%d;\n")
1717 _T("\t\t\t\tORIGIN=%d;\n")
1718 _T("\t\t\t\tINTERVAL=%d;\n")
1719 _T("\t\t\t\tRETENTION=%d;\n")
1720 _T("\t\t\t\tINSTANCE=\"%s\";\n")
1721 _T("\t\t\t\tDELTA=%d;\n")
1722 _T("\t\t\t\tADVANCED_SCHEDULE=%d;\n")
1723 _T("\t\t\t\tALL_THRESHOLDS=%d;\n"),
1724 (TCHAR *)strName, (TCHAR *)strDescr,
1725 m_iDataType, m_iSource,
1726 m_iAdvSchedule ? 0 : m_iPollingInterval,
1727 m_iRetentionTime, (TCHAR *)strInstance,
1728 m_iDeltaCalculation, m_iAdvSchedule,
1729 m_iProcessAllThresholds);
1730
1731 if (m_pszScript != NULL)
1732 {
1733 strTemp = m_pszScript;
1734 EscapeString(strTemp);
1735 str += _T("\t\t\t\tSCRIPT=\"");
1736 str += strTemp;
1737 str += _T("\";\n");
1738 }
1739
1740 if (m_iAdvSchedule)
1741 {
1742 str += _T("\t\t\t\t@SCHEDULE\n\t\t\t\t{\n");
1743 for(i = 0; i < m_dwNumSchedules; i++)
1744 {
1745 strTemp = m_ppScheduleList[i];
1746 EscapeString(strTemp);
1747 str.AddFormattedString(_T("\t\t\t\t\t\"%s\";\n"));
1748 }
1749 str += _T("\t\t\t\t}\n");
1750 }
1751
1752 str += _T("\t\t\t\t@THRESHOLDS\n\t\t\t\t{\n");
1753 for(i = 0; i < m_dwNumThresholds; i++)
1754 {
1755 m_ppThresholdList[i]->CreateNXMPRecord(str);
1756 }
1757
1758 Unlock();
1759 str += _T("\t\t\t\t}\n\t\t\t}\n");
1760 }
1761
1762
1763 //
1764 // Modify item - intended for updating items in system templates
1765 //
1766
1767 void DCItem::SystemModify(const TCHAR *pszName, int nOrigin, int nRetention, int nInterval, int nDataType)
1768 {
1769 m_iDataType = nDataType;
1770 m_iPollingInterval = nInterval;
1771 m_iRetentionTime = nRetention;
1772 m_iSource = nOrigin;
1773 nx_strncpy(m_szName, pszName, MAX_ITEM_NAME);
1774 }
1775
1776
1777 //
1778 // Finish DCI parsing from management pack:
1779 // 1. Generate unique ID for DCI
1780 // 2. Generate unique ID for thresholds
1781 // 3. Associate all thresholds
1782 //
1783
1784 void DCItem::FinishMPParsing(void)
1785 {
1786 DWORD i;
1787
1788 m_dwId = CreateUniqueId(IDG_ITEM);
1789 for(i = 0; i < m_dwNumThresholds; i++)
1790 {
1791 m_ppThresholdList[i]->CreateId();
1792 m_ppThresholdList[i]->Associate(this);
1793 }
1794 }
1795
1796
1797 //
1798 // Add threshold to the list
1799 //
1800
1801 void DCItem::AddThreshold(Threshold *pThreshold)
1802 {
1803 m_dwNumThresholds++;
1804 m_ppThresholdList = (Threshold **)realloc(m_ppThresholdList, sizeof(Threshold *) * m_dwNumThresholds);
1805 m_ppThresholdList[m_dwNumThresholds - 1] = pThreshold;
1806 }
1807
1808
1809 //
1810 // Add schedule
1811 //
1812
1813 void DCItem::AddSchedule(TCHAR *pszSchedule)
1814 {
1815 m_dwNumSchedules++;
1816 m_ppScheduleList = (TCHAR **)realloc(m_ppScheduleList, sizeof(TCHAR *) * m_dwNumSchedules);
1817 m_ppScheduleList[m_dwNumSchedules - 1] = _tcsdup(pszSchedule);
1818 }
1819
1820
1821 //
1822 // Enumerate all thresholds
1823 //
1824
1825 BOOL DCItem::EnumThresholds(BOOL (* pfCallback)(Threshold *, DWORD, void *), void *pArg)
1826 {
1827 DWORD i;
1828 BOOL bRet = TRUE;
1829
1830 Lock();
1831 for(i = 0; i < m_dwNumThresholds; i++)
1832 {
1833 if (!pfCallback(m_ppThresholdList[i], i, pArg))
1834 {
1835 bRet = FALSE;
1836 break;
1837 }
1838 }
1839 Unlock();
1840 return bRet;
1841 }
1842
1843
1844 //
1845 // Expand macros in text
1846 //
1847
1848 void DCItem::ExpandMacros(const TCHAR *src, TCHAR *dst, size_t dstLen)
1849 {
1850 String temp;
1851 TCHAR *head, *rest, *macro;
1852 int index = 0, index2;
1853
1854 temp = src;
1855 while((index = temp.Find(_T("%{"), index)) != String::npos)
1856 {
1857 head = temp.SubStr(0, index);
1858 index2 = temp.Find(_T("}"), index);
1859 if (index2 == String::npos)
1860 {
1861 free(head);
1862 break; // Missing closing }
1863 }
1864 rest = temp.SubStr(index2 + 1, -1);
1865 macro = temp.SubStr(index + 2, index2 - index - 2);
1866 StrStrip(macro);
1867
1868 temp = head;
1869 if (!_tcscmp(macro, _T("node_id")))
1870 {
1871 if (m_pNode != NULL)
1872 {
1873 temp.AddFormattedString(_T("%d"), m_pNode->Id());
1874 }
1875 else
1876 {
1877 temp += _T("(error)");
1878 }
1879 }
1880 else if (!_tcscmp(macro, _T("node_name")))
1881 {
1882 if (m_pNode != NULL)
1883 {
1884 temp += m_pNode->Name();
1885 }
1886 else
1887 {
1888 temp += _T("(error)");
1889 }
1890 }
1891 else if (!_tcscmp(macro, _T("node_primary_ip")))
1892 {
1893 if (m_pNode != NULL)
1894 {
1895 TCHAR ipAddr[32];
1896
1897 temp += IpToStr(m_pNode->IpAddr(), ipAddr);
1898 }
1899 else
1900 {
1901 temp += _T("(error)");
1902 }
1903 }
1904 else if (!_tcsncmp(macro, _T("script:"), 7))
1905 {
1906 NXSL_Program *script;
1907 NXSL_ServerEnv *pEnv;
1908
1909 g_pScriptLibrary->Lock();
1910 script = g_pScriptLibrary->FindScript(&macro[7]);
1911 if (script != NULL)
1912 {
1913 pEnv = new NXSL_ServerEnv;
1914 if (m_pNode != NULL)
1915 script->SetGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, m_pNode)));
1916
1917 if (script->Run(pEnv) == 0)
1918 {
1919 temp += script->GetResult()->GetValueAsCString();
1920 DbgPrintf(4, "DCItem::ExpandMacros(%d,\"%s\"): Script %s executed successfully", m_dwId, src, &macro[7]);
1921 }
1922 else
1923 {
1924 DbgPrintf(4, "DCItem::ExpandMacros(%d,\"%s\"): Script %s execution error: %s",
1925 m_dwId, src, &macro[7], script->GetErrorText());
1926 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, _T("ssd"), &macro[7],
1927 script->GetErrorText(), m_dwId);
1928 }
1929 }
1930 else
1931 {
1932 DbgPrintf(4, "DCItem::ExpandMacros(%d,\"%s\"): Cannot find script %s", m_dwId, src, &macro[7]);
1933 }
1934 g_pScriptLibrary->Unlock();
1935 }
1936 temp += rest;
1937
1938 free(head);
1939 free(rest);
1940 free(macro);
1941 }
1942 nx_strncpy(dst, (TCHAR *)temp, dstLen);
1943 }