Fixed force poll on DCI with custom schedule Fixes #1293
[public/netxms.git] / src / server / core / dcobject.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2013 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: dcobject.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Default retention time for collected data
27 */
28 int DCObject::m_defaultRetentionTime = 30;
29
30 /**
31 * Default data collection polling interval
32 */
33 int DCObject::m_defaultPollingInterval = 60;
34
35 /**
36 * Default constructor for DCObject
37 */
38 DCObject::DCObject()
39 {
40 m_id = 0;
41 m_guid = uuid::generate();
42 m_dwTemplateId = 0;
43 m_dwTemplateItemId = 0;
44 m_busy = 0;
45 m_scheduledForDeletion = 0;
46 m_iPollingInterval = 3600;
47 m_iRetentionTime = 0;
48 m_source = DS_INTERNAL;
49 m_status = ITEM_STATUS_NOT_SUPPORTED;
50 m_name[0] = 0;
51 m_description[0] = 0;
52 m_systemTag[0] = 0;
53 m_tLastPoll = 0;
54 m_owner = NULL;
55 m_hMutex = MutexCreateRecursive();
56 m_schedules = NULL;
57 m_tLastCheck = 0;
58 m_flags = 0;
59 m_dwErrorCount = 0;
60 m_dwResourceId = 0;
61 m_sourceNode = 0;
62 m_pszPerfTabSettings = NULL;
63 m_snmpPort = 0; // use default
64 m_transformationScriptSource = NULL;
65 m_transformationScript = NULL;
66 m_comments = NULL;
67 m_forcePoll = false;
68 }
69
70 /**
71 * Create DCObject from another DCObject
72 */
73 DCObject::DCObject(const DCObject *pSrc)
74 {
75 m_id = pSrc->m_id;
76 m_guid = pSrc->m_guid;
77 m_dwTemplateId = pSrc->m_dwTemplateId;
78 m_dwTemplateItemId = pSrc->m_dwTemplateItemId;
79 m_busy = 0;
80 m_scheduledForDeletion = 0;
81 m_iPollingInterval = pSrc->m_iPollingInterval;
82 m_iRetentionTime = pSrc->m_iRetentionTime;
83 m_source = pSrc->m_source;
84 m_status = pSrc->m_status;
85 m_tLastPoll = 0;
86 _tcscpy(m_name, pSrc->m_name);
87 _tcscpy(m_description, pSrc->m_description);
88 _tcscpy(m_systemTag, pSrc->m_systemTag);
89 m_owner = NULL;
90 m_hMutex = MutexCreateRecursive();
91 m_tLastCheck = 0;
92 m_dwErrorCount = 0;
93 m_flags = pSrc->m_flags;
94 m_dwResourceId = pSrc->m_dwResourceId;
95 m_sourceNode = pSrc->m_sourceNode;
96 m_pszPerfTabSettings = (pSrc->m_pszPerfTabSettings != NULL) ? _tcsdup(pSrc->m_pszPerfTabSettings) : NULL;
97 m_snmpPort = pSrc->m_snmpPort;
98 m_comments = (pSrc->m_comments != NULL) ? _tcsdup(pSrc->m_comments) : NULL;
99 m_forcePoll = false;
100
101 m_transformationScriptSource = NULL;
102 m_transformationScript = NULL;
103 setTransformationScript(pSrc->m_transformationScriptSource);
104
105 m_schedules = (pSrc->m_schedules != NULL) ? new StringList(pSrc->m_schedules) : NULL;
106 }
107
108 /**
109 * Constructor for creating new DCObject from scratch
110 */
111 DCObject::DCObject(UINT32 dwId, const TCHAR *szName, int iSource,
112 int iPollingInterval, int iRetentionTime, Template *pNode,
113 const TCHAR *pszDescription, const TCHAR *systemTag)
114 {
115 m_id = dwId;
116 m_guid = uuid::generate();
117 m_dwTemplateId = 0;
118 m_dwTemplateItemId = 0;
119 nx_strncpy(m_name, szName, MAX_ITEM_NAME);
120 if (pszDescription != NULL)
121 nx_strncpy(m_description, pszDescription, MAX_DB_STRING);
122 else
123 _tcscpy(m_description, m_name);
124 nx_strncpy(m_systemTag, CHECK_NULL_EX(systemTag), MAX_DB_STRING);
125 m_source = iSource;
126 m_iPollingInterval = iPollingInterval;
127 m_iRetentionTime = iRetentionTime;
128 m_status = ITEM_STATUS_ACTIVE;
129 m_busy = 0;
130 m_scheduledForDeletion = 0;
131 m_tLastPoll = 0;
132 m_owner = pNode;
133 m_hMutex = MutexCreateRecursive();
134 m_flags = 0;
135 m_schedules = NULL;
136 m_tLastCheck = 0;
137 m_dwErrorCount = 0;
138 m_dwResourceId = 0;
139 m_sourceNode = 0;
140 m_pszPerfTabSettings = NULL;
141 m_snmpPort = 0; // use default
142 m_transformationScriptSource = NULL;
143 m_transformationScript = NULL;
144 m_comments = NULL;
145 m_forcePoll = false;
146 }
147
148 /**
149 * Create DCObject from import file
150 */
151 DCObject::DCObject(ConfigEntry *config, Template *owner)
152 {
153 m_id = CreateUniqueId(IDG_ITEM);
154 m_guid = config->getSubEntryValueAsUUID(_T("guid"));
155 if (m_guid.isNull())
156 m_guid = uuid::generate();
157 m_dwTemplateId = 0;
158 m_dwTemplateItemId = 0;
159 nx_strncpy(m_name, config->getSubEntryValue(_T("name"), 0, _T("unnamed")), MAX_ITEM_NAME);
160 nx_strncpy(m_description, config->getSubEntryValue(_T("description"), 0, m_name), MAX_DB_STRING);
161 nx_strncpy(m_systemTag, config->getSubEntryValue(_T("systemTag"), 0, _T("")), MAX_DB_STRING);
162 m_source = (BYTE)config->getSubEntryValueAsInt(_T("origin"));
163 m_iPollingInterval = config->getSubEntryValueAsInt(_T("interval"));
164 m_iRetentionTime = config->getSubEntryValueAsInt(_T("retention"));
165 m_status = ITEM_STATUS_ACTIVE;
166 m_busy = 0;
167 m_scheduledForDeletion = 0;
168 m_flags = (UINT16)config->getSubEntryValueAsInt(_T("flags"));
169 m_tLastPoll = 0;
170 m_owner = owner;
171 m_hMutex = MutexCreateRecursive();
172 m_tLastCheck = 0;
173 m_dwErrorCount = 0;
174 m_dwResourceId = 0;
175 m_sourceNode = 0;
176 const TCHAR *perfTabSettings = config->getSubEntryValue(_T("perfTabSettings"));
177 m_pszPerfTabSettings = (perfTabSettings != NULL) ? _tcsdup(perfTabSettings) : NULL;
178 m_snmpPort = (WORD)config->getSubEntryValueAsInt(_T("snmpPort"));
179 m_schedules = NULL;
180
181 m_transformationScriptSource = NULL;
182 m_transformationScript = NULL;
183 m_comments = NULL;
184 m_forcePoll = false;
185 setTransformationScript(config->getSubEntryValue(_T("transformation")));
186
187 // for compatibility with old format
188 if (config->getSubEntryValueAsInt(_T("advancedSchedule")))
189 m_flags |= DCF_ADVANCED_SCHEDULE;
190
191 ConfigEntry *schedules = config->findEntry(_T("schedules"));
192 if (schedules != NULL)
193 schedules = schedules->findEntry(_T("schedule"));
194 if ((schedules != NULL) && (schedules->getValueCount() > 0))
195 {
196 m_schedules = new StringList();
197 int count = schedules->getValueCount();
198 for(int i = 0; i < count; i++)
199 {
200 m_schedules->add(schedules->getValue(i));
201 }
202 }
203 }
204
205 /**
206 * Destructor
207 */
208 DCObject::~DCObject()
209 {
210 safe_free(m_transformationScriptSource);
211 delete m_transformationScript;
212 delete m_schedules;
213 safe_free(m_pszPerfTabSettings);
214 safe_free(m_comments);
215 MutexDestroy(m_hMutex);
216 }
217
218 /**
219 * Load custom schedules from database
220 * (assumes that no schedules was created before this call)
221 */
222 bool DCObject::loadCustomSchedules(DB_HANDLE hdb)
223 {
224 if (!(m_flags & DCF_ADVANCED_SCHEDULE))
225 return true;
226
227 TCHAR query[256];
228
229 _sntprintf(query, 256, _T("SELECT schedule FROM dci_schedules WHERE item_id=%d"), m_id);
230 DB_RESULT hResult = DBSelect(hdb, query);
231 if (hResult != NULL)
232 {
233 int count = DBGetNumRows(hResult);
234 if (count > 0)
235 {
236 m_schedules = new StringList();
237 for(int i = 0; i < count; i++)
238 {
239 m_schedules->addPreallocated(DBGetField(hResult, i, 0, NULL, 0));
240 }
241 }
242 DBFreeResult(hResult);
243 }
244
245 return hResult != NULL;
246 }
247
248 /**
249 * Check if associated cluster resource is active. Returns true also if
250 * DCI has no resource association
251 */
252 bool DCObject::matchClusterResource()
253 {
254 Cluster *pCluster;
255
256 if ((m_dwResourceId == 0) || (m_owner->getObjectClass() != OBJECT_NODE))
257 return true;
258
259 pCluster = ((Node *)m_owner)->getMyCluster();
260 if (pCluster == NULL)
261 return false; // Has association, but cluster object cannot be found
262
263 return pCluster->isResourceOnNode(m_dwResourceId, m_owner->getId());
264 }
265
266 /**
267 * Expand macros in text
268 */
269 void DCObject::expandMacros(const TCHAR *src, TCHAR *dst, size_t dstLen)
270 {
271 String temp;
272 TCHAR *head, *rest, *macro;
273 int index = 0, index2;
274
275 temp = src;
276 while((index = temp.find(_T("%{"), index)) != String::npos)
277 {
278 head = temp.substring(0, index);
279 index2 = temp.find(_T("}"), index);
280 if (index2 == String::npos)
281 {
282 free(head);
283 break; // Missing closing }
284 }
285 rest = temp.substring(index2 + 1, -1);
286 macro = temp.substring(index + 2, index2 - index - 2);
287 StrStrip(macro);
288
289 temp = head;
290 if (!_tcscmp(macro, _T("node_id")))
291 {
292 if (m_owner != NULL)
293 {
294 temp.appendFormattedString(_T("%d"), m_owner->getId());
295 }
296 else
297 {
298 temp += _T("(error)");
299 }
300 }
301 else if (!_tcscmp(macro, _T("node_name")))
302 {
303 if (m_owner != NULL)
304 {
305 temp += m_owner->getName();
306 }
307 else
308 {
309 temp += _T("(error)");
310 }
311 }
312 else if (!_tcscmp(macro, _T("node_primary_ip")))
313 {
314 if ((m_owner != NULL) && (m_owner->getObjectClass() == OBJECT_NODE))
315 {
316 TCHAR ipAddr[64];
317 temp += ((Node *)m_owner)->getIpAddress().toString(ipAddr);
318 }
319 else
320 {
321 temp += _T("(error)");
322 }
323 }
324 else if (!_tcsncmp(macro, _T("script:"), 7))
325 {
326 NXSL_VM *vm = g_pScriptLibrary->createVM(&macro[7], new NXSL_ServerEnv);
327 if (vm != NULL)
328 {
329 if (m_owner != NULL)
330 vm->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, m_owner)));
331
332 if (vm->run(0, NULL))
333 {
334 NXSL_Value *result = vm->getResult();
335 if (result != NULL)
336 temp += CHECK_NULL_EX(result->getValueAsCString());
337 DbgPrintf(4, _T("DCItem::expandMacros(%d,\"%s\"): Script %s executed successfully"), m_id, src, &macro[7]);
338 }
339 else
340 {
341 DbgPrintf(4, _T("DCItem::expandMacros(%d,\"%s\"): Script %s execution error: %s"),
342 m_id, src, &macro[7], vm->getErrorText());
343 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", &macro[7], vm->getErrorText(), m_id);
344 }
345 delete vm;
346 }
347 else
348 {
349 DbgPrintf(4, _T("DCItem::expandMacros(%d,\"%s\"): Cannot find script %s"), m_id, src, &macro[7]);
350 }
351 }
352 temp += rest;
353
354 free(head);
355 free(rest);
356 free(macro);
357 }
358 nx_strncpy(dst, temp, dstLen);
359 }
360
361 /**
362 * Delete all collected data
363 */
364 bool DCObject::deleteAllData()
365 {
366 return false;
367 }
368
369 /**
370 * Clean expired data
371 */
372 void DCObject::deleteExpiredData()
373 {
374 }
375
376 /**
377 * Add schedule
378 */
379 void DCObject::addSchedule(const TCHAR *pszSchedule)
380 {
381 if (m_schedules == NULL)
382 m_schedules = new StringList();
383 m_schedules->add(pszSchedule);
384 }
385
386 /**
387 * Set new ID and node/template association
388 */
389 void DCObject::changeBinding(UINT32 dwNewId, Template *newOwner, BOOL doMacroExpansion)
390 {
391 lock();
392 m_owner = newOwner;
393 if (dwNewId != 0)
394 {
395 m_id = dwNewId;
396 m_guid = uuid::generate();
397 }
398
399 if (doMacroExpansion)
400 {
401 expandMacros(m_name, m_name, MAX_ITEM_NAME);
402 expandMacros(m_description, m_description, MAX_DB_STRING);
403 }
404
405 unlock();
406 }
407
408 /**
409 * Set DCI status
410 */
411 void DCObject::setStatus(int status, bool generateEvent)
412 {
413 if (generateEvent && (m_owner != NULL) && (m_status != (BYTE)status) && IsEventSource(m_owner->getObjectClass()))
414 {
415 static UINT32 eventCode[3] = { EVENT_DCI_ACTIVE, EVENT_DCI_DISABLED, EVENT_DCI_UNSUPPORTED };
416 static const TCHAR *originName[8] = { _T("Internal"), _T("NetXMS Agent"), _T("SNMP"), _T("CheckPoint SNMP"), _T("Push"), _T("WinPerf"), _T("iLO"), _T("Script") };
417 PostEvent(eventCode[status], m_owner->getId(), "dssds", m_id, m_name, m_description, m_source, originName[m_source]);
418 }
419 m_status = (BYTE)status;
420 }
421
422 /**
423 * Match schedule to current time
424 */
425 bool DCObject::matchSchedule(struct tm *pCurrTime, const TCHAR *pszSchedule, BOOL *bWithSeconds, time_t currTimestamp)
426 {
427 TCHAR szValue[256], expandedSchedule[1024];
428 const TCHAR *realSchedule = pszSchedule;
429
430 if (_tcslen(pszSchedule) > 4 && !_tcsncmp(pszSchedule, _T("%["), 2))
431 {
432 TCHAR *scriptName = _tcsdup(pszSchedule + 2);
433 if (scriptName != NULL)
434 {
435 bool success = false;
436 TCHAR *closingBracker = _tcschr(scriptName, _T(']'));
437 if (closingBracker != NULL)
438 {
439 *closingBracker = 0;
440
441 NXSL_VM *vm = g_pScriptLibrary->createVM(scriptName, new NXSL_ServerEnv);
442 if (vm != NULL)
443 {
444 vm->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, m_owner)));
445 vm->setGlobalVariable(_T("$dci"), createNXSLObject());
446 if (vm->run(0, NULL))
447 {
448 NXSL_Value *result = vm->getResult();
449 if (result != NULL)
450 {
451 const TCHAR *temp = result->getValueAsCString();
452 if (temp != NULL)
453 {
454 DbgPrintf(7, _T("DCObject::matchSchedule(%%[%s]) expanded to \"%s\""), scriptName, temp);
455 nx_strncpy(expandedSchedule, temp, 1024);
456 realSchedule = expandedSchedule;
457 success = true;
458 }
459 }
460 }
461 else
462 {
463 DbgPrintf(4, _T("DCObject::matchSchedule(%%[%s]) script execution failed (%s)"), scriptName, vm->getErrorText());
464 }
465 delete vm;
466 }
467 g_pScriptLibrary->unlock();
468 }
469 else
470 {
471 DbgPrintf(4, _T("DCObject::matchSchedule: invalid script schedule syntax in %d [%s]"), m_id, m_name);
472 }
473 free(scriptName);
474 if (!success)
475 return false;
476 }
477 else
478 {
479 DbgPrintf(4, _T("DCObject::matchSchedule: invalid script schedule syntax in %d [%s]"), m_id, m_name);
480 return false;
481 }
482 }
483
484 // Minute
485 const TCHAR *pszCurr = ExtractWord(realSchedule, szValue);
486 if (!MatchScheduleElement(szValue, pCurrTime->tm_min, 59, NULL))
487 return false;
488
489 // Hour
490 pszCurr = ExtractWord(pszCurr, szValue);
491 if (!MatchScheduleElement(szValue, pCurrTime->tm_hour, 23, NULL))
492 return false;
493
494 // Day of month
495 pszCurr = ExtractWord(pszCurr, szValue);
496 if (!MatchScheduleElement(szValue, pCurrTime->tm_mday, GetLastMonthDay(pCurrTime), NULL))
497 return false;
498
499 // Month
500 pszCurr = ExtractWord(pszCurr, szValue);
501 if (!MatchScheduleElement(szValue, pCurrTime->tm_mon + 1, 12, NULL))
502 return false;
503
504 // Day of week
505 pszCurr = ExtractWord(pszCurr, szValue);
506 for(int i = 0; szValue[i] != 0; i++)
507 if (szValue[i] == _T('7'))
508 szValue[i] = _T('0');
509 if (!MatchScheduleElement(szValue, pCurrTime->tm_wday, 7, pCurrTime))
510 return false;
511
512 // Seconds
513 szValue[0] = _T('\0');
514 ExtractWord(pszCurr, szValue);
515 if (szValue[0] != _T('\0'))
516 {
517 if (bWithSeconds)
518 *bWithSeconds = TRUE;
519 return MatchScheduleElement(szValue, pCurrTime->tm_sec, 59, NULL, currTimestamp);
520 }
521
522 return true;
523 }
524
525 /**
526 * Check if data collection object have to be polled
527 */
528 bool DCObject::isReadyForPolling(time_t currTime)
529 {
530 bool result;
531
532 lock();
533 if(m_forcePoll)
534 {
535 m_forcePoll = false;
536 unlock();
537 return true;
538 }
539 if ((m_status != ITEM_STATUS_DISABLED) && (!m_busy) &&
540 isCacheLoaded() && (m_source != DS_PUSH_AGENT) &&
541 matchClusterResource() && hasValue() && (getAgentCacheMode() == AGENT_CACHE_OFF))
542 {
543 if (m_flags & DCF_ADVANCED_SCHEDULE)
544 {
545 if (m_schedules != NULL)
546 {
547 struct tm tmCurrLocal, tmLastLocal;
548 memcpy(&tmCurrLocal, localtime(&currTime), sizeof(struct tm));
549 memcpy(&tmLastLocal, localtime(&m_tLastCheck), sizeof(struct tm));
550 result = false;
551 for(int i = 0; i < m_schedules->size(); i++)
552 {
553 BOOL bWithSeconds = FALSE;
554 if (matchSchedule(&tmCurrLocal, m_schedules->get(i), &bWithSeconds, currTime))
555 {
556 // TODO: do we have to take care about the schedules with seconds
557 // that trigger polling too often?
558 if (bWithSeconds || (currTime - m_tLastCheck >= 60) || (tmCurrLocal.tm_min != tmLastLocal.tm_min))
559 {
560 result = true;
561 break;
562 }
563 }
564 }
565 }
566 else
567 {
568 result = false;
569 }
570 m_tLastCheck = currTime;
571 }
572 else
573 {
574 if (m_status == ITEM_STATUS_NOT_SUPPORTED)
575 result = (m_tLastPoll + getEffectivePollingInterval() * 10 <= currTime);
576 else
577 result = (m_tLastPoll + getEffectivePollingInterval() <= currTime);
578 }
579 }
580 else
581 {
582 result = false;
583 }
584 unlock();
585 return result;
586 }
587
588 /**
589 * Returns true if internal cache is loaded. If data collection object
590 * does not have cache should return true
591 */
592 bool DCObject::isCacheLoaded()
593 {
594 return true;
595 }
596
597 /**
598 * Prepare object for deletion
599 */
600 bool DCObject::prepareForDeletion()
601 {
602 DbgPrintf(9, _T("DCObject::prepareForDeletion for DCO %d"), m_id);
603
604 lock();
605 m_status = ITEM_STATUS_DISABLED; // Prevent future polls
606 m_scheduledForDeletion = 1;
607 bool canDelete = (m_busy ? false : true);
608 unlock();
609 DbgPrintf(9, _T("DCObject::prepareForDeletion: completed for DCO %d, canDelete=%d"), m_id, (int)canDelete);
610
611 return canDelete;
612 }
613
614 /**
615 * Create NXCP message with object data
616 */
617 void DCObject::createMessage(NXCPMessage *pMsg)
618 {
619 lock();
620 pMsg->setField(VID_DCI_ID, m_id);
621 pMsg->setField(VID_DCOBJECT_TYPE, (WORD)getType());
622 pMsg->setField(VID_TEMPLATE_ID, m_dwTemplateId);
623 pMsg->setField(VID_NAME, m_name);
624 pMsg->setField(VID_DESCRIPTION, m_description);
625 pMsg->setField(VID_TRANSFORMATION_SCRIPT, CHECK_NULL_EX(m_transformationScriptSource));
626 pMsg->setField(VID_FLAGS, m_flags);
627 pMsg->setField(VID_SYSTEM_TAG, m_systemTag);
628 pMsg->setField(VID_POLLING_INTERVAL, (UINT32)m_iPollingInterval);
629 pMsg->setField(VID_RETENTION_TIME, (UINT32)m_iRetentionTime);
630 pMsg->setField(VID_DCI_SOURCE_TYPE, (WORD)m_source);
631 pMsg->setField(VID_DCI_STATUS, (WORD)m_status);
632 pMsg->setField(VID_RESOURCE_ID, m_dwResourceId);
633 pMsg->setField(VID_AGENT_PROXY, m_sourceNode);
634 pMsg->setField(VID_SNMP_PORT, m_snmpPort);
635 if (m_comments != NULL)
636 pMsg->setField(VID_COMMENTS, m_comments);
637 if (m_pszPerfTabSettings != NULL)
638 pMsg->setField(VID_PERFTAB_SETTINGS, m_pszPerfTabSettings);
639 if (m_schedules != NULL)
640 {
641 pMsg->setField(VID_NUM_SCHEDULES, (UINT32)m_schedules->size());
642 UINT32 fieldId = VID_DCI_SCHEDULE_BASE;
643 for(int i = 0; i < m_schedules->size(); i++, fieldId++)
644 pMsg->setField(fieldId, m_schedules->get(i));
645 }
646 else
647 {
648 pMsg->setField(VID_NUM_SCHEDULES, (UINT32)0);
649 }
650 unlock();
651 }
652
653 /**
654 * Update data collection object from NXCP message
655 */
656 void DCObject::updateFromMessage(NXCPMessage *pMsg)
657 {
658 lock();
659
660 pMsg->getFieldAsString(VID_NAME, m_name, MAX_ITEM_NAME);
661 pMsg->getFieldAsString(VID_DESCRIPTION, m_description, MAX_DB_STRING);
662 pMsg->getFieldAsString(VID_SYSTEM_TAG, m_systemTag, MAX_DB_STRING);
663 m_flags = pMsg->getFieldAsUInt16(VID_FLAGS);
664 m_source = (BYTE)pMsg->getFieldAsUInt16(VID_DCI_SOURCE_TYPE);
665 m_iPollingInterval = pMsg->getFieldAsUInt32(VID_POLLING_INTERVAL);
666 m_iRetentionTime = pMsg->getFieldAsUInt32(VID_RETENTION_TIME);
667 setStatus(pMsg->getFieldAsUInt16(VID_DCI_STATUS), true);
668 m_dwResourceId = pMsg->getFieldAsUInt32(VID_RESOURCE_ID);
669 m_sourceNode = pMsg->getFieldAsUInt32(VID_AGENT_PROXY);
670 safe_free(m_pszPerfTabSettings);
671 m_pszPerfTabSettings = pMsg->getFieldAsString(VID_PERFTAB_SETTINGS);
672 m_snmpPort = pMsg->getFieldAsUInt16(VID_SNMP_PORT);
673 TCHAR *pszStr = pMsg->getFieldAsString(VID_TRANSFORMATION_SCRIPT);
674 safe_free_and_null(m_comments);
675 m_comments = pMsg->getFieldAsString(VID_COMMENTS);
676 setTransformationScript(pszStr);
677 safe_free(pszStr);
678
679 // Update schedules
680 int count = pMsg->getFieldAsInt32(VID_NUM_SCHEDULES);
681 if (count > 0)
682 {
683 if (m_schedules != NULL)
684 m_schedules->clear();
685 else
686 m_schedules = new StringList();
687
688 UINT32 fieldId = VID_DCI_SCHEDULE_BASE;
689 for(int i = 0; i < count; i++, fieldId++)
690 {
691 TCHAR *s = pMsg->getFieldAsString(fieldId);
692 if (s != NULL)
693 {
694 m_schedules->addPreallocated(s);
695 }
696 }
697 }
698 else
699 {
700 delete_and_null(m_schedules);
701 }
702
703 unlock();
704 }
705
706 /**
707 * Save to database
708 */
709 bool DCObject::saveToDatabase(DB_HANDLE hdb)
710 {
711 TCHAR query[1024];
712
713 lock();
714
715 // Save schedules
716 _sntprintf(query, 1024, _T("DELETE FROM dci_schedules WHERE item_id=%d"), (int)m_id);
717 bool success = DBQuery(hdb, query);
718 if (success && (m_schedules != NULL))
719 {
720 for(int i = 0; i < m_schedules->size(); i++)
721 {
722 _sntprintf(query, 1024, _T("INSERT INTO dci_schedules (item_id,schedule_id,schedule) VALUES (%d,%d,%s)"),
723 m_id, i + 1, (const TCHAR *)DBPrepareString(hdb, m_schedules->get(i)));
724 success = DBQuery(hdb, query);
725 if (!success)
726 break;
727 }
728 }
729
730 unlock();
731
732 return success;
733 }
734
735 /**
736 * Delete object and collected data from database
737 */
738 void DCObject::deleteFromDatabase()
739 {
740 TCHAR query[256];
741 _sntprintf(query, sizeof(query) / sizeof(TCHAR), _T("DELETE FROM dci_schedules WHERE item_id=%d"), (int)m_id);
742 QueueSQLRequest(query);
743 }
744
745 /**
746 * Get list of used events
747 */
748 void DCObject::getEventList(UINT32 **ppdwList, UINT32 *pdwSize)
749 {
750 *ppdwList = NULL;
751 *pdwSize = 0;
752 }
753
754 /**
755 * Create management pack record
756 */
757 void DCObject::createExportRecord(String &str)
758 {
759 }
760
761 /**
762 * Load data collection object thresholds from database
763 */
764 bool DCObject::loadThresholdsFromDB(DB_HANDLE hdb)
765 {
766 return true;
767 }
768
769 /**
770 * Update DC object from template object
771 */
772 void DCObject::updateFromTemplate(DCObject *src)
773 {
774 lock();
775
776 expandMacros(src->m_name, m_name, MAX_ITEM_NAME);
777 expandMacros(src->m_description, m_description, MAX_DB_STRING);
778 expandMacros(src->m_systemTag, m_systemTag, MAX_DB_STRING);
779
780 m_iPollingInterval = src->m_iPollingInterval;
781 m_iRetentionTime = src->m_iRetentionTime;
782 m_source = src->m_source;
783 setStatus(src->m_status, true);
784 m_flags = src->m_flags;
785 m_sourceNode = src->m_sourceNode;
786 m_dwResourceId = src->m_dwResourceId;
787 m_snmpPort = src->m_snmpPort;
788
789 safe_free(m_pszPerfTabSettings);
790 m_pszPerfTabSettings = _tcsdup_ex(src->m_pszPerfTabSettings);
791
792 setTransformationScript(src->m_transformationScriptSource);
793
794 // Copy schedules
795 delete m_schedules;
796 m_schedules = (src->m_schedules != NULL) ? new StringList(src->m_schedules) : NULL;
797
798 unlock();
799 }
800
801 /**
802 * Process new collected value. Should return true on success.
803 * If returns false, current poll result will be converted into data collection error.
804 *
805 * @return true on success
806 */
807 bool DCObject::processNewValue(time_t nTimeStamp, const void *value, bool *updateStatus)
808 {
809 *updateStatus = false;
810 return false;
811 }
812
813 /**
814 * Process new data collection error
815 */
816 void DCObject::processNewError(bool noInstance)
817 {
818 time_t now = time(NULL);
819 processNewError(noInstance, now);
820 }
821
822 /**
823 * Process new data collection error
824 */
825 void DCObject::processNewError(bool noInstance, time_t now)
826 {
827 }
828
829 /**
830 * Should return true if object has (or can have) value
831 */
832 bool DCObject::hasValue()
833 {
834 return true;
835 }
836
837 /**
838 * Set new transformation script
839 */
840 void DCObject::setTransformationScript(const TCHAR *source)
841 {
842 free(m_transformationScriptSource);
843 delete m_transformationScript;
844 if (source != NULL)
845 {
846 m_transformationScriptSource = _tcsdup(source);
847 StrStrip(m_transformationScriptSource);
848 if (m_transformationScriptSource[0] != 0)
849 {
850 TCHAR errorText[1024];
851 m_transformationScript = NXSLCompile(m_transformationScriptSource, errorText, 1024, NULL);
852 if (m_transformationScript == NULL)
853 {
854 nxlog_write(MSG_TRANSFORMATION_SCRIPT_COMPILATION_ERROR, NXLOG_WARNING, "dsdss",
855 getOwnerId(), getOwnerName(), m_id, m_name, errorText);
856 }
857 }
858 else
859 {
860 m_transformationScript = NULL;
861 }
862 }
863 else
864 {
865 m_transformationScriptSource = NULL;
866 m_transformationScript = NULL;
867 }
868 }
869
870 /**
871 * Get actual agent cache mode
872 */
873 INT16 DCObject::getAgentCacheMode()
874 {
875 if ((m_owner->getObjectClass() != OBJECT_NODE) ||
876 ((m_source != DS_NATIVE_AGENT) && (m_source != DS_SNMP_AGENT)))
877 return AGENT_CACHE_OFF;
878
879 Node *node = (Node *)m_owner;
880 if (m_sourceNode != 0)
881 {
882 node = (Node *)FindObjectById(m_sourceNode, OBJECT_NODE);
883 if (node == NULL)
884 {
885 return AGENT_CACHE_OFF;
886 }
887 }
888
889 if ((m_source == DS_SNMP_AGENT) && (node->getEffectiveSnmpProxy() == 0))
890 return AGENT_CACHE_OFF;
891
892 INT16 mode = DCF_GET_CACHE_MODE(m_flags);
893 if (mode != AGENT_CACHE_DEFAULT)
894 return mode;
895 return node->getAgentCacheMode();
896 }
897
898 /**
899 * Create DCObject from import file
900 */
901 void DCObject::updateFromImport(ConfigEntry *config)
902 {
903 lock();
904 nx_strncpy(m_name, config->getSubEntryValue(_T("name"), 0, _T("unnamed")), MAX_ITEM_NAME);
905 nx_strncpy(m_description, config->getSubEntryValue(_T("description"), 0, m_name), MAX_DB_STRING);
906 nx_strncpy(m_systemTag, config->getSubEntryValue(_T("systemTag"), 0, _T("")), MAX_DB_STRING);
907 m_source = (BYTE)config->getSubEntryValueAsInt(_T("origin"));
908 m_iPollingInterval = config->getSubEntryValueAsInt(_T("interval"));
909 m_iRetentionTime = config->getSubEntryValueAsInt(_T("retention"));
910 m_flags = (UINT16)config->getSubEntryValueAsInt(_T("flags"));
911 const TCHAR *perfTabSettings = config->getSubEntryValue(_T("perfTabSettings"));
912 safe_free(m_pszPerfTabSettings);
913 m_pszPerfTabSettings = _tcsdup_ex(perfTabSettings);
914 m_snmpPort = (WORD)config->getSubEntryValueAsInt(_T("snmpPort"));
915
916 setTransformationScript(config->getSubEntryValue(_T("transformation")));
917
918 ConfigEntry *schedules = config->findEntry(_T("schedules"));
919 if (schedules != NULL)
920 schedules = schedules->findEntry(_T("schedule"));
921 if ((schedules != NULL) && (schedules->getValueCount() > 0))
922 {
923 if (m_schedules != NULL)
924 m_schedules->clear();
925 else
926 m_schedules = new StringList();
927
928 int count = schedules->getValueCount();
929 for(int i = 0; i < count; i++)
930 {
931 m_schedules->add(schedules->getValue(i));
932 }
933 }
934 else
935 {
936 delete_and_null(m_schedules);
937 }
938 unlock();
939 }
940
941 /**
942 * Get owner ID
943 */
944 UINT32 DCObject::getOwnerId() const
945 {
946 return (m_owner != NULL) ? m_owner->getId() : 0;
947 }
948
949 /**
950 * Get owner name
951 */
952 const TCHAR *DCObject::getOwnerName() const
953 {
954 return (m_owner != NULL) ? m_owner->getName() : _T("(null)");
955 }
956
957 /**
958 * Create NXSL object for this data collection object
959 */
960 NXSL_Value *DCObject::createNXSLObject()
961 {
962 return new NXSL_Value(new NXSL_Object(&g_nxslDciClass, new DCObjectInfo(this)));
963 }
964
965 /**
966 * Data collection object info - constructor
967 */
968 DCObjectInfo::DCObjectInfo(DCObject *object)
969 {
970 m_id = object->getId();
971 m_type = object->getType();
972 nx_strncpy(m_name, object->getName(), MAX_ITEM_NAME);
973 nx_strncpy(m_description, object->getDescription(), MAX_DB_STRING);
974 nx_strncpy(m_systemTag, object->getSystemTag(), MAX_DB_STRING);
975 nx_strncpy(m_instance, (m_type == DCO_TYPE_ITEM) ? ((DCItem *)object)->getInstance() : _T(""), MAX_DB_STRING);
976 m_comments = _tcsdup_ex(object->getComments());
977 m_dataType = (m_type == DCO_TYPE_ITEM) ? ((DCItem *)object)->getDataType() : -1;
978 m_origin = object->getDataSource();
979 m_status = object->getStatus();
980 m_errorCount = object->getErrorCount();
981 m_lastPollTime = object->getLastPollTime();
982 }
983
984 /**
985 * Data collection object info - destructor
986 */
987 DCObjectInfo::~DCObjectInfo()
988 {
989 free(m_comments);
990 }