b3a4a32de67e8202575d0df3ecad0166c94dac39
[public/netxms.git] / src / server / core / dctarget.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2017 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: dctarget.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Data collector thread pool
27 */
28 extern ThreadPool *g_dataCollectorThreadPool;
29
30 /**
31 * Data collector worker
32 */
33 void DataCollector(void *arg);
34
35 /**
36 * Default constructor
37 */
38 DataCollectionTarget::DataCollectionTarget() : Template()
39 {
40 m_deletedItems = new IntegerArray<UINT32>(32, 32);
41 m_deletedTables = new IntegerArray<UINT32>(32, 32);
42 m_pingLastTimeStamp = 0;
43 m_pingTime = PING_TIME_TIMEOUT;
44 m_lastConfigurationPoll = 0;
45 m_lastStatusPoll = 0;
46 m_lastInstancePoll = 0;
47 m_hPollerMutex = MutexCreate();
48 }
49
50 /**
51 * Constructor for creating new data collection capable objects
52 */
53 DataCollectionTarget::DataCollectionTarget(const TCHAR *name) : Template(name)
54 {
55 m_deletedItems = new IntegerArray<UINT32>(32, 32);
56 m_deletedTables = new IntegerArray<UINT32>(32, 32);
57 m_pingLastTimeStamp = 0;
58 m_pingTime = PING_TIME_TIMEOUT;
59 m_lastConfigurationPoll = 0;
60 m_lastStatusPoll = 0;
61 m_lastInstancePoll = 0;
62 m_hPollerMutex = MutexCreate();
63 }
64
65 /**
66 * Destructor
67 */
68 DataCollectionTarget::~DataCollectionTarget()
69 {
70 delete m_deletedItems;
71 delete m_deletedTables;
72 MutexDestroy(m_hPollerMutex);
73 }
74
75 /**
76 * Delete object from database
77 */
78 bool DataCollectionTarget::deleteFromDatabase(DB_HANDLE hdb)
79 {
80 bool success = Template::deleteFromDatabase(hdb);
81 if (success)
82 {
83 TCHAR query[256];
84 _sntprintf(query, 256, _T("DROP TABLE idata_%d"), (int)m_id);
85 QueueSQLRequest(query);
86
87 _sntprintf(query, 256, _T("DROP TABLE tdata_%d"), (int)m_id);
88 QueueSQLRequest(query);
89 }
90 return success;
91 }
92
93 /**
94 * Create NXCP message with object's data
95 */
96 void DataCollectionTarget::fillMessageInternal(NXCPMessage *msg)
97 {
98 Template::fillMessageInternal(msg);
99 }
100
101 /**
102 * Create NXCP message with object's data - stage 2
103 */
104 void DataCollectionTarget::fillMessageInternalStage2(NXCPMessage *msg)
105 {
106 Template::fillMessageInternalStage2(msg);
107
108 // Sent all DCIs marked for display on overview page or in tooltips
109 UINT32 fieldIdOverview = VID_OVERVIEW_DCI_LIST_BASE;
110 UINT32 countOverview = 0;
111 UINT32 fieldIdTooltip = VID_TOOLTIP_DCI_LIST_BASE;
112 UINT32 countTooltip = 0;
113 lockDciAccess(false);
114 for(int i = 0; i < m_dcObjects->size(); i++)
115 {
116 DCObject *dci = m_dcObjects->get(i);
117 if ((dci->getType() == DCO_TYPE_ITEM) &&
118 (dci->getStatus() == ITEM_STATUS_ACTIVE) &&
119 (((DCItem *)dci)->getInstanceDiscoveryMethod() == IDM_NONE))
120 {
121 if (dci->isShowInObjectOverview())
122 {
123 countOverview++;
124 ((DCItem *)dci)->fillLastValueMessage(msg, fieldIdOverview);
125 fieldIdOverview += 50;
126 }
127 if (dci->isShowOnObjectTooltip())
128 {
129 countTooltip++;
130 ((DCItem *)dci)->fillLastValueMessage(msg, fieldIdTooltip);
131 fieldIdTooltip += 50;
132 }
133 }
134 }
135 unlockDciAccess();
136 msg->setField(VID_OVERVIEW_DCI_COUNT, countOverview);
137 msg->setField(VID_TOOLTIP_DCI_COUNT, countTooltip);
138 }
139
140 /**
141 * Modify object from message
142 */
143 UINT32 DataCollectionTarget::modifyFromMessageInternal(NXCPMessage *pRequest)
144 {
145 return Template::modifyFromMessageInternal(pRequest);
146 }
147
148 /**
149 * Update cache for all DCI's
150 */
151 void DataCollectionTarget::updateDciCache()
152 {
153 lockDciAccess(false);
154 for(int i = 0; i < m_dcObjects->size(); i++)
155 {
156 if (m_dcObjects->get(i)->getType() == DCO_TYPE_ITEM)
157 {
158 ((DCItem *)m_dcObjects->get(i))->updateCacheSize();
159 }
160 }
161 unlockDciAccess();
162 }
163
164 /**
165 * Clean expired DCI data
166 */
167 void DataCollectionTarget::cleanDCIData(DB_HANDLE hdb)
168 {
169 String queryItems = _T("DELETE FROM idata_");
170 queryItems.append(m_id);
171 queryItems.append(_T(" WHERE "));
172
173 String queryTables = _T("DELETE FROM tdata_");
174 queryTables.append(m_id);
175 queryTables.append(_T(" WHERE "));
176
177 int itemCount = 0;
178 int tableCount = 0;
179 time_t now = time(NULL);
180
181 lockDciAccess(false);
182 for(int i = 0; i < m_dcObjects->size(); i++)
183 {
184 DCObject *o = m_dcObjects->get(i);
185 if (o->getType() == DCO_TYPE_ITEM)
186 {
187 if (itemCount > 0)
188 queryItems.append(_T(" OR "));
189 queryItems.append(_T("(item_id="));
190 queryItems.append(o->getId());
191 queryItems.append(_T(" AND idata_timestamp<"));
192 queryItems.append((INT64)(now - o->getEffectiveRetentionTime() * 86400));
193 queryItems.append(_T(')'));
194 itemCount++;
195 }
196 else if (o->getType() == DCO_TYPE_TABLE)
197 {
198 if (tableCount > 0)
199 queryTables.append(_T(" OR "));
200 queryTables.append(_T("(item_id="));
201 queryTables.append(o->getId());
202 queryTables.append(_T(" AND tdata_timestamp<"));
203 queryTables.append((INT64)(now - o->getEffectiveRetentionTime() * 86400));
204 queryTables.append(_T(')'));
205 tableCount++;
206 }
207 }
208 unlockDciAccess();
209
210 lockProperties();
211 for(int i = 0; i < m_deletedItems->size(); i++)
212 {
213 if (itemCount > 0)
214 queryItems.append(_T(" OR "));
215 queryItems.append(_T("item_id="));
216 queryItems.append(m_deletedItems->get(i));
217 itemCount++;
218 }
219 m_deletedItems->clear();
220
221 for(int i = 0; i < m_deletedTables->size(); i++)
222 {
223 if (tableCount > 0)
224 queryTables.append(_T(" OR "));
225 queryTables.append(_T("item_id="));
226 queryTables.append(m_deletedItems->get(i));
227 tableCount++;
228 }
229 m_deletedTables->clear();
230 unlockProperties();
231
232 if (itemCount > 0)
233 {
234 nxlog_debug(6, _T("DataCollectionTarget::cleanDCIData(%s [%d]): running query \"%s\""), m_name, m_id, (const TCHAR *)queryItems);
235 DBQuery(hdb, queryItems);
236 }
237
238 if (tableCount > 0)
239 {
240 nxlog_debug(6, _T("DataCollectionTarget::cleanDCIData(%s [%d]): running query \"%s\""), m_name, m_id, (const TCHAR *)queryTables);
241 DBQuery(hdb, queryTables);
242 }
243 }
244
245 /**
246 * Schedule cleanup of DCI data after DCI deletion
247 */
248 void DataCollectionTarget::scheduleItemDataCleanup(UINT32 dciId)
249 {
250 lockProperties();
251 m_deletedItems->add(dciId);
252 unlockProperties();
253 }
254
255 /**
256 * Schedule cleanup of table DCI data after DCI deletion
257 */
258 void DataCollectionTarget::scheduleTableDataCleanup(UINT32 dciId)
259 {
260 lockProperties();
261 m_deletedTables->add(dciId);
262 unlockProperties();
263 }
264
265 /**
266 * Get last collected values of given table
267 */
268 UINT32 DataCollectionTarget::getTableLastValues(UINT32 dciId, NXCPMessage *msg)
269 {
270 UINT32 rcc = RCC_INVALID_DCI_ID;
271
272 lockDciAccess(false);
273
274 for(int i = 0; i < m_dcObjects->size(); i++)
275 {
276 DCObject *object = m_dcObjects->get(i);
277 if ((object->getId() == dciId) && (object->getType() == DCO_TYPE_TABLE))
278 {
279 ((DCTable *)object)->fillLastValueMessage(msg);
280 rcc = RCC_SUCCESS;
281 break;
282 }
283 }
284
285 unlockDciAccess();
286 return rcc;
287 }
288
289 /**
290 * Apply DCI from template
291 * dcObject passed to this method should be a template's DCI
292 */
293 bool DataCollectionTarget::applyTemplateItem(UINT32 dwTemplateId, DCObject *dcObject)
294 {
295 bool bResult = true;
296
297 lockDciAccess(true); // write lock
298
299 nxlog_debug(5, _T("Applying DCO \"%s\" to target \"%s\""), dcObject->getName(), m_name);
300
301 // Check if that template item exists
302 int i;
303 for(i = 0; i < m_dcObjects->size(); i++)
304 if ((m_dcObjects->get(i)->getTemplateId() == dwTemplateId) &&
305 (m_dcObjects->get(i)->getTemplateItemId() == dcObject->getId()))
306 break; // Item with specified id already exist
307
308 if (i == m_dcObjects->size())
309 {
310 // New item from template, just add it
311 DCObject *newObject;
312 switch(dcObject->getType())
313 {
314 case DCO_TYPE_ITEM:
315 newObject = new DCItem((DCItem *)dcObject);
316 break;
317 case DCO_TYPE_TABLE:
318 newObject = new DCTable((DCTable *)dcObject);
319 break;
320 default:
321 newObject = NULL;
322 break;
323 }
324 if (newObject != NULL)
325 {
326 newObject->setTemplateId(dwTemplateId, dcObject->getId());
327 newObject->changeBinding(CreateUniqueId(IDG_ITEM), this, TRUE);
328 bResult = addDCObject(newObject, true);
329 }
330 }
331 else
332 {
333 // Update existing item unless it is disabled
334 DCObject *curr = m_dcObjects->get(i);
335 if ((curr->getStatus() != ITEM_STATUS_DISABLED) || (g_flags & AF_APPLY_TO_DISABLED_DCI_FROM_TEMPLATE))
336 {
337 curr->updateFromTemplate(dcObject);
338 DbgPrintf(9, _T("DCO \"%s\" NOT disabled or ApplyDCIFromTemplateToDisabledDCI set, updated (%d)"),
339 dcObject->getName(), curr->getStatus());
340 if ((curr->getType() == DCO_TYPE_ITEM) && (((DCItem *)curr)->getInstanceDiscoveryMethod() != IDM_NONE))
341 {
342 updateInstanceDiscoveryItems((DCItem *)curr);
343 }
344 }
345 else
346 {
347 DbgPrintf(9, _T("DCO \"%s\" is disabled and ApplyDCIFromTemplateToDisabledDCI not set, no update (%d)"),
348 dcObject->getName(), curr->getStatus());
349 }
350 }
351
352 unlockDciAccess();
353
354 if (bResult)
355 {
356 lockProperties();
357 setModified(MODIFY_DATA_COLLECTION, false);
358 unlockProperties();
359 }
360 return bResult;
361 }
362
363 /**
364 * Clean deleted template items from target's DCI list
365 * Arguments is template id and list of valid template item ids.
366 * all items related to given template and not presented in list should be deleted.
367 */
368 void DataCollectionTarget::cleanDeletedTemplateItems(UINT32 dwTemplateId, UINT32 dwNumItems, UINT32 *pdwItemList)
369 {
370 UINT32 i, j, dwNumDeleted, *pdwDeleteList;
371
372 lockDciAccess(true); // write lock
373
374 pdwDeleteList = (UINT32 *)malloc(sizeof(UINT32) * m_dcObjects->size());
375 dwNumDeleted = 0;
376
377 for(i = 0; i < (UINT32)m_dcObjects->size(); i++)
378 if (m_dcObjects->get(i)->getTemplateId() == dwTemplateId)
379 {
380 for(j = 0; j < dwNumItems; j++)
381 if (m_dcObjects->get(i)->getTemplateItemId() == pdwItemList[j])
382 break;
383
384 // Delete DCI if it's not in list
385 if (j == dwNumItems)
386 pdwDeleteList[dwNumDeleted++] = m_dcObjects->get(i)->getId();
387 }
388
389 for(i = 0; i < dwNumDeleted; i++)
390 deleteDCObject(pdwDeleteList[i], false);
391
392 unlockDciAccess();
393 free(pdwDeleteList);
394 }
395
396 /**
397 * Unbind data collection target from template, i.e either remove DCI
398 * association with template or remove these DCIs at all
399 */
400 void DataCollectionTarget::unbindFromTemplate(UINT32 dwTemplateId, bool removeDCI)
401 {
402 if (removeDCI)
403 {
404 lockDciAccess(true); // write lock
405
406 UINT32 *deleteList = (UINT32 *)malloc(sizeof(UINT32) * m_dcObjects->size());
407 int numDeleted = 0;
408
409 int i;
410 for(i = 0; i < m_dcObjects->size(); i++)
411 if (m_dcObjects->get(i)->getTemplateId() == dwTemplateId)
412 {
413 deleteList[numDeleted++] = m_dcObjects->get(i)->getId();
414 }
415
416 for(i = 0; i < numDeleted; i++)
417 deleteDCObject(deleteList[i], false);
418
419 unlockDciAccess();
420 free(deleteList);
421 }
422 else
423 {
424 lockDciAccess(false);
425
426 for(int i = 0; i < m_dcObjects->size(); i++)
427 if (m_dcObjects->get(i)->getTemplateId() == dwTemplateId)
428 {
429 m_dcObjects->get(i)->setTemplateId(0, 0);
430 }
431
432 unlockDciAccess();
433 }
434 }
435
436 /**
437 * Get list of DCIs to be shown on performance tab
438 */
439 UINT32 DataCollectionTarget::getPerfTabDCIList(NXCPMessage *pMsg)
440 {
441 lockDciAccess(false);
442
443 UINT32 dwId = VID_SYSDCI_LIST_BASE, dwCount = 0;
444 for(int i = 0; i < m_dcObjects->size(); i++)
445 {
446 DCObject *object = m_dcObjects->get(i);
447 if ((object->getPerfTabSettings() != NULL) &&
448 object->hasValue() &&
449 (object->getStatus() == ITEM_STATUS_ACTIVE) &&
450 object->matchClusterResource())
451 {
452 pMsg->setField(dwId++, object->getId());
453 pMsg->setField(dwId++, object->getDescription());
454 pMsg->setField(dwId++, (WORD)object->getStatus());
455 pMsg->setField(dwId++, object->getPerfTabSettings());
456 pMsg->setField(dwId++, (WORD)object->getType());
457 pMsg->setField(dwId++, object->getTemplateItemId());
458 if (object->getType() == DCO_TYPE_ITEM)
459 {
460 pMsg->setField(dwId++, ((DCItem *)object)->getInstance());
461 if ((object->getTemplateItemId() != 0) && (object->getTemplateId() == m_id))
462 {
463 // DCI created via instance discovery - send ID of root template item
464 // to allow UI to resolve double template case
465 // (template -> instance discovery item on node -> actual item on node)
466 DCObject *src = getDCObjectById(object->getTemplateItemId(), false);
467 pMsg->setField(dwId++, (src != NULL) ? src->getTemplateItemId() : 0);
468 dwId += 2;
469 }
470 else
471 {
472 dwId += 3;
473 }
474 }
475 else
476 {
477 dwId += 4;
478 }
479 dwCount++;
480 }
481 }
482 pMsg->setField(VID_NUM_ITEMS, dwCount);
483
484 unlockDciAccess();
485 return RCC_SUCCESS;
486 }
487
488 /**
489 * Get threshold violation summary into NXCP message
490 */
491 UINT32 DataCollectionTarget::getThresholdSummary(NXCPMessage *msg, UINT32 baseId)
492 {
493 UINT32 varId = baseId;
494
495 msg->setField(varId++, m_id);
496 UINT32 countId = varId++;
497 UINT32 count = 0;
498
499 lockDciAccess(false);
500 for(int i = 0; i < m_dcObjects->size(); i++)
501 {
502 DCObject *object = m_dcObjects->get(i);
503 if (object->hasValue() && (object->getType() == DCO_TYPE_ITEM) && (object->getStatus() == ITEM_STATUS_ACTIVE))
504 {
505 if (((DCItem *)object)->hasActiveThreshold())
506 {
507 ((DCItem *)object)->fillLastValueMessage(msg, varId);
508 varId += 50;
509 count++;
510 }
511 }
512 }
513 unlockDciAccess();
514 msg->setField(countId, count);
515 return varId;
516 }
517
518 /**
519 * Process new DCI value
520 */
521 bool DataCollectionTarget::processNewDCValue(DCObject *dco, time_t currTime, const void *value)
522 {
523 bool updateStatus;
524 bool result = dco->processNewValue(currTime, value, &updateStatus);
525 if (updateStatus)
526 {
527 calculateCompoundStatus(FALSE);
528 }
529 return result;
530 }
531
532 /**
533 * Check if data collection is disabled
534 */
535 bool DataCollectionTarget::isDataCollectionDisabled()
536 {
537 return false;
538 }
539
540 /**
541 * Put items which requires polling into the queue
542 */
543 void DataCollectionTarget::queueItemsForPolling()
544 {
545 if ((m_status == STATUS_UNMANAGED) || isDataCollectionDisabled() || m_isDeleted)
546 return; // Do not collect data for unmanaged objects or if data collection is disabled
547
548 time_t currTime = time(NULL);
549
550 lockDciAccess(false);
551 for(int i = 0; i < m_dcObjects->size(); i++)
552 {
553 DCObject *object = m_dcObjects->get(i);
554 if (object->isReadyForPolling(currTime))
555 {
556 object->setBusyFlag();
557 incRefCount(); // Increment reference count for each queued DCI
558
559 if ((object->getDataSource() == DS_NATIVE_AGENT) ||
560 (object->getDataSource() == DS_WINPERF) ||
561 (object->getDataSource() == DS_SSH) ||
562 (object->getDataSource() == DS_SMCLP))
563 {
564 TCHAR key[32];
565 _sntprintf(key, 32, _T("%08X/%s"),
566 m_id, (object->getDataSource() == DS_SSH) ? _T("ssh") :
567 (object->getDataSource() == DS_SMCLP) ? _T("smclp") : _T("agent"));
568 ThreadPoolExecuteSerialized(g_dataCollectorThreadPool, key, DataCollector, object);
569 }
570 else
571 {
572 ThreadPoolExecute(g_dataCollectorThreadPool, DataCollector, object);
573 }
574 nxlog_debug(8, _T("DataCollectionTarget(%s)->QueueItemsForPolling(): item %d \"%s\" added to queue"), m_name, object->getId(), object->getName());
575 }
576 }
577 unlockDciAccess();
578 }
579
580 /**
581 * Get object from parameter
582 */
583 NetObj *DataCollectionTarget::objectFromParameter(const TCHAR *param)
584 {
585 TCHAR *eptr, arg[256];
586 AgentGetParameterArg(param, 1, arg, 256);
587 UINT32 objectId = _tcstoul(arg, &eptr, 0);
588 if (*eptr != 0)
589 {
590 // Argument is object's name
591 objectId = 0;
592 }
593
594 // Find child object with requested ID or name
595 NetObj *object = NULL;
596 lockChildList(false);
597 for(int i = 0; i < m_childList->size(); i++)
598 {
599 NetObj *curr = m_childList->get(i);
600 if (((objectId == 0) && (!_tcsicmp(curr->getName(), arg))) ||
601 (objectId == curr->getId()))
602 {
603 object = curr;
604 break;
605 }
606 }
607 unlockChildList();
608 return object;
609 }
610
611 /**
612 * Get value for server's internal parameter
613 */
614 UINT32 DataCollectionTarget::getInternalItem(const TCHAR *param, size_t bufSize, TCHAR *buffer)
615 {
616 UINT32 dwError = DCE_SUCCESS;
617
618 if (!_tcsicmp(param, _T("Status")))
619 {
620 _sntprintf(buffer, bufSize, _T("%d"), m_status);
621 }
622 else if (!_tcsicmp(param, _T("Dummy")) || MatchString(_T("Dummy(*)"), param, FALSE))
623 {
624 _tcscpy(buffer, _T("0"));
625 }
626 else if (MatchString(_T("ChildStatus(*)"), param, FALSE))
627 {
628 NetObj *object = objectFromParameter(param);
629 if (object != NULL)
630 {
631 _sntprintf(buffer, bufSize, _T("%d"), object->getStatus());
632 }
633 else
634 {
635 dwError = DCE_NOT_SUPPORTED;
636 }
637 }
638 else if (MatchString(_T("ConditionStatus(*)"), param, FALSE))
639 {
640 TCHAR *pEnd, szArg[256];
641 UINT32 dwId;
642 NetObj *pObject = NULL;
643
644 AgentGetParameterArg(param, 1, szArg, 256);
645 dwId = _tcstoul(szArg, &pEnd, 0);
646 if (*pEnd == 0)
647 {
648 pObject = FindObjectById(dwId);
649 if (pObject != NULL)
650 if (pObject->getObjectClass() != OBJECT_CONDITION)
651 pObject = NULL;
652 }
653 else
654 {
655 // Argument is object's name
656 pObject = FindObjectByName(szArg, OBJECT_CONDITION);
657 }
658
659 if (pObject != NULL)
660 {
661 if (pObject->isTrustedNode(m_id))
662 {
663 _sntprintf(buffer, bufSize, _T("%d"), pObject->getStatus());
664 }
665 else
666 {
667 dwError = DCE_NOT_SUPPORTED;
668 }
669 }
670 else
671 {
672 dwError = DCE_NOT_SUPPORTED;
673 }
674 }
675 else
676 {
677 dwError = DCE_NOT_SUPPORTED;
678 }
679
680 return dwError;
681 }
682
683 /**
684 * Run data collection script. Returns pointer to NXSL VM after successful run and NULL on failure.
685 */
686 NXSL_VM *DataCollectionTarget::runDataCollectionScript(const TCHAR *param, DataCollectionTarget *targetObject)
687 {
688 TCHAR name[256];
689 nx_strncpy(name, param, 256);
690 Trim(name);
691
692 ObjectArray<NXSL_Value> args(16, 16, false);
693
694 // Can be in form parameter(arg1, arg2, ... argN)
695 TCHAR *p = _tcschr(name, _T('('));
696 if (p != NULL)
697 {
698 if (name[_tcslen(name) - 1] != _T(')'))
699 return NULL;
700 name[_tcslen(name) - 1] = 0;
701
702 if (!ParseValueList(&p, args))
703 {
704 // argument parsing error
705 args.clear();
706 return NULL;
707 }
708 }
709
710 NXSL_VM *vm = CreateServerScriptVM(name);
711 if (vm != NULL)
712 {
713 vm->setGlobalVariable(_T("$object"), createNXSLObject());
714 if (getObjectClass() == OBJECT_NODE)
715 {
716 vm->setGlobalVariable(_T("$node"), createNXSLObject());
717 }
718 vm->setGlobalVariable(_T("$isCluster"), new NXSL_Value((getObjectClass() == OBJECT_CLUSTER) ? 1 : 0));
719 if (targetObject != NULL)
720 {
721 vm->setGlobalVariable(_T("$targetObject"), targetObject->createNXSLObject());
722 }
723 if (!vm->run(&args))
724 {
725 DbgPrintf(4, _T("DataCollectionTarget(%s)->runDataCollectionScript(%s): Script execution error: %s"), m_name, param, vm->getErrorText());
726 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", name, vm->getErrorText(), m_id);
727 delete_and_null(vm);
728 }
729 }
730 else
731 {
732 args.setOwner(true);
733 }
734 nxlog_debug(7, _T("DataCollectionTarget(%s)->runDataCollectionScript(%s): %s"), m_name, param, (vm != NULL) ? _T("success") : _T("failure"));
735 return vm;
736 }
737
738 /**
739 * Get parameter value from NXSL script
740 */
741 UINT32 DataCollectionTarget::getScriptItem(const TCHAR *param, size_t bufSize, TCHAR *buffer, DataCollectionTarget *targetObject)
742 {
743 UINT32 rc = DCE_NOT_SUPPORTED;
744 NXSL_VM *vm = runDataCollectionScript(param, targetObject);
745 if (vm != NULL)
746 {
747 NXSL_Value *value = vm->getResult();
748 if (value->isNull())
749 {
750 // NULL value is an error indicator
751 rc = DCE_COLLECTION_ERROR;
752 }
753 else
754 {
755 const TCHAR *dciValue = value->getValueAsCString();
756 nx_strncpy(buffer, CHECK_NULL_EX(dciValue), bufSize);
757 rc = DCE_SUCCESS;
758 }
759 delete vm;
760 }
761 nxlog_debug(7, _T("DataCollectionTarget(%s)->getScriptItem(%s): rc=%d"), m_name, param, rc);
762 return rc;
763 }
764
765 /**
766 * Get list from library script
767 */
768 UINT32 DataCollectionTarget::getListFromScript(const TCHAR *param, StringList **list, DataCollectionTarget *targetObject)
769 {
770 UINT32 rc = DCE_NOT_SUPPORTED;
771 NXSL_VM *vm = runDataCollectionScript(param, targetObject);
772 if (vm != NULL)
773 {
774 rc = DCE_SUCCESS;
775 NXSL_Value *value = vm->getResult();
776 if (value->isArray())
777 {
778 *list = value->getValueAsArray()->toStringList();
779 }
780 else if (value->isString())
781 {
782 *list = new StringList;
783 (*list)->add(value->getValueAsCString());
784 }
785 else if (value->isNull())
786 {
787 rc = DCE_COLLECTION_ERROR;
788 }
789 else
790 {
791 *list = new StringList;
792 }
793 delete vm;
794 }
795 nxlog_debug(7, _T("DataCollectionTarget(%s)->getListFromScript(%s): rc=%d"), m_name, param, rc);
796 return rc;
797 }
798
799 /**
800 * Get table from NXSL script
801 */
802 UINT32 DataCollectionTarget::getScriptTable(const TCHAR *param, Table **result, DataCollectionTarget *targetObject)
803 {
804 UINT32 rc = DCE_NOT_SUPPORTED;
805 NXSL_VM *vm = runDataCollectionScript(param, targetObject);
806 if (vm != NULL)
807 {
808 NXSL_Value *value = vm->getResult();
809 if (value->isObject(_T("Table")))
810 {
811 *result = (Table *)value->getValueAsObject()->getData();
812 (*result)->incRefCount();
813 rc = DCE_SUCCESS;
814 }
815 else
816 {
817 rc = DCE_COLLECTION_ERROR;
818 }
819 delete vm;
820 }
821 nxlog_debug(7, _T("DataCollectionTarget(%s)->getScriptTable(%s): rc=%d"), m_name, param, rc);
822 return rc;
823 }
824
825 /**
826 * Get string map from library script
827 */
828 UINT32 DataCollectionTarget::getStringMapFromScript(const TCHAR *param, StringMap **map, DataCollectionTarget *targetObject)
829 {
830 TCHAR name[256];
831 nx_strncpy(name, param, 256);
832 Trim(name);
833
834 ObjectArray<NXSL_Value> args(16, 16, false);
835
836 // Can be in form parameter(arg1, arg2, ... argN)
837 TCHAR *p = _tcschr(name, _T('('));
838 if (p != NULL)
839 {
840 if (name[_tcslen(name) - 1] != _T(')'))
841 return DCE_NOT_SUPPORTED;
842 name[_tcslen(name) - 1] = 0;
843
844 if (!ParseValueList(&p, args))
845 {
846 // argument parsing error
847 args.clear();
848 return DCE_NOT_SUPPORTED;
849 }
850 }
851
852 UINT32 rc = DCE_NOT_SUPPORTED;
853 NXSL_VM *vm = CreateServerScriptVM(name);
854 if (vm != NULL)
855 {
856 vm->setGlobalVariable(_T("$object"), createNXSLObject());
857 if (getObjectClass() == OBJECT_NODE)
858 {
859 vm->setGlobalVariable(_T("$node"), createNXSLObject());
860 }
861 vm->setGlobalVariable(_T("$isCluster"), new NXSL_Value((getObjectClass() == OBJECT_CLUSTER) ? 1 : 0));
862 if (targetObject != NULL)
863 {
864 vm->setGlobalVariable(_T("$targetObject"), targetObject->createNXSLObject());
865 }
866 if (vm->run(&args))
867 {
868 rc = DCE_SUCCESS;
869 NXSL_Value *value = vm->getResult();
870 if (value->isHashMap())
871 {
872 *map = value->getValueAsHashMap()->toStringMap();
873 }
874 else if (value->isArray())
875 {
876 *map = new StringMap();
877 NXSL_Array *a = value->getValueAsArray();
878 for(int i = 0; i < a->size(); i++)
879 {
880 NXSL_Value *v = a->getByPosition(i);
881 if (v->isString())
882 {
883 (*map)->set(v->getValueAsCString(), v->getValueAsCString());
884 }
885 }
886 }
887 else if (value->isString())
888 {
889 *map = new StringMap();
890 (*map)->set(value->getValueAsCString(), value->getValueAsCString());
891 }
892 else if (value->isNull())
893 {
894 rc = DCE_COLLECTION_ERROR;
895 }
896 else
897 {
898 *map = new StringMap();
899 }
900 }
901 else
902 {
903 DbgPrintf(4, _T("DataCollectionTarget(%s)->getListFromScript(%s): Script execution error: %s"), m_name, param, vm->getErrorText());
904 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", name, vm->getErrorText(), m_id);
905 rc = DCE_COLLECTION_ERROR;
906 }
907 delete vm;
908 }
909 else
910 {
911 args.setOwner(true);
912 DbgPrintf(4, _T("DataCollectionTarget(%s)->getListFromScript(%s): script \"%s\" not found"), m_name, param, name);
913 }
914 DbgPrintf(7, _T("DataCollectionTarget(%s)->getListFromScript(%s): rc=%d"), m_name, param, rc);
915 return rc;
916 }
917
918 /**
919 * Get last (current) DCI values for summary table.
920 */
921 void DataCollectionTarget::getDciValuesSummary(SummaryTable *tableDefinition, Table *tableData)
922 {
923 if (tableDefinition->isTableDciSource())
924 getTableDciValuesSummary(tableDefinition, tableData);
925 else
926 getItemDciValuesSummary(tableDefinition, tableData);
927 }
928
929 /**
930 * Get last (current) DCI values for summary table using single-value DCIs
931 */
932 void DataCollectionTarget::getItemDciValuesSummary(SummaryTable *tableDefinition, Table *tableData)
933 {
934 int offset = tableDefinition->isMultiInstance() ? 2 : 1;
935 int baseRow = tableData->getNumRows();
936 bool rowAdded = false;
937 lockDciAccess(false);
938 for(int i = 0; i < tableDefinition->getNumColumns(); i++)
939 {
940 SummaryTableColumn *tc = tableDefinition->getColumn(i);
941 for(int j = 0; j < m_dcObjects->size(); j++)
942 {
943 DCObject *object = m_dcObjects->get(j);
944 if ((object->getType() == DCO_TYPE_ITEM) && object->hasValue() &&
945 (object->getStatus() == ITEM_STATUS_ACTIVE) &&
946 ((tc->m_flags & COLUMN_DEFINITION_REGEXP_MATCH) ?
947 RegexpMatch(object->getName(), tc->m_dciName, FALSE) :
948 !_tcsicmp(object->getName(), tc->m_dciName)
949 ))
950 {
951 int row;
952 if (tableDefinition->isMultiInstance())
953 {
954 // Find instance
955 const TCHAR *instance = ((DCItem *)object)->getInstance();
956 for(row = baseRow; row < tableData->getNumRows(); row++)
957 {
958 const TCHAR *v = tableData->getAsString(row, 1);
959 if (!_tcscmp(CHECK_NULL_EX(v), instance))
960 break;
961 }
962 if (row == tableData->getNumRows())
963 {
964 tableData->addRow();
965 tableData->set(0, m_name);
966 tableData->set(1, instance);
967 tableData->setObjectId(m_id);
968 }
969 }
970 else
971 {
972 if (!rowAdded)
973 {
974 tableData->addRow();
975 tableData->set(0, m_name);
976 tableData->setObjectId(m_id);
977 rowAdded = true;
978 }
979 row = tableData->getNumRows() - 1;
980 }
981 tableData->setStatusAt(row, i + offset, ((DCItem *)object)->getThresholdSeverity());
982 tableData->setCellObjectIdAt(row, i + offset, object->getId());
983 tableData->getColumnDefinitions()->get(i + offset)->setDataType(((DCItem *)object)->getDataType());
984 if (tableDefinition->getAggregationFunction() == F_LAST)
985 {
986 if (tc->m_flags & COLUMN_DEFINITION_MULTIVALUED)
987 {
988 StringList *values = String(((DCItem *)object)->getLastValue()).split(tc->m_separator);
989 tableData->setAt(row, i + offset, values->get(0));
990 for(int r = 1; r < values->size(); r++)
991 {
992 if (row + r >= tableData->getNumRows())
993 {
994 tableData->addRow();
995 tableData->setObjectId(m_id);
996 tableData->setBaseRow(row);
997 }
998 tableData->setAt(row + r, i + offset, values->get(r));
999 tableData->setStatusAt(row + r, i + offset, ((DCItem *)object)->getThresholdSeverity());
1000 tableData->setCellObjectIdAt(row + r, i + offset, object->getId());
1001 }
1002 }
1003 else
1004 {
1005 tableData->setAt(row, i + offset, ((DCItem *)object)->getLastValue());
1006 }
1007 }
1008 else
1009 {
1010 tableData->setAt(row, i + offset,
1011 ((DCItem *)object)->getAggregateValue(
1012 tableDefinition->getAggregationFunction(),
1013 tableDefinition->getPeriodStart(),
1014 tableDefinition->getPeriodEnd()));
1015 }
1016
1017 if (!tableDefinition->isMultiInstance())
1018 break;
1019 }
1020 }
1021 }
1022 unlockDciAccess();
1023 }
1024
1025 /**
1026 * Get last (current) DCI values for summary table using table DCIs
1027 */
1028 void DataCollectionTarget::getTableDciValuesSummary(SummaryTable *tableDefinition, Table *tableData)
1029 {
1030 lockDciAccess(false);
1031 for(int i = 0; i < m_dcObjects->size(); i++)
1032 {
1033 DCObject *o = m_dcObjects->get(i);
1034 if ((o->getType() == DCO_TYPE_TABLE) && o->hasValue() &&
1035 (o->getStatus() == ITEM_STATUS_ACTIVE) &&
1036 !_tcsicmp(o->getName(), tableDefinition->getTableDciName()))
1037 {
1038 Table *lastValue = ((DCTable*)o)->getLastValue();
1039 if (lastValue == NULL)
1040 continue;
1041
1042 for(int j = 0; j < lastValue->getNumRows(); j++)
1043 {
1044 tableData->addRow();
1045 tableData->setObjectId(m_id);
1046 tableData->set(0, m_name);
1047 for(int k = 0; k < lastValue->getNumColumns(); k++)
1048 {
1049 int columnIndex = tableData->getColumnIndex(lastValue->getColumnName(k));
1050 if (columnIndex == -1)
1051 columnIndex = tableData->addColumn(lastValue->getColumnDefinition(k));
1052 tableData->set(columnIndex, lastValue->getAsString(j, k));
1053 }
1054 }
1055 }
1056 }
1057 unlockDciAccess();
1058 }
1059
1060 /**
1061 * Must return true if object is a possible event source
1062 */
1063 bool DataCollectionTarget::isEventSource()
1064 {
1065 return true;
1066 }
1067
1068 /**
1069 * Returns most critical status of DCI used for
1070 * status calculation
1071 */
1072 int DataCollectionTarget::getMostCriticalDCIStatus()
1073 {
1074 int status = -1;
1075 lockDciAccess(false);
1076 for(int i = 0; i < m_dcObjects->size(); i++)
1077 {
1078 DCObject *curr = m_dcObjects->get(i);
1079 if (curr->isStatusDCO() && (curr->getType() == DCO_TYPE_ITEM) &&
1080 curr->hasValue() && (curr->getStatus() == ITEM_STATUS_ACTIVE))
1081 {
1082 if (getObjectClass() == OBJECT_CLUSTER && !curr->isAggregateOnCluster())
1083 continue; // Calculated only on those that are agregated on cluster
1084
1085 ItemValue *value = ((DCItem *)curr)->getInternalLastValue();
1086 if (value != NULL && (INT32)*value >= STATUS_NORMAL && (INT32)*value <= STATUS_CRITICAL)
1087 status = std::max(status, (INT32)*value);
1088 delete value;
1089 }
1090 }
1091 unlockDciAccess();
1092 return (status == -1) ? STATUS_UNKNOWN : status;
1093 }
1094
1095 /**
1096 * Calculate compound status
1097 */
1098 void DataCollectionTarget::calculateCompoundStatus(BOOL bForcedRecalc)
1099 {
1100 NetObj::calculateCompoundStatus(bForcedRecalc);
1101 }
1102
1103 /**
1104 * Returns last ping time
1105 */
1106 UINT32 DataCollectionTarget::getPingTime()
1107 {
1108 if ((time(NULL) - m_pingLastTimeStamp) > g_dwStatusPollingInterval)
1109 {
1110 updatePingData();
1111 DbgPrintf(7, _T("DataCollectionTarget::getPingTime: update ping time is required! Last ping time %d."), m_pingLastTimeStamp);
1112 }
1113 return m_pingTime;
1114 }
1115
1116 /**
1117 * Update ping data
1118 */
1119 void DataCollectionTarget::updatePingData()
1120 {
1121 m_pingLastTimeStamp = 0;
1122 m_pingTime = PING_TIME_TIMEOUT;
1123 }
1124
1125 /**
1126 * Enter maintenance mode
1127 */
1128 void DataCollectionTarget::enterMaintenanceMode()
1129 {
1130 DbgPrintf(4, _T("Entering maintenance mode for %s [%d]"), m_name, m_id);
1131 UINT64 eventId = PostEvent2(EVENT_MAINTENANCE_MODE_ENTERED, m_id, NULL);
1132 lockProperties();
1133 m_maintenanceMode = true;
1134 m_maintenanceEventId = eventId;
1135 setModified(MODIFY_COMMON_PROPERTIES);
1136 unlockProperties();
1137 }
1138
1139 /**
1140 * Leave maintenance mode
1141 */
1142 void DataCollectionTarget::leaveMaintenanceMode()
1143 {
1144 DbgPrintf(4, _T("Leaving maintenance mode for %s [%d]"), m_name, m_id);
1145 PostEvent(EVENT_MAINTENANCE_MODE_LEFT, m_id, NULL);
1146 lockProperties();
1147 m_maintenanceMode = false;
1148 m_maintenanceEventId = 0;
1149 setModified(MODIFY_COMMON_PROPERTIES);
1150 unlockProperties();
1151 }
1152
1153 /**
1154 * Update cache size for given data collection item
1155 */
1156 void DataCollectionTarget::updateDCItemCacheSize(UINT32 dciId, UINT32 conditionId)
1157 {
1158 lockDciAccess(false);
1159 DCObject *dci = getDCObjectById(dciId, false);
1160 if ((dci != NULL) && (dci->getType() == DCO_TYPE_ITEM))
1161 {
1162 ((DCItem *)dci)->updateCacheSize(conditionId);
1163 }
1164 unlockDciAccess();
1165 }
1166
1167 /**
1168 * Returns true if object is data collection target
1169 */
1170 bool DataCollectionTarget::isDataCollectionTarget()
1171 {
1172 return true;
1173 }
1174
1175 /**
1176 * Add data collection element to proxy info structure
1177 */
1178 void DataCollectionTarget::addProxyDataCollectionElement(ProxyInfo *info, const DCObject *dco)
1179 {
1180 info->msg->setField(info->fieldId++, dco->getId());
1181 info->msg->setField(info->fieldId++, (INT16)dco->getType());
1182 info->msg->setField(info->fieldId++, (INT16)dco->getDataSource());
1183 info->msg->setField(info->fieldId++, dco->getName());
1184 info->msg->setField(info->fieldId++, (INT32)dco->getEffectivePollingInterval());
1185 info->msg->setFieldFromTime(info->fieldId++, dco->getLastPollTime());
1186 info->msg->setField(info->fieldId++, m_guid);
1187 info->msg->setField(info->fieldId++, dco->getSnmpPort());
1188 if (dco->getType() == DCO_TYPE_ITEM)
1189 info->msg->setField(info->fieldId++, ((DCItem *)dco)->getSnmpRawValueType());
1190 else
1191 info->msg->setField(info->fieldId++, (INT16)0);
1192 info->fieldId += 1;
1193 info->count++;
1194 }
1195
1196 /**
1197 * Add SNMP target to proxy info structure
1198 */
1199 void DataCollectionTarget::addProxySnmpTarget(ProxyInfo *info, const Node *node)
1200 {
1201 info->msg->setField(info->nodeInfoFieldId++, m_guid);
1202 info->msg->setField(info->nodeInfoFieldId++, node->getIpAddress());
1203 info->msg->setField(info->nodeInfoFieldId++, node->getSNMPVersion());
1204 info->msg->setField(info->nodeInfoFieldId++, node->getSNMPPort());
1205 SNMP_SecurityContext *snmpSecurity = node->getSnmpSecurityContext();
1206 info->msg->setField(info->nodeInfoFieldId++, (INT16)snmpSecurity->getAuthMethod());
1207 info->msg->setField(info->nodeInfoFieldId++, (INT16)snmpSecurity->getPrivMethod());
1208 info->msg->setFieldFromMBString(info->nodeInfoFieldId++, snmpSecurity->getUser());
1209 info->msg->setFieldFromMBString(info->nodeInfoFieldId++, snmpSecurity->getAuthPassword());
1210 info->msg->setFieldFromMBString(info->nodeInfoFieldId++, snmpSecurity->getPrivPassword());
1211 delete snmpSecurity;
1212 info->nodeInfoFieldId += 41;
1213 info->nodeInfoCount++;
1214 }
1215
1216 /**
1217 * Collect info for SNMP proxy and DCI source (proxy) nodes
1218 * Default implementation adds only agent based DCIs with source node set to requesting node
1219 */
1220 void DataCollectionTarget::collectProxyInfo(ProxyInfo *info)
1221 {
1222 lockDciAccess(false);
1223 for(int i = 0; i < m_dcObjects->size(); i++)
1224 {
1225 DCObject *dco = m_dcObjects->get(i);
1226 if (dco->getStatus() == ITEM_STATUS_DISABLED)
1227 continue;
1228
1229 if ((dco->getDataSource() == DS_NATIVE_AGENT) && (dco->getSourceNode() == info->proxyId) &&
1230 dco->hasValue() && (dco->getAgentCacheMode() == AGENT_CACHE_ON))
1231 {
1232 addProxyDataCollectionElement(info, dco);
1233 }
1234 }
1235 unlockDciAccess();
1236 }
1237
1238 /**
1239 * Callback for colecting proxied SNMP DCIs
1240 */
1241 void DataCollectionTarget::collectProxyInfoCallback(NetObj *object, void *data)
1242 {
1243 ((DataCollectionTarget *)object)->collectProxyInfo((ProxyInfo *)data);
1244 }
1245
1246 /**
1247 * Get effective source node for given data collection object
1248 */
1249 UINT32 DataCollectionTarget::getEffectiveSourceNode(DCObject *dco)
1250 {
1251 return dco->getSourceNode();
1252 }
1253
1254 /**
1255 * Filter for selecting templates from objects
1256 */
1257 static bool TemplateSelectionFilter(NetObj *object, void *userData)
1258 {
1259 return (object->getObjectClass() == OBJECT_TEMPLATE) && !object->isDeleted() && ((Template *)object)->isAutoApplyEnabled();
1260 }
1261
1262 /**
1263 * Apply user templates
1264 */
1265 void DataCollectionTarget::applyUserTemplates()
1266 {
1267 if (IsShutdownInProgress())
1268 return;
1269
1270 ObjectArray<NetObj> *templates = g_idxObjectById.getObjects(true, TemplateSelectionFilter);
1271 for(int i = 0; i < templates->size(); i++)
1272 {
1273 Template *pTemplate = (Template *)templates->get(i);
1274 AutoBindDecision decision = pTemplate->isApplicable(this);
1275 if (decision == AutoBindDecision_Bind)
1276 {
1277 if (!pTemplate->isChild(m_id))
1278 {
1279 DbgPrintf(4, _T("DataCollectionTarget::applyUserTemplates(): applying template %d \"%s\" to object %d \"%s\""),
1280 pTemplate->getId(), pTemplate->getName(), m_id, m_name);
1281 pTemplate->applyToTarget(this);
1282 PostEvent(EVENT_TEMPLATE_AUTOAPPLY, g_dwMgmtNode, "isis", m_id, m_name, pTemplate->getId(), pTemplate->getName());
1283 }
1284 }
1285 else if (decision == AutoBindDecision_Unbind)
1286 {
1287 if (pTemplate->isAutoRemoveEnabled() && pTemplate->isChild(m_id))
1288 {
1289 DbgPrintf(4, _T("DataCollectionTarget::applyUserTemplates(): removing template %d \"%s\" from object %d \"%s\""),
1290 pTemplate->getId(), pTemplate->getName(), m_id, m_name);
1291 pTemplate->deleteChild(this);
1292 deleteParent(pTemplate);
1293 pTemplate->queueRemoveFromTarget(m_id, true);
1294 PostEvent(EVENT_TEMPLATE_AUTOREMOVE, g_dwMgmtNode, "isis", m_id, m_name, pTemplate->getId(), pTemplate->getName());
1295 }
1296 }
1297 pTemplate->decRefCount();
1298 }
1299 delete templates;
1300 }
1301
1302 /**
1303 * Filter for selecting containers from objects
1304 */
1305 static bool ContainerSelectionFilter(NetObj *object, void *userData)
1306 {
1307 return (object->getObjectClass() == OBJECT_CONTAINER) && !object->isDeleted() && ((Container *)object)->isAutoBindEnabled();
1308 }
1309
1310 /**
1311 * Update container membership
1312 */
1313 void DataCollectionTarget::updateContainerMembership()
1314 {
1315 if (IsShutdownInProgress())
1316 return;
1317
1318 ObjectArray<NetObj> *containers = g_idxObjectById.getObjects(true, ContainerSelectionFilter);
1319 for(int i = 0; i < containers->size(); i++)
1320 {
1321 Container *pContainer = (Container *)containers->get(i);
1322 AutoBindDecision decision = pContainer->isSuitableForObject(this);
1323 if (decision == AutoBindDecision_Bind)
1324 {
1325 if (!pContainer->isChild(m_id))
1326 {
1327 DbgPrintf(4, _T("DataCollectionTarget::updateContainerMembership(): binding object %d \"%s\" to container %d \"%s\""),
1328 m_id, m_name, pContainer->getId(), pContainer->getName());
1329 pContainer->addChild(this);
1330 addParent(pContainer);
1331 PostEvent(EVENT_CONTAINER_AUTOBIND, g_dwMgmtNode, "isis", m_id, m_name, pContainer->getId(), pContainer->getName());
1332 pContainer->calculateCompoundStatus();
1333 }
1334 }
1335 else if (decision == AutoBindDecision_Unbind)
1336 {
1337 if (pContainer->isAutoUnbindEnabled() && pContainer->isChild(m_id))
1338 {
1339 DbgPrintf(4, _T("DataCollectionTarget::updateContainerMembership(): removing object %d \"%s\" from container %d \"%s\""),
1340 m_id, m_name, pContainer->getId(), pContainer->getName());
1341 pContainer->deleteChild(this);
1342 deleteParent(pContainer);
1343 PostEvent(EVENT_CONTAINER_AUTOUNBIND, g_dwMgmtNode, "isis", m_id, m_name, pContainer->getId(), pContainer->getName());
1344 pContainer->calculateCompoundStatus();
1345 }
1346 }
1347 pContainer->decRefCount();
1348 }
1349 delete containers;
1350 }
1351
1352 /**
1353 * Serialize object to JSON
1354 */
1355 json_t *DataCollectionTarget::toJson()
1356 {
1357 json_t *root = Template::toJson();
1358 json_object_set_new(root, "pingTime", json_integer(m_pingTime));
1359 json_object_set_new(root, "pingLastTimeStamp", json_integer(m_pingLastTimeStamp));
1360 return root;
1361 }
1362
1363 /**
1364 * Entry point for status poll worker thread
1365 */
1366 void DataCollectionTarget::statusPollWorkerEntry(PollerInfo *poller)
1367 {
1368 statusPollWorkerEntry(poller, NULL, 0);
1369 }
1370
1371 /**
1372 * Entry point for status poll worker thread
1373 */
1374 void DataCollectionTarget::statusPollWorkerEntry(PollerInfo *poller, ClientSession *session, UINT32 rqId)
1375 {
1376 poller->startExecution();
1377 statusPoll(poller, session, rqId);
1378 delete poller;
1379 }
1380
1381 /**
1382 * Entry point for second level status poll (called by parent object)
1383 */
1384 void DataCollectionTarget::statusPollPollerEntry(PollerInfo *poller, ClientSession *session, UINT32 rqId)
1385 {
1386 poller->setStatus(_T("child poll"));
1387 statusPoll(poller, session, rqId);
1388 }
1389
1390 /**
1391 * Perform status poll on this data collection target. Default implementation do nothing.
1392 */
1393 void DataCollectionTarget::statusPoll(PollerInfo *poller, ClientSession *session, UINT32 rqId)
1394 {
1395 }
1396
1397 /**
1398 * Entry point for configuration poll worker thread
1399 */
1400 void DataCollectionTarget::configurationPollWorkerEntry(PollerInfo *poller)
1401 {
1402 configurationPollWorkerEntry(poller, NULL, 0);
1403 }
1404
1405 /**
1406 * Entry point for configuration poll worker thread
1407 */
1408 void DataCollectionTarget::configurationPollWorkerEntry(PollerInfo *poller, ClientSession *session, UINT32 rqId)
1409 {
1410 poller->startExecution();
1411 ObjectTransactionStart();
1412 configurationPoll(poller, session, rqId);
1413 ObjectTransactionEnd();
1414 delete poller;
1415 }
1416
1417 /**
1418 * Perform configuration poll on this data collection target. Default implementation do nothing.
1419 */
1420 void DataCollectionTarget::configurationPoll(PollerInfo *poller, ClientSession *session, UINT32 rqId)
1421 {
1422 }
1423
1424 /**
1425 * Entry point for instance discovery poll worker thread
1426 */
1427 void DataCollectionTarget::instanceDiscoveryPollWorkerEntry(PollerInfo *poller)
1428 {
1429 instanceDiscoveryPollWorkerEntry(poller, NULL, 0);
1430 }
1431
1432 /**
1433 * Entry point for instance discovery poll worker thread
1434 */
1435 void DataCollectionTarget::instanceDiscoveryPollWorkerEntry(PollerInfo *poller, ClientSession *session, UINT32 requestId)
1436 {
1437 poller->startExecution();
1438 ObjectTransactionStart();
1439 instanceDiscoveryPoll(poller, session, requestId);
1440 ObjectTransactionEnd();
1441 delete poller;
1442 }
1443
1444 /**
1445 * Perform instance discovery poll on data collection target
1446 */
1447 void DataCollectionTarget::instanceDiscoveryPoll(PollerInfo *poller, ClientSession *session, UINT32 requestId)
1448 {
1449 if (m_runtimeFlags & DCDF_DELETE_IN_PROGRESS)
1450 {
1451 if (requestId == 0)
1452 m_runtimeFlags &= ~DCDF_QUEUED_FOR_INSTANCE_POLL;
1453 return;
1454 }
1455
1456 if (IsShutdownInProgress())
1457 return;
1458
1459 poller->setStatus(_T("wait for lock"));
1460 pollerLock();
1461
1462 if (IsShutdownInProgress())
1463 {
1464 pollerUnlock();
1465 return;
1466 }
1467
1468 m_pollRequestor = session;
1469 sendPollerMsg(requestId, _T("Starting instance discovery poll for %s %s\r\n"), getObjectClassName(), m_name);
1470 DbgPrintf(4, _T("Starting instance discovery poll for %s %s (ID: %d)"), getObjectClassName(), m_name, m_id);
1471
1472 // Check if DataCollectionTarget is marked as unreachable
1473 if (!(m_state & DCSF_UNREACHABLE))
1474 {
1475 poller->setStatus(_T("instance discovery"));
1476 doInstanceDiscovery(requestId);
1477
1478 // Execute hook script
1479 poller->setStatus(_T("hook"));
1480 executeHookScript(_T("InstancePoll"));
1481 }
1482 else
1483 {
1484 sendPollerMsg(requestId, POLLER_WARNING _T("%s is marked as unreachable, instance discovery poll aborted\r\n"), getObjectClassName());
1485 DbgPrintf(4, _T("%s is marked as unreachable, instance discovery poll aborted"), getObjectClassName());
1486 }
1487
1488 m_lastInstancePoll = time(NULL);
1489
1490 // Finish instance discovery poll
1491 poller->setStatus(_T("cleanup"));
1492 if (requestId == 0)
1493 m_runtimeFlags &= ~DCDF_QUEUED_FOR_INSTANCE_POLL;
1494 pollerUnlock();
1495 DbgPrintf(4, _T("Finished instance discovery poll for %s %s (ID: %d)"), getObjectClassName(), m_name, m_id);
1496 }
1497
1498 /**
1499 * Get list of instances for given data collection object. Default implementation always returns NULL.
1500 */
1501 StringMap *DataCollectionTarget::getInstanceList(DCObject *dco)
1502 {
1503 return NULL;
1504 }
1505
1506 /**
1507 * Do instance discovery
1508 */
1509 void DataCollectionTarget::doInstanceDiscovery(UINT32 requestId)
1510 {
1511 sendPollerMsg(requestId, _T("Running DCI instance discovery\r\n"));
1512
1513 // collect instance discovery DCIs
1514 ObjectArray<DCObject> rootObjects;
1515 lockDciAccess(false);
1516 for(int i = 0; i < m_dcObjects->size(); i++)
1517 {
1518 DCObject *object = m_dcObjects->get(i);
1519 if (object->getInstanceDiscoveryMethod() != IDM_NONE)
1520 {
1521 object->setBusyFlag();
1522 rootObjects.add(object);
1523 }
1524 }
1525 unlockDciAccess();
1526
1527 // process instance discovery DCIs
1528 // it should be done that way to prevent DCI list lock for long time
1529 bool changed = false;
1530 for(int i = 0; i < rootObjects.size(); i++)
1531 {
1532 DCObject *object = rootObjects.get(i);
1533 DbgPrintf(5, _T("DataCollectionTarget::doInstanceDiscovery(%s [%u]): Updating instances for instance discovery DCO %s [%d]"),
1534 m_name, m_id, object->getName(), object->getId());
1535 sendPollerMsg(requestId, _T(" Updating instances for %s [%d]\r\n"), object->getName(), object->getId());
1536 StringMap *instances = getInstanceList(object);
1537 if (instances != NULL)
1538 {
1539 DbgPrintf(5, _T("DataCollectionTarget::doInstanceDiscovery(%s [%u]): read %d values"), m_name, m_id, instances->size());
1540 object->filterInstanceList(instances);
1541 if (updateInstances(object, instances, requestId))
1542 changed = true;
1543 delete instances;
1544 }
1545 else
1546 {
1547 DbgPrintf(5, _T("DataCollectionTarget::doInstanceDiscovery(%s [%u]): failed to get instance list for DCO %s [%d]"),
1548 m_name, m_id, object->getName(), object->getId());
1549 sendPollerMsg(requestId, POLLER_ERROR _T(" Failed to get instance list\r\n"));
1550 }
1551 object->clearBusyFlag();
1552 }
1553
1554 if (changed)
1555 onDataCollectionChange();
1556 }
1557
1558 /**
1559 * Callback for finding instance
1560 */
1561 static EnumerationCallbackResult FindInstanceCallback(const TCHAR *key, const void *value, void *data)
1562 {
1563 return !_tcscmp((const TCHAR *)data, key) ? _STOP : _CONTINUE;
1564 }
1565
1566 /**
1567 * Data for CreateInstanceDCI
1568 */
1569 struct CreateInstanceDCOData
1570 {
1571 DCObject *root;
1572 DataCollectionTarget *object;
1573 UINT32 requestId;
1574 };
1575
1576 /**
1577 * Callback for creating instance DCIs
1578 */
1579 static EnumerationCallbackResult CreateInstanceDCI(const TCHAR *key, const void *value, void *data)
1580 {
1581 DataCollectionTarget *object = ((CreateInstanceDCOData *)data)->object;
1582 DCObject *root = ((CreateInstanceDCOData *)data)->root;
1583
1584 DbgPrintf(5, _T("DataCollectionTarget::updateInstances(%s [%u], %s [%u]): creating new DCO for instance \"%s\""),
1585 object->getName(), object->getId(), root->getName(), root->getId(), key);
1586 object->sendPollerMsg(((CreateInstanceDCOData *)data)->requestId, _T(" Creating new DCO for instance \"%s\"\r\n"), key);
1587
1588 DCObject *dco = root->clone();
1589
1590 dco->setTemplateId(object->getId(), root->getId());
1591 dco->setInstance((const TCHAR *)value);
1592 dco->setInstanceDiscoveryMethod(IDM_NONE);
1593 dco->setInstanceDiscoveryData(key);
1594 dco->setInstanceFilter(NULL);
1595 dco->expandInstance();
1596 dco->changeBinding(CreateUniqueId(IDG_ITEM), object, FALSE);
1597 object->addDCObject(dco, true);
1598 return _CONTINUE;
1599 }
1600
1601 /**
1602 * Update instance DCIs created from instance discovery DCI
1603 */
1604 bool DataCollectionTarget::updateInstances(DCObject *root, StringMap *instances, UINT32 requestId)
1605 {
1606 bool changed = false;
1607
1608 lockDciAccess(true);
1609
1610 // Delete DCIs for missing instances and update existing
1611 IntegerArray<UINT32> deleteList;
1612 for(int i = 0; i < m_dcObjects->size(); i++)
1613 {
1614 DCObject *object = m_dcObjects->get(i);
1615 if ((object->getTemplateId() != m_id) || (object->getTemplateItemId() != root->getId()))
1616 continue;
1617
1618 const TCHAR *dcoInstance = object->getInstanceDiscoveryData();
1619 if (instances->forEach(FindInstanceCallback, (void *)dcoInstance) == _STOP)
1620 {
1621 // found, remove value from instances
1622 DbgPrintf(5, _T("DataCollectionTarget::updateInstances(%s [%u], %s [%u]): instance \"%s\" found"),
1623 m_name, m_id, root->getName(), root->getId(), dcoInstance);
1624 const TCHAR *name = instances->get(dcoInstance);
1625 if (_tcscmp(name, object->getInstance()))
1626 {
1627 object->setInstance(name);
1628 object->updateFromTemplate(root);
1629 changed = true;
1630 }
1631 instances->remove(dcoInstance);
1632 }
1633 else
1634 {
1635 // not found, delete DCO
1636 DbgPrintf(5, _T("DataCollectionTarget::updateInstances(%s [%u], %s [%u]): instance \"%s\" not found, instance DCO will be deleted"),
1637 m_name, m_id, root->getName(), root->getId(), dcoInstance);
1638 sendPollerMsg(requestId, _T(" Existing instance \"%s\" not found and will be deleted\r\n"), dcoInstance);
1639 deleteList.add(object->getId());
1640 changed = true;
1641 }
1642 }
1643
1644 for(int i = 0; i < deleteList.size(); i++)
1645 deleteDCObject(deleteList.get(i), false);
1646
1647 // Create new instances
1648 if (instances->size() > 0)
1649 {
1650 CreateInstanceDCOData data;
1651 data.root = root;
1652 data.object = this;
1653 data.requestId = requestId;
1654 instances->forEach(CreateInstanceDCI, &data);
1655 changed = true;
1656 }
1657
1658 unlockDciAccess();
1659 return changed;
1660 }
1661
1662 /**
1663 * Execute hook script
1664 *
1665 * @param hookName hook name. Will find and excute script named Hook::hookName
1666 */
1667 void DataCollectionTarget::executeHookScript(const TCHAR *hookName)
1668 {
1669 TCHAR scriptName[MAX_PATH] = _T("Hook::");
1670 nx_strncpy(&scriptName[6], hookName, MAX_PATH - 6);
1671 NXSL_VM *vm = CreateServerScriptVM(scriptName);
1672 if (vm == NULL)
1673 {
1674 DbgPrintf(7, _T("DataCollectionTarget::executeHookScript(%s [%u]): hook script \"%s\" not found"), m_name, m_id, scriptName);
1675 return;
1676 }
1677
1678 vm->setGlobalVariable(_T("$object"), createNXSLObject());
1679 if (!vm->run())
1680 {
1681 DbgPrintf(4, _T("DataCollectionTarget::executeHookScript(%s [%u]): hook script \"%s\" execution error: %s"),
1682 m_name, m_id, scriptName, vm->getErrorText());
1683 }
1684 delete vm;
1685 }