Minor bugfixes
[public/netxms.git] / src / server / core / dctarget.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2016 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 * Default constructor
27 */
28 DataCollectionTarget::DataCollectionTarget() : Template()
29 {
30 m_pingLastTimeStamp = 0;
31 m_pingTime = PING_TIME_TIMEOUT;
32 }
33
34 /**
35 * Constructor for creating new data collection capable objects
36 */
37 DataCollectionTarget::DataCollectionTarget(const TCHAR *name) : Template(name)
38 {
39 m_pingLastTimeStamp = 0;
40 m_pingTime = PING_TIME_TIMEOUT;
41 }
42
43 /**
44 * Destructor
45 */
46 DataCollectionTarget::~DataCollectionTarget()
47 {
48 m_pingLastTimeStamp = 0;
49 m_pingTime = PING_TIME_TIMEOUT;
50 }
51
52 /**
53 * Delete object from database
54 */
55 bool DataCollectionTarget::deleteFromDatabase(DB_HANDLE hdb)
56 {
57 bool success = Template::deleteFromDatabase(hdb);
58 if (success)
59 {
60 TCHAR query[256];
61 _sntprintf(query, 256, _T("DROP TABLE idata_%d"), (int)m_id);
62 QueueSQLRequest(query);
63
64 _sntprintf(query, 256, _T("DROP TABLE tdata_%d"), (int)m_id);
65 QueueSQLRequest(query);
66 }
67 return success;
68 }
69
70 /**
71 * Create NXCP message with object's data
72 */
73 void DataCollectionTarget::fillMessageInternal(NXCPMessage *msg)
74 {
75 Template::fillMessageInternal(msg);
76 }
77
78 /**
79 * Create NXCP message with object's data - stage 2
80 */
81 void DataCollectionTarget::fillMessageInternalStage2(NXCPMessage *msg)
82 {
83 Template::fillMessageInternalStage2(msg);
84
85 // Sent all DCIs marked for display on overview page or in tooltips
86 UINT32 fieldIdOverview = VID_OVERVIEW_DCI_LIST_BASE;
87 UINT32 countOverview = 0;
88 UINT32 fieldIdTooltip = VID_TOOLTIP_DCI_LIST_BASE;
89 UINT32 countTooltip = 0;
90 lockDciAccess(false);
91 for(int i = 0; i < m_dcObjects->size(); i++)
92 {
93 DCObject *dci = m_dcObjects->get(i);
94 if ((dci->getType() == DCO_TYPE_ITEM) &&
95 (dci->getStatus() == ITEM_STATUS_ACTIVE) &&
96 (((DCItem *)dci)->getInstanceDiscoveryMethod() == IDM_NONE))
97 {
98 if (dci->isShowInObjectOverview())
99 {
100 countOverview++;
101 ((DCItem *)dci)->fillLastValueMessage(msg, fieldIdOverview);
102 fieldIdOverview += 50;
103 }
104 if (dci->isShowOnObjectTooltip())
105 {
106 countTooltip++;
107 ((DCItem *)dci)->fillLastValueMessage(msg, fieldIdTooltip);
108 fieldIdTooltip += 50;
109 }
110 }
111 }
112 unlockDciAccess();
113 msg->setField(VID_OVERVIEW_DCI_COUNT, countOverview);
114 msg->setField(VID_TOOLTIP_DCI_COUNT, countTooltip);
115 }
116
117 /**
118 * Modify object from message
119 */
120 UINT32 DataCollectionTarget::modifyFromMessageInternal(NXCPMessage *pRequest)
121 {
122 return Template::modifyFromMessageInternal(pRequest);
123 }
124
125 /**
126 * Update cache for all DCI's
127 */
128 void DataCollectionTarget::updateDciCache()
129 {
130 lockDciAccess(false);
131 for(int i = 0; i < m_dcObjects->size(); i++)
132 {
133 if (m_dcObjects->get(i)->getType() == DCO_TYPE_ITEM)
134 {
135 ((DCItem *)m_dcObjects->get(i))->updateCacheSize();
136 }
137 }
138 unlockDciAccess();
139 }
140
141 /**
142 * Clean expired DCI data
143 */
144 void DataCollectionTarget::cleanDCIData(DB_HANDLE hdb)
145 {
146 String queryItems = _T("DELETE FROM idata_");
147 queryItems.append(m_id);
148 queryItems.append(_T(" WHERE "));
149
150 String queryTables = _T("DELETE FROM tdata_");
151 queryTables.append(m_id);
152 queryTables.append(_T(" WHERE "));
153
154 int itemCount = 0;
155 int tableCount = 0;
156 time_t now = time(NULL);
157
158 lockDciAccess(false);
159 for(int i = 0; i < m_dcObjects->size(); i++)
160 {
161 DCObject *o = m_dcObjects->get(i);
162 if (o->getType() == DCO_TYPE_ITEM)
163 {
164 if (itemCount > 0)
165 queryItems.append(_T(" OR "));
166 queryItems.append(_T("(item_id="));
167 queryItems.append(o->getId());
168 queryItems.append(_T(" AND idata_timestamp<"));
169 queryItems.append((INT64)(now - o->getEffectiveRetentionTime() * 86400));
170 queryItems.append(_T(')'));
171 itemCount++;
172 }
173 else if (o->getType() == DCO_TYPE_TABLE)
174 {
175 if (tableCount > 0)
176 queryTables.append(_T(" OR "));
177 queryTables.append(_T("(item_id="));
178 queryTables.append(o->getId());
179 queryTables.append(_T(" AND tdata_timestamp<"));
180 queryTables.append((INT64)(now - o->getEffectiveRetentionTime() * 86400));
181 queryTables.append(_T(')'));
182 tableCount++;
183 }
184 }
185 unlockDciAccess();
186
187 if (itemCount > 0)
188 {
189 DbgPrintf(6, _T("DataCollectionTarget::cleanDCIData(%s [%d]): running query \"%s\""), m_name, m_id, (const TCHAR *)queryItems);
190 DBQuery(hdb, queryItems);
191 }
192
193 if (tableCount > 0)
194 {
195 DbgPrintf(6, _T("DataCollectionTarget::cleanDCIData(%s [%d]): running query \"%s\""), m_name, m_id, (const TCHAR *)queryTables);
196 DBQuery(hdb, queryTables);
197 }
198 }
199
200 /**
201 * Get last collected values of given table
202 */
203 UINT32 DataCollectionTarget::getTableLastValues(UINT32 dciId, NXCPMessage *msg)
204 {
205 UINT32 rcc = RCC_INVALID_DCI_ID;
206
207 lockDciAccess(false);
208
209 for(int i = 0; i < m_dcObjects->size(); i++)
210 {
211 DCObject *object = m_dcObjects->get(i);
212 if ((object->getId() == dciId) && (object->getType() == DCO_TYPE_TABLE))
213 {
214 ((DCTable *)object)->fillLastValueMessage(msg);
215 rcc = RCC_SUCCESS;
216 break;
217 }
218 }
219
220 unlockDciAccess();
221 return rcc;
222 }
223
224 /**
225 * Apply DCI from template
226 * dcObject passed to this method should be a template's DCI
227 */
228 bool DataCollectionTarget::applyTemplateItem(UINT32 dwTemplateId, DCObject *dcObject)
229 {
230 bool bResult = true;
231
232 lockDciAccess(true); // write lock
233
234 DbgPrintf(5, _T("Applying DCO \"%s\" to target \"%s\""), dcObject->getName(), m_name);
235
236 // Check if that template item exists
237 int i;
238 for(i = 0; i < m_dcObjects->size(); i++)
239 if ((m_dcObjects->get(i)->getTemplateId() == dwTemplateId) &&
240 (m_dcObjects->get(i)->getTemplateItemId() == dcObject->getId()))
241 break; // Item with specified id already exist
242
243 if (i == m_dcObjects->size())
244 {
245 // New item from template, just add it
246 DCObject *newObject;
247 switch(dcObject->getType())
248 {
249 case DCO_TYPE_ITEM:
250 newObject = new DCItem((DCItem *)dcObject);
251 break;
252 case DCO_TYPE_TABLE:
253 newObject = new DCTable((DCTable *)dcObject);
254 break;
255 default:
256 newObject = NULL;
257 break;
258 }
259 if (newObject != NULL)
260 {
261 newObject->setTemplateId(dwTemplateId, dcObject->getId());
262 newObject->changeBinding(CreateUniqueId(IDG_ITEM), this, TRUE);
263 bResult = addDCObject(newObject, true);
264 }
265 }
266 else
267 {
268 // Update existing item unless it is disabled
269 DCObject *curr = m_dcObjects->get(i);
270 if ((curr->getStatus() != ITEM_STATUS_DISABLED) || (g_flags & AF_APPLY_TO_DISABLED_DCI_FROM_TEMPLATE))
271 {
272 curr->updateFromTemplate(dcObject);
273 DbgPrintf(9, _T("DCO \"%s\" NOT disabled or ApplyDCIFromTemplateToDisabledDCI set, updated (%d)"),
274 dcObject->getName(), curr->getStatus());
275 if ((curr->getType() == DCO_TYPE_ITEM) && (((DCItem *)curr)->getInstanceDiscoveryMethod() != IDM_NONE))
276 {
277 updateInstanceDiscoveryItems((DCItem *)curr);
278 }
279 }
280 else
281 {
282 DbgPrintf(9, _T("DCO \"%s\" is disabled and ApplyDCIFromTemplateToDisabledDCI not set, no update (%d)"),
283 dcObject->getName(), curr->getStatus());
284 }
285 }
286
287 unlockDciAccess();
288
289 if (bResult)
290 {
291 lockProperties();
292 m_isModified = true;
293 unlockProperties();
294 }
295 return bResult;
296 }
297
298 /**
299 * Clean deleted template items from target's DCI list
300 * Arguments is template id and list of valid template item ids.
301 * all items related to given template and not presented in list should be deleted.
302 */
303 void DataCollectionTarget::cleanDeletedTemplateItems(UINT32 dwTemplateId, UINT32 dwNumItems, UINT32 *pdwItemList)
304 {
305 UINT32 i, j, dwNumDeleted, *pdwDeleteList;
306
307 lockDciAccess(true); // write lock
308
309 pdwDeleteList = (UINT32 *)malloc(sizeof(UINT32) * m_dcObjects->size());
310 dwNumDeleted = 0;
311
312 for(i = 0; i < (UINT32)m_dcObjects->size(); i++)
313 if (m_dcObjects->get(i)->getTemplateId() == dwTemplateId)
314 {
315 for(j = 0; j < dwNumItems; j++)
316 if (m_dcObjects->get(i)->getTemplateItemId() == pdwItemList[j])
317 break;
318
319 // Delete DCI if it's not in list
320 if (j == dwNumItems)
321 pdwDeleteList[dwNumDeleted++] = m_dcObjects->get(i)->getId();
322 }
323
324 for(i = 0; i < dwNumDeleted; i++)
325 deleteDCObject(pdwDeleteList[i], false);
326
327 unlockDciAccess();
328 free(pdwDeleteList);
329 }
330
331 /**
332 * Unbind data collection target from template, i.e either remove DCI
333 * association with template or remove these DCIs at all
334 */
335 void DataCollectionTarget::unbindFromTemplate(UINT32 dwTemplateId, bool removeDCI)
336 {
337 if (removeDCI)
338 {
339 lockDciAccess(true); // write lock
340
341 UINT32 *deleteList = (UINT32 *)malloc(sizeof(UINT32) * m_dcObjects->size());
342 int numDeleted = 0;
343
344 int i;
345 for(i = 0; i < m_dcObjects->size(); i++)
346 if (m_dcObjects->get(i)->getTemplateId() == dwTemplateId)
347 {
348 deleteList[numDeleted++] = m_dcObjects->get(i)->getId();
349 }
350
351 for(i = 0; i < numDeleted; i++)
352 deleteDCObject(deleteList[i], false);
353
354 unlockDciAccess();
355 free(deleteList);
356 }
357 else
358 {
359 lockDciAccess(false);
360
361 for(int i = 0; i < m_dcObjects->size(); i++)
362 if (m_dcObjects->get(i)->getTemplateId() == dwTemplateId)
363 {
364 m_dcObjects->get(i)->setTemplateId(0, 0);
365 }
366
367 unlockDciAccess();
368 }
369 }
370
371 /**
372 * Get list of DCIs to be shown on performance tab
373 */
374 UINT32 DataCollectionTarget::getPerfTabDCIList(NXCPMessage *pMsg)
375 {
376 lockDciAccess(false);
377
378 UINT32 dwId = VID_SYSDCI_LIST_BASE, dwCount = 0;
379 for(int i = 0; i < m_dcObjects->size(); i++)
380 {
381 DCObject *object = m_dcObjects->get(i);
382 if ((object->getPerfTabSettings() != NULL) &&
383 object->hasValue() &&
384 (object->getStatus() == ITEM_STATUS_ACTIVE) &&
385 object->matchClusterResource())
386 {
387 pMsg->setField(dwId++, object->getId());
388 pMsg->setField(dwId++, object->getDescription());
389 pMsg->setField(dwId++, (WORD)object->getStatus());
390 pMsg->setField(dwId++, object->getPerfTabSettings());
391 pMsg->setField(dwId++, (WORD)object->getType());
392 pMsg->setField(dwId++, object->getTemplateItemId());
393 if (object->getType() == DCO_TYPE_ITEM)
394 {
395 pMsg->setField(dwId++, ((DCItem *)object)->getInstance());
396 if ((object->getTemplateItemId() != 0) && (object->getTemplateId() == m_id))
397 {
398 // DCI created via instance discovery - send ID of root template item
399 // to allow UI to resolve double template case
400 // (template -> instance discovery item on node -> actual item on node)
401 DCObject *src = getDCObjectById(object->getTemplateItemId(), false);
402 pMsg->setField(dwId++, (src != NULL) ? src->getTemplateItemId() : 0);
403 dwId += 2;
404 }
405 else
406 {
407 dwId += 3;
408 }
409 }
410 else
411 {
412 dwId += 4;
413 }
414 dwCount++;
415 }
416 }
417 pMsg->setField(VID_NUM_ITEMS, dwCount);
418
419 unlockDciAccess();
420 return RCC_SUCCESS;
421 }
422
423 /**
424 * Get threshold violation summary into NXCP message
425 */
426 UINT32 DataCollectionTarget::getThresholdSummary(NXCPMessage *msg, UINT32 baseId)
427 {
428 UINT32 varId = baseId;
429
430 msg->setField(varId++, m_id);
431 UINT32 countId = varId++;
432 UINT32 count = 0;
433
434 lockDciAccess(false);
435 for(int i = 0; i < m_dcObjects->size(); i++)
436 {
437 DCObject *object = m_dcObjects->get(i);
438 if (object->hasValue() && (object->getType() == DCO_TYPE_ITEM) && (object->getStatus() == ITEM_STATUS_ACTIVE))
439 {
440 if (((DCItem *)object)->hasActiveThreshold())
441 {
442 ((DCItem *)object)->fillLastValueMessage(msg, varId);
443 varId += 50;
444 count++;
445 }
446 }
447 }
448 unlockDciAccess();
449 msg->setField(countId, count);
450 return varId;
451 }
452
453 /**
454 * Process new DCI value
455 */
456 bool DataCollectionTarget::processNewDCValue(DCObject *dco, time_t currTime, const void *value)
457 {
458 bool updateStatus;
459 bool result = dco->processNewValue(currTime, value, &updateStatus);
460 if (updateStatus)
461 {
462 calculateCompoundStatus(FALSE);
463 }
464 return result;
465 }
466
467 /**
468 * Check if data collection is disabled
469 */
470 bool DataCollectionTarget::isDataCollectionDisabled()
471 {
472 return false;
473 }
474
475 /**
476 * Put items which requires polling into the queue
477 */
478 void DataCollectionTarget::queueItemsForPolling(Queue *pollerQueue)
479 {
480 if ((m_status == STATUS_UNMANAGED) || isDataCollectionDisabled() || m_isDeleted)
481 return; // Do not collect data for unmanaged objects or if data collection is disabled
482
483 time_t currTime = time(NULL);
484
485 lockDciAccess(false);
486 for(int i = 0; i < m_dcObjects->size(); i++)
487 {
488 DCObject *object = m_dcObjects->get(i);
489 if (object->isReadyForPolling(currTime))
490 {
491 object->setBusyFlag();
492 incRefCount(); // Increment reference count for each queued DCI
493 pollerQueue->put(object);
494 nxlog_debug(8, _T("DataCollectionTarget(%s)->QueueItemsForPolling(): item %d \"%s\" added to queue"), m_name, object->getId(), object->getName());
495 }
496 }
497 unlockDciAccess();
498 }
499
500 /**
501 * Get object from parameter
502 */
503 NetObj *DataCollectionTarget::objectFromParameter(const TCHAR *param)
504 {
505 TCHAR *eptr, arg[256];
506 AgentGetParameterArg(param, 1, arg, 256);
507 UINT32 objectId = _tcstoul(arg, &eptr, 0);
508 if (*eptr != 0)
509 {
510 // Argument is object's name
511 objectId = 0;
512 }
513
514 // Find child object with requested ID or name
515 NetObj *object = NULL;
516 lockChildList(false);
517 for(int i = 0; i < m_childList->size(); i++)
518 {
519 NetObj *curr = m_childList->get(i);
520 if (((objectId == 0) && (!_tcsicmp(curr->getName(), arg))) ||
521 (objectId == curr->getId()))
522 {
523 object = curr;
524 break;
525 }
526 }
527 unlockChildList();
528 return object;
529 }
530
531 /**
532 * Get value for server's internal parameter
533 */
534 UINT32 DataCollectionTarget::getInternalItem(const TCHAR *param, size_t bufSize, TCHAR *buffer)
535 {
536 UINT32 dwError = DCE_SUCCESS;
537
538 if (!_tcsicmp(param, _T("Status")))
539 {
540 _sntprintf(buffer, bufSize, _T("%d"), m_status);
541 }
542 else if (!_tcsicmp(param, _T("Dummy")) || MatchString(_T("Dummy(*)"), param, FALSE))
543 {
544 _tcscpy(buffer, _T("0"));
545 }
546 else if (MatchString(_T("ChildStatus(*)"), param, FALSE))
547 {
548 NetObj *object = objectFromParameter(param);
549 if (object != NULL)
550 {
551 _sntprintf(buffer, bufSize, _T("%d"), object->getStatus());
552 }
553 else
554 {
555 dwError = DCE_NOT_SUPPORTED;
556 }
557 }
558 else if (MatchString(_T("ConditionStatus(*)"), param, FALSE))
559 {
560 TCHAR *pEnd, szArg[256];
561 UINT32 dwId;
562 NetObj *pObject = NULL;
563
564 AgentGetParameterArg(param, 1, szArg, 256);
565 dwId = _tcstoul(szArg, &pEnd, 0);
566 if (*pEnd == 0)
567 {
568 pObject = FindObjectById(dwId);
569 if (pObject != NULL)
570 if (pObject->getObjectClass() != OBJECT_CONDITION)
571 pObject = NULL;
572 }
573 else
574 {
575 // Argument is object's name
576 pObject = FindObjectByName(szArg, OBJECT_CONDITION);
577 }
578
579 if (pObject != NULL)
580 {
581 if (pObject->isTrustedNode(m_id))
582 {
583 _sntprintf(buffer, bufSize, _T("%d"), pObject->getStatus());
584 }
585 else
586 {
587 dwError = DCE_NOT_SUPPORTED;
588 }
589 }
590 else
591 {
592 dwError = DCE_NOT_SUPPORTED;
593 }
594 }
595 else
596 {
597 dwError = DCE_NOT_SUPPORTED;
598 }
599
600 return dwError;
601 }
602
603 /**
604 * Run data collection script. Returns pointer to NXSL VM after successful run and NULL on failure.
605 */
606 NXSL_VM *DataCollectionTarget::runDataCollectionScript(const TCHAR *param, DataCollectionTarget *targetObject)
607 {
608 TCHAR name[256];
609 nx_strncpy(name, param, 256);
610 Trim(name);
611
612 ObjectArray<NXSL_Value> args(16, 16, false);
613
614 // Can be in form parameter(arg1, arg2, ... argN)
615 TCHAR *p = _tcschr(name, _T('('));
616 if (p != NULL)
617 {
618 if (name[_tcslen(name) - 1] != _T(')'))
619 return NULL;
620 name[_tcslen(name) - 1] = 0;
621
622 if (!ParseValueList(&p, args))
623 {
624 // argument parsing error
625 args.clear();
626 return NULL;
627 }
628 }
629
630 NXSL_VM *vm = CreateServerScriptVM(name);
631 if (vm != NULL)
632 {
633 vm->setGlobalVariable(_T("$object"), createNXSLObject());
634 if (getObjectClass() == OBJECT_NODE)
635 {
636 vm->setGlobalVariable(_T("$node"), createNXSLObject());
637 }
638 vm->setGlobalVariable(_T("$isCluster"), new NXSL_Value((getObjectClass() == OBJECT_CLUSTER) ? 1 : 0));
639 if (targetObject != NULL)
640 {
641 vm->setGlobalVariable(_T("$targetObject"), targetObject->createNXSLObject());
642 }
643 if (!vm->run(&args))
644 {
645 DbgPrintf(4, _T("DataCollectionTarget(%s)->runDataCollectionScript(%s): Script execution error: %s"), m_name, param, vm->getErrorText());
646 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", name, vm->getErrorText(), m_id);
647 delete_and_null(vm);
648 }
649 }
650 else
651 {
652 args.setOwner(true);
653 }
654 nxlog_debug(7, _T("DataCollectionTarget(%s)->runDataCollectionScript(%s): %s"), m_name, param, (vm != NULL) ? _T("success") : _T("failure"));
655 return vm;
656 }
657
658 /**
659 * Get parameter value from NXSL script
660 */
661 UINT32 DataCollectionTarget::getScriptItem(const TCHAR *param, size_t bufSize, TCHAR *buffer, DataCollectionTarget *targetObject)
662 {
663 UINT32 rc = DCE_NOT_SUPPORTED;
664 NXSL_VM *vm = runDataCollectionScript(param, targetObject);
665 if (vm != NULL)
666 {
667 NXSL_Value *value = vm->getResult();
668 if (value->isNull())
669 {
670 // NULL value is an error indicator
671 rc = DCE_COLLECTION_ERROR;
672 }
673 else
674 {
675 const TCHAR *dciValue = value->getValueAsCString();
676 nx_strncpy(buffer, CHECK_NULL_EX(dciValue), bufSize);
677 rc = DCE_SUCCESS;
678 }
679 delete vm;
680 }
681 nxlog_debug(7, _T("DataCollectionTarget(%s)->getScriptItem(%s): rc=%d"), m_name, param, rc);
682 return rc;
683 }
684
685 /**
686 * Get list from library script
687 */
688 UINT32 DataCollectionTarget::getListFromScript(const TCHAR *param, StringList **list, DataCollectionTarget *targetObject)
689 {
690 UINT32 rc = DCE_NOT_SUPPORTED;
691 NXSL_VM *vm = runDataCollectionScript(param, targetObject);
692 if (vm != NULL)
693 {
694 rc = DCE_SUCCESS;
695 NXSL_Value *value = vm->getResult();
696 if (value->isArray())
697 {
698 *list = value->getValueAsArray()->toStringList();
699 }
700 else if (value->isString())
701 {
702 *list = new StringList;
703 (*list)->add(value->getValueAsCString());
704 }
705 else if (value->isNull())
706 {
707 rc = DCE_COLLECTION_ERROR;
708 }
709 else
710 {
711 *list = new StringList;
712 }
713 delete vm;
714 }
715 nxlog_debug(7, _T("DataCollectionTarget(%s)->getListFromScript(%s): rc=%d"), m_name, param, rc);
716 return rc;
717 }
718
719 /**
720 * Get table from NXSL script
721 */
722 UINT32 DataCollectionTarget::getScriptTable(const TCHAR *param, Table **result, DataCollectionTarget *targetObject)
723 {
724 UINT32 rc = DCE_NOT_SUPPORTED;
725 NXSL_VM *vm = runDataCollectionScript(param, targetObject);
726 if (vm != NULL)
727 {
728 NXSL_Value *value = vm->getResult();
729 if (value->isObject(_T("Table")))
730 {
731 *result = (Table *)value->getValueAsObject()->getData();
732 (*result)->incRefCount();
733 rc = DCE_SUCCESS;
734 }
735 else
736 {
737 rc = DCE_COLLECTION_ERROR;
738 }
739 delete vm;
740 }
741 nxlog_debug(7, _T("DataCollectionTarget(%s)->getScriptTable(%s): rc=%d"), m_name, param, rc);
742 return rc;
743 }
744
745 /**
746 * Get string map from library script
747 */
748 UINT32 DataCollectionTarget::getStringMapFromScript(const TCHAR *param, StringMap **map, DataCollectionTarget *targetObject)
749 {
750 TCHAR name[256];
751 nx_strncpy(name, param, 256);
752 Trim(name);
753
754 ObjectArray<NXSL_Value> args(16, 16, false);
755
756 // Can be in form parameter(arg1, arg2, ... argN)
757 TCHAR *p = _tcschr(name, _T('('));
758 if (p != NULL)
759 {
760 if (name[_tcslen(name) - 1] != _T(')'))
761 return DCE_NOT_SUPPORTED;
762 name[_tcslen(name) - 1] = 0;
763
764 if (!ParseValueList(&p, args))
765 {
766 // argument parsing error
767 args.clear();
768 return DCE_NOT_SUPPORTED;
769 }
770 }
771
772 UINT32 rc = DCE_NOT_SUPPORTED;
773 NXSL_VM *vm = CreateServerScriptVM(name);
774 if (vm != NULL)
775 {
776 vm->setGlobalVariable(_T("$object"), createNXSLObject());
777 if (getObjectClass() == OBJECT_NODE)
778 {
779 vm->setGlobalVariable(_T("$node"), createNXSLObject());
780 }
781 vm->setGlobalVariable(_T("$isCluster"), new NXSL_Value((getObjectClass() == OBJECT_CLUSTER) ? 1 : 0));
782 if (targetObject != NULL)
783 {
784 vm->setGlobalVariable(_T("$targetObject"), targetObject->createNXSLObject());
785 }
786 if (vm->run(&args))
787 {
788 rc = DCE_SUCCESS;
789 NXSL_Value *value = vm->getResult();
790 if (value->isHashMap())
791 {
792 *map = value->getValueAsHashMap()->toStringMap();
793 }
794 else if (value->isArray())
795 {
796 *map = new StringMap();
797 NXSL_Array *a = value->getValueAsArray();
798 for(int i = 0; i < a->size(); i++)
799 {
800 NXSL_Value *v = a->getByPosition(i);
801 if (v->isString())
802 {
803 (*map)->set(v->getValueAsCString(), v->getValueAsCString());
804 }
805 }
806 }
807 else if (value->isString())
808 {
809 *map = new StringMap();
810 (*map)->set(value->getValueAsCString(), value->getValueAsCString());
811 }
812 else if (value->isNull())
813 {
814 rc = DCE_COLLECTION_ERROR;
815 }
816 else
817 {
818 *map = new StringMap();
819 }
820 }
821 else
822 {
823 DbgPrintf(4, _T("DataCollectionTarget(%s)->getListFromScript(%s): Script execution error: %s"), m_name, param, vm->getErrorText());
824 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", name, vm->getErrorText(), m_id);
825 rc = DCE_COLLECTION_ERROR;
826 }
827 delete vm;
828 }
829 else
830 {
831 args.setOwner(true);
832 DbgPrintf(4, _T("DataCollectionTarget(%s)->getListFromScript(%s): script \"%s\" not found"), m_name, param, name);
833 }
834 DbgPrintf(7, _T("DataCollectionTarget(%s)->getListFromScript(%s): rc=%d"), m_name, param, rc);
835 return rc;
836 }
837
838 /**
839 * Get last (current) DCI values for summary table.
840 */
841 void DataCollectionTarget::getDciValuesSummarySingleValue(SummaryTable *tableDefinition, Table *tableData)
842 {
843 int offset = tableDefinition->isMultiInstance() ? 2 : 1;
844 int baseRow = tableData->getNumRows();
845 bool rowAdded = false;
846 lockDciAccess(false);
847 for(int i = 0; i < tableDefinition->getNumColumns(); i++)
848 {
849 SummaryTableColumn *tc = tableDefinition->getColumn(i);
850 for(int j = 0; j < m_dcObjects->size(); j++)
851 {
852 DCObject *object = m_dcObjects->get(j);
853 if ((object->getType() == DCO_TYPE_ITEM) && object->hasValue() &&
854 (object->getStatus() == ITEM_STATUS_ACTIVE) &&
855 ((tc->m_flags & COLUMN_DEFINITION_REGEXP_MATCH) ?
856 RegexpMatch(object->getName(), tc->m_dciName, FALSE) :
857 !_tcsicmp(object->getName(), tc->m_dciName)
858 ))
859 {
860 int row;
861 if (tableDefinition->isMultiInstance())
862 {
863 // Find instance
864 const TCHAR *instance = ((DCItem *)object)->getInstance();
865 for(row = baseRow; row < tableData->getNumRows(); row++)
866 {
867 const TCHAR *v = tableData->getAsString(row, 1);
868 if (!_tcscmp(CHECK_NULL_EX(v), instance))
869 break;
870 }
871 if (row == tableData->getNumRows())
872 {
873 tableData->addRow();
874 tableData->set(0, m_name);
875 tableData->set(1, instance);
876 tableData->setObjectId(m_id);
877 }
878 }
879 else
880 {
881 if (!rowAdded)
882 {
883 tableData->addRow();
884 tableData->set(0, m_name);
885 tableData->setObjectId(m_id);
886 rowAdded = true;
887 }
888 row = tableData->getNumRows() - 1;
889 }
890 tableData->setStatusAt(row, i + offset, ((DCItem *)object)->getThresholdSeverity());
891 tableData->setCellObjectIdAt(row, i + offset, object->getId());
892 tableData->getColumnDefinitions()->get(i + offset)->setDataType(((DCItem *)object)->getDataType());
893 if (tableDefinition->getAggregationFunction() == F_LAST)
894 {
895 if (tc->m_flags & COLUMN_DEFINITION_MULTIVALUED)
896 {
897 StringList *values = String(((DCItem *)object)->getLastValue()).split(tc->m_separator);
898 tableData->setAt(row, i + offset, values->get(0));
899 for(int r = 1; r < values->size(); r++)
900 {
901 if (row + r >= tableData->getNumRows())
902 {
903 tableData->addRow();
904 tableData->setObjectId(m_id);
905 tableData->setBaseRow(row);
906 }
907 tableData->setAt(row + r, i + offset, values->get(r));
908 tableData->setStatusAt(row + r, i + offset, ((DCItem *)object)->getThresholdSeverity());
909 tableData->setCellObjectIdAt(row + r, i + offset, object->getId());
910 }
911 }
912 else
913 {
914 tableData->setAt(row, i + offset, ((DCItem *)object)->getLastValue());
915 }
916 }
917 else
918 {
919 tableData->setAt(row, i + offset,
920 ((DCItem *)object)->getAggregateValue(
921 tableDefinition->getAggregationFunction(),
922 tableDefinition->getPeriodStart(),
923 tableDefinition->getPeriodEnd()));
924 }
925
926 if (!tableDefinition->isMultiInstance())
927 break;
928 }
929 }
930 }
931 unlockDciAccess();
932 }
933
934 void DataCollectionTarget::getDciValuesSummaryTableValue(SummaryTable *tableDefinition, Table *tableData)
935 {
936 DCObject *o;
937 Table *lastValue;
938 int collIndex;
939
940 lockDciAccess(false);
941 for(int i = 0; i < m_dcObjects->size(); i++)
942 {
943 o = m_dcObjects->get(i);
944 if ((o->getType() == DCO_TYPE_TABLE) && o->hasValue() &&
945 (o->getStatus() == ITEM_STATUS_ACTIVE) &&
946 !_tcscmp(o->getName(), tableDefinition->getTableDciName()))
947 {
948 lastValue = ((DCTable*)o)->getLastValue();
949 if (lastValue == NULL)
950 continue;
951
952 for(int j = 0; j < lastValue->getNumRows(); j++)
953 {
954 tableData->addRow();
955 tableData->set(0, m_name);
956 for(int k = 0; k < lastValue->getNumColumns(); k++)
957 {
958 collIndex = tableData->getColumnIndex(lastValue->getColumnName(k));
959 if (collIndex == -1)
960 {
961 tableData->addColumn(lastValue->getColumnName(k), lastValue->getColumnDataType(k));
962 collIndex = tableData->getNumColumns() - 1;
963 }
964 switch(lastValue->getColumnDataType(k))
965 {
966 case DCI_DT_INT:
967 tableData->set(collIndex, lastValue->getAsInt(j, k));
968 continue;
969 case DCI_DT_UINT:
970 tableData->set(collIndex, lastValue->getAsUInt(j, k));
971 continue;
972 case DCI_DT_INT64:
973 tableData->set(collIndex, lastValue->getAsInt64(j, k));
974 continue;
975 case DCI_DT_UINT64:
976 tableData->set(collIndex, lastValue->getAsUInt64(j, k));
977 continue;
978 case DCI_DT_STRING:
979 tableData->set(collIndex, lastValue->getAsString(j, k));
980 continue;
981 case DCI_DT_FLOAT:
982 tableData->set(collIndex, lastValue->getAsDouble(j, k));
983 continue;
984 }
985 }
986 }
987 }
988 }
989 unlockDciAccess();
990 }
991
992 /**
993 * Must return true if object is a possible event source
994 */
995 bool DataCollectionTarget::isEventSource()
996 {
997 return true;
998 }
999
1000 /**
1001 * Returns most critical status of DCI used for
1002 * status calculation
1003 */
1004 int DataCollectionTarget::getMostCriticalDCIStatus()
1005 {
1006 int status = -1;
1007 lockDciAccess(false);
1008 for(int i = 0; i < m_dcObjects->size(); i++)
1009 {
1010 DCObject *curr = m_dcObjects->get(i);
1011 if (curr->isStatusDCO() && (curr->getType() == DCO_TYPE_ITEM) &&
1012 curr->hasValue() && (curr->getStatus() == ITEM_STATUS_ACTIVE))
1013 {
1014 if (getObjectClass() == OBJECT_CLUSTER && !curr->isAggregateOnCluster())
1015 continue; // Calculated only on those that are agregated on cluster
1016
1017 ItemValue *value = ((DCItem *)curr)->getInternalLastValue();
1018 if (value != NULL && (INT32)*value >= STATUS_NORMAL && (INT32)*value <= STATUS_CRITICAL)
1019 status = max(status, (INT32)*value);
1020 delete value;
1021 }
1022 }
1023 unlockDciAccess();
1024 return (status == -1) ? STATUS_UNKNOWN : status;
1025 }
1026
1027 /**
1028 * Calculate compound status
1029 */
1030 void DataCollectionTarget::calculateCompoundStatus(BOOL bForcedRecalc)
1031 {
1032 NetObj::calculateCompoundStatus(bForcedRecalc);
1033 }
1034
1035 /**
1036 * Returns last ping time
1037 */
1038 UINT32 DataCollectionTarget::getPingTime()
1039 {
1040 if ((time(NULL) - m_pingLastTimeStamp) > g_dwStatusPollingInterval)
1041 {
1042 updatePingData();
1043 DbgPrintf(7, _T("DataCollectionTarget::getPingTime: update ping time is required! Last ping time %d."), m_pingLastTimeStamp);
1044 }
1045 return m_pingTime;
1046 }
1047
1048 /**
1049 * Update ping data
1050 */
1051 void DataCollectionTarget::updatePingData()
1052 {
1053 m_pingLastTimeStamp = 0;
1054 m_pingTime = PING_TIME_TIMEOUT;
1055 }
1056
1057 /**
1058 * Enter maintenance mode
1059 */
1060 void DataCollectionTarget::enterMaintenanceMode()
1061 {
1062 DbgPrintf(4, _T("Entering maintenance mode for %s [%d]"), m_name, m_id);
1063 UINT64 eventId = PostEvent2(EVENT_MAINTENANCE_MODE_ENTERED, m_id, NULL);
1064 lockProperties();
1065 m_maintenanceMode = true;
1066 m_maintenanceEventId = eventId;
1067 setModified();
1068 unlockProperties();
1069 }
1070
1071 /**
1072 * Leave maintenance mode
1073 */
1074 void DataCollectionTarget::leaveMaintenanceMode()
1075 {
1076 DbgPrintf(4, _T("Leaving maintenance mode for %s [%d]"), m_name, m_id);
1077 PostEvent(EVENT_MAINTENANCE_MODE_LEFT, m_id, NULL);
1078 lockProperties();
1079 m_maintenanceMode = false;
1080 m_maintenanceEventId = 0;
1081 setModified();
1082 unlockProperties();
1083 }
1084
1085 /**
1086 * Update cache size for given data collection item
1087 */
1088 void DataCollectionTarget::updateDCItemCacheSize(UINT32 dciId, UINT32 conditionId)
1089 {
1090 lockDciAccess(false);
1091 DCObject *dci = getDCObjectById(dciId, false);
1092 if ((dci != NULL) && (dci->getType() == DCO_TYPE_ITEM))
1093 {
1094 ((DCItem *)dci)->updateCacheSize(conditionId);
1095 }
1096 unlockDciAccess();
1097 }
1098
1099 /**
1100 * Returns true if object is data collection target
1101 */
1102 bool DataCollectionTarget::isDataCollectionTarget()
1103 {
1104 return true;
1105 }
1106
1107 /**
1108 * Add data collection element to proxy info structure
1109 */
1110 void DataCollectionTarget::addProxyDataCollectionElement(ProxyInfo *info, const DCObject *dco)
1111 {
1112 info->msg->setField(info->fieldId++, dco->getId());
1113 info->msg->setField(info->fieldId++, (INT16)dco->getType());
1114 info->msg->setField(info->fieldId++, (INT16)dco->getDataSource());
1115 info->msg->setField(info->fieldId++, dco->getName());
1116 info->msg->setField(info->fieldId++, (INT32)dco->getEffectivePollingInterval());
1117 info->msg->setFieldFromTime(info->fieldId++, dco->getLastPollTime());
1118 info->msg->setField(info->fieldId++, m_guid);
1119 info->msg->setField(info->fieldId++, dco->getSnmpPort());
1120 if (dco->getType() == DCO_TYPE_ITEM)
1121 info->msg->setField(info->fieldId++, ((DCItem *)dco)->getSnmpRawValueType());
1122 else
1123 info->msg->setField(info->fieldId++, (INT16)0);
1124 info->fieldId += 1;
1125 info->count++;
1126 }
1127
1128 /**
1129 * Add SNMP target to proxy info structure
1130 */
1131 void DataCollectionTarget::addProxySnmpTarget(ProxyInfo *info, const Node *node)
1132 {
1133 info->msg->setField(info->nodeInfoFieldId++, m_guid);
1134 info->msg->setField(info->nodeInfoFieldId++, node->getIpAddress());
1135 info->msg->setField(info->nodeInfoFieldId++, node->getSNMPVersion());
1136 info->msg->setField(info->nodeInfoFieldId++, node->getSNMPPort());
1137 SNMP_SecurityContext *snmpSecurity = node->getSnmpSecurityContext();
1138 info->msg->setField(info->nodeInfoFieldId++, (INT16)snmpSecurity->getAuthMethod());
1139 info->msg->setField(info->nodeInfoFieldId++, (INT16)snmpSecurity->getPrivMethod());
1140 info->msg->setFieldFromMBString(info->nodeInfoFieldId++, snmpSecurity->getUser());
1141 info->msg->setFieldFromMBString(info->nodeInfoFieldId++, snmpSecurity->getAuthPassword());
1142 info->msg->setFieldFromMBString(info->nodeInfoFieldId++, snmpSecurity->getPrivPassword());
1143 delete snmpSecurity;
1144 info->nodeInfoFieldId += 41;
1145 info->nodeInfoCount++;
1146 }
1147
1148 /**
1149 * Collect info for SNMP proxy and DCI source (proxy) nodes
1150 * Default implementation adds only agent based DCIs with source node set to requesting node
1151 */
1152 void DataCollectionTarget::collectProxyInfo(ProxyInfo *info)
1153 {
1154 lockDciAccess(false);
1155 for(int i = 0; i < m_dcObjects->size(); i++)
1156 {
1157 DCObject *dco = m_dcObjects->get(i);
1158 if (dco->getStatus() == ITEM_STATUS_DISABLED)
1159 continue;
1160
1161 if ((dco->getDataSource() == DS_NATIVE_AGENT) && (dco->getSourceNode() == info->proxyId) &&
1162 dco->hasValue() && (dco->getAgentCacheMode() == AGENT_CACHE_ON))
1163 {
1164 addProxyDataCollectionElement(info, dco);
1165 }
1166 }
1167 unlockDciAccess();
1168 }
1169
1170 /**
1171 * Callback for colecting proxied SNMP DCIs
1172 */
1173 void DataCollectionTarget::collectProxyInfoCallback(NetObj *object, void *data)
1174 {
1175 ((DataCollectionTarget *)object)->collectProxyInfo((ProxyInfo *)data);
1176 }
1177
1178 /**
1179 * Get effective source node for given data collection object
1180 */
1181 UINT32 DataCollectionTarget::getEffectiveSourceNode(DCObject *dco)
1182 {
1183 return dco->getSourceNode();
1184 }
1185
1186 /**
1187 * Filter for selecting templates from objects
1188 */
1189 static bool TemplateSelectionFilter(NetObj *object, void *userData)
1190 {
1191 return (object->getObjectClass() == OBJECT_TEMPLATE) && !object->isDeleted() && ((Template *)object)->isAutoApplyEnabled();
1192 }
1193
1194 /**
1195 * Apply user templates
1196 */
1197 void DataCollectionTarget::applyUserTemplates()
1198 {
1199 if (IsShutdownInProgress())
1200 return;
1201
1202 ObjectArray<NetObj> *templates = g_idxObjectById.getObjects(true, TemplateSelectionFilter);
1203 for(int i = 0; i < templates->size(); i++)
1204 {
1205 Template *pTemplate = (Template *)templates->get(i);
1206 AutoBindDecision decision = pTemplate->isApplicable(this);
1207 if (decision == AutoBindDecision_Bind)
1208 {
1209 if (!pTemplate->isChild(m_id))
1210 {
1211 DbgPrintf(4, _T("DataCollectionTarget::applyUserTemplates(): applying template %d \"%s\" to object %d \"%s\""),
1212 pTemplate->getId(), pTemplate->getName(), m_id, m_name);
1213 pTemplate->applyToTarget(this);
1214 PostEvent(EVENT_TEMPLATE_AUTOAPPLY, g_dwMgmtNode, "isis", m_id, m_name, pTemplate->getId(), pTemplate->getName());
1215 }
1216 }
1217 else if (decision == AutoBindDecision_Unbind)
1218 {
1219 if (pTemplate->isAutoRemoveEnabled() && pTemplate->isChild(m_id))
1220 {
1221 DbgPrintf(4, _T("DataCollectionTarget::applyUserTemplates(): removing template %d \"%s\" from object %d \"%s\""),
1222 pTemplate->getId(), pTemplate->getName(), m_id, m_name);
1223 pTemplate->deleteChild(this);
1224 deleteParent(pTemplate);
1225 pTemplate->queueRemoveFromTarget(m_id, true);
1226 PostEvent(EVENT_TEMPLATE_AUTOREMOVE, g_dwMgmtNode, "isis", m_id, m_name, pTemplate->getId(), pTemplate->getName());
1227 }
1228 }
1229 pTemplate->decRefCount();
1230 }
1231 delete templates;
1232 }
1233
1234 /**
1235 * Filter for selecting containers from objects
1236 */
1237 static bool ContainerSelectionFilter(NetObj *object, void *userData)
1238 {
1239 return (object->getObjectClass() == OBJECT_CONTAINER) && !object->isDeleted() && ((Container *)object)->isAutoBindEnabled();
1240 }
1241
1242 /**
1243 * Update container membership
1244 */
1245 void DataCollectionTarget::updateContainerMembership()
1246 {
1247 if (IsShutdownInProgress())
1248 return;
1249
1250 ObjectArray<NetObj> *containers = g_idxObjectById.getObjects(true, ContainerSelectionFilter);
1251 for(int i = 0; i < containers->size(); i++)
1252 {
1253 Container *pContainer = (Container *)containers->get(i);
1254 AutoBindDecision decision = pContainer->isSuitableForObject(this);
1255 if (decision == AutoBindDecision_Bind)
1256 {
1257 if (!pContainer->isChild(m_id))
1258 {
1259 DbgPrintf(4, _T("DataCollectionTarget::updateContainerMembership(): binding object %d \"%s\" to container %d \"%s\""),
1260 m_id, m_name, pContainer->getId(), pContainer->getName());
1261 pContainer->addChild(this);
1262 addParent(pContainer);
1263 PostEvent(EVENT_CONTAINER_AUTOBIND, g_dwMgmtNode, "isis", m_id, m_name, pContainer->getId(), pContainer->getName());
1264 pContainer->calculateCompoundStatus();
1265 }
1266 }
1267 else if (decision == AutoBindDecision_Unbind)
1268 {
1269 if (pContainer->isAutoUnbindEnabled() && pContainer->isChild(m_id))
1270 {
1271 DbgPrintf(4, _T("DataCollectionTarget::updateContainerMembership(): removing object %d \"%s\" from container %d \"%s\""),
1272 m_id, m_name, pContainer->getId(), pContainer->getName());
1273 pContainer->deleteChild(this);
1274 deleteParent(pContainer);
1275 PostEvent(EVENT_CONTAINER_AUTOUNBIND, g_dwMgmtNode, "isis", m_id, m_name, pContainer->getId(), pContainer->getName());
1276 pContainer->calculateCompoundStatus();
1277 }
1278 }
1279 pContainer->decRefCount();
1280 }
1281 delete containers;
1282 }
1283
1284 /**
1285 * Serialize object to JSON
1286 */
1287 json_t *DataCollectionTarget::toJson()
1288 {
1289 json_t *root = Template::toJson();
1290 json_object_set_new(root, "pingTime", json_integer(m_pingTime));
1291 json_object_set_new(root, "pingLastTimeStamp", json_integer(m_pingLastTimeStamp));
1292 return root;
1293 }