raw_dci_values UPDATE optimization; next build number; minor fixes in Windows build
[public/netxms.git] / src / server / core / dctarget.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2014 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: dctarget.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Default constructor
27 */
28 DataCollectionTarget::DataCollectionTarget() : Template()
29 {
30 }
31
32 /**
33 * Constructor for creating new data collection capable objects
34 */
35 DataCollectionTarget::DataCollectionTarget(const TCHAR *name) : Template(name)
36 {
37 }
38
39 /**
40 * Destructor
41 */
42 DataCollectionTarget::~DataCollectionTarget()
43 {
44 }
45
46 /**
47 * Delete object from database
48 */
49 bool DataCollectionTarget::deleteFromDB(DB_HANDLE hdb)
50 {
51 bool success = Template::deleteFromDB(hdb);
52 if (success)
53 {
54 TCHAR query[256];
55 _sntprintf(query, 256, _T("DROP TABLE idata_%d"), (int)m_dwId);
56 success = DBQuery(hdb, query) ? true : false;
57 if (success)
58 {
59 _sntprintf(query, 256, _T("DROP TABLE tdata_rows_%d"), (int)m_dwId);
60 success = DBQuery(hdb, query) ? true : false;
61 }
62 if (success)
63 {
64 _sntprintf(query, 256, _T("DROP TABLE tdata_records_%d"), (int)m_dwId);
65 success = DBQuery(hdb, query) ? true : false;
66 }
67 if (success)
68 {
69 _sntprintf(query, 256, _T("DROP TABLE tdata_%d"), (int)m_dwId);
70 success = DBQuery(hdb, query) ? true : false;
71 }
72 }
73 return success;
74 }
75
76 /**
77 * Create NXCP message with object's data
78 */
79 void DataCollectionTarget::CreateMessage(CSCPMessage *msg)
80 {
81 Template::CreateMessage(msg);
82 }
83
84 /**
85 * Modify object from message
86 */
87 UINT32 DataCollectionTarget::ModifyFromMessage(CSCPMessage *pRequest, BOOL bAlreadyLocked)
88 {
89 if (!bAlreadyLocked)
90 LockData();
91
92 return Template::ModifyFromMessage(pRequest, TRUE);
93 }
94
95 /**
96 * Update cache for all DCI's
97 */
98 void DataCollectionTarget::updateDciCache()
99 {
100 lockDciAccess(false);
101 for(int i = 0; i < m_dcObjects->size(); i++)
102 {
103 if (m_dcObjects->get(i)->getType() == DCO_TYPE_ITEM)
104 {
105 ((DCItem *)m_dcObjects->get(i))->updateCacheSize();
106 }
107 }
108 unlockDciAccess();
109 }
110
111 /**
112 * Clean expired DCI data
113 */
114 void DataCollectionTarget::cleanDCIData()
115 {
116 lockDciAccess(false);
117 for(int i = 0; i < m_dcObjects->size(); i++)
118 m_dcObjects->get(i)->deleteExpiredData();
119 unlockDciAccess();
120 }
121
122 /**
123 * Get last collected values of given table
124 */
125 UINT32 DataCollectionTarget::getTableLastValues(UINT32 dciId, CSCPMessage *msg)
126 {
127 UINT32 rcc = RCC_INVALID_DCI_ID;
128
129 lockDciAccess(false);
130
131 for(int i = 0; i < m_dcObjects->size(); i++)
132 {
133 DCObject *object = m_dcObjects->get(i);
134 if ((object->getId() == dciId) && (object->getType() == DCO_TYPE_TABLE))
135 {
136 ((DCTable *)object)->fillLastValueMessage(msg);
137 rcc = RCC_SUCCESS;
138 break;
139 }
140 }
141
142 unlockDciAccess();
143 return rcc;
144 }
145
146 /**
147 * Apply DCI from template
148 * pItem passed to this method should be a template's DCI
149 */
150 bool DataCollectionTarget::applyTemplateItem(UINT32 dwTemplateId, DCObject *dcObject)
151 {
152 bool bResult = true;
153
154 lockDciAccess(true); // write lock
155
156 DbgPrintf(5, _T("Applying DCO \"%s\" to target \"%s\""), dcObject->getName(), m_szName);
157
158 // Check if that template item exists
159 int i;
160 for(i = 0; i < m_dcObjects->size(); i++)
161 if ((m_dcObjects->get(i)->getTemplateId() == dwTemplateId) &&
162 (m_dcObjects->get(i)->getTemplateItemId() == dcObject->getId()))
163 break; // Item with specified id already exist
164
165 if (i == m_dcObjects->size())
166 {
167 // New item from template, just add it
168 DCObject *newObject;
169 switch(dcObject->getType())
170 {
171 case DCO_TYPE_ITEM:
172 newObject = new DCItem((DCItem *)dcObject);
173 break;
174 case DCO_TYPE_TABLE:
175 newObject = new DCTable((DCTable *)dcObject);
176 break;
177 default:
178 newObject = NULL;
179 break;
180 }
181 if (newObject != NULL)
182 {
183 newObject->setTemplateId(dwTemplateId, dcObject->getId());
184 newObject->changeBinding(CreateUniqueId(IDG_ITEM), this, TRUE);
185 bResult = addDCObject(newObject, true);
186 }
187 }
188 else
189 {
190 // Update existing item unless it is disabled
191 DCObject *curr = m_dcObjects->get(i);
192 if (curr->getStatus() != ITEM_STATUS_DISABLED || (g_flags & AF_APPLY_TO_DISABLED_DCI_FROM_TEMPLATE))
193 {
194 curr->updateFromTemplate(dcObject);
195 DbgPrintf(9, _T("DCO \"%s\" NOT disabled or ApplyDCIFromTemplateToDisabledDCI set, updated (%d)"),
196 dcObject->getName(), curr->getStatus());
197 if ((curr->getType() == DCO_TYPE_ITEM) && (((DCItem *)curr)->getInstanceDiscoveryMethod() != IDM_NONE))
198 {
199 updateInstanceDiscoveryItems((DCItem *)curr);
200 }
201 }
202 else
203 {
204 DbgPrintf(9, _T("DCO \"%s\" is disabled and ApplyDCIFromTemplateToDisabledDCI not set, no update (%d)"),
205 dcObject->getName(), curr->getStatus());
206 }
207 }
208
209 unlockDciAccess();
210
211 if (bResult)
212 {
213 LockData();
214 m_isModified = true;
215 UnlockData();
216 }
217 return bResult;
218 }
219
220 /**
221 * Clean deleted template items from target's DCI list
222 * Arguments is template id and list of valid template item ids.
223 * all items related to given template and not presented in list should be deleted.
224 */
225 void DataCollectionTarget::cleanDeletedTemplateItems(UINT32 dwTemplateId, UINT32 dwNumItems, UINT32 *pdwItemList)
226 {
227 UINT32 i, j, dwNumDeleted, *pdwDeleteList;
228
229 lockDciAccess(true); // write lock
230
231 pdwDeleteList = (UINT32 *)malloc(sizeof(UINT32) * m_dcObjects->size());
232 dwNumDeleted = 0;
233
234 for(i = 0; i < (UINT32)m_dcObjects->size(); i++)
235 if (m_dcObjects->get(i)->getTemplateId() == dwTemplateId)
236 {
237 for(j = 0; j < dwNumItems; j++)
238 if (m_dcObjects->get(i)->getTemplateItemId() == pdwItemList[j])
239 break;
240
241 // Delete DCI if it's not in list
242 if (j == dwNumItems)
243 pdwDeleteList[dwNumDeleted++] = m_dcObjects->get(i)->getId();
244 }
245
246 for(i = 0; i < dwNumDeleted; i++)
247 deleteDCObject(pdwDeleteList[i], false);
248
249 unlockDciAccess();
250 free(pdwDeleteList);
251 }
252
253 /**
254 * Unbind data collection target from template, i.e either remove DCI
255 * association with template or remove these DCIs at all
256 */
257 void DataCollectionTarget::unbindFromTemplate(UINT32 dwTemplateId, BOOL bRemoveDCI)
258 {
259 UINT32 i;
260
261 if (bRemoveDCI)
262 {
263 lockDciAccess(true); // write lock
264
265 UINT32 *pdwDeleteList = (UINT32 *)malloc(sizeof(UINT32) * m_dcObjects->size());
266 UINT32 dwNumDeleted = 0;
267
268 for(i = 0; i < (UINT32)m_dcObjects->size(); i++)
269 if (m_dcObjects->get(i)->getTemplateId() == dwTemplateId)
270 {
271 pdwDeleteList[dwNumDeleted++] = m_dcObjects->get(i)->getId();
272 }
273
274 for(i = 0; i < dwNumDeleted; i++)
275 deleteDCObject(pdwDeleteList[i], false);
276
277 unlockDciAccess();
278
279 safe_free(pdwDeleteList);
280 }
281 else
282 {
283 lockDciAccess(false);
284
285 for(int i = 0; i < m_dcObjects->size(); i++)
286 if (m_dcObjects->get(i)->getTemplateId() == dwTemplateId)
287 {
288 m_dcObjects->get(i)->setTemplateId(0, 0);
289 }
290
291 unlockDciAccess();
292 }
293 }
294
295 /**
296 * Get list of DCIs to be shown on performance tab
297 */
298 UINT32 DataCollectionTarget::getPerfTabDCIList(CSCPMessage *pMsg)
299 {
300 lockDciAccess(false);
301
302 UINT32 dwId = VID_SYSDCI_LIST_BASE, dwCount = 0;
303 for(int i = 0; i < m_dcObjects->size(); i++)
304 {
305 DCObject *object = m_dcObjects->get(i);
306 if ((object->getPerfTabSettings() != NULL) &&
307 object->hasValue() &&
308 (object->getStatus() == ITEM_STATUS_ACTIVE) &&
309 object->matchClusterResource())
310 {
311 pMsg->SetVariable(dwId++, object->getId());
312 pMsg->SetVariable(dwId++, object->getDescription());
313 pMsg->SetVariable(dwId++, (WORD)object->getStatus());
314 pMsg->SetVariable(dwId++, object->getPerfTabSettings());
315 pMsg->SetVariable(dwId++, (WORD)object->getType());
316 pMsg->SetVariable(dwId++, object->getTemplateItemId());
317 if (object->getType() == DCO_TYPE_ITEM)
318 {
319 pMsg->SetVariable(dwId++, ((DCItem *)object)->getInstance());
320 dwId += 3;
321 }
322 else
323 {
324 dwId += 4;
325 }
326 dwCount++;
327 }
328 }
329 pMsg->SetVariable(VID_NUM_ITEMS, dwCount);
330
331 unlockDciAccess();
332 return RCC_SUCCESS;
333 }
334
335 /**
336 * Get threshold violation summary into NXCP message
337 */
338 UINT32 DataCollectionTarget::getThresholdSummary(CSCPMessage *msg, UINT32 baseId)
339 {
340 UINT32 varId = baseId;
341
342 msg->SetVariable(varId++, m_dwId);
343 UINT32 countId = varId++;
344 UINT32 count = 0;
345
346 lockDciAccess(false);
347 for(int i = 0; i < m_dcObjects->size(); i++)
348 {
349 DCObject *object = m_dcObjects->get(i);
350 if (object->hasValue() && (object->getType() == DCO_TYPE_ITEM) && object->getStatus() == ITEM_STATUS_ACTIVE)
351 {
352 if (((DCItem *)object)->hasActiveThreshold())
353 {
354 ((DCItem *)object)->fillLastValueMessage(msg, varId);
355 varId += 50;
356 count++;
357 }
358 }
359 }
360 unlockDciAccess();
361 msg->SetVariable(countId, count);
362 return varId;
363 }
364
365 /**
366 * Process new DCI value
367 */
368 bool DataCollectionTarget::processNewDCValue(DCObject *dco, time_t currTime, void *value)
369 {
370 lockDciAccess(false);
371 bool result = dco->processNewValue(currTime, value);
372 unlockDciAccess();
373 return result;
374 }
375
376 /**
377 * Check if data collection is disabled
378 */
379 bool DataCollectionTarget::isDataCollectionDisabled()
380 {
381 return false;
382 }
383
384 /**
385 * Put items which requires polling into the queue
386 */
387 void DataCollectionTarget::queueItemsForPolling(Queue *pPollerQueue)
388 {
389 if ((m_iStatus == STATUS_UNMANAGED) || isDataCollectionDisabled() || m_isDeleted)
390 return; // Do not collect data for unmanaged objects or if data collection is disabled
391
392 time_t currTime = time(NULL);
393
394 lockDciAccess(false);
395 for(int i = 0; i < m_dcObjects->size(); i++)
396 {
397 DCObject *object = m_dcObjects->get(i);
398 if (object->isReadyForPolling(currTime))
399 {
400 object->setBusyFlag(TRUE);
401 incRefCount(); // Increment reference count for each queued DCI
402 pPollerQueue->Put(object);
403 DbgPrintf(8, _T("DataCollectionTarget(%s)->QueueItemsForPolling(): item %d \"%s\" added to queue"), m_szName, object->getId(), object->getName());
404 }
405 }
406 unlockDciAccess();
407 }
408
409 /**
410 * Get value for server's internal parameter
411 */
412 UINT32 DataCollectionTarget::getInternalItem(const TCHAR *param, size_t bufSize, TCHAR *buffer)
413 {
414 UINT32 dwError = DCE_SUCCESS;
415
416 if (!_tcsicmp(param, _T("Status")))
417 {
418 _sntprintf(buffer, bufSize, _T("%d"), m_iStatus);
419 }
420 else if (!_tcsicmp(param, _T("Dummy")))
421 {
422 _tcscpy(buffer, _T("0"));
423 }
424 else if (MatchString(_T("ChildStatus(*)"), param, FALSE))
425 {
426 TCHAR *pEnd, szArg[256];
427 UINT32 i, dwId;
428 NetObj *pObject = NULL;
429
430 AgentGetParameterArg(param, 1, szArg, 256);
431 dwId = _tcstoul(szArg, &pEnd, 0);
432 if (*pEnd != 0)
433 {
434 // Argument is object's name
435 dwId = 0;
436 }
437
438 // Find child object with requested ID or name
439 LockChildList(FALSE);
440 for(i = 0; i < m_dwChildCount; i++)
441 {
442 if (((dwId == 0) && (!_tcsicmp(m_pChildList[i]->Name(), szArg))) ||
443 (dwId == m_pChildList[i]->Id()))
444 {
445 pObject = m_pChildList[i];
446 break;
447 }
448 }
449 UnlockChildList();
450
451 if (pObject != NULL)
452 {
453 _sntprintf(buffer, bufSize, _T("%d"), pObject->Status());
454 }
455 else
456 {
457 dwError = DCE_NOT_SUPPORTED;
458 }
459 }
460 else if (MatchString(_T("ConditionStatus(*)"), param, FALSE))
461 {
462 TCHAR *pEnd, szArg[256];
463 UINT32 dwId;
464 NetObj *pObject = NULL;
465
466 AgentGetParameterArg(param, 1, szArg, 256);
467 dwId = _tcstoul(szArg, &pEnd, 0);
468 if (*pEnd == 0)
469 {
470 pObject = FindObjectById(dwId);
471 if (pObject != NULL)
472 if (pObject->Type() != OBJECT_CONDITION)
473 pObject = NULL;
474 }
475 else
476 {
477 // Argument is object's name
478 pObject = FindObjectByName(szArg, OBJECT_CONDITION);
479 }
480
481 if (pObject != NULL)
482 {
483 if (pObject->isTrustedNode(m_dwId))
484 {
485 _sntprintf(buffer, bufSize, _T("%d"), pObject->Status());
486 }
487 else
488 {
489 dwError = DCE_NOT_SUPPORTED;
490 }
491 }
492 else
493 {
494 dwError = DCE_NOT_SUPPORTED;
495 }
496 }
497 else if (MatchString(_T("PingTime(*)"), param, FALSE))
498 {
499 TCHAR *pEnd, szArg[256];
500 UINT32 i, dwId;
501 NetObj *pObject = NULL;
502
503 AgentGetParameterArg(param, 1, szArg, 256);
504 dwId = _tcstoul(szArg, &pEnd, 0);
505 if (*pEnd != 0)
506 {
507 // Argument is object's name
508 dwId = 0;
509 }
510
511 // Find child object with requested ID or name
512 LockChildList(FALSE);
513 for(i = 0; i < m_dwChildCount; i++)
514 {
515 if (((dwId == 0) && (!_tcsicmp(m_pChildList[i]->Name(), szArg))) ||
516 (dwId == m_pChildList[i]->Id()))
517 {
518 pObject = m_pChildList[i];
519 break;
520 }
521 }
522 UnlockChildList();
523
524 if (pObject != NULL)
525 {
526 _sntprintf(buffer, bufSize, _T("%d"), ((Interface *)pObject)->getPingTime());
527 }
528 else
529 {
530 dwError = DCE_NOT_SUPPORTED;
531 }
532 }
533 else if (!_tcsicmp(_T("PingTime"), param))
534 {
535 NetObj *pObject = NULL;
536
537 // Find child object with requested ID or name
538 LockChildList(FALSE);
539 for(int i = 0; i < (int)m_dwChildCount; i++)
540 {
541 if (m_pChildList[i]->IpAddr() == m_dwIpAddr)
542 {
543 pObject = m_pChildList[i];
544 break;
545 }
546 }
547 UnlockChildList();
548
549 if (pObject != NULL)
550 {
551 _sntprintf(buffer, bufSize, _T("%d"), ((Interface *)pObject)->getPingTime());
552 }
553 else
554 {
555 dwError = DCE_NOT_SUPPORTED;
556 }
557 }
558 else
559 {
560 dwError = DCE_NOT_SUPPORTED;
561 }
562
563 return dwError;
564 }
565
566 /**
567 * Get parameter value from NXSL script
568 */
569 UINT32 DataCollectionTarget::getScriptItem(const TCHAR *param, size_t bufSize, TCHAR *buffer)
570 {
571 TCHAR name[256];
572 nx_strncpy(name, param, 256);
573 Trim(name);
574
575 ObjectArray<NXSL_Value> args(16, 16, false);
576
577 // Can be in form parameter(arg1, arg2, ... argN)
578 TCHAR *p = _tcschr(name, _T('('));
579 if (p != NULL)
580 {
581 if (name[_tcslen(name) - 1] != _T(')'))
582 return DCE_NOT_SUPPORTED;
583 name[_tcslen(name) - 1] = 0;
584
585 *p = 0;
586 p++;
587
588 TCHAR *s;
589 do
590 {
591 s = _tcschr(p, _T(','));
592 if (s != NULL)
593 *s = 0;
594 Trim(p);
595 args.add(new NXSL_Value(p));
596 p = s + 1;
597 } while(s != NULL);
598 }
599
600 UINT32 rc = DCE_NOT_SUPPORTED;
601 NXSL_VM *vm = g_pScriptLibrary->createVM(name, new NXSL_ServerEnv);
602 if (vm != NULL)
603 {
604 vm->setGlobalVariable(_T("$object"), new NXSL_Value(new NXSL_Object(&g_nxslNetObjClass, this)));
605 if (Type() == OBJECT_NODE)
606 {
607 vm->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, this)));
608 }
609 vm->setGlobalVariable(_T("$isCluster"), new NXSL_Value((Type() == OBJECT_CLUSTER) ? 1 : 0));
610 if (vm->run(&args))
611 {
612 NXSL_Value *value = vm->getResult();
613 nx_strncpy(buffer, value->getValueAsCString(), bufSize);
614 rc = DCE_SUCCESS;
615 }
616 else
617 {
618 DbgPrintf(4, _T("DataCollectionTarget(%s)->getScriptItem(%s): Script execution error: %s"), m_szName, param, vm->getErrorText());
619 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", name, vm->getErrorText(), m_dwId);
620 rc = DCE_COMM_ERROR;
621 }
622 delete vm;
623 }
624 else
625 {
626 args.setOwner(true);
627 }
628 DbgPrintf(7, _T("DataCollectionTarget(%s)->getScriptItem(%s): rc=%d"), m_szName, param, rc);
629 return rc;
630 }
631
632 /**
633 * Get last (current) DCI values for summary table.
634 */
635 void DataCollectionTarget::getLastValuesSummary(SummaryTable *tableDefinition, Table *tableData)
636 {
637 bool rowAdded = false;
638 lockDciAccess(false);
639 for(int i = 0; i < tableDefinition->getNumColumns(); i++)
640 {
641 SummaryTableColumn *tc = tableDefinition->getColumn(i);
642 for(int j = 0; j < m_dcObjects->size(); j++)
643 {
644 DCObject *object = m_dcObjects->get(j);
645 if ((object->getType() == DCO_TYPE_ITEM) && object->hasValue() &&
646 (object->getStatus() == ITEM_STATUS_ACTIVE) &&
647 ((tc->m_flags & COLUMN_DEFINITION_REGEXP_MATCH) ?
648 RegexpMatch(object->getName(), tc->m_dciName, FALSE) :
649 !_tcsicmp(object->getName(), tc->m_dciName)
650 ))
651 {
652 if (!rowAdded)
653 {
654 tableData->addRow();
655 tableData->set(0, m_szName);
656 tableData->setObjectId(tableData->getNumRows() - 1, m_dwId);
657 rowAdded = true;
658 }
659 tableData->set(i + 1, ((DCItem *)object)->getLastValue());
660 tableData->setStatus(i + 1, ((DCItem *)object)->getThresholdSeverity());
661 tableData->getColumnDefinitions()->get(i + 1)->setDataType(((DCItem *)object)->getDataType());
662 }
663 }
664 }
665 unlockDciAccess();
666 }
667
668 /**
669 * Must return true if object is a possible event source
670 */
671 bool DataCollectionTarget::isEventSource()
672 {
673 return true;
674 }