530baad299c136ad59877e94de3abeb40aee8a8b
[public/netxms.git] / src / server / core / dcitem.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2014 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: dcitem.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * DCI cache loader queue
27 */
28 extern Queue g_dciCacheLoaderQueue;
29
30 /**
31 * Default constructor for DCItem
32 */
33 DCItem::DCItem() : DCObject()
34 {
35 m_thresholds = NULL;
36 m_dataType = DCI_DT_INT;
37 m_deltaCalculation = DCM_ORIGINAL_VALUE;
38 m_sampleCount = 0;
39 m_instance[0] = 0;
40 m_cacheSize = 0;
41 m_ppValueCache = NULL;
42 m_tPrevValueTimeStamp = 0;
43 m_bCacheLoaded = false;
44 m_nBaseUnits = DCI_BASEUNITS_OTHER;
45 m_nMultiplier = 1;
46 m_customUnitName = NULL;
47 m_snmpRawValueType = SNMP_RAWTYPE_NONE;
48 m_instanceDiscoveryMethod = IDM_NONE;
49 m_instanceDiscoveryData = NULL;
50 m_instanceFilterSource = NULL;
51 m_instanceFilter = NULL;
52 }
53
54 /**
55 * Create DCItem from another DCItem
56 */
57 DCItem::DCItem(const DCItem *pSrc) : DCObject(pSrc)
58 {
59 m_dataType = pSrc->m_dataType;
60 m_deltaCalculation = pSrc->m_deltaCalculation;
61 m_sampleCount = pSrc->m_sampleCount;
62 _tcscpy(m_instance, pSrc->m_instance);
63 m_cacheSize = 0;
64 m_ppValueCache = NULL;
65 m_tPrevValueTimeStamp = 0;
66 m_bCacheLoaded = false;
67 m_nBaseUnits = pSrc->m_nBaseUnits;
68 m_nMultiplier = pSrc->m_nMultiplier;
69 m_customUnitName = (pSrc->m_customUnitName != NULL) ? _tcsdup(pSrc->m_customUnitName) : NULL;
70 m_snmpRawValueType = pSrc->m_snmpRawValueType;
71 m_instanceDiscoveryMethod = pSrc->m_instanceDiscoveryMethod;
72 m_instanceDiscoveryData = (pSrc->m_instanceDiscoveryData != NULL) ? _tcsdup(pSrc->m_instanceDiscoveryData) : NULL;
73 m_instanceFilterSource = NULL;
74 m_instanceFilter = NULL;
75 setInstanceFilter(pSrc->m_instanceFilterSource);
76
77 // Copy thresholds
78 if (pSrc->getThresholdCount() > 0)
79 {
80 m_thresholds = new ObjectArray<Threshold>(pSrc->m_thresholds->size(), 8, true);
81 for(int i = 0; i < pSrc->m_thresholds->size(); i++)
82 {
83 Threshold *t = new Threshold(pSrc->m_thresholds->get(i));
84 t->createId();
85 m_thresholds->add(t);
86 }
87 }
88 else
89 {
90 m_thresholds = NULL;
91 }
92 }
93
94 /**
95 * Constructor for creating DCItem from database
96 * Assumes that fields in SELECT query are in following order:
97 * item_id,name,source,datatype,polling_interval,retention_time,status,
98 * delta_calculation,transformation,template_id,description,instance,
99 * template_item_id,flags,resource_id,proxy_node,base_units,unit_multiplier,
100 * custom_units_name,perftab_settings,system_tag,snmp_port,snmp_raw_value_type,
101 * instd_method,instd_data,instd_filter,samples,comments
102 */
103 DCItem::DCItem(DB_RESULT hResult, int iRow, Template *pNode) : DCObject()
104 {
105 m_id = DBGetFieldULong(hResult, iRow, 0);
106 DBGetField(hResult, iRow, 1, m_name, MAX_ITEM_NAME);
107 m_source = (BYTE)DBGetFieldLong(hResult, iRow, 2);
108 m_dataType = (BYTE)DBGetFieldLong(hResult, iRow, 3);
109 m_iPollingInterval = DBGetFieldLong(hResult, iRow, 4);
110 m_iRetentionTime = DBGetFieldLong(hResult, iRow, 5);
111 m_status = (BYTE)DBGetFieldLong(hResult, iRow, 6);
112 m_deltaCalculation = (BYTE)DBGetFieldLong(hResult, iRow, 7);
113 TCHAR *pszTmp = DBGetField(hResult, iRow, 8, NULL, 0);
114 setTransformationScript(pszTmp);
115 free(pszTmp);
116 m_dwTemplateId = DBGetFieldULong(hResult, iRow, 9);
117 DBGetField(hResult, iRow, 10, m_szDescription, MAX_DB_STRING);
118 DBGetField(hResult, iRow, 11, m_instance, MAX_DB_STRING);
119 m_dwTemplateItemId = DBGetFieldULong(hResult, iRow, 12);
120 m_thresholds = NULL;
121 m_pNode = pNode;
122 m_cacheSize = 0;
123 m_ppValueCache = NULL;
124 m_tPrevValueTimeStamp = 0;
125 m_bCacheLoaded = false;
126 m_flags = (WORD)DBGetFieldLong(hResult, iRow, 13);
127 m_dwResourceId = DBGetFieldULong(hResult, iRow, 14);
128 m_dwProxyNode = DBGetFieldULong(hResult, iRow, 15);
129 m_nBaseUnits = DBGetFieldLong(hResult, iRow, 16);
130 m_nMultiplier = DBGetFieldLong(hResult, iRow, 17);
131 m_customUnitName = DBGetField(hResult, iRow, 18, NULL, 0);
132 m_pszPerfTabSettings = DBGetField(hResult, iRow, 19, NULL, 0);
133 DBGetField(hResult, iRow, 20, m_systemTag, MAX_DB_STRING);
134 m_snmpPort = (WORD)DBGetFieldLong(hResult, iRow, 21);
135 m_snmpRawValueType = (WORD)DBGetFieldLong(hResult, iRow, 22);
136 m_instanceDiscoveryMethod = (WORD)DBGetFieldLong(hResult, iRow, 23);
137 m_instanceDiscoveryData = DBGetField(hResult, iRow, 24, NULL, 0);
138 m_instanceFilterSource = NULL;
139 m_instanceFilter = NULL;
140 pszTmp = DBGetField(hResult, iRow, 25, NULL, 0);
141 setInstanceFilter(pszTmp);
142 free(pszTmp);
143 m_sampleCount = DBGetFieldLong(hResult, iRow, 26);
144 m_comments = DBGetField(hResult, iRow, 27, NULL, 0);
145
146 // Load last raw value from database
147 TCHAR szQuery[256];
148 _sntprintf(szQuery, 256, _T("SELECT raw_value,last_poll_time FROM raw_dci_values WHERE item_id=%d"), m_id);
149 DB_RESULT hTempResult = DBSelect(g_hCoreDB, szQuery);
150 if (hTempResult != NULL)
151 {
152 if (DBGetNumRows(hTempResult) > 0)
153 {
154 TCHAR szBuffer[MAX_DB_STRING];
155 m_prevRawValue = DBGetField(hTempResult, 0, 0, szBuffer, MAX_DB_STRING);
156 m_tPrevValueTimeStamp = DBGetFieldULong(hTempResult, 0, 1);
157 m_tLastPoll = m_tPrevValueTimeStamp;
158 }
159 DBFreeResult(hTempResult);
160 }
161
162 loadCustomSchedules();
163 }
164
165 /**
166 * Constructor for creating new DCItem from scratch
167 */
168 DCItem::DCItem(UINT32 dwId, const TCHAR *szName, int iSource, int iDataType,
169 int iPollingInterval, int iRetentionTime, Template *pNode,
170 const TCHAR *pszDescription, const TCHAR *systemTag)
171 : DCObject(dwId, szName, iSource, iPollingInterval, iRetentionTime, pNode, pszDescription, systemTag)
172 {
173 m_instance[0] = 0;
174 m_dataType = iDataType;
175 m_deltaCalculation = DCM_ORIGINAL_VALUE;
176 m_sampleCount = 0;
177 m_thresholds = NULL;
178 m_cacheSize = 0;
179 m_ppValueCache = NULL;
180 m_tPrevValueTimeStamp = 0;
181 m_bCacheLoaded = false;
182 m_nBaseUnits = DCI_BASEUNITS_OTHER;
183 m_nMultiplier = 1;
184 m_customUnitName = NULL;
185 m_snmpRawValueType = SNMP_RAWTYPE_NONE;
186 m_instanceDiscoveryMethod = IDM_NONE;
187 m_instanceDiscoveryData = NULL;
188 m_instanceFilterSource = NULL;
189 m_instanceFilter = NULL;
190
191 updateCacheSize();
192 }
193
194 /**
195 * Create DCItem from import file
196 */
197 DCItem::DCItem(ConfigEntry *config, Template *owner) : DCObject(config, owner)
198 {
199 nx_strncpy(m_instance, config->getSubEntryValue(_T("instance"), 0, _T("")), MAX_DB_STRING);
200 m_dataType = (BYTE)config->getSubEntryValueAsInt(_T("dataType"));
201 m_deltaCalculation = (BYTE)config->getSubEntryValueAsInt(_T("delta"));
202 m_sampleCount = (BYTE)config->getSubEntryValueAsInt(_T("samples"));
203 m_cacheSize = 0;
204 m_ppValueCache = NULL;
205 m_tPrevValueTimeStamp = 0;
206 m_bCacheLoaded = false;
207 m_nBaseUnits = DCI_BASEUNITS_OTHER;
208 m_nMultiplier = 1;
209 m_customUnitName = NULL;
210 m_snmpRawValueType = (WORD)config->getSubEntryValueAsInt(_T("snmpRawValueType"));
211 m_instanceDiscoveryMethod = (WORD)config->getSubEntryValueAsInt(_T("instanceDiscoveryMethod"));
212 const TCHAR *value = config->getSubEntryValue(_T("instanceDiscoveryData"));
213 m_instanceDiscoveryData = (value != NULL) ? _tcsdup(value) : NULL;
214 m_instanceFilterSource = NULL;
215 m_instanceFilter = NULL;
216 setInstanceFilter(config->getSubEntryValue(_T("instanceFilter")));
217
218 // for compatibility with old format
219 if (config->getSubEntryValueAsInt(_T("allThresholds")))
220 m_flags |= DCF_ALL_THRESHOLDS;
221 if (config->getSubEntryValueAsInt(_T("rawValueInOctetString")))
222 m_flags |= DCF_RAW_VALUE_OCTET_STRING;
223
224 ConfigEntry *thresholdsRoot = config->findEntry(_T("thresholds"));
225 if (thresholdsRoot != NULL)
226 {
227 ObjectArray<ConfigEntry> *thresholds = thresholdsRoot->getSubEntries(_T("threshold#*"));
228 m_thresholds = new ObjectArray<Threshold>(thresholds->size(), 8, true);
229 for(int i = 0; i < thresholds->size(); i++)
230 {
231 m_thresholds->add(new Threshold(thresholds->get(i), this));
232 }
233 delete thresholds;
234 }
235 else
236 {
237 m_thresholds = NULL;
238 }
239
240 updateCacheSize();
241 }
242
243 /**
244 * Destructor
245 */
246 DCItem::~DCItem()
247 {
248 delete m_thresholds;
249
250 safe_free(m_instanceDiscoveryData);
251 safe_free(m_instanceFilterSource);
252 delete m_instanceFilter;
253 safe_free(m_customUnitName);
254 clearCache();
255 }
256
257 /**
258 * Delete all thresholds
259 */
260 void DCItem::deleteAllThresholds()
261 {
262 lock();
263 delete_and_null(m_thresholds);
264 unlock();
265 }
266
267 /**
268 * Clear data cache
269 */
270 void DCItem::clearCache()
271 {
272 UINT32 i;
273
274 for(i = 0; i < m_cacheSize; i++)
275 delete m_ppValueCache[i];
276 safe_free(m_ppValueCache);
277 m_ppValueCache = NULL;
278 m_cacheSize = 0;
279 }
280
281 /**
282 * Load data collection items thresholds from database
283 */
284 bool DCItem::loadThresholdsFromDB()
285 {
286 bool result = false;
287
288 DB_STATEMENT hStmt = DBPrepare(g_hCoreDB,
289 _T("SELECT threshold_id,fire_value,rearm_value,check_function,")
290 _T("check_operation,sample_count,script,event_code,current_state,")
291 _T("rearm_event_code,repeat_interval,current_severity,")
292 _T("last_event_timestamp,match_count FROM thresholds WHERE item_id=? ")
293 _T("ORDER BY sequence_number"));
294 if (hStmt != NULL)
295 {
296 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
297 DB_RESULT hResult = DBSelectPrepared(hStmt);
298 if (hResult != NULL)
299 {
300 int count = DBGetNumRows(hResult);
301 if (count > 0)
302 {
303 m_thresholds = new ObjectArray<Threshold>(count, 8, true);
304 for(int i = 0; i < count; i++)
305 m_thresholds->add(new Threshold(hResult, i, this));
306 }
307 DBFreeResult(hResult);
308 result = true;
309 }
310 DBFreeStatement(hStmt);
311 }
312 return result;
313 }
314
315 /**
316 * Save to database
317 */
318 BOOL DCItem::saveToDB(DB_HANDLE hdb)
319 {
320 // Prepare and execute query
321 DB_STATEMENT hStmt;
322 if (IsDatabaseRecordExist(hdb, _T("items"), _T("item_id"), m_id))
323 {
324 hStmt = DBPrepare(hdb,
325 _T("UPDATE items SET node_id=?,template_id=?,name=?,source=?,")
326 _T("datatype=?,polling_interval=?,retention_time=?,status=?,")
327 _T("delta_calculation=?,transformation=?,description=?,")
328 _T("instance=?,template_item_id=?,flags=?,")
329 _T("resource_id=?,proxy_node=?,base_units=?,")
330 _T("unit_multiplier=?,custom_units_name=?,perftab_settings=?,")
331 _T("system_tag=?,snmp_port=?,snmp_raw_value_type=?,")
332 _T("instd_method=?,instd_data=?,instd_filter=?,samples=?,comments=? WHERE item_id=?"));
333 }
334 else
335 {
336 hStmt = DBPrepare(hdb,
337 _T("INSERT INTO items (node_id,template_id,name,source,")
338 _T("datatype,polling_interval,retention_time,status,delta_calculation,")
339 _T("transformation,description,instance,template_item_id,flags,")
340 _T("resource_id,proxy_node,base_units,unit_multiplier,")
341 _T("custom_units_name,perftab_settings,system_tag,snmp_port,snmp_raw_value_type,")
342 _T("instd_method,instd_data,instd_filter,samples,comments,item_id) VALUES ")
343 _T("(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
344 }
345 if (hStmt == NULL)
346 return FALSE;
347
348 lock();
349
350 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, (m_pNode == NULL) ? 0 : m_pNode->getId());
351 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_dwTemplateId);
352 DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, m_name, DB_BIND_STATIC);
353 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, (INT32)m_source);
354 DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, (INT32)m_dataType);
355 DBBind(hStmt, 6, DB_SQLTYPE_INTEGER, (INT32)m_iPollingInterval);
356 DBBind(hStmt, 7, DB_SQLTYPE_INTEGER, (INT32)m_iRetentionTime);
357 DBBind(hStmt, 8, DB_SQLTYPE_INTEGER, (INT32)m_status);
358 DBBind(hStmt, 9, DB_SQLTYPE_INTEGER, (INT32)m_deltaCalculation);
359 DBBind(hStmt, 10, DB_SQLTYPE_TEXT, m_transformationScriptSource, DB_BIND_STATIC);
360 DBBind(hStmt, 11, DB_SQLTYPE_VARCHAR, m_szDescription, DB_BIND_STATIC);
361 DBBind(hStmt, 12, DB_SQLTYPE_VARCHAR, m_instance, DB_BIND_STATIC);
362 DBBind(hStmt, 13, DB_SQLTYPE_INTEGER, m_dwTemplateItemId);
363 DBBind(hStmt, 14, DB_SQLTYPE_INTEGER, (UINT32)m_flags);
364 DBBind(hStmt, 15, DB_SQLTYPE_INTEGER, m_dwResourceId);
365 DBBind(hStmt, 16, DB_SQLTYPE_INTEGER, m_dwProxyNode);
366 DBBind(hStmt, 17, DB_SQLTYPE_INTEGER, (INT32)m_nBaseUnits);
367 DBBind(hStmt, 18, DB_SQLTYPE_INTEGER, (INT32)m_nMultiplier);
368 DBBind(hStmt, 19, DB_SQLTYPE_VARCHAR, m_customUnitName, DB_BIND_STATIC);
369 DBBind(hStmt, 20, DB_SQLTYPE_VARCHAR, m_pszPerfTabSettings, DB_BIND_STATIC);
370 DBBind(hStmt, 21, DB_SQLTYPE_VARCHAR, m_systemTag, DB_BIND_STATIC);
371 DBBind(hStmt, 22, DB_SQLTYPE_INTEGER, (INT32)m_snmpPort);
372 DBBind(hStmt, 23, DB_SQLTYPE_INTEGER, (INT32)m_snmpRawValueType);
373 DBBind(hStmt, 24, DB_SQLTYPE_INTEGER, (INT32)m_instanceDiscoveryMethod);
374 DBBind(hStmt, 25, DB_SQLTYPE_VARCHAR, m_instanceDiscoveryData, DB_BIND_STATIC);
375 DBBind(hStmt, 26, DB_SQLTYPE_TEXT, m_instanceFilterSource, DB_BIND_STATIC);
376 DBBind(hStmt, 27, DB_SQLTYPE_INTEGER, (INT32)m_sampleCount);
377 DBBind(hStmt, 28, DB_SQLTYPE_TEXT, m_comments, DB_BIND_STATIC);
378 DBBind(hStmt, 29, DB_SQLTYPE_INTEGER, m_id);
379
380 BOOL bResult = DBExecute(hStmt);
381 DBFreeStatement(hStmt);
382
383 // Save thresholds
384 if (bResult && (m_thresholds != NULL))
385 {
386 for(int i = 0; i < m_thresholds->size(); i++)
387 m_thresholds->get(i)->saveToDB(hdb, i);
388 }
389
390 // Delete non-existing thresholds
391 TCHAR query[256];
392 _sntprintf(query, 256, _T("SELECT threshold_id FROM thresholds WHERE item_id=%d"), m_id);
393 DB_RESULT hResult = DBSelect(hdb, query);
394 if (hResult != NULL)
395 {
396 int iNumRows = DBGetNumRows(hResult);
397 for(int i = 0; i < iNumRows; i++)
398 {
399 UINT32 dwId = DBGetFieldULong(hResult, i, 0);
400 int j;
401 for(j = 0; j < getThresholdCount(); j++)
402 if (m_thresholds->get(j)->getId() == dwId)
403 break;
404 if (j == getThresholdCount())
405 {
406 _sntprintf(query, 256, _T("DELETE FROM thresholds WHERE threshold_id=%d"), dwId);
407 DBQuery(hdb, query);
408 }
409 }
410 DBFreeResult(hResult);
411 }
412
413 // Create record in raw_dci_values if needed
414 _sntprintf(query, 256, _T("SELECT item_id FROM raw_dci_values WHERE item_id=%d"), m_id);
415 hResult = DBSelect(hdb, query);
416 if (hResult != NULL)
417 {
418 if (DBGetNumRows(hResult) == 0)
419 {
420 _sntprintf(query, 256, _T("INSERT INTO raw_dci_values (item_id,raw_value,last_poll_time) VALUES (%d,%s,%ld)"),
421 m_id, (const TCHAR *)DBPrepareString(hdb, m_prevRawValue.getString()), (long)m_tPrevValueTimeStamp);
422 DBQuery(hdb, query);
423 }
424 DBFreeResult(hResult);
425 }
426
427 unlock();
428 return bResult ? DCObject::saveToDB(hdb) : FALSE;
429 }
430
431 /**
432 * Check last value for threshold violations
433 */
434 void DCItem::checkThresholds(ItemValue &value)
435 {
436 static const TCHAR *paramNamesReach[] = { _T("dciName"), _T("dciDescription"), _T("thresholdValue"), _T("currentValue"), _T("dciId"), _T("instance"), _T("isRepeatedEvent") };
437 static const TCHAR *paramNamesRearm[] = { _T("dciName"), _T("dciDescription"), _T("dciId"), _T("instance"), _T("thresholdValue"), _T("currentValue") };
438
439 if (m_thresholds == NULL)
440 return;
441
442 UINT32 dwInterval;
443 ItemValue checkValue;
444 EVENT_TEMPLATE *evt;
445 time_t now = time(NULL);
446
447 for(int i = 0; i < m_thresholds->size(); i++)
448 {
449 Threshold *t = m_thresholds->get(i);
450 ThresholdCheckResult result = t->check(value, m_ppValueCache, checkValue, m_pNode, this);
451 switch(result)
452 {
453 case ACTIVATED:
454 PostEventWithNames(t->getEventCode(), m_pNode->getId(), "ssssisd",
455 paramNamesReach, m_name, m_szDescription, t->getStringValue(),
456 (const TCHAR *)checkValue, m_id, m_instance, 0);
457 evt = FindEventTemplateByCode(t->getEventCode());
458 if (evt != NULL)
459 t->markLastEvent((int)evt->dwSeverity);
460 if (!(m_flags & DCF_ALL_THRESHOLDS))
461 i = m_thresholds->size(); // Stop processing
462 break;
463 case DEACTIVATED:
464 PostEventWithNames(t->getRearmEventCode(), m_pNode->getId(), "ssisss",
465 paramNamesRearm, m_name, m_szDescription, m_id, m_instance,
466 t->getStringValue(), (const TCHAR *)checkValue);
467 break;
468 case ALREADY_ACTIVE:
469 // Check if we need to re-sent threshold violation event
470 if (t->getRepeatInterval() == -1)
471 dwInterval = g_thresholdRepeatInterval;
472 else
473 dwInterval = (UINT32)t->getRepeatInterval();
474 if ((dwInterval != 0) && (t->getLastEventTimestamp() + (time_t)dwInterval < now))
475 {
476 PostEventWithNames(t->getEventCode(), m_pNode->getId(), "ssssisd",
477 paramNamesReach, m_name, m_szDescription, t->getStringValue(),
478 (const TCHAR *)checkValue, m_id, m_instance, 1);
479 evt = FindEventTemplateByCode(t->getEventCode());
480 if (evt != NULL)
481 t->markLastEvent((int)evt->dwSeverity);
482 }
483
484 if (!(m_flags & DCF_ALL_THRESHOLDS))
485 {
486 i = m_thresholds->size(); // Threshold condition still true, stop processing
487 }
488 break;
489 default:
490 break;
491 }
492 }
493 }
494
495 /**
496 * Create NXCP message with item data
497 */
498 void DCItem::createMessage(NXCPMessage *pMsg)
499 {
500 DCObject::createMessage(pMsg);
501
502 lock();
503 pMsg->setField(VID_INSTANCE, m_instance);
504 pMsg->setField(VID_DCI_DATA_TYPE, (WORD)m_dataType);
505 pMsg->setField(VID_DCI_DELTA_CALCULATION, (WORD)m_deltaCalculation);
506 pMsg->setField(VID_SAMPLE_COUNT, (WORD)m_sampleCount);
507 pMsg->setField(VID_BASE_UNITS, (WORD)m_nBaseUnits);
508 pMsg->setField(VID_MULTIPLIER, (UINT32)m_nMultiplier);
509 pMsg->setField(VID_SNMP_RAW_VALUE_TYPE, m_snmpRawValueType);
510 pMsg->setField(VID_INSTD_METHOD, m_instanceDiscoveryMethod);
511 if (m_instanceDiscoveryData != NULL)
512 pMsg->setField(VID_INSTD_DATA, m_instanceDiscoveryData);
513 if (m_instanceFilterSource != NULL)
514 pMsg->setField(VID_INSTD_FILTER, m_instanceFilterSource);
515 if (m_customUnitName != NULL)
516 pMsg->setField(VID_CUSTOM_UNITS_NAME, m_customUnitName);
517 if (m_thresholds != NULL)
518 {
519 pMsg->setField(VID_NUM_THRESHOLDS, (UINT32)m_thresholds->size());
520 UINT32 dwId = VID_DCI_THRESHOLD_BASE;
521 for(int i = 0; i < m_thresholds->size(); i++, dwId += 20)
522 m_thresholds->get(i)->createMessage(pMsg, dwId);
523 }
524 else
525 {
526 pMsg->setField(VID_NUM_THRESHOLDS, (UINT32)0);
527 }
528 unlock();
529 }
530
531 /**
532 * Delete item and collected data from database
533 */
534 void DCItem::deleteFromDatabase()
535 {
536 TCHAR szQuery[256];
537
538 DCObject::deleteFromDatabase();
539
540 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("DELETE FROM items WHERE item_id=%d"), m_id);
541 QueueSQLRequest(szQuery);
542 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("DELETE FROM idata_%d WHERE item_id=%d"), m_pNode->getId(), m_id);
543 QueueSQLRequest(szQuery);
544 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("DELETE FROM thresholds WHERE item_id=%d"), m_id);
545 QueueSQLRequest(szQuery);
546 }
547
548 /**
549 * Update item from NXCP message
550 */
551 void DCItem::updateFromMessage(NXCPMessage *pMsg, UINT32 *pdwNumMaps, UINT32 **ppdwMapIndex, UINT32 **ppdwMapId)
552 {
553 DCObject::updateFromMessage(pMsg);
554
555 lock();
556
557 pMsg->getFieldAsString(VID_INSTANCE, m_instance, MAX_DB_STRING);
558 m_dataType = (BYTE)pMsg->getFieldAsUInt16(VID_DCI_DATA_TYPE);
559 m_deltaCalculation = (BYTE)pMsg->getFieldAsUInt16(VID_DCI_DELTA_CALCULATION);
560 m_sampleCount = (int)pMsg->getFieldAsUInt16(VID_SAMPLE_COUNT);
561 m_nBaseUnits = pMsg->getFieldAsUInt16(VID_BASE_UNITS);
562 m_nMultiplier = (int)pMsg->getFieldAsUInt32(VID_MULTIPLIER);
563 safe_free(m_customUnitName);
564 m_customUnitName = pMsg->getFieldAsString(VID_CUSTOM_UNITS_NAME);
565 m_snmpRawValueType = pMsg->getFieldAsUInt16(VID_SNMP_RAW_VALUE_TYPE);
566 m_instanceDiscoveryMethod = pMsg->getFieldAsUInt16(VID_INSTD_METHOD);
567
568 safe_free(m_instanceDiscoveryData);
569 m_instanceDiscoveryData = pMsg->getFieldAsString(VID_INSTD_DATA);
570
571 TCHAR *pszStr = pMsg->getFieldAsString(VID_INSTD_FILTER);
572 setInstanceFilter(pszStr);
573 safe_free(pszStr);
574
575 // Update thresholds
576 UINT32 dwNum = pMsg->getFieldAsUInt32(VID_NUM_THRESHOLDS);
577 UINT32 *newThresholds = (UINT32 *)malloc(sizeof(UINT32) * dwNum);
578 *ppdwMapIndex = (UINT32 *)malloc(dwNum * sizeof(UINT32));
579 *ppdwMapId = (UINT32 *)malloc(dwNum * sizeof(UINT32));
580 *pdwNumMaps = 0;
581
582 // Read all new threshold ids from message
583 for(UINT32 i = 0, dwId = VID_DCI_THRESHOLD_BASE; i < dwNum; i++, dwId += 10)
584 {
585 newThresholds[i] = pMsg->getFieldAsUInt32(dwId);
586 }
587
588 // Check if some thresholds was deleted, and reposition others if needed
589 Threshold **ppNewList = (Threshold **)malloc(sizeof(Threshold *) * dwNum);
590 memset(ppNewList, 0, sizeof(Threshold *) * dwNum);
591 for(int i = 0; i < getThresholdCount(); i++)
592 {
593 UINT32 j;
594 for(j = 0; j < dwNum; j++)
595 if (m_thresholds->get(i)->getId() == newThresholds[j])
596 break;
597 if (j == dwNum)
598 {
599 // No threshold with that id in new list, delete it
600 m_thresholds->remove(i);
601 i--;
602 }
603 else
604 {
605 // Move existing thresholds to appropriate positions in new list
606 ppNewList[j] = m_thresholds->get(i);
607 }
608 }
609
610 // Add or update thresholds
611 for(UINT32 i = 0, dwId = VID_DCI_THRESHOLD_BASE; i < dwNum; i++, dwId += 10)
612 {
613 if (newThresholds[i] == 0) // New threshold?
614 {
615 ppNewList[i] = new Threshold(this);
616 ppNewList[i]->createId();
617
618 // Add index -> id mapping
619 (*ppdwMapIndex)[*pdwNumMaps] = i;
620 (*ppdwMapId)[*pdwNumMaps] = ppNewList[i]->getId();
621 (*pdwNumMaps)++;
622 }
623 if (ppNewList[i] != NULL)
624 ppNewList[i]->updateFromMessage(pMsg, dwId);
625 }
626
627 if (dwNum > 0)
628 {
629 if (m_thresholds != NULL)
630 {
631 m_thresholds->setOwner(false);
632 m_thresholds->clear();
633 m_thresholds->setOwner(true);
634 }
635 else
636 {
637 m_thresholds = new ObjectArray<Threshold>((int)dwNum, 8, true);
638 }
639 for(UINT32 i = 0; i < dwNum; i++)
640 {
641 if (ppNewList[i] != NULL)
642 m_thresholds->add(ppNewList[i]);
643 }
644 }
645 else
646 {
647 delete_and_null(m_thresholds);
648 }
649
650 safe_free(ppNewList);
651 safe_free(newThresholds);
652 updateCacheSize();
653 unlock();
654 }
655
656 /**
657 * Process new collected value. Should return true on success.
658 * If returns false, current poll result will be converted into data collection error.
659 *
660 * @return true on success
661 */
662 bool DCItem::processNewValue(time_t tmTimeStamp,const void *originalValue, bool *updateStatus)
663 {
664 static int updateRawValueTypes[] = { DB_SQLTYPE_VARCHAR, DB_SQLTYPE_VARCHAR, DB_SQLTYPE_INTEGER, DB_SQLTYPE_INTEGER };
665 static int updateValueTypes[] = { DB_SQLTYPE_INTEGER, DB_SQLTYPE_INTEGER, DB_SQLTYPE_VARCHAR };
666
667 ItemValue rawValue, *pValue;
668
669 lock();
670
671 // Normally m_pNode shouldn't be NULL for polled items, but who knows...
672 if (m_pNode == NULL)
673 {
674 unlock();
675 return false;
676 }
677
678 // Create new ItemValue object and transform it as needed
679 pValue = new ItemValue((const TCHAR *)originalValue, (UINT32)tmTimeStamp);
680 if (m_tPrevValueTimeStamp == 0)
681 m_prevRawValue = *pValue; // Delta should be zero for first poll
682 rawValue = *pValue;
683
684 // Cluster can have only aggregated data, and transformation
685 // should not be used on aggregation
686 if ((m_pNode->getObjectClass() != OBJECT_CLUSTER) || (m_flags & DCF_TRANSFORM_AGGREGATED))
687 {
688 if (!transform(*pValue, tmTimeStamp - m_tPrevValueTimeStamp))
689 {
690 unlock();
691 return false;
692 }
693 }
694
695 m_dwErrorCount = 0;
696
697 if (isStatusDCO() && ((m_cacheSize == 0) || !m_bCacheLoaded || ((UINT32)*pValue != (UINT32)*m_ppValueCache[0])))
698 {
699 *updateStatus = true;
700 }
701 else
702 {
703 *updateStatus = false;
704 }
705
706 m_prevRawValue = rawValue;
707 m_tPrevValueTimeStamp = tmTimeStamp;
708
709 // Save raw value into database
710 QueueRawDciDataUpdate(tmTimeStamp, m_id, (const TCHAR *)originalValue, pValue->getString());
711
712 // Save transformed value to database
713 if ((m_flags & DCF_NO_STORAGE) == 0)
714 QueueIDataInsert(tmTimeStamp, m_pNode->getId(), m_id, pValue->getString());
715 if (g_flags & AF_PERFDATA_STORAGE_DRIVER_LOADED)
716 PerfDataStorageRequest(this, tmTimeStamp, pValue->getString());
717
718 // Check thresholds and add value to cache
719 if (m_bCacheLoaded)
720 {
721 checkThresholds(*pValue);
722 }
723
724 if (m_cacheSize > 0)
725 {
726 delete m_ppValueCache[m_cacheSize - 1];
727 memmove(&m_ppValueCache[1], m_ppValueCache, sizeof(ItemValue *) * (m_cacheSize - 1));
728 m_ppValueCache[0] = pValue;
729 }
730 else
731 {
732 delete pValue;
733 }
734
735 unlock();
736 return true;
737 }
738
739 /**
740 * Process new data collection error
741 */
742 void DCItem::processNewError()
743 {
744 lock();
745
746 // Normally m_pNode shouldn't be NULL for polled items, but who knows...
747 if (m_pNode == NULL)
748 {
749 unlock();
750 return;
751 }
752
753 m_dwErrorCount++;
754
755 for(int i = 0; i < getThresholdCount(); i++)
756 {
757 Threshold *thr = m_thresholds->get(i);
758 ThresholdCheckResult result = thr->checkError(m_dwErrorCount);
759 switch(result)
760 {
761 case ACTIVATED:
762 PostEvent(thr->getEventCode(), m_pNode->getId(), "ssssis", m_name,
763 m_szDescription, _T(""), _T(""), m_id, m_instance);
764 if (!(m_flags & DCF_ALL_THRESHOLDS))
765 i = m_thresholds->size(); // Stop processing
766 break;
767 case DEACTIVATED:
768 PostEvent(thr->getRearmEventCode(), m_pNode->getId(), "ssis", m_name,
769 m_szDescription, m_id, m_instance);
770 break;
771 case ALREADY_ACTIVE:
772 if (!(m_flags & DCF_ALL_THRESHOLDS))
773 i = m_thresholds->size(); // Threshold condition still true, stop processing
774 break;
775 default:
776 break;
777 }
778 }
779
780 unlock();
781 }
782
783 /**
784 * Transform received value
785 */
786 bool DCItem::transform(ItemValue &value, time_t nElapsedTime)
787 {
788 bool success = true;
789
790 switch(m_deltaCalculation)
791 {
792 case DCM_SIMPLE:
793 switch(m_dataType)
794 {
795 case DCI_DT_INT:
796 value = (INT32)value - (INT32)m_prevRawValue;
797 break;
798 case DCI_DT_UINT:
799 value = (UINT32)value - (UINT32)m_prevRawValue;
800 break;
801 case DCI_DT_INT64:
802 value = (INT64)value - (INT64)m_prevRawValue;
803 break;
804 case DCI_DT_UINT64:
805 value = (UINT64)value - (UINT64)m_prevRawValue;
806 break;
807 case DCI_DT_FLOAT:
808 value = (double)value - (double)m_prevRawValue;
809 break;
810 case DCI_DT_STRING:
811 value = (INT32)((_tcscmp((const TCHAR *)value, (const TCHAR *)m_prevRawValue) == 0) ? 0 : 1);
812 break;
813 default:
814 // Delta calculation is not supported for other types
815 break;
816 }
817 break;
818 case DCM_AVERAGE_PER_MINUTE:
819 nElapsedTime /= 60; // Convert to minutes
820 case DCM_AVERAGE_PER_SECOND:
821 // Check elapsed time to prevent divide-by-zero exception
822 if (nElapsedTime == 0)
823 nElapsedTime++;
824
825 switch(m_dataType)
826 {
827 case DCI_DT_INT:
828 value = ((INT32)value - (INT32)m_prevRawValue) / (INT32)nElapsedTime;
829 break;
830 case DCI_DT_UINT:
831 value = ((UINT32)value - (UINT32)m_prevRawValue) / (UINT32)nElapsedTime;
832 break;
833 case DCI_DT_INT64:
834 value = ((INT64)value - (INT64)m_prevRawValue) / (INT64)nElapsedTime;
835 break;
836 case DCI_DT_UINT64:
837 value = ((UINT64)value - (UINT64)m_prevRawValue) / (UINT64)nElapsedTime;
838 break;
839 case DCI_DT_FLOAT:
840 value = ((double)value - (double)m_prevRawValue) / (double)nElapsedTime;
841 break;
842 case DCI_DT_STRING:
843 // I don't see any meaning in _T("average delta per second (minute)") for string
844 // values, so result will be 0 if there are no difference between
845 // current and previous values, and 1 otherwise
846 value = (INT32)((_tcscmp((const TCHAR *)value, (const TCHAR *)m_prevRawValue) == 0) ? 0 : 1);
847 break;
848 default:
849 // Delta calculation is not supported for other types
850 break;
851 }
852 break;
853 default: // Default is no transformation
854 break;
855 }
856
857 if (m_transformationScript != NULL)
858 {
859 NXSL_Value *pValue = new NXSL_Value((const TCHAR *)value);
860 m_transformationScript->setGlobalVariable(_T("$object"), new NXSL_Value(new NXSL_Object(&g_nxslNetObjClass, m_pNode)));
861 if (m_pNode->getObjectClass() == OBJECT_NODE)
862 {
863 m_transformationScript->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, m_pNode)));
864 }
865 m_transformationScript->setGlobalVariable(_T("$dci"), new NXSL_Value(new NXSL_Object(&g_nxslDciClass, this)));
866 m_transformationScript->setGlobalVariable(_T("$isCluster"), new NXSL_Value((m_pNode->getObjectClass() == OBJECT_CLUSTER) ? 1 : 0));
867
868 if (m_transformationScript->run(1, &pValue))
869 {
870 pValue = m_transformationScript->getResult();
871 if (pValue != NULL)
872 {
873 switch(m_dataType)
874 {
875 case DCI_DT_INT:
876 value = pValue->getValueAsInt32();
877 break;
878 case DCI_DT_UINT:
879 value = pValue->getValueAsUInt32();
880 break;
881 case DCI_DT_INT64:
882 value = pValue->getValueAsInt64();
883 break;
884 case DCI_DT_UINT64:
885 value = pValue->getValueAsUInt64();
886 break;
887 case DCI_DT_FLOAT:
888 value = pValue->getValueAsReal();
889 break;
890 case DCI_DT_STRING:
891 value = CHECK_NULL_EX(pValue->getValueAsCString());
892 break;
893 default:
894 break;
895 }
896 }
897 }
898 else if (m_transformationScript->getErrorCode() == NXSL_ERR_EXECUTION_ABORTED)
899 {
900 DbgPrintf(6, _T("Transformation script for DCI \"%s\" [%d] on node %s [%d] aborted"),
901 m_szDescription, m_id, (m_pNode != NULL) ? m_pNode->getName() : _T("(null)"), (m_pNode != NULL) ? m_pNode->getId() : 0);
902 success = false;
903 }
904 else
905 {
906 TCHAR szBuffer[1024];
907
908 _sntprintf(szBuffer, 1024, _T("DCI::%s::%d::TransformationScript"),
909 (m_pNode != NULL) ? m_pNode->getName() : _T("(null)"), m_id);
910 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", szBuffer, m_transformationScript->getErrorText(), m_id);
911 success = false;
912 }
913 }
914 return success;
915 }
916
917 /**
918 * Set new ID and node/template association
919 */
920 void DCItem::changeBinding(UINT32 dwNewId, Template *pNewNode, BOOL doMacroExpansion)
921 {
922 DCObject::changeBinding(dwNewId, pNewNode, doMacroExpansion);
923
924 lock();
925 if (dwNewId != 0)
926 {
927 for(int i = 0; i < getThresholdCount(); i++)
928 m_thresholds->get(i)->bindToItem(m_id, m_pNode->getId());
929 }
930
931 if (doMacroExpansion)
932 expandMacros(m_instance, m_instance, MAX_DB_STRING);
933
934 clearCache();
935 updateCacheSize();
936 unlock();
937 }
938
939 /**
940 * Update required cache size depending on thresholds
941 * dwCondId is an identifier of calling condition object id. If it is not 0,
942 * GetCacheSizeForDCI should be called with bNoLock == TRUE for appropriate
943 * condition object
944 */
945 void DCItem::updateCacheSize(UINT32 dwCondId)
946 {
947 UINT32 dwSize, dwRequiredSize;
948
949 // Sanity check
950 if (m_pNode == NULL)
951 {
952 DbgPrintf(3, _T("DCItem::updateCacheSize() called for DCI %d when m_pNode == NULL"), m_id);
953 return;
954 }
955
956 // Minimum cache size is 1 for nodes (so GetLastValue can work)
957 // and it is always 0 for templates
958 if (((m_pNode->getObjectClass() == OBJECT_NODE) || (m_pNode->getObjectClass() == OBJECT_MOBILEDEVICE) ||
959 ((m_pNode->getObjectClass() == OBJECT_CLUSTER) && isAggregateOnCluster())) &&
960 (m_instanceDiscoveryMethod == IDM_NONE))
961 {
962 dwRequiredSize = 1;
963
964 // Calculate required cache size
965 for(int i = 0; i < getThresholdCount(); i++)
966 if (dwRequiredSize < m_thresholds->get(i)->getRequiredCacheSize())
967 dwRequiredSize = m_thresholds->get(i)->getRequiredCacheSize();
968
969 ObjectArray<NetObj> *conditions = g_idxConditionById.getObjects(true);
970 for(int i = 0; i < conditions->size(); i++)
971 {
972 Condition *c = (Condition *)conditions->get(i);
973 dwSize = c->getCacheSizeForDCI(m_id, dwCondId == c->getId());
974 if (dwSize > dwRequiredSize)
975 dwRequiredSize = dwSize;
976 c->decRefCount();
977 }
978 delete conditions;
979 }
980 else
981 {
982 dwRequiredSize = 0;
983 }
984
985 // Update cache if needed
986 if (dwRequiredSize < m_cacheSize)
987 {
988 // Destroy unneeded values
989 if (m_cacheSize > 0)
990 {
991 for(UINT32 i = dwRequiredSize; i < m_cacheSize; i++)
992 delete m_ppValueCache[i];
993 }
994
995 m_cacheSize = dwRequiredSize;
996 m_requiredCacheSize = dwRequiredSize;
997 if (m_cacheSize > 0)
998 {
999 m_ppValueCache = (ItemValue **)realloc(m_ppValueCache, sizeof(ItemValue *) * m_cacheSize);
1000 }
1001 else
1002 {
1003 safe_free(m_ppValueCache);
1004 m_ppValueCache = NULL;
1005 }
1006 }
1007 else if (dwRequiredSize > m_cacheSize)
1008 {
1009 // Load missing values from database
1010 // Skip caching for DCIs where estimated time to fill the cache is less then 5 minutes
1011 // to reduce load on database at server startup
1012 if ((m_pNode != NULL) && ((dwRequiredSize - m_cacheSize) * m_iPollingInterval > 300))
1013 {
1014 m_pNode->incRefCount();
1015 m_requiredCacheSize = dwRequiredSize;
1016 m_bCacheLoaded = false;
1017 g_dciCacheLoaderQueue.Put(this);
1018 }
1019 else
1020 {
1021 // will not read data from database, fill cache with empty values
1022 m_ppValueCache = (ItemValue **)realloc(m_ppValueCache, sizeof(ItemValue *) * dwRequiredSize);
1023 for(UINT32 i = m_cacheSize; i < dwRequiredSize; i++)
1024 m_ppValueCache[i] = new ItemValue(_T(""), 1);
1025 DbgPrintf(7, _T("Cache load skipped for parameter %s [%d]"), m_name, (int)m_id);
1026 m_cacheSize = dwRequiredSize;
1027 m_bCacheLoaded = true;
1028 }
1029 }
1030 }
1031
1032 /**
1033 * Reload cache from database
1034 */
1035 void DCItem::reloadCache()
1036 {
1037 TCHAR szBuffer[MAX_DB_STRING];
1038
1039 switch(g_dbSyntax)
1040 {
1041 case DB_SYNTAX_MSSQL:
1042 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT TOP %d idata_value,idata_timestamp FROM idata_%d ")
1043 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC"),
1044 m_requiredCacheSize, m_pNode->getId(), m_id);
1045 break;
1046 case DB_SYNTAX_ORACLE:
1047 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT * FROM (SELECT idata_value,idata_timestamp FROM idata_%d ")
1048 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC) WHERE ROWNUM <= %d"),
1049 m_pNode->getId(), m_id, m_requiredCacheSize);
1050 break;
1051 case DB_SYNTAX_MYSQL:
1052 case DB_SYNTAX_PGSQL:
1053 case DB_SYNTAX_SQLITE:
1054 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT idata_value,idata_timestamp FROM idata_%d ")
1055 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC LIMIT %d"),
1056 m_pNode->getId(), m_id, m_requiredCacheSize);
1057 break;
1058 case DB_SYNTAX_DB2:
1059 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT idata_value,idata_timestamp FROM idata_%d ")
1060 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC FETCH FIRST %d ROWS ONLY"),
1061 m_pNode->getId(), m_id, m_requiredCacheSize);
1062 break;
1063 default:
1064 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT idata_value,idata_timestamp FROM idata_%d ")
1065 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC"),
1066 m_pNode->getId(), m_id);
1067 break;
1068 }
1069
1070 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1071 DB_ASYNC_RESULT hResult = DBAsyncSelect(hdb, szBuffer);
1072
1073 lock();
1074
1075 UINT32 i;
1076 for(i = 0; i < m_cacheSize; i++)
1077 delete m_ppValueCache[i];
1078
1079 if (m_cacheSize != m_requiredCacheSize)
1080 {
1081 m_ppValueCache = (ItemValue **)realloc(m_ppValueCache, sizeof(ItemValue *) * m_requiredCacheSize);
1082 }
1083
1084 if (hResult != NULL)
1085 {
1086 // Create cache entries
1087 bool moreData = true;
1088 for(i = 0; (i < m_requiredCacheSize) && moreData; i++)
1089 {
1090 moreData = DBFetch(hResult);
1091 if (moreData)
1092 {
1093 DBGetFieldAsync(hResult, 0, szBuffer, MAX_DB_STRING);
1094 m_ppValueCache[i] = new ItemValue(szBuffer, DBGetFieldAsyncULong(hResult, 1));
1095 }
1096 else
1097 {
1098 m_ppValueCache[i] = new ItemValue(_T(""), 1); // Empty value
1099 }
1100 }
1101
1102 // Fill up cache with empty values if we don't have enough values in database
1103 for(; i < m_requiredCacheSize; i++)
1104 m_ppValueCache[i] = new ItemValue(_T(""), 1);
1105
1106 DBFreeAsyncResult(hResult);
1107 }
1108 else
1109 {
1110 // Error reading data from database, fill cache with empty values
1111 for(i = 0; i < m_requiredCacheSize; i++)
1112 m_ppValueCache[i] = new ItemValue(_T(""), 1);
1113 }
1114
1115 m_cacheSize = m_requiredCacheSize;
1116 m_bCacheLoaded = true;
1117 unlock();
1118
1119 DBConnectionPoolReleaseConnection(hdb);
1120 }
1121
1122 /**
1123 * Put last value into CSCP message
1124 */
1125 void DCItem::fillLastValueMessage(NXCPMessage *pMsg, UINT32 dwId)
1126 {
1127 lock();
1128 pMsg->setField(dwId++, m_id);
1129 pMsg->setField(dwId++, m_name);
1130 pMsg->setField(dwId++, m_szDescription);
1131 pMsg->setField(dwId++, (WORD)m_source);
1132 if (m_cacheSize > 0)
1133 {
1134 pMsg->setField(dwId++, (WORD)m_dataType);
1135 pMsg->setField(dwId++, (TCHAR *)m_ppValueCache[0]->getString());
1136 pMsg->setField(dwId++, m_ppValueCache[0]->getTimeStamp());
1137 }
1138 else
1139 {
1140 pMsg->setField(dwId++, (WORD)DCI_DT_NULL);
1141 pMsg->setField(dwId++, _T(""));
1142 pMsg->setField(dwId++, (UINT32)0);
1143 }
1144 pMsg->setField(dwId++, (WORD)(matchClusterResource() ? m_status : ITEM_STATUS_DISABLED)); // show resource-bound DCIs as inactive if cluster resource is not on this node
1145 pMsg->setField(dwId++, (WORD)getType());
1146 pMsg->setField(dwId++, m_dwErrorCount);
1147 pMsg->setField(dwId++, m_dwTemplateItemId);
1148
1149 int i;
1150 for(i = 0; i < getThresholdCount(); i++)
1151 {
1152 if (m_thresholds->get(i)->isReached())
1153 break;
1154 }
1155 if (i < getThresholdCount())
1156 {
1157 pMsg->setField(dwId++, (WORD)1);
1158 m_thresholds->get(i)->createMessage(pMsg, dwId);
1159 }
1160 else
1161 {
1162 pMsg->setField(dwId++, (WORD)0);
1163 }
1164
1165 unlock();
1166 }
1167
1168 /**
1169 * Get item's last value for use in NXSL
1170 */
1171 NXSL_Value *DCItem::getRawValueForNXSL()
1172 {
1173 lock();
1174 NXSL_Value *value = new NXSL_Value(m_prevRawValue.getString());
1175 unlock();
1176 return value;
1177 }
1178
1179 /**
1180 * Get item's last value for use in NXSL
1181 */
1182 NXSL_Value *DCItem::getValueForNXSL(int nFunction, int nPolls)
1183 {
1184 NXSL_Value *pValue;
1185
1186 lock();
1187 switch(nFunction)
1188 {
1189 case F_LAST:
1190 pValue = (m_cacheSize > 0) ? new NXSL_Value(m_ppValueCache[0]->getString()) : new NXSL_Value;
1191 break;
1192 case F_DIFF:
1193 if (m_cacheSize >= 2)
1194 {
1195 ItemValue result;
1196
1197 CalculateItemValueDiff(result, m_dataType, *m_ppValueCache[0], *m_ppValueCache[1]);
1198 pValue = new NXSL_Value(result.getString());
1199 }
1200 else
1201 {
1202 pValue = new NXSL_Value;
1203 }
1204 break;
1205 case F_AVERAGE:
1206 if (m_cacheSize > 0)
1207 {
1208 ItemValue result;
1209
1210 CalculateItemValueAverage(result, m_dataType,
1211 min(m_cacheSize, (UINT32)nPolls), m_ppValueCache);
1212 pValue = new NXSL_Value(result.getString());
1213 }
1214 else
1215 {
1216 pValue = new NXSL_Value;
1217 }
1218 break;
1219 case F_DEVIATION:
1220 if (m_cacheSize > 0)
1221 {
1222 ItemValue result;
1223
1224 CalculateItemValueMD(result, m_dataType,
1225 min(m_cacheSize, (UINT32)nPolls), m_ppValueCache);
1226 pValue = new NXSL_Value(result.getString());
1227 }
1228 else
1229 {
1230 pValue = new NXSL_Value;
1231 }
1232 break;
1233 case F_ERROR:
1234 pValue = new NXSL_Value((INT32)((m_dwErrorCount >= (UINT32)nPolls) ? 1 : 0));
1235 break;
1236 default:
1237 pValue = new NXSL_Value;
1238 break;
1239 }
1240 unlock();
1241 return pValue;
1242 }
1243
1244 /**
1245 * Get last value
1246 */
1247 const TCHAR *DCItem::getLastValue()
1248 {
1249 lock();
1250 const TCHAR *v = (m_cacheSize > 0) ? (const TCHAR *)m_ppValueCache[0]->getString() : NULL;
1251 unlock();
1252 return v;
1253 }
1254
1255 /**
1256 * Get copy of internal last value object. Caller is responsible for destroying returned object.
1257 */
1258 ItemValue *DCItem::getInternalLastValue()
1259 {
1260 lock();
1261 ItemValue *v = (m_cacheSize > 0) ? new ItemValue(m_ppValueCache[0]) : NULL;
1262 unlock();
1263 return v;
1264 }
1265
1266 /**
1267 * Get aggregate value. Returned value must be deallocated by caller.
1268 *
1269 * @return dynamically allocated value or NULL on error
1270 */
1271 TCHAR *DCItem::getAggregateValue(AggregationFunction func, time_t periodStart, time_t periodEnd)
1272 {
1273 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1274 TCHAR query[1024];
1275 TCHAR *result = NULL;
1276
1277 static const TCHAR *functions[] = { _T(""), _T("min"), _T("max"), _T("avg"), _T("sum") };
1278
1279 if (g_dbSyntax == DB_SYNTAX_ORACLE)
1280 {
1281 _sntprintf(query, 1024, _T("SELECT %s(coalesce(to_number(idata_value),0)) FROM idata_%u ")
1282 _T("WHERE item_id=? AND idata_timestamp BETWEEN ? AND ?"),
1283 functions[func], m_pNode->getId());
1284 }
1285 else if (g_dbSyntax == DB_SYNTAX_MSSQL)
1286 {
1287 _sntprintf(query, 1024, _T("SELECT %s(coalesce(cast(idata_value as float),0)) FROM idata_%u ")
1288 _T("WHERE item_id=? AND (idata_timestamp BETWEEN ? AND ?) AND isnumeric(idata_value)=1"),
1289 functions[func], m_pNode->getId());
1290 }
1291 else if (g_dbSyntax == DB_SYNTAX_PGSQL)
1292 {
1293 _sntprintf(query, 1024, _T("SELECT %s(coalesce(idata_value::double precision,0)) FROM idata_%u ")
1294 _T("WHERE item_id=? AND idata_timestamp BETWEEN ? AND ?"),
1295 functions[func], m_pNode->getId());
1296 }
1297 else
1298 {
1299 _sntprintf(query, 1024, _T("SELECT %s(coalesce(idata_value,0)) FROM idata_%u ")
1300 _T("WHERE item_id=? and idata_timestamp between ? and ?"),
1301 functions[func], m_pNode->getId());
1302 }
1303
1304 DB_STATEMENT hStmt = DBPrepare(hdb, query);
1305 if (hStmt != NULL)
1306 {
1307 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
1308 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, (INT32)periodStart);
1309 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (INT32)periodEnd);
1310 DB_RESULT hResult = DBSelectPrepared(hStmt);
1311 if (hResult != NULL)
1312 {
1313 if (DBGetNumRows(hResult) == 1)
1314 {
1315 result = DBGetField(hResult, 0, 0, NULL, 0);
1316 }
1317 DBFreeResult(hResult);
1318 }
1319 DBFreeStatement(hStmt);
1320 }
1321
1322 DBConnectionPoolReleaseConnection(hdb);
1323 return result;
1324 }
1325
1326 /**
1327 * Clean expired data
1328 */
1329 void DCItem::deleteExpiredData()
1330 {
1331 TCHAR szQuery[256];
1332 time_t now;
1333
1334 now = time(NULL);
1335 lock();
1336 _sntprintf(szQuery, 256, _T("DELETE FROM idata_%d WHERE (item_id=%d) AND (idata_timestamp<%ld)"),
1337 (int)m_pNode->getId(), (int)m_id, (long)(now - (time_t)m_iRetentionTime * 86400));
1338 unlock();
1339 QueueSQLRequest(szQuery);
1340 }
1341
1342 /**
1343 * Delete all collected data
1344 */
1345 bool DCItem::deleteAllData()
1346 {
1347 TCHAR szQuery[256];
1348 bool success;
1349
1350 lock();
1351 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1352 _sntprintf(szQuery, 256, _T("DELETE FROM idata_%d WHERE item_id=%d"), m_pNode->getId(), m_id);
1353 success = DBQuery(hdb, szQuery) ? true : false;
1354 DBConnectionPoolReleaseConnection(hdb);
1355 clearCache();
1356 updateCacheSize();
1357 unlock();
1358 return success;
1359 }
1360
1361 /**
1362 * Update from template item
1363 */
1364 void DCItem::updateFromTemplate(DCObject *src)
1365 {
1366 DCObject::updateFromTemplate(src);
1367
1368 if (src->getType() != DCO_TYPE_ITEM)
1369 {
1370 DbgPrintf(2, _T("INTERNAL ERROR: DCItem::updateFromTemplate(%d, %d): source type is %d"), (int)m_id, (int)src->getId(), src->getType());
1371 return;
1372 }
1373
1374 lock();
1375 DCItem *item = (DCItem *)src;
1376
1377 m_dataType = item->m_dataType;
1378 m_deltaCalculation = item->m_deltaCalculation;
1379
1380 m_nBaseUnits = item->m_nBaseUnits;
1381 m_nMultiplier = item->m_nMultiplier;
1382 safe_free(m_customUnitName);
1383 m_customUnitName = (item->m_customUnitName != NULL) ? _tcsdup(item->m_customUnitName) : NULL;
1384
1385 // Copy thresholds
1386 // ***************************
1387 // First, skip matching thresholds
1388 int count = min(getThresholdCount(), item->getThresholdCount());
1389 int i;
1390 for(i = 0; i < count; i++)
1391 if (!m_thresholds->get(i)->compare(item->m_thresholds->get(i)))
1392 break;
1393 count = i; // First unmatched threshold's position
1394
1395 // Delete all original thresholds starting from first unmatched
1396 while(count < getThresholdCount())
1397 m_thresholds->remove(count);
1398
1399 // (Re)create thresholds starting from first unmatched
1400 if ((m_thresholds == NULL) && (item->getThresholdCount() > 0))
1401 m_thresholds = new ObjectArray<Threshold>(item->getThresholdCount(), 8, true);
1402 for(i = count; i < item->getThresholdCount(); i++)
1403 {
1404 Threshold *t = new Threshold(item->m_thresholds->get(i));
1405 t->createId();
1406 t->bindToItem(m_id, m_pNode->getId());
1407 m_thresholds->add(t);
1408 }
1409
1410 if ((item->getInstanceDiscoveryMethod() != IDM_NONE) && (m_instanceDiscoveryMethod == IDM_NONE))
1411 {
1412 expandInstance();
1413 }
1414 else
1415 {
1416 expandMacros(item->m_instance, m_instance, MAX_DB_STRING);
1417 m_instanceDiscoveryMethod = item->m_instanceDiscoveryMethod;
1418 safe_free(m_instanceDiscoveryData);
1419 m_instanceDiscoveryData = _tcsdup_ex(item->m_instanceDiscoveryData);
1420 safe_free_and_null(m_instanceFilterSource);
1421 delete_and_null(m_instanceFilter);
1422 setInstanceFilter(item->m_instanceFilterSource);
1423 }
1424
1425 updateCacheSize();
1426 unlock();
1427 }
1428
1429 /**
1430 * Set new instance discovery filter script
1431 */
1432 void DCItem::setInstanceFilter(const TCHAR *pszScript)
1433 {
1434 safe_free(m_instanceFilterSource);
1435 delete m_instanceFilter;
1436 if (pszScript != NULL)
1437 {
1438 m_instanceFilterSource = _tcsdup(pszScript);
1439 StrStrip(m_instanceFilterSource);
1440 if (m_instanceFilterSource[0] != 0)
1441 {
1442 /* TODO: add compilation error handling */
1443 m_instanceFilter = NXSLCompileAndCreateVM(m_instanceFilterSource, NULL, 0, new NXSL_ServerEnv);
1444 }
1445 else
1446 {
1447 m_instanceFilter = NULL;
1448 }
1449 }
1450 else
1451 {
1452 m_instanceFilterSource = NULL;
1453 m_instanceFilter = NULL;
1454 }
1455 }
1456
1457 /**
1458 * Get list of used events
1459 */
1460 void DCItem::getEventList(UINT32 **ppdwList, UINT32 *pdwSize)
1461 {
1462 lock();
1463
1464 if (getThresholdCount() > 0)
1465 {
1466 *ppdwList = (UINT32 *)realloc(*ppdwList, sizeof(UINT32) * (*pdwSize + m_thresholds->size() * 2));
1467 UINT32 j = *pdwSize;
1468 *pdwSize += m_thresholds->size() * 2;
1469 for(int i = 0; i < m_thresholds->size(); i++)
1470 {
1471 (*ppdwList)[j++] = m_thresholds->get(i)->getEventCode();
1472 (*ppdwList)[j++] = m_thresholds->get(i)->getRearmEventCode();
1473 }
1474 }
1475
1476 unlock();
1477 }
1478
1479 /**
1480 * Create management pack record
1481 */
1482 void DCItem::createNXMPRecord(String &str)
1483 {
1484 UINT32 i;
1485
1486 lock();
1487
1488 str.addFormattedString(_T("\t\t\t\t<dci id=\"%d\">\n")
1489 _T("\t\t\t\t\t<name>%s</name>\n")
1490 _T("\t\t\t\t\t<description>%s</description>\n")
1491 _T("\t\t\t\t\t<dataType>%d</dataType>\n")
1492 _T("\t\t\t\t\t<samples>%d</samples>\n")
1493 _T("\t\t\t\t\t<origin>%d</origin>\n")
1494 _T("\t\t\t\t\t<interval>%d</interval>\n")
1495 _T("\t\t\t\t\t<retention>%d</retention>\n")
1496 _T("\t\t\t\t\t<instance>%s</instance>\n")
1497 _T("\t\t\t\t\t<systemTag>%s</systemTag>\n")
1498 _T("\t\t\t\t\t<delta>%d</delta>\n")
1499 _T("\t\t\t\t\t<flags>%d</flags>\n")
1500 _T("\t\t\t\t\t<snmpRawValueType>%d</snmpRawValueType>\n")
1501 _T("\t\t\t\t\t<snmpPort>%d</snmpPort>\n")
1502 _T("\t\t\t\t\t<instanceDiscoveryMethod>%d</instanceDiscoveryMethod>\n"),
1503 (int)m_id, (const TCHAR *)EscapeStringForXML2(m_name),
1504 (const TCHAR *)EscapeStringForXML2(m_szDescription),
1505 m_dataType, m_sampleCount, (int)m_source, m_iPollingInterval, m_iRetentionTime,
1506 (const TCHAR *)EscapeStringForXML2(m_instance),
1507 (const TCHAR *)EscapeStringForXML2(m_systemTag),
1508 (int)m_deltaCalculation, (int)m_flags,
1509 (int)m_snmpRawValueType, (int)m_snmpPort, (int)m_instanceDiscoveryMethod);
1510
1511 if (m_transformationScriptSource != NULL)
1512 {
1513 str += _T("\t\t\t\t\t<transformation>");
1514 str.addDynamicString(EscapeStringForXML(m_transformationScriptSource, -1));
1515 str += _T("</transformation>\n");
1516 }
1517
1518 if (m_dwNumSchedules > 0)
1519 {
1520 str += _T("\t\t\t\t\t<schedules>\n");
1521 for(i = 0; i < m_dwNumSchedules; i++)
1522 str.addFormattedString(_T("\t\t\t\t\t\t<schedule>%s</schedule>\n"), (const TCHAR *)EscapeStringForXML2(m_ppScheduleList[i]));
1523 str += _T("\t\t\t\t\t</schedules>\n");
1524 }
1525
1526 if (m_thresholds != NULL)
1527 {
1528 str += _T("\t\t\t\t\t<thresholds>\n");
1529 for(i = 0; i < (UINT32)m_thresholds->size(); i++)
1530 {
1531 m_thresholds->get(i)->createNXMPRecord(str, i + 1);
1532 }
1533 str += _T("\t\t\t\t\t</thresholds>\n");
1534 }
1535
1536 if (m_pszPerfTabSettings != NULL)
1537 {
1538 str += _T("\t\t\t\t\t<perfTabSettings>");
1539 str.addDynamicString(EscapeStringForXML(m_pszPerfTabSettings, -1));
1540 str += _T("</perfTabSettings>\n");
1541 }
1542
1543 if (m_instanceDiscoveryData != NULL)
1544 {
1545 str += _T("\t\t\t\t\t<instanceDiscoveryData>");
1546 str.addDynamicString(EscapeStringForXML(m_instanceDiscoveryData, -1));
1547 str += _T("</instanceDiscoveryData>\n");
1548 }
1549
1550 if (m_instanceFilterSource != NULL)
1551 {
1552 str += _T("\t\t\t\t\t<instanceFilter>");
1553 str.addDynamicString(EscapeStringForXML(m_instanceFilterSource, -1));
1554 str += _T("</instanceFilter>\n");
1555 }
1556
1557 unlock();
1558 str += _T("\t\t\t\t</dci>\n");
1559 }
1560
1561 /**
1562 * Add threshold to the list
1563 */
1564 void DCItem::addThreshold(Threshold *pThreshold)
1565 {
1566 if (m_thresholds == NULL)
1567 m_thresholds = new ObjectArray<Threshold>(8, 8, true);
1568 m_thresholds->add(pThreshold);
1569 }
1570
1571 /**
1572 * Enumerate all thresholds
1573 */
1574 BOOL DCItem::enumThresholds(BOOL (* pfCallback)(Threshold *, UINT32, void *), void *pArg)
1575 {
1576 BOOL bRet = TRUE;
1577
1578 lock();
1579 if (m_thresholds != NULL)
1580 {
1581 for(int i = 0; i < m_thresholds->size(); i++)
1582 {
1583 if (!pfCallback(m_thresholds->get(i), i, pArg))
1584 {
1585 bRet = FALSE;
1586 break;
1587 }
1588 }
1589 }
1590 unlock();
1591 return bRet;
1592 }
1593
1594 /**
1595 * Test DCI's transformation script
1596 */
1597 bool DCItem::testTransformation(DataCollectionTarget *object, const TCHAR *script, const TCHAR *value, TCHAR *buffer, size_t bufSize)
1598 {
1599 bool success = false;
1600 NXSL_VM *vm = NXSLCompileAndCreateVM(script, buffer, (int)bufSize, new NXSL_ServerEnv);
1601 if (vm != NULL)
1602 {
1603 NXSL_Value *pValue = new NXSL_Value(value);
1604 vm->setGlobalVariable(_T("$object"), new NXSL_Value(new NXSL_Object(&g_nxslNetObjClass, object)));
1605 if (object->getObjectClass() == OBJECT_NODE)
1606 {
1607 vm->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, object)));
1608 }
1609 //vm->setGlobalVariable(_T("$dci"), new NXSL_Value(new NXSL_Object(&g_nxslDciClass, this)));
1610 vm->setGlobalVariable(_T("$isCluster"), new NXSL_Value((object->getObjectClass() == OBJECT_CLUSTER) ? 1 : 0));
1611
1612 if (vm->run(1, &pValue))
1613 {
1614 pValue = vm->getResult();
1615 if (pValue != NULL)
1616 {
1617 if (pValue->isNull())
1618 {
1619 nx_strncpy(buffer, _T("(null)"), bufSize);
1620 }
1621 else if (pValue->isObject())
1622 {
1623 nx_strncpy(buffer, _T("(object)"), bufSize);
1624 }
1625 else if (pValue->isArray())
1626 {
1627 nx_strncpy(buffer, _T("(array)"), bufSize);
1628 }
1629 else
1630 {
1631 const TCHAR *strval;
1632
1633 strval = pValue->getValueAsCString();
1634 nx_strncpy(buffer, CHECK_NULL(strval), bufSize);
1635 }
1636 }
1637 else
1638 {
1639 nx_strncpy(buffer, _T("(null)"), bufSize);
1640 }
1641 success = true;
1642 }
1643 else
1644 {
1645 nx_strncpy(buffer, vm->getErrorText(), bufSize);
1646 }
1647 }
1648 delete vm;
1649 return success;
1650 }
1651
1652 /**
1653 * Fill NXCP message with thresholds
1654 */
1655 void DCItem::fillMessageWithThresholds(NXCPMessage *msg)
1656 {
1657 lock();
1658
1659 msg->setField(VID_NUM_THRESHOLDS, (UINT32)getThresholdCount());
1660 UINT32 id = VID_DCI_THRESHOLD_BASE;
1661 for(int i = 0; i < getThresholdCount(); i++, id += 20)
1662 {
1663 m_thresholds->get(i)->createMessage(msg, id);
1664 }
1665
1666 unlock();
1667 }
1668
1669 /**
1670 * Check if DCI has active threshold
1671 */
1672 bool DCItem::hasActiveThreshold()
1673 {
1674 bool result = false;
1675 lock();
1676 for(int i = 0; i < getThresholdCount(); i++)
1677 {
1678 if (m_thresholds->get(i)->isReached())
1679 {
1680 result = true;
1681 break;
1682 }
1683 }
1684 unlock();
1685 return result;
1686 }
1687
1688 /**
1689 * Get severity of active threshold. If no active threshold exist, returns SEVERITY_NORMAL.
1690 */
1691 int DCItem::getThresholdSeverity()
1692 {
1693 int result = SEVERITY_NORMAL;
1694 lock();
1695 for(int i = 0; i < getThresholdCount(); i++)
1696 {
1697 Threshold *t = m_thresholds->get(i);
1698 if (t->isReached())
1699 {
1700 result = t->getCurrentSeverity();
1701 break;
1702 }
1703 }
1704 unlock();
1705 return result;
1706 }
1707
1708 /**
1709 * Returns true if internal cache is loaded. If data collection object
1710 * does not have cache should return true
1711 */
1712 bool DCItem::isCacheLoaded()
1713 {
1714 return m_bCacheLoaded;
1715 }
1716
1717 /**
1718 * Should return true if object has (or can have) value
1719 */
1720 bool DCItem::hasValue()
1721 {
1722 if (m_pNode->getObjectClass() == OBJECT_CLUSTER)
1723 return isAggregateOnCluster() && (m_instanceDiscoveryMethod == IDM_NONE);
1724 return m_instanceDiscoveryMethod == IDM_NONE;
1725 }
1726
1727 /**
1728 * Expand {instance} macro in name and description
1729 */
1730 void DCItem::expandInstance()
1731 {
1732 String temp = m_name;
1733 temp.replace(_T("{instance}"), m_instanceDiscoveryData);
1734 temp.replace(_T("{instance-name}"), m_instance);
1735 nx_strncpy(m_name, (const TCHAR *)temp, MAX_ITEM_NAME);
1736
1737 temp = m_szDescription;
1738 temp.replace(_T("{instance}"), m_instanceDiscoveryData);
1739 temp.replace(_T("{instance-name}"), m_instance);
1740 nx_strncpy(m_szDescription, (const TCHAR *)temp, MAX_DB_STRING);
1741 }
1742
1743 /**
1744 * Filter callback data
1745 */
1746 struct FilterCallbackData
1747 {
1748 StringMap *filteredInstances;
1749 DCItem *dci;
1750 NXSL_VM *instanceFilter;
1751 };
1752
1753 /**
1754 * Callback for filtering instances
1755 */
1756 static bool FilterCallback(const TCHAR *key, const void *value, void *data)
1757 {
1758 NXSL_VM *instanceFilter = ((FilterCallbackData *)data)->instanceFilter;
1759 DCItem *dci = ((FilterCallbackData *)data)->dci;
1760
1761 NXSL_Value *pValue = new NXSL_Value(key);
1762 instanceFilter->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, dci->getNode())));
1763 instanceFilter->setGlobalVariable(_T("$dci"), new NXSL_Value(new NXSL_Object(&g_nxslDciClass, dci)));
1764
1765 if (instanceFilter->run(1, &pValue))
1766 {
1767 bool accepted;
1768 const TCHAR *instance = key;
1769 const TCHAR *name = (const TCHAR *)value;
1770 pValue = instanceFilter->getResult();
1771 if (pValue != NULL)
1772 {
1773 if (pValue->isArray())
1774 {
1775 NXSL_Array *array = pValue->getValueAsArray();
1776 if (array->size() > 0)
1777 {
1778 accepted = array->get(0)->getValueAsInt32() ? true : false;
1779 if (accepted && (array->size() > 1))
1780 {
1781 // transformed value
1782 const TCHAR *newValue = array->get(1)->getValueAsCString();
1783 if ((newValue != NULL) && (*newValue != 0))
1784 {
1785 DbgPrintf(5, _T("DCItem::filterInstanceList(%s [%d]): instance \"%s\" replaced by \"%s\""),
1786 dci->getName(), dci->getId(), instance, newValue);
1787 instance = newValue;
1788 }
1789
1790 if (array->size() > 2)
1791 {
1792 // instance name
1793 const TCHAR *newName = array->get(2)->getValueAsCString();
1794 if ((newName != NULL) && (*newName != 0))
1795 {
1796 DbgPrintf(5, _T("DCItem::filterInstanceList(%s [%d]): instance \"%s\" name set to \"%s\""),
1797 dci->getName(), dci->getId(), instance, newName);
1798 name = newName;
1799 }
1800 }
1801 }
1802 }
1803 else
1804 {
1805 accepted = true;
1806 }
1807 }
1808 else
1809 {
1810 accepted = pValue->getValueAsInt32() ? true : false;
1811 }
1812 }
1813 else
1814 {
1815 accepted = true;
1816 }
1817 if (accepted)
1818 {
1819 ((FilterCallbackData *)data)->filteredInstances->set(instance, name);
1820 }
1821 else
1822 {
1823 DbgPrintf(5, _T("DCItem::filterInstanceList(%s [%d]): instance \"%s\" removed by filtering script"),
1824 dci->getName(), dci->getId(), key);
1825 }
1826 }
1827 else
1828 {
1829 TCHAR szBuffer[1024];
1830
1831 _sntprintf(szBuffer, 1024, _T("DCI::%s::%d::InstanceFilter"),
1832 (dci->getNode() != NULL) ? dci->getNode()->getName() : _T("(null)"), dci->getId());
1833 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", szBuffer, instanceFilter->getErrorText(), dci->getId());
1834 ((FilterCallbackData *)data)->filteredInstances->set(key, (const TCHAR *)value);
1835 }
1836 return true;
1837 }
1838
1839 /**
1840 * Filter instance list
1841 */
1842 void DCItem::filterInstanceList(StringMap *instances)
1843 {
1844 if (m_instanceFilter == NULL)
1845 return;
1846
1847 StringMap filteredInstances;
1848 FilterCallbackData data;
1849 data.filteredInstances = &filteredInstances;
1850 data.instanceFilter = m_instanceFilter;
1851 data.dci = this;
1852 instances->forEach(FilterCallback, &data);
1853 instances->clear();
1854 instances->addAll(&filteredInstances);
1855 }