Queue class can shrink buffer; cosmetic code refactoring
[public/netxms.git] / src / server / core / template.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2015 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: template.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Redefined status calculation for template group
27 */
28 void TemplateGroup::calculateCompoundStatus(BOOL bForcedRecalc)
29 {
30 m_iStatus = STATUS_NORMAL;
31 }
32
33 /**
34 * Called by client session handler to check if threshold summary should be shown for this object.
35 */
36 bool TemplateGroup::showThresholdSummary()
37 {
38 return false;
39 }
40
41 /**
42 * Template object constructor
43 */
44 Template::Template() : NetObj()
45 {
46 m_dcObjects = new ObjectArray<DCObject>(8, 16, true);
47 m_dciLockStatus = -1;
48 m_flags = 0;
49 m_dwVersion = 0x00010000; // Initial version is 1.0
50 m_applyFilter = NULL;
51 m_applyFilterSource = NULL;
52 m_iStatus = STATUS_NORMAL;
53 m_dciAccessLock = RWLockCreate();
54 m_dciListModified = false;
55 }
56
57 /**
58 * Constructor for new template object
59 */
60 Template::Template(const TCHAR *pszName) : NetObj()
61 {
62 nx_strncpy(m_name, pszName, MAX_OBJECT_NAME);
63 m_dcObjects = new ObjectArray<DCObject>(8, 16, true);
64 m_dciLockStatus = -1;
65 m_flags = 0;
66 m_dwVersion = 0x00010000; // Initial version is 1.0
67 m_applyFilter = NULL;
68 m_applyFilterSource = NULL;
69 m_iStatus = STATUS_NORMAL;
70 m_isHidden = true;
71 m_dciAccessLock = RWLockCreate();
72 m_dciListModified = false;
73 }
74
75 /**
76 * Create template object from import file
77 */
78 Template::Template(ConfigEntry *config) : NetObj()
79 {
80 m_isHidden = true;
81 m_dciLockStatus = -1;
82 m_iStatus = STATUS_NORMAL;
83 m_dciAccessLock = RWLockCreate();
84 m_dciListModified = false;
85
86 // Name and version
87 nx_strncpy(m_name, config->getSubEntryValue(_T("name"), 0, _T("Unnamed Template")), MAX_OBJECT_NAME);
88 m_dwVersion = config->getSubEntryValueAsUInt(_T("version"), 0, 0x00010000);
89 m_flags = config->getSubEntryValueAsUInt(_T("flags"), 0, 0);
90
91 // Auto-apply filter
92 m_applyFilter = NULL;
93 m_applyFilterSource = NULL;
94 if (m_flags & TF_AUTO_APPLY)
95 setAutoApplyFilter(config->getSubEntryValue(_T("filter")));
96
97 // Data collection
98 m_dcObjects = new ObjectArray<DCObject>(8, 16, true);
99 ConfigEntry *dcRoot = config->findEntry(_T("dataCollection"));
100 if (dcRoot != NULL)
101 {
102 ObjectArray<ConfigEntry> *dcis = dcRoot->getSubEntries(_T("dci#*"));
103 for(int i = 0; i < dcis->size(); i++)
104 {
105 m_dcObjects->add(new DCItem(dcis->get(i), this));
106 }
107 delete dcis;
108
109 ObjectArray<ConfigEntry> *dctables = dcRoot->getSubEntries(_T("dctable#*"));
110 for(int i = 0; i < dctables->size(); i++)
111 {
112 m_dcObjects->add(new DCTable(dctables->get(i), this));
113 }
114 delete dctables;
115 }
116 }
117
118 /**
119 * Destructor
120 */
121 Template::~Template()
122 {
123 delete m_dcObjects;
124 delete m_applyFilter;
125 safe_free(m_applyFilterSource);
126 RWLockDestroy(m_dciAccessLock);
127 }
128
129 /**
130 * Destroy all related data collection items.
131 */
132 void Template::destroyItems()
133 {
134 m_dcObjects->clear();
135 }
136
137 /**
138 * Set auto apply filter.
139 *
140 * @param filter new filter script code or NULL to clear filter
141 */
142 void Template::setAutoApplyFilter(const TCHAR *filter)
143 {
144 lockProperties();
145 safe_free(m_applyFilterSource);
146 delete m_applyFilter;
147 if (filter != NULL)
148 {
149 TCHAR error[256];
150
151 m_applyFilterSource = _tcsdup(filter);
152 m_applyFilter = NXSLCompileAndCreateVM(m_applyFilterSource, error, 256, new NXSL_ServerEnv);
153 if (m_applyFilter == NULL)
154 nxlog_write(MSG_TEMPLATE_SCRIPT_COMPILATION_ERROR, EVENTLOG_WARNING_TYPE, "dss", m_id, m_name, error);
155 }
156 else
157 {
158 m_applyFilterSource = NULL;
159 m_applyFilter = NULL;
160 }
161 setModified();
162 unlockProperties();
163 }
164
165 /**
166 * Create template object from database data
167 *
168 * @param dwId object ID
169 */
170 BOOL Template::loadFromDatabase(UINT32 dwId)
171 {
172 TCHAR szQuery[256];
173 DB_RESULT hResult;
174 UINT32 i, dwNumNodes, dwNodeId;
175 NetObj *pObject;
176 BOOL bResult = TRUE;
177
178 m_id = dwId;
179
180 if (!loadCommonProperties())
181 return FALSE;
182
183 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("SELECT version,flags,apply_filter FROM templates WHERE id=%d"), dwId);
184 hResult = DBSelect(g_hCoreDB, szQuery);
185 if (hResult == NULL)
186 return FALSE; // Query failed
187
188 if (DBGetNumRows(hResult) == 0)
189 {
190 // No object with given ID in database
191 DBFreeResult(hResult);
192 return FALSE;
193 }
194
195 m_dwVersion = DBGetFieldULong(hResult, 0, 0);
196 m_flags = DBGetFieldULong(hResult, 0, 1);
197 if (m_flags & TF_AUTO_APPLY)
198 {
199 m_applyFilterSource = DBGetField(hResult, 0, 2, NULL, 0);
200 if (m_applyFilterSource != NULL)
201 {
202 TCHAR error[256];
203
204 m_applyFilter = NXSLCompileAndCreateVM(m_applyFilterSource, error, 256, new NXSL_ServerEnv);
205 if (m_applyFilter == NULL)
206 nxlog_write(MSG_TEMPLATE_SCRIPT_COMPILATION_ERROR, EVENTLOG_WARNING_TYPE, "dss", m_id, m_name, error);
207 }
208 }
209 DBFreeResult(hResult);
210
211 // Load DCI and access list
212 loadACLFromDB();
213 loadItemsFromDB();
214 for(i = 0; i < (UINT32)m_dcObjects->size(); i++)
215 if (!m_dcObjects->get(i)->loadThresholdsFromDB())
216 bResult = FALSE;
217
218 // Load related nodes list
219 if (!m_isDeleted)
220 {
221 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("SELECT node_id FROM dct_node_map WHERE template_id=%d"), m_id);
222 hResult = DBSelect(g_hCoreDB, szQuery);
223 if (hResult != NULL)
224 {
225 dwNumNodes = DBGetNumRows(hResult);
226 for(i = 0; i < dwNumNodes; i++)
227 {
228 dwNodeId = DBGetFieldULong(hResult, i, 0);
229 pObject = FindObjectById(dwNodeId);
230 if (pObject != NULL)
231 {
232 if ((pObject->getObjectClass() == OBJECT_NODE) || (pObject->getObjectClass() == OBJECT_CLUSTER) || (pObject->getObjectClass() == OBJECT_MOBILEDEVICE))
233 {
234 AddChild(pObject);
235 pObject->AddParent(this);
236 }
237 else
238 {
239 nxlog_write(MSG_DCT_MAP_NOT_NODE, EVENTLOG_ERROR_TYPE, "dd", m_id, dwNodeId);
240 }
241 }
242 else
243 {
244 nxlog_write(MSG_INVALID_DCT_MAP, EVENTLOG_ERROR_TYPE, "dd", m_id, dwNodeId);
245 }
246 }
247 DBFreeResult(hResult);
248 }
249 }
250
251 m_iStatus = STATUS_NORMAL;
252
253 return bResult;
254 }
255
256 /**
257 * Save object to database
258 */
259 BOOL Template::saveToDatabase(DB_HANDLE hdb)
260 {
261 lockProperties();
262
263 if (!saveCommonProperties(hdb))
264 {
265 unlockProperties();
266 return FALSE;
267 }
268
269 DB_STATEMENT hStmt;
270 if (IsDatabaseRecordExist(hdb, _T("templates"), _T("id"), m_id))
271 {
272 hStmt = DBPrepare(hdb, _T("UPDATE templates SET version=?,flags=?,apply_filter=? WHERE id=?"));
273 }
274 else
275 {
276 hStmt = DBPrepare(hdb, _T("INSERT INTO templates (version,flags,apply_filter,id) VALUES (?,?,?,?)"));
277 }
278 if (hStmt == NULL)
279 {
280 unlockProperties();
281 return FALSE;
282 }
283
284 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_dwVersion);
285 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_flags);
286 DBBind(hStmt, 3, DB_SQLTYPE_TEXT, m_applyFilterSource, DB_BIND_STATIC);
287 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, m_id);
288 BOOL success = DBExecute(hStmt);
289 DBFreeStatement(hStmt);
290
291 if (success)
292 {
293 TCHAR query[256];
294
295 // Update members list
296 _sntprintf(query, 256, _T("DELETE FROM dct_node_map WHERE template_id=%d"), m_id);
297 DBQuery(hdb, query);
298 LockChildList(FALSE);
299 for(UINT32 i = 0; i < m_dwChildCount; i++)
300 {
301 _sntprintf(query, 256, _T("INSERT INTO dct_node_map (template_id,node_id) VALUES (%d,%d)"), m_id, m_pChildList[i]->getId());
302 DBQuery(hdb, query);
303 }
304 UnlockChildList();
305
306 // Save access list
307 saveACLToDB(hdb);
308 }
309
310 unlockProperties();
311
312 // Save data collection items
313 lockDciAccess(false);
314 for(int i = 0; i < m_dcObjects->size(); i++)
315 m_dcObjects->get(i)->saveToDB(hdb);
316 unlockDciAccess();
317
318 // Clear modifications flag
319 lockProperties();
320 m_isModified = false;
321 unlockProperties();
322
323 return success;
324 }
325
326 /**
327 * Delete object from database
328 */
329 bool Template::deleteFromDatabase(DB_HANDLE hdb)
330 {
331 bool success = NetObj::deleteFromDatabase(hdb);
332 if (success)
333 {
334 if (getObjectClass() == OBJECT_TEMPLATE)
335 {
336 success = executeQueryOnObject(hdb, _T("DELETE FROM templates WHERE id=?"));
337 if (success)
338 success = executeQueryOnObject(hdb, _T("DELETE FROM dct_node_map WHERE template_id=?"));
339 }
340 else
341 {
342 success = executeQueryOnObject(hdb, _T("DELETE FROM dct_node_map WHERE node_id=?"));
343 }
344 if (success)
345 success = executeQueryOnObject(hdb, _T("DELETE FROM items WHERE node_id=?"));
346 if (success)
347 success = executeQueryOnObject(hdb, _T("UPDATE items SET template_id=0 WHERE template_id=?"));
348 }
349 return success;
350 }
351
352 /**
353 * Load data collection items from database
354 */
355 void Template::loadItemsFromDB()
356 {
357 DB_STATEMENT hStmt = DBPrepare(g_hCoreDB,
358 _T("SELECT item_id,name,source,datatype,polling_interval,retention_time,")
359 _T("status,delta_calculation,transformation,template_id,description,")
360 _T("instance,template_item_id,flags,resource_id,")
361 _T("proxy_node,base_units,unit_multiplier,custom_units_name,")
362 _T("perftab_settings,system_tag,snmp_port,snmp_raw_value_type,")
363 _T("instd_method,instd_data,instd_filter,samples,comments FROM items WHERE node_id=?"));
364 if (hStmt != NULL)
365 {
366 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
367 DB_RESULT hResult = DBSelectPrepared(hStmt);
368 if (hResult != NULL)
369 {
370 int count = DBGetNumRows(hResult);
371 for(int i = 0; i < count; i++)
372 m_dcObjects->add(new DCItem(hResult, i, this));
373 DBFreeResult(hResult);
374 }
375 DBFreeStatement(hStmt);
376 }
377
378 hStmt = DBPrepare(g_hCoreDB,
379 _T("SELECT item_id,template_id,template_item_id,name,")
380 _T("description,flags,source,snmp_port,polling_interval,retention_time,")
381 _T("status,system_tag,resource_id,proxy_node,perftab_settings,")
382 _T("transformation_script,comments FROM dc_tables WHERE node_id=?"));
383 if (hStmt != NULL)
384 {
385 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
386 DB_RESULT hResult = DBSelectPrepared(hStmt);
387 if (hResult != NULL)
388 {
389 int count = DBGetNumRows(hResult);
390 for(int i = 0; i < count; i++)
391 m_dcObjects->add(new DCTable(hResult, i, this));
392 DBFreeResult(hResult);
393 }
394 DBFreeStatement(hStmt);
395 }
396 }
397
398 /**
399 * Add data collection object to node
400 */
401 bool Template::addDCObject(DCObject *object, bool alreadyLocked)
402 {
403 int i;
404 bool success = false;
405
406 if (!alreadyLocked)
407 lockDciAccess(true); // write lock
408
409 // Check if that object exists
410 for(i = 0; i < m_dcObjects->size(); i++)
411 if (m_dcObjects->get(i)->getId() == object->getId())
412 break; // Object with specified id already exist
413
414 if (i == m_dcObjects->size()) // Add new item
415 {
416 m_dcObjects->add(object);
417 object->setLastPollTime(0); // Cause item to be polled immediatelly
418 if (object->getStatus() != ITEM_STATUS_DISABLED)
419 object->setStatus(ITEM_STATUS_ACTIVE, false);
420 object->setBusyFlag(FALSE);
421 success = true;
422 }
423
424 if (!alreadyLocked)
425 unlockDciAccess();
426
427 if (success)
428 {
429 lockProperties();
430 setModified();
431 unlockProperties();
432 }
433 return success;
434 }
435
436 /**
437 * Delete data collection object from node
438 */
439 bool Template::deleteDCObject(UINT32 dcObjectId, bool needLock)
440 {
441 bool success = false;
442
443 if (needLock)
444 lockDciAccess(true); // write lock
445
446 // Check if that item exists
447 for(int i = 0; i < m_dcObjects->size(); i++)
448 {
449 DCObject *object = m_dcObjects->get(i);
450 if (object->getId() == dcObjectId)
451 {
452 // Check if it is instance DCI
453 if ((object->getType() == DCO_TYPE_ITEM) && (((DCItem *)object)->getInstanceDiscoveryMethod() != IDM_NONE))
454 {
455 deleteChildDCIs(dcObjectId);
456 }
457 // Destroy item
458 DbgPrintf(7, _T("Template::DeleteDCObject: deleting DCObject %d from object %d"), (int)dcObjectId, (int)m_id);
459 destroyItem(object, i);
460 success = true;
461 DbgPrintf(7, _T("Template::DeleteDCObject: DCO deleted from object %d"), (int)m_id);
462 break;
463 }
464 }
465
466 if (needLock)
467 unlockDciAccess();
468 return success;
469 }
470
471 /**
472 * Delets child DCI objects of instance discovery DCI.
473 * It is assumed that list is already locked
474 */
475 void Template::deleteChildDCIs(UINT32 dcObjectId)
476 {
477 for(int i = 0; i < m_dcObjects->size(); i++)
478 {
479 DCObject *subObject = m_dcObjects->get(i);
480 if (subObject->getTemplateItemId() == dcObjectId)
481 {
482 destroyItem(subObject, i);
483 i--;
484 DbgPrintf(7, _T("Template::DeleteDCObject: deleting DCObject %d created by DCObject %d instance discovery from object %d"), (int)subObject->getId(), (int)dcObjectId, (int)m_id);
485 }
486 }
487 }
488
489 /**
490 * Delets DCI object deletes or shedules
491 * deletion from DB and removes it from index
492 * It is assumed that list is already locked
493 */
494 void Template::destroyItem(DCObject *object, int index)
495 {
496 if (object->prepareForDeletion())
497 {
498 // Physically delete DCI only if it is not busy
499 // Busy DCIs will be deleted by data collector
500 object->deleteFromDatabase();
501 m_dcObjects->remove(index);
502 }
503 else
504 {
505 m_dcObjects->unlink(index);
506 DbgPrintf(7, _T("Template::DeleteItem: destruction of DCO %d delayed"), (int)object->getId());
507 }
508 }
509
510 /**
511 * Modify data collection object from NXCP message
512 */
513 bool Template::updateDCObject(UINT32 dwItemId, NXCPMessage *pMsg, UINT32 *pdwNumMaps, UINT32 **ppdwMapIndex, UINT32 **ppdwMapId)
514 {
515 bool success = false;
516
517 lockDciAccess(false);
518
519 // Check if that item exists
520 for(int i = 0; i < m_dcObjects->size(); i++)
521 {
522 DCObject *object = m_dcObjects->get(i);
523 if (object->getId() == dwItemId)
524 {
525 if (object->getType() == DCO_TYPE_ITEM)
526 {
527 ((DCItem *)object)->updateFromMessage(pMsg, pdwNumMaps, ppdwMapIndex, ppdwMapId);
528 if (((DCItem *)object)->getInstanceDiscoveryMethod() != IDM_NONE)
529 {
530 updateInstanceDiscoveryItems((DCItem *)object);
531 }
532 }
533 else
534 {
535 object->updateFromMessage(pMsg);
536 }
537 success = true;
538 m_isModified = true;
539 break;
540 }
541 }
542
543 unlockDciAccess();
544 return success;
545 }
546
547 /**
548 * Update DCIs created by instance dicovery.
549 * This method expects DCI access already locked.
550 *
551 * @param dci instance discovery template DCI
552 */
553 void Template::updateInstanceDiscoveryItems(DCItem *dci)
554 {
555 for(int i = 0; i < m_dcObjects->size(); i++)
556 {
557 DCObject *object = m_dcObjects->get(i);
558 if ((object->getType() == DCO_TYPE_ITEM) && (object->getTemplateId() == m_id) && (object->getTemplateItemId() == dci->getId()))
559 {
560 object->updateFromTemplate(dci);
561 }
562 }
563 }
564
565 /**
566 * Set status for group of DCIs
567 */
568 bool Template::setItemStatus(UINT32 dwNumItems, UINT32 *pdwItemList, int iStatus)
569 {
570 bool success = true;
571
572 lockDciAccess(false);
573 for(UINT32 i = 0; i < dwNumItems; i++)
574 {
575 int j;
576 for(j = 0; j < m_dcObjects->size(); j++)
577 {
578 if (m_dcObjects->get(j)->getId() == pdwItemList[i])
579 {
580 m_dcObjects->get(j)->setStatus(iStatus, true);
581 break;
582 }
583 }
584 if (j == m_dcObjects->size())
585 success = false; // Invalid DCI ID provided
586 }
587 unlockDciAccess();
588 return success;
589 }
590
591 /**
592 * Lock data collection items list
593 */
594 bool Template::lockDCIList(int sessionId, const TCHAR *pszNewOwner, TCHAR *pszCurrOwner)
595 {
596 bool success;
597
598 lockProperties();
599 if (m_dciLockStatus == -1)
600 {
601 m_dciLockStatus = sessionId;
602 m_dciListModified = false;
603 nx_strncpy(m_szCurrDCIOwner, pszNewOwner, MAX_SESSION_NAME);
604 success = true;
605 }
606 else
607 {
608 if (pszCurrOwner != NULL)
609 _tcscpy(pszCurrOwner, m_szCurrDCIOwner);
610 success = false;
611 }
612 unlockProperties();
613 return success;
614 }
615
616 /**
617 * Unlock data collection items list
618 */
619 bool Template::unlockDCIList(int sessionId)
620 {
621 bool success = false;
622 bool callChangeHook = false;
623
624 lockProperties();
625 if (m_dciLockStatus == sessionId)
626 {
627 m_dciLockStatus = -1;
628 if (m_dciListModified)
629 {
630 if (getObjectClass() == OBJECT_TEMPLATE)
631 m_dwVersion++;
632 setModified();
633 callChangeHook = true;
634 }
635 m_dciListModified = false;
636 success = true;
637 }
638 unlockProperties();
639
640 if (callChangeHook)
641 onDataCollectionChange();
642
643 return success;
644 }
645
646 /**
647 * Send DCI list to client
648 */
649 void Template::sendItemsToClient(ClientSession *pSession, UINT32 dwRqId)
650 {
651 NXCPMessage msg;
652
653 // Prepare message
654 msg.setId(dwRqId);
655 msg.setCode(CMD_NODE_DCI);
656
657 lockDciAccess(false);
658
659 // Walk through items list
660 for(int i = 0; i < m_dcObjects->size(); i++)
661 {
662 m_dcObjects->get(i)->createMessage(&msg);
663 pSession->sendMessage(&msg);
664 msg.deleteAllFields();
665 }
666
667 unlockDciAccess();
668
669 // Send end-of-list indicator
670 msg.setEndOfSequence();
671 pSession->sendMessage(&msg);
672 }
673
674 /**
675 * Get DCI's data type
676 */
677 int Template::getItemType(UINT32 dwItemId)
678 {
679 int iType = -1;
680
681 lockDciAccess(false);
682 // Check if that item exists
683 for(int i = 0; i < m_dcObjects->size(); i++)
684 {
685 DCObject *object = m_dcObjects->get(i);
686 if (object->getId() == dwItemId)
687 {
688 if (object->getType() == DCO_TYPE_ITEM)
689 {
690 iType = ((DCItem *)object)->getDataType();
691 }
692 break;
693 }
694 }
695
696 unlockDciAccess();
697 return iType;
698 }
699
700 /**
701 * Get item by it's id
702 */
703 DCObject *Template::getDCObjectById(UINT32 itemId, bool lock)
704 {
705 DCObject *object = NULL;
706
707 if (lock)
708 lockDciAccess(false);
709
710 for(int i = 0; i < m_dcObjects->size(); i++)
711 {
712 DCObject *curr = m_dcObjects->get(i);
713 if (curr->getId() == itemId)
714 {
715 object = curr;
716 break;
717 }
718 }
719
720 if (lock)
721 unlockDciAccess();
722 return object;
723 }
724
725 /**
726 * Get item by template item id
727 */
728 DCObject *Template::getDCObjectByTemplateId(UINT32 tmplItemId)
729 {
730 DCObject *object = NULL;
731
732 lockDciAccess(false);
733 // Check if that item exists
734 for(int i = 0; i < m_dcObjects->size(); i++)
735 {
736 DCObject *curr = m_dcObjects->get(i);
737 if (curr->getTemplateItemId() == tmplItemId)
738 {
739 object = curr;
740 break;
741 }
742 }
743
744 unlockDciAccess();
745 return object;
746 }
747
748 /**
749 * Get item by it's name (case-insensetive)
750 */
751 DCObject *Template::getDCObjectByName(const TCHAR *name)
752 {
753 DCObject *object = NULL;
754
755 lockDciAccess(false);
756 // Check if that item exists
757 for(int i = 0; i < m_dcObjects->size(); i++)
758 {
759 DCObject *curr = m_dcObjects->get(i);
760 if (!_tcsicmp(curr->getName(), name))
761 {
762 object = curr;
763 break;
764 }
765 }
766 unlockDciAccess();
767 return object;
768 }
769
770 /**
771 * Get item by it's description (case-insensetive)
772 */
773 DCObject *Template::getDCObjectByDescription(const TCHAR *description)
774 {
775 DCObject *object = NULL;
776
777 lockDciAccess(false);
778 // Check if that item exists
779 for(int i = 0; i < m_dcObjects->size(); i++)
780 {
781 DCObject *curr = m_dcObjects->get(i);
782 if (!_tcsicmp(curr->getDescription(), description))
783 {
784 object = curr;
785 break;
786 }
787 }
788 unlockDciAccess();
789 return object;
790 }
791
792 /**
793 * Get item by it's index
794 */
795 DCObject *Template::getDCObjectByIndex(int index)
796 {
797 lockDciAccess(false);
798 DCObject *object = m_dcObjects->get(index);
799 unlockDciAccess();
800 return object;
801 }
802
803 /**
804 * Get all DC objects with matching name and description
805 */
806 NXSL_Value *Template::getAllDCObjectsForNXSL(const TCHAR *name, const TCHAR *description)
807 {
808 NXSL_Array *list = new NXSL_Array();
809 lockDciAccess(false);
810 for(int i = 0; i < m_dcObjects->size(); i++)
811 {
812 DCObject *curr = m_dcObjects->get(i);
813 if (((name == NULL) || MatchString(name, curr->getName(), false)) &&
814 ((description == NULL) || MatchString(description, curr->getDescription(), false)))
815 {
816 list->set(list->size(), new NXSL_Value(new NXSL_Object(&g_nxslDciClass, curr)));
817 }
818 }
819 unlockDciAccess();
820 return new NXSL_Value(list);
821 }
822
823 /**
824 * Redefined status calculation for template
825 */
826 void Template::calculateCompoundStatus(BOOL bForcedRecalc)
827 {
828 m_iStatus = STATUS_NORMAL;
829 }
830
831 /**
832 * Create NXCP message with object's data
833 */
834 void Template::fillMessageInternal(NXCPMessage *pMsg)
835 {
836 NetObj::fillMessageInternal(pMsg);
837 pMsg->setField(VID_TEMPLATE_VERSION, m_dwVersion);
838 pMsg->setField(VID_FLAGS, m_flags);
839 pMsg->setField(VID_AUTOBIND_FILTER, CHECK_NULL_EX(m_applyFilterSource));
840 }
841
842 /**
843 * Modify object from NXCP message
844 */
845 UINT32 Template::modifyFromMessageInternal(NXCPMessage *pRequest)
846 {
847 // Change template version
848 if (pRequest->isFieldExist(VID_TEMPLATE_VERSION))
849 m_dwVersion = pRequest->getFieldAsUInt32(VID_TEMPLATE_VERSION);
850
851 // Change flags
852 if (pRequest->isFieldExist(VID_FLAGS))
853 m_flags = pRequest->getFieldAsUInt32(VID_FLAGS);
854
855 // Change apply filter
856 if (pRequest->isFieldExist(VID_AUTOBIND_FILTER))
857 {
858 safe_free(m_applyFilterSource);
859 delete m_applyFilter;
860 m_applyFilterSource = pRequest->getFieldAsString(VID_AUTOBIND_FILTER);
861 if (m_applyFilterSource != NULL)
862 {
863 TCHAR error[256];
864
865 m_applyFilter = NXSLCompileAndCreateVM(m_applyFilterSource, error, 256, new NXSL_ServerEnv);
866 if (m_applyFilter == NULL)
867 nxlog_write(MSG_TEMPLATE_SCRIPT_COMPILATION_ERROR, EVENTLOG_WARNING_TYPE, "dss", m_id, m_name, error);
868 }
869 else
870 {
871 m_applyFilter = NULL;
872 }
873 }
874
875 return NetObj::modifyFromMessageInternal(pRequest);
876 }
877
878 /**
879 * Apply template to data collection target
880 */
881 BOOL Template::applyToTarget(DataCollectionTarget *target)
882 {
883 UINT32 *pdwItemList;
884 BOOL bErrors = FALSE;
885
886 // Link node to template
887 if (!isChild(target->getId()))
888 {
889 AddChild(target);
890 target->AddParent(this);
891 }
892
893 pdwItemList = (UINT32 *)malloc(sizeof(UINT32) * m_dcObjects->size());
894 DbgPrintf(2, _T("Apply %d items from template \"%s\" to target \"%s\""),
895 m_dcObjects->size(), m_name, target->getName());
896
897 // Copy items
898 for(int i = 0; i < m_dcObjects->size(); i++)
899 {
900 DCObject *object = m_dcObjects->get(i);
901 pdwItemList[i] = object->getId();
902 if (!target->applyTemplateItem(m_id, object))
903 {
904 bErrors = TRUE;
905 }
906 }
907
908 // Clean items deleted from template
909 target->cleanDeletedTemplateItems(m_id, m_dcObjects->size(), pdwItemList);
910
911 // Cleanup
912 free(pdwItemList);
913
914 target->onDataCollectionChange();
915
916 // Queue update if target is a cluster
917 if (target->getObjectClass() == OBJECT_CLUSTER)
918 {
919 target->queueUpdate();
920 }
921
922 return bErrors;
923 }
924
925 /**
926 * Queue template update
927 */
928 void Template::queueUpdate()
929 {
930 lockProperties();
931 for(UINT32 i = 0; i < m_dwChildCount; i++)
932 if ((m_pChildList[i]->getObjectClass() == OBJECT_NODE) || (m_pChildList[i]->getObjectClass() == OBJECT_CLUSTER) || (m_pChildList[i]->getObjectClass() == OBJECT_MOBILEDEVICE))
933 {
934 incRefCount();
935 TEMPLATE_UPDATE_INFO *pInfo = (TEMPLATE_UPDATE_INFO *)malloc(sizeof(TEMPLATE_UPDATE_INFO));
936 pInfo->updateType = APPLY_TEMPLATE;
937 pInfo->pTemplate = this;
938 pInfo->targetId = m_pChildList[i]->getId();
939 pInfo->removeDCI = false;
940 g_pTemplateUpdateQueue->put(pInfo);
941 }
942 unlockProperties();
943 }
944
945 /**
946 * Queue template remove from node
947 */
948 void Template::queueRemoveFromTarget(UINT32 targetId, bool removeDCI)
949 {
950 lockProperties();
951 incRefCount();
952 TEMPLATE_UPDATE_INFO *pInfo = (TEMPLATE_UPDATE_INFO *)malloc(sizeof(TEMPLATE_UPDATE_INFO));
953 pInfo->updateType = REMOVE_TEMPLATE;
954 pInfo->pTemplate = this;
955 pInfo->targetId = targetId;
956 pInfo->removeDCI = removeDCI;
957 g_pTemplateUpdateQueue->put(pInfo);
958 unlockProperties();
959 }
960
961 /**
962 * Get list of events used by DCIs
963 */
964 UINT32 *Template::getDCIEventsList(UINT32 *pdwCount)
965 {
966 UINT32 i, j, *pdwList;
967 DCItem *pItem = NULL;
968
969 pdwList = NULL;
970 *pdwCount = 0;
971
972 lockDciAccess(false);
973 for(i = 0; i < (UINT32)m_dcObjects->size(); i++)
974 {
975 m_dcObjects->get(i)->getEventList(&pdwList, pdwCount);
976 }
977 unlockDciAccess();
978
979 // Clean list from duplicates
980 for(i = 0; i < *pdwCount; i++)
981 {
982 for(j = i + 1; j < *pdwCount; j++)
983 {
984 if (pdwList[i] == pdwList[j])
985 {
986 (*pdwCount)--;
987 memmove(&pdwList[j], &pdwList[j + 1], sizeof(UINT32) * (*pdwCount - j));
988 j--;
989 }
990 }
991 }
992
993 return pdwList;
994 }
995
996 /**
997 * Get list of scripts used by DCIs
998 */
999 StringSet *Template::getDCIScriptList()
1000 {
1001 StringSet *list = new StringSet;
1002
1003 lockDciAccess(false);
1004 for(int i = 0; i < m_dcObjects->size(); i++)
1005 {
1006 DCObject *o = m_dcObjects->get(i);
1007 if (o->getDataSource() == DS_SCRIPT)
1008 {
1009 const TCHAR *name = o->getName();
1010 const TCHAR *p = _tcschr(name, _T('('));
1011 if (p != NULL)
1012 {
1013 TCHAR buffer[256];
1014 nx_strncpy(buffer, name, p - name + 1);
1015 list->add(buffer);
1016 }
1017 else
1018 {
1019 list->add(name);
1020 }
1021 }
1022 }
1023 unlockDciAccess();
1024 return list;
1025 }
1026
1027 /**
1028 * Create management pack record
1029 */
1030 void Template::createNXMPRecord(String &str)
1031 {
1032 TCHAR guid[48];
1033 str.appendFormattedString(_T("\t\t<template id=\"%d\">\n\t\t\t<guid>%s</guid>\n\t\t\t<name>%s</name>\n\t\t\t<flags>%d</flags>\n"),
1034 m_id, uuid_to_string(m_guid, guid), (const TCHAR *)EscapeStringForXML2(m_name), m_flags);
1035
1036 // Path in groups
1037 StringList path;
1038 ObjectArray<NetObj> *list = getParentList(OBJECT_TEMPLATEGROUP);
1039 TemplateGroup *parent = NULL;
1040 while(list->size() > 0)
1041 {
1042 parent = (TemplateGroup *)list->get(0);
1043 path.add(parent->getName());
1044 delete list;
1045 list = parent->getParentList(OBJECT_TEMPLATEGROUP);
1046 }
1047 delete list;
1048
1049 str.append(_T("\t\t\t<path>\n"));
1050 for(int j = path.size() - 1, id = 1; j >= 0; j--, id++)
1051 {
1052 str.append(_T("\t\t\t\t<element id=\""));
1053 str.append(id);
1054 str.append(_T("\">"));
1055 str.append(path.get(j));
1056 str.append(_T("</element>\n"));
1057 }
1058 str.append(_T("\t\t\t</path>\n\t\t\t<dataCollection>\n"));
1059
1060 lockDciAccess(false);
1061 for(int i = 0; i < m_dcObjects->size(); i++)
1062 m_dcObjects->get(i)->createNXMPRecord(str);
1063 unlockDciAccess();
1064
1065 str.append(_T("\t\t\t</dataCollection>\n"));
1066 lockProperties();
1067 if (m_applyFilterSource != NULL)
1068 {
1069 str.append(_T("\t\t\t<filter>"));
1070 str.appendPreallocated(EscapeStringForXML(m_applyFilterSource, -1));
1071 str.append(_T("</filter>\n"));
1072 }
1073 unlockProperties();
1074 str.append(_T("\t\t</template>\n"));
1075 }
1076
1077 /**
1078 * Enumerate all DCIs
1079 */
1080 bool Template::enumDCObjects(bool (* pfCallback)(DCObject *, UINT32, void *), void *pArg)
1081 {
1082 bool success = true;
1083
1084 lockDciAccess(false);
1085 for(int i = 0; i < m_dcObjects->size(); i++)
1086 {
1087 if (!pfCallback(m_dcObjects->get(i), i, pArg))
1088 {
1089 success = false;
1090 break;
1091 }
1092 }
1093 unlockDciAccess();
1094 return success;
1095 }
1096
1097 /**
1098 * (Re)associate all DCIs
1099 */
1100 void Template::associateItems()
1101 {
1102 lockDciAccess(false);
1103 for(int i = 0; i < m_dcObjects->size(); i++)
1104 m_dcObjects->get(i)->changeBinding(0, this, FALSE);
1105 unlockDciAccess();
1106 }
1107
1108 /**
1109 * Prepare template for deletion
1110 */
1111 void Template::prepareForDeletion()
1112 {
1113 if (getObjectClass() == OBJECT_TEMPLATE)
1114 {
1115 UINT32 i;
1116
1117 LockChildList(FALSE);
1118 for(i = 0; i < m_dwChildCount; i++)
1119 {
1120 if ((m_pChildList[i]->getObjectClass() == OBJECT_NODE) || (m_pChildList[i]->getObjectClass() == OBJECT_MOBILEDEVICE))
1121 queueRemoveFromTarget(m_pChildList[i]->getId(), TRUE);
1122 }
1123 UnlockChildList();
1124 }
1125 NetObj::prepareForDeletion();
1126 }
1127
1128 /**
1129 * Check if template should be automatically applied to node
1130 * Returns AutoBindDecision_Bind if applicable, AutoBindDecision_Unbind if not,
1131 * AutoBindDecision_Ignore if no change required (script error or no auto apply)
1132 */
1133 AutoBindDecision Template::isApplicable(Node *node)
1134 {
1135 AutoBindDecision result = AutoBindDecision_Ignore;
1136
1137 lockProperties();
1138 if ((m_flags & TF_AUTO_APPLY) && (m_applyFilter != NULL))
1139 {
1140 m_applyFilter->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, node)));
1141 if (m_applyFilter->run())
1142 {
1143 NXSL_Value *value = m_applyFilter->getResult();
1144 result = ((value != NULL) && (value->getValueAsInt32() != 0)) ? AutoBindDecision_Bind : AutoBindDecision_Unbind;
1145 }
1146 else
1147 {
1148 TCHAR buffer[1024];
1149
1150 _sntprintf(buffer, 1024, _T("Template::%s::%d"), m_name, m_id);
1151 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", buffer, m_applyFilter->getErrorText(), m_id);
1152 nxlog_write(MSG_TEMPLATE_SCRIPT_EXECUTION_ERROR, EVENTLOG_WARNING_TYPE, "dss", m_id, m_name, m_applyFilter->getErrorText());
1153 }
1154 }
1155 unlockProperties();
1156 return result;
1157 }
1158
1159 /**
1160 * Get last (current) DCI values. Moved to Template from DataCollectionTarget to allow
1161 * simplified creation of DCI selection dialog in management console. For classes not
1162 * derived from DataCollectionTarget actual values will always be empty strings
1163 * with data type DCI_DT_NULL.
1164 */
1165 UINT32 Template::getLastValues(NXCPMessage *msg, bool objectTooltipOnly, bool overviewOnly, bool includeNoValueObjects)
1166 {
1167 lockDciAccess(false);
1168
1169 UINT32 dwId = VID_DCI_VALUES_BASE, dwCount = 0;
1170 for(int i = 0; i < m_dcObjects->size(); i++)
1171 {
1172 DCObject *object = m_dcObjects->get(i);
1173 if ((object->hasValue() || includeNoValueObjects) &&
1174 (!objectTooltipOnly || object->isShowOnObjectTooltip()) &&
1175 (!overviewOnly || object->isShowInObjectOverview()))
1176 {
1177 if (object->getType() == DCO_TYPE_ITEM)
1178 {
1179 ((DCItem *)object)->fillLastValueMessage(msg, dwId);
1180 dwId += 50;
1181 dwCount++;
1182 }
1183 else if (object->getType() == DCO_TYPE_TABLE)
1184 {
1185 ((DCTable *)object)->fillLastValueSummaryMessage(msg, dwId);
1186 dwId += 50;
1187 dwCount++;
1188 }
1189 }
1190 }
1191 msg->setField(VID_NUM_ITEMS, dwCount);
1192
1193 unlockDciAccess();
1194 return RCC_SUCCESS;
1195 }
1196
1197 /**
1198 * Called when data collection configuration changed
1199 */
1200 void Template::onDataCollectionChange()
1201 {
1202 }