"Fossies" - the Fresh Open Source Software Archive

Member "netxms-3.8.405/src/server/core/dctarget.cpp" (8 Jun 2021, 84565 Bytes) of package /linux/misc/netxms-3.8.405.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "dctarget.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.8.382_vs_3.8.405.

    1 /*
    2 ** NetXMS - Network Management System
    3 ** Copyright (C) 2003-2021 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 #include <npe.h>
   25 #include <nxcore_websvc.h>
   26 
   27 /**
   28  * Data collector thread pool
   29  */
   30 extern ThreadPool *g_dataCollectorThreadPool;
   31 
   32 /**
   33  * Data collector worker
   34  */
   35 void DataCollector(const shared_ptr<DCObject>& dcObject);
   36 
   37 /**
   38  * Throttle housekeeper if needed. Returns false if shutdown time has arrived and housekeeper process should be aborted.
   39  */
   40 bool ThrottleHousekeeper();
   41 
   42 /**
   43  * Lock IDATA writes
   44  */
   45 void LockIDataWrites();
   46 
   47 /**
   48  * Unlock IDATA writes
   49  */
   50 void UnlockIDataWrites();
   51 
   52 /**
   53  * Poller thread pool
   54  */
   55 extern ThreadPool *g_pollerThreadPool;
   56 
   57 /**
   58  * Default constructor
   59  */
   60 DataCollectionTarget::DataCollectionTarget() : super(), m_statusPollState(_T("status")),
   61          m_configurationPollState(_T("configuration")), m_instancePollState(_T("instance")),
   62          m_deletedItems(0, 32), m_deletedTables(0, 32), m_geoAreas(0, 16)
   63 {
   64    m_hPollerMutex = MutexCreate();
   65    m_mutexProxyLoadFactor = MutexCreate();
   66    m_proxyLoadFactor = 0;
   67    m_geoLocationControlMode = GEOLOCATION_NO_CONTROL;
   68    m_geoLocationRestrictionsViolated = false;
   69    m_instanceDiscoveryPending = false;
   70 }
   71 
   72 /**
   73  * Constructor for creating new data collection capable objects
   74  */
   75 DataCollectionTarget::DataCollectionTarget(const TCHAR *name) : super(name), m_statusPollState(_T("status")),
   76          m_configurationPollState(_T("configuration")), m_instancePollState(_T("instance")),
   77          m_deletedItems(0, 32), m_deletedTables(0, 32), m_geoAreas(0, 16)
   78 {
   79    m_hPollerMutex = MutexCreate();
   80    m_mutexProxyLoadFactor = MutexCreate();
   81    m_proxyLoadFactor = 0;
   82    m_geoLocationControlMode = GEOLOCATION_NO_CONTROL;
   83    m_geoLocationRestrictionsViolated = false;
   84    m_instanceDiscoveryPending = false;
   85 }
   86 
   87 /**
   88  * Destructor
   89  */
   90 DataCollectionTarget::~DataCollectionTarget()
   91 {
   92    MutexDestroy(m_hPollerMutex);
   93    MutexDestroy(m_mutexProxyLoadFactor);
   94 }
   95 
   96 /**
   97  * Delete object from database
   98  */
   99 bool DataCollectionTarget::deleteFromDatabase(DB_HANDLE hdb)
  100 {
  101    bool success = executeQueryOnObject(hdb, _T("DELETE FROM dct_node_map WHERE node_id=?"));
  102 
  103    // TSDB: to avoid heavy query on idata tables let collected data expire instead of deleting it immediately
  104    if (success && ((g_dbSyntax != DB_SYNTAX_TSDB) || !(g_flags & AF_SINGLE_TABLE_PERF_DATA)))
  105    {
  106       TCHAR query[256];
  107       _sntprintf(query, 256, (g_flags & AF_SINGLE_TABLE_PERF_DATA) ? _T("DELETE FROM idata WHERE item_id IN (SELECT item_id FROM items WHERE node_id=%u)") : _T("DROP TABLE idata_%u"), m_id);
  108       QueueSQLRequest(query);
  109 
  110       _sntprintf(query, 256, (g_flags & AF_SINGLE_TABLE_PERF_DATA) ? _T("DELETE FROM tdata WHERE item_id IN (SELECT item_id FROM dc_tables WHERE node_id=%u)") : _T("DROP TABLE tdata_%u"), m_id);
  111       QueueSQLRequest(query);
  112    }
  113 
  114    if (success)
  115       success = executeQueryOnObject(hdb, _T("DELETE FROM dc_targets WHERE id=?"));
  116 
  117    if (success)
  118       success = super::deleteFromDatabase(hdb);
  119 
  120    return success;
  121 }
  122 
  123 /**
  124  * Create NXCP message with object's data
  125  */
  126 void DataCollectionTarget::fillMessageInternal(NXCPMessage *msg, UINT32 userId)
  127 {
  128    super::fillMessageInternal(msg, userId);
  129    msg->setField(VID_GEOLOCATION_CTRL_MODE, static_cast<int16_t>(m_geoLocationControlMode));
  130    msg->setFieldFromInt32Array(VID_GEO_AREAS, m_geoAreas);
  131 }
  132 
  133 /**
  134  * Create NXCP message with object's data - stage 2
  135  */
  136 void DataCollectionTarget::fillMessageInternalStage2(NXCPMessage *msg, UINT32 userId)
  137 {
  138    super::fillMessageInternalStage2(msg, userId);
  139 
  140    // Sent all DCIs marked for display on overview page or in tooltips
  141    uint32_t fieldIdOverview = VID_OVERVIEW_DCI_LIST_BASE;
  142    uint32_t countOverview = 0;
  143    uint32_t fieldIdTooltip = VID_TOOLTIP_DCI_LIST_BASE;
  144    uint32_t countTooltip = 0;
  145    readLockDciAccess();
  146    for(int i = 0; i < m_dcObjects->size(); i++)
  147     {
  148       DCObject *dci = m_dcObjects->get(i);
  149       if ((dci->getType() == DCO_TYPE_ITEM) &&
  150           (dci->getStatus() == ITEM_STATUS_ACTIVE) &&
  151           (dci->getInstanceDiscoveryMethod() == IDM_NONE) &&
  152           dci->hasAccess(userId))
  153         {
  154          if  (dci->isShowInObjectOverview())
  155          {
  156             countOverview++;
  157             ((DCItem *)dci)->fillLastValueMessage(msg, fieldIdOverview);
  158             fieldIdOverview += 50;
  159          }
  160          if  (dci->isShowOnObjectTooltip())
  161          {
  162             countTooltip++;
  163             ((DCItem *)dci)->fillLastValueMessage(msg, fieldIdTooltip);
  164             fieldIdTooltip += 50;
  165          }
  166         }
  167     }
  168    unlockDciAccess();
  169    msg->setField(VID_OVERVIEW_DCI_COUNT, countOverview);
  170    msg->setField(VID_TOOLTIP_DCI_COUNT, countTooltip);
  171 }
  172 
  173 /**
  174  * Modify object from message
  175  */
  176 UINT32 DataCollectionTarget::modifyFromMessageInternal(NXCPMessage *request)
  177 {
  178    if (request->isFieldExist(VID_GEOLOCATION_CTRL_MODE))
  179       m_geoLocationControlMode = static_cast<GeoLocationControlMode>(request->getFieldAsInt16(VID_GEOLOCATION_CTRL_MODE));
  180 
  181    if (request->isFieldExist(VID_GEO_AREAS))
  182       request->getFieldAsInt32Array(VID_GEO_AREAS, &m_geoAreas);
  183 
  184    return super::modifyFromMessageInternal(request);
  185 }
  186 
  187 /**
  188  * Create object from database data
  189  */
  190 bool DataCollectionTarget::loadFromDatabase(DB_HANDLE hdb, UINT32 id)
  191 {
  192    TCHAR query[512];
  193    _sntprintf(query, 512, _T("SELECT config_poll_timestamp,instance_poll_timestamp,geolocation_ctrl_mode,geo_areas FROM dc_targets WHERE id=%u"), m_id);
  194    DB_RESULT hResult = DBSelect(hdb, query);
  195    if (hResult == nullptr)
  196       return false;
  197 
  198    m_configurationPollState.setLastCompleted(DBGetFieldLong(hResult, 0, 0));
  199    m_instancePollState.setLastCompleted(DBGetFieldLong(hResult, 0, 1));
  200    m_geoLocationControlMode = static_cast<GeoLocationControlMode>(DBGetFieldLong(hResult, 0, 2));
  201 
  202    TCHAR areas[2000];
  203    DBGetField(hResult, 0, 3, areas, 2000);
  204    Trim(areas);
  205    if (*areas != 0)
  206    {
  207       TCHAR *curr = areas;
  208       while(true)
  209       {
  210          TCHAR *next = _tcschr(curr, _T(','));
  211          if (next != nullptr)
  212             *next = 0;
  213 
  214          TCHAR *eptr;
  215          uint32_t id = _tcstoul(curr, &eptr, 10);
  216          if ((id != 0) && (*eptr == 0))
  217             m_geoAreas.add(id);
  218 
  219          if (next == nullptr)
  220             break;
  221          curr = next + 1;
  222       }
  223    }
  224 
  225    DBFreeResult(hResult);
  226 
  227    if (static_cast<uint32_t>(time(nullptr) - m_configurationPollState.getLastCompleted()) < g_configurationPollingInterval)
  228       m_runtimeFlags |= ODF_CONFIGURATION_POLL_PASSED;
  229 
  230    return true;
  231 }
  232 
  233 /**
  234  * Save object to database
  235  */
  236 bool DataCollectionTarget::saveToDatabase(DB_HANDLE hdb)
  237 {
  238    bool success = super::saveToDatabase(hdb);
  239 
  240    if (success && (m_modified & MODIFY_DC_TARGET))
  241    {
  242       static const TCHAR *columns[] = { _T("config_poll_timestamp"), _T("instance_poll_timestamp"), _T("geolocation_ctrl_mode"), _T("geo_areas"), nullptr };
  243       DB_STATEMENT hStmt = DBPrepareMerge(hdb, _T("dc_targets"), _T("id"), m_id, columns);
  244       if (hStmt != nullptr)
  245       {
  246          StringBuffer areas;
  247          lockProperties();
  248          for(int i = 0; i < m_geoAreas.size(); i++)
  249          {
  250             areas.append(m_geoAreas.get(i));
  251             areas.append(_T(','));
  252          }
  253          unlockProperties();
  254          areas.shrink();
  255 
  256          DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, static_cast<uint32_t>(m_configurationPollState.getLastCompleted()));
  257          DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, static_cast<uint32_t>(m_instancePollState.getLastCompleted()));
  258          DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, static_cast<int32_t>(m_geoLocationControlMode));
  259          DBBind(hStmt, 4, DB_SQLTYPE_VARCHAR, areas, DB_BIND_STATIC);
  260          DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, m_id);
  261          success = DBExecute(hStmt);
  262          DBFreeStatement(hStmt);
  263       }
  264       else
  265       {
  266          success = false;
  267       }
  268    }
  269 
  270    if ((success) && (m_modified & MODIFY_DATA_COLLECTION))
  271       success = saveDCIListForCleanup(hdb);
  272 
  273    return success;
  274 }
  275 
  276 /**
  277  * Update cache for all DCI's
  278  */
  279 void DataCollectionTarget::updateDciCache()
  280 {
  281     readLockDciAccess();
  282    for(int i = 0; i < m_dcObjects->size(); i++)
  283     {
  284         if (m_dcObjects->get(i)->getType() == DCO_TYPE_ITEM)
  285         {
  286             static_cast<DCItem*>(m_dcObjects->get(i))->updateCacheSize();
  287         }
  288     }
  289     unlockDciAccess();
  290 }
  291 
  292 /**
  293  * Calculate DCI cutoff time for cleaning expired DCI data using TSDB drop_chunks() function
  294  */
  295 void DataCollectionTarget::calculateDciCutoffTimes(time_t *cutoffTimeIData, time_t *cutoffTimeTData)
  296 {
  297    time_t now = time(nullptr);
  298 
  299    readLockDciAccess();
  300    for(int i = 0; i < m_dcObjects->size(); i++)
  301    {
  302       DCObject *o = m_dcObjects->get(i);
  303       DCObjectStorageClass sclass = o->getStorageClass();
  304       if (sclass == DCObjectStorageClass::DEFAULT)
  305          continue;
  306 
  307       time_t *cutoffTime = (o->getType() == DCO_TYPE_ITEM) ? &cutoffTimeIData[static_cast<int>(sclass) - 1] : &cutoffTimeTData[static_cast<int>(sclass) - 1];
  308       if ((*cutoffTime == 0) || (*cutoffTime > now - o->getEffectiveRetentionTime() * 86400))
  309          *cutoffTime = now - o->getEffectiveRetentionTime() * 86400;
  310    }
  311    unlockDciAccess();
  312 }
  313 
  314 /**
  315  * Clean expired DCI data
  316  */
  317 void DataCollectionTarget::cleanDCIData(DB_HANDLE hdb)
  318 {
  319    StringBuffer queryItems = _T("DELETE FROM idata");
  320    if (g_flags & AF_SINGLE_TABLE_PERF_DATA)
  321    {
  322       queryItems.append(_T(" WHERE node_id="));
  323       queryItems.append(m_id);
  324       queryItems.append(_T(" AND "));
  325    }
  326    else
  327    {
  328       queryItems.append(_T('_'));
  329       queryItems.append(m_id);
  330       queryItems.append(_T(" WHERE "));
  331    }
  332 
  333    StringBuffer queryTables = _T("DELETE FROM tdata");
  334    if (g_flags & AF_SINGLE_TABLE_PERF_DATA)
  335    {
  336       queryTables.append(_T(" WHERE node_id="));
  337       queryTables.append(m_id);
  338       queryTables.append(_T(" AND "));
  339    }
  340    else
  341    {
  342       queryTables.append(_T('_'));
  343       queryTables.append(m_id);
  344       queryTables.append(_T(" WHERE "));
  345    }
  346 
  347    int itemCount = 0;
  348    int tableCount = 0;
  349    time_t now = time(nullptr);
  350 
  351    readLockDciAccess();
  352 
  353    // Check if all DCIs has same retention time
  354    bool sameRetentionTimeItems = true;
  355    bool sameRetentionTimeTables = true;
  356    int retentionTimeItems = -1;
  357    int retentionTimeTables = -1;
  358    for(int i = 0; i < m_dcObjects->size(); i++)
  359    {
  360       DCObject *o = m_dcObjects->get(i);
  361       if (!o->isDataStorageEnabled())
  362          continue;   // Ignore "do not store" objects
  363 
  364       if (o->getType() == DCO_TYPE_ITEM)
  365       {
  366          if (retentionTimeItems == -1)
  367          {
  368             retentionTimeItems = o->getEffectiveRetentionTime();
  369          }
  370          else if (retentionTimeItems != o->getEffectiveRetentionTime())
  371          {
  372             sameRetentionTimeItems = false;
  373             break;
  374          }
  375       }
  376       else if (o->getType() == DCO_TYPE_TABLE)
  377       {
  378          if (retentionTimeTables == -1)
  379          {
  380             retentionTimeTables = o->getEffectiveRetentionTime();
  381          }
  382          else if (retentionTimeTables != o->getEffectiveRetentionTime())
  383          {
  384             sameRetentionTimeTables = false;
  385             break;
  386          }
  387       }
  388    }
  389 
  390    for(int i = 0; i < m_dcObjects->size(); i++)
  391    {
  392       DCObject *o = m_dcObjects->get(i);
  393       if (!o->isDataStorageEnabled())
  394          continue;   // Ignore "do not store" objects
  395 
  396       if ((o->getType() == DCO_TYPE_ITEM) && !sameRetentionTimeItems)
  397       {
  398          if (itemCount > 0)
  399             queryItems.append(_T(" OR "));
  400          queryItems.append(_T("(item_id="));
  401          queryItems.append(o->getId());
  402          queryItems.append(_T(" AND idata_timestamp<"));
  403          queryItems.append(static_cast<int64_t>(now - o->getEffectiveRetentionTime() * 86400));
  404          queryItems.append(_T(')'));
  405          itemCount++;
  406       }
  407       else if ((o->getType() == DCO_TYPE_TABLE) && !sameRetentionTimeTables)
  408       {
  409          if (tableCount > 0)
  410             queryTables.append(_T(" OR "));
  411          queryTables.append(_T("(item_id="));
  412          queryTables.append(o->getId());
  413          queryTables.append(_T(" AND tdata_timestamp<"));
  414          queryTables.append(static_cast<int64_t>(now - o->getEffectiveRetentionTime() * 86400));
  415          queryTables.append(_T(')'));
  416          tableCount++;
  417       }
  418    }
  419    unlockDciAccess();
  420 
  421    if (sameRetentionTimeItems && (retentionTimeItems != -1))
  422    {
  423       queryItems.append(_T("idata_timestamp<"));
  424       queryItems.append(static_cast<int64_t>(now - retentionTimeItems * 86400));
  425       itemCount++;   // Indicate that query should be run
  426    }
  427 
  428    if (sameRetentionTimeTables && (retentionTimeTables != -1))
  429    {
  430       queryTables.append(_T("tdata_timestamp<"));
  431       queryTables.append(static_cast<int64_t>(now - retentionTimeTables * 86400));
  432       tableCount++;   // Indicate that query should be run
  433    }
  434 
  435    lockProperties();
  436    for(int i = 0; i < m_deletedItems.size(); i++)
  437    {
  438       if (itemCount > 0)
  439          queryItems.append(_T(" OR "));
  440       queryItems.append(_T("item_id="));
  441       queryItems.append(m_deletedItems.get(i));
  442       itemCount++;
  443    }
  444    m_deletedItems.clear();
  445 
  446    for(int i = 0; i < m_deletedTables.size(); i++)
  447    {
  448       if (tableCount > 0)
  449          queryTables.append(_T(" OR "));
  450       queryTables.append(_T("item_id="));
  451       queryTables.append(m_deletedItems.get(i));
  452       tableCount++;
  453    }
  454    m_deletedTables.clear();
  455    setModified(MODIFY_DATA_COLLECTION, false);  //To update cleanup lists in database
  456    unlockProperties();
  457 
  458    if (itemCount > 0)
  459    {
  460       LockIDataWrites();
  461       nxlog_debug_tag(_T("housekeeper"), 6, _T("DataCollectionTarget::cleanDCIData(%s [%d]): running query \"%s\""), m_name, m_id, (const TCHAR *)queryItems);
  462       DBQuery(hdb, queryItems);
  463       UnlockIDataWrites();
  464       if (!ThrottleHousekeeper())
  465          return;
  466    }
  467 
  468    if (tableCount > 0)
  469    {
  470       nxlog_debug_tag(_T("housekeeper"), 6, _T("DataCollectionTarget::cleanDCIData(%s [%d]): running query \"%s\""), m_name, m_id, (const TCHAR *)queryTables);
  471       DBQuery(hdb, queryTables);
  472    }
  473 }
  474 
  475 /**
  476  * Queue prediction engine training when necessary
  477  */
  478 void DataCollectionTarget::queuePredictionEngineTraining()
  479 {
  480    readLockDciAccess();
  481    for(int i = 0; i < m_dcObjects->size(); i++)
  482    {
  483       DCObject *o = m_dcObjects->get(i);
  484       if (o->getType() == DCO_TYPE_ITEM)
  485       {
  486          DCItem *dci = static_cast<DCItem*>(o);
  487          if (dci->getPredictionEngine()[0] != 0)
  488          {
  489             PredictionEngine *e = FindPredictionEngine(dci->getPredictionEngine());
  490             if ((e != nullptr) && e->requiresTraining())
  491             {
  492                QueuePredictionEngineTraining(e, dci);
  493             }
  494          }
  495       }
  496    }
  497    unlockDciAccess();
  498 }
  499 
  500 /**
  501  * Schedule cleanup of DCI data after DCI deletion
  502  */
  503 void DataCollectionTarget::scheduleItemDataCleanup(UINT32 dciId)
  504 {
  505    lockProperties();
  506    m_deletedItems.add(dciId);
  507    unlockProperties();
  508 }
  509 
  510 /**
  511  * Schedule cleanup of table DCI data after DCI deletion
  512  */
  513 void DataCollectionTarget::scheduleTableDataCleanup(UINT32 dciId)
  514 {
  515    lockProperties();
  516    m_deletedTables.add(dciId);
  517    unlockProperties();
  518 }
  519 
  520 /**
  521  * Save DCI list that should be cleared
  522  */
  523 bool DataCollectionTarget::saveDCIListForCleanup(DB_HANDLE hdb)
  524 {
  525    bool success = executeQueryOnObject(hdb, _T("DELETE FROM dci_delete_list WHERE node_id=?"));
  526 
  527    lockProperties();
  528    if (success && (!m_deletedItems.isEmpty() || !m_deletedTables.isEmpty()))
  529    {
  530       DB_STATEMENT hStmt = DBPrepare(hdb, _T("INSERT INTO dci_delete_list (node_id,dci_id,type) VALUES (?,?,?)"), (m_deletedItems.size() + m_deletedTables.size()) > 1);
  531       if (hStmt == nullptr)
  532          return false;
  533 
  534       DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
  535       DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, _T("i"), 1);
  536       for(int i = 0; i < m_deletedItems.size() && success; i++)
  537       {
  538          DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_deletedItems.get(i));
  539          success = DBExecute(hStmt);
  540       }
  541 
  542       DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, _T("t"), 1);
  543       for(int i = 0; i < m_deletedTables.size() && success; i++)
  544       {
  545          DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_deletedTables.get(i));
  546          success = DBExecute(hStmt);
  547       }
  548       DBFreeStatement(hStmt);
  549    }
  550    unlockProperties();
  551 
  552    return success;
  553 }
  554 
  555 /**
  556  * Load DCI list that should be cleared
  557  */
  558 void DataCollectionTarget::loadDCIListForCleanup(DB_HANDLE hdb)
  559 {
  560    DB_STATEMENT hStmt = DBPrepare(hdb,
  561                  _T("SELECT dci_id FROM dci_delete_list WHERE type='i' AND node_id=?"));
  562    if (hStmt != NULL)
  563    {
  564       DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
  565       DB_RESULT hResult = DBSelectPrepared(hStmt);
  566       if (hResult != NULL)
  567       {
  568          int count = DBGetNumRows(hResult);
  569          for(int i = 0; i < count; i++)
  570          {
  571             m_deletedItems.add(DBGetFieldULong(hResult, i, 0));
  572          }
  573          DBFreeResult(hResult);
  574       }
  575       DBFreeStatement(hStmt);
  576    }
  577 
  578    hStmt = DBPrepare(hdb,
  579                  _T("SELECT dci_id FROM dci_delete_list WHERE type='t' AND node_id=?"));
  580    if (hStmt != NULL)
  581    {
  582       DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
  583       DB_RESULT hResult = DBSelectPrepared(hStmt);
  584       if (hResult != NULL)
  585       {
  586          int count = DBGetNumRows(hResult);
  587          for(int i = 0; i < count; i++)
  588             m_deletedTables.add(DBGetFieldULong(hResult, i, 0));
  589          DBFreeResult(hResult);
  590       }
  591       DBFreeStatement(hStmt);
  592    }
  593 }
  594 
  595 /**
  596  * Get last value of DCI (either table or single value)
  597  */
  598 uint32_t DataCollectionTarget::getDciLastValue(uint32_t dciId, NXCPMessage *msg)
  599 {
  600    uint32_t rcc = RCC_INVALID_DCI_ID;
  601 
  602    readLockDciAccess();
  603 
  604    for(int i = 0; i < m_dcObjects->size(); i++)
  605    {
  606       DCObject *object = m_dcObjects->get(i);
  607       if (object->getId() == dciId)
  608       {
  609          msg->setField(VID_DCOBJECT_TYPE, static_cast<int16_t>(object->getType()));
  610          if (object->getType() == DCO_TYPE_TABLE)
  611          {
  612             static_cast<DCTable*>(object)->fillLastValueMessage(msg);
  613          }
  614          else
  615          {
  616             static_cast<DCItem*>(object)->fillLastValueMessage(msg);
  617          }
  618          rcc = RCC_SUCCESS;
  619          break;
  620       }
  621    }
  622 
  623    unlockDciAccess();
  624    return rcc;
  625 }
  626 
  627 /**
  628  * Get last collected values of given table
  629  */
  630 uint32_t DataCollectionTarget::getTableLastValue(uint32_t dciId, NXCPMessage *msg)
  631 {
  632    uint32_t rcc = RCC_INVALID_DCI_ID;
  633 
  634    readLockDciAccess();
  635 
  636    for(int i = 0; i < m_dcObjects->size(); i++)
  637     {
  638         DCObject *object = m_dcObjects->get(i);
  639         if (object->getId() == dciId)
  640         {
  641            if (object->getType() == DCO_TYPE_TABLE)
  642            {
  643               static_cast<DCTable*>(object)->fillLastValueMessage(msg);
  644               rcc = RCC_SUCCESS;
  645            }
  646            else
  647            {
  648               rcc = RCC_INCOMPATIBLE_OPERATION;
  649            }
  650             break;
  651         }
  652     }
  653 
  654    unlockDciAccess();
  655    return rcc;
  656 }
  657 
  658 /**
  659  * Apply DCI from template
  660  * dcObject passed to this method should be a template's DCI
  661  */
  662 bool DataCollectionTarget::applyTemplateItem(UINT32 dwTemplateId, DCObject *dcObject)
  663 {
  664    bool bResult = true;
  665 
  666    writeLockDciAccess();    // write lock
  667 
  668    nxlog_debug_tag(_T("obj.dc"), 5, _T("Applying data collection object \"%s\" to target \"%s\""), dcObject->getName().cstr(), m_name);
  669 
  670    // Check if that template item exists
  671     int i;
  672    for(i = 0; i < m_dcObjects->size(); i++)
  673       if ((m_dcObjects->get(i)->getTemplateId() == dwTemplateId) &&
  674           (m_dcObjects->get(i)->getTemplateItemId() == dcObject->getId()))
  675          break;   // Item with specified id already exist
  676 
  677    if (i == m_dcObjects->size())
  678    {
  679       // New item from template, just add it
  680         DCObject *newObject = dcObject->clone();
  681       newObject->setTemplateId(dwTemplateId, dcObject->getId());
  682       newObject->changeBinding(CreateUniqueId(IDG_ITEM), self(), TRUE);
  683       bResult = addDCObject(newObject, true);
  684    }
  685    else
  686    {
  687       // Update existing item unless it is disabled
  688       DCObject *curr = m_dcObjects->get(i);
  689       curr->updateFromTemplate(dcObject);
  690       NotifyClientsOnDCIUpdate(*this, curr);
  691       if (curr->getInstanceDiscoveryMethod() != IDM_NONE)
  692       {
  693          updateInstanceDiscoveryItems(curr);
  694       }
  695    }
  696 
  697    unlockDciAccess();
  698 
  699     if (bResult)
  700         setModified(MODIFY_DATA_COLLECTION, false);
  701    return bResult;
  702 }
  703 
  704 /**
  705  * Clean deleted template items from target's DCI list
  706  * Arguments is template id and list of valid template item ids.
  707  * all items related to given template and not presented in list should be deleted.
  708  */
  709 void DataCollectionTarget::cleanDeletedTemplateItems(uint32_t templateId, const IntegerArray<uint32_t>& dciList)
  710 {
  711    writeLockDciAccess();  // write lock
  712 
  713    IntegerArray<uint32_t> deleteList;
  714    for(int i = 0; i < m_dcObjects->size(); i++)
  715       if (m_dcObjects->get(i)->getTemplateId() == templateId)
  716       {
  717          int j;
  718          for(j = 0; j < dciList.size(); j++)
  719             if (m_dcObjects->get(i)->getTemplateItemId() == dciList.get(j))
  720                break;
  721 
  722          // Delete DCI if it's not in list
  723          if (j == dciList.size())
  724             deleteList.add(m_dcObjects->get(i)->getId());
  725       }
  726 
  727    for(int i = 0; i < deleteList.size(); i++)
  728       deleteDCObject(deleteList.get(i), false, 0);
  729 
  730    unlockDciAccess();
  731 }
  732 
  733 /**
  734  * Unbind data collection target from template, i.e either remove DCI
  735  * association with template or remove these DCIs at all
  736  */
  737 void DataCollectionTarget::unbindFromTemplate(const shared_ptr<DataCollectionOwner>& templateObject, bool removeDCI)
  738 {
  739    if ((getObjectClass() == OBJECT_NODE) && (templateObject->getObjectClass() == OBJECT_TEMPLATE))
  740    {
  741       static_cast<Template&>(*templateObject).removeAllPolicies(static_cast<Node*>(this));
  742    }
  743 
  744    uint32_t templateId = templateObject->getId();
  745    if (removeDCI)
  746    {
  747       writeLockDciAccess();  // write lock
  748 
  749       uint32_t *deleteList = MemAllocArray<uint32_t>(m_dcObjects->size());
  750         int numDeleted = 0;
  751 
  752         int i;
  753       for(i = 0; i < m_dcObjects->size(); i++)
  754          if (m_dcObjects->get(i)->getTemplateId() == templateId)
  755          {
  756             deleteList[numDeleted++] = m_dcObjects->get(i)->getId();
  757          }
  758 
  759         for(i = 0; i < numDeleted; i++)
  760             deleteDCObject(deleteList[i], false, 0);
  761 
  762       unlockDciAccess();
  763         MemFree(deleteList);
  764    }
  765    else
  766    {
  767       readLockDciAccess();
  768 
  769       for(int i = 0; i < m_dcObjects->size(); i++)
  770          if (m_dcObjects->get(i)->getTemplateId() == templateId)
  771          {
  772             m_dcObjects->get(i)->setTemplateId(0, 0);
  773          }
  774 
  775       unlockDciAccess();
  776    }
  777 }
  778 
  779 /**
  780  * Get list of DCIs to be shown on performance tab
  781  */
  782 UINT32 DataCollectionTarget::getPerfTabDCIList(NXCPMessage *pMsg, UINT32 userId)
  783 {
  784     readLockDciAccess();
  785 
  786     UINT32 dwId = VID_SYSDCI_LIST_BASE, dwCount = 0;
  787    for(int i = 0; i < m_dcObjects->size(); i++)
  788     {
  789         DCObject *object = m_dcObjects->get(i);
  790       if ((object->getPerfTabSettings() != nullptr) &&
  791           object->hasValue() &&
  792           (object->getStatus() == ITEM_STATUS_ACTIVE) &&
  793           object->matchClusterResource() &&
  794           object->hasAccess(userId))
  795         {
  796             pMsg->setField(dwId++, object->getId());
  797             pMsg->setField(dwId++, object->getDescription());
  798             pMsg->setField(dwId++, (WORD)object->getStatus());
  799             pMsg->setField(dwId++, object->getPerfTabSettings());
  800             pMsg->setField(dwId++, (WORD)object->getType());
  801             pMsg->setField(dwId++, object->getTemplateItemId());
  802             if (object->getType() == DCO_TYPE_ITEM)
  803             {
  804                 pMsg->setField(dwId++, ((DCItem *)object)->getInstance());
  805             if ((object->getTemplateItemId() != 0) && (object->getTemplateId() == m_id))
  806             {
  807                // DCI created via instance discovery - send ID of root template item
  808                // to allow UI to resolve double template case
  809                // (template -> instance discovery item on node -> actual item on node)
  810                shared_ptr<DCObject> src = getDCObjectById(object->getTemplateItemId(), userId, false);
  811                pMsg->setField(dwId++, (src != nullptr) ? src->getTemplateItemId() : 0);
  812                dwId += 2;
  813             }
  814             else
  815             {
  816                    dwId += 3;
  817             }
  818             }
  819             else
  820             {
  821                 dwId += 4;
  822             }
  823             dwCount++;
  824         }
  825     }
  826    pMsg->setField(VID_NUM_ITEMS, dwCount);
  827 
  828     unlockDciAccess();
  829    return RCC_SUCCESS;
  830 }
  831 
  832 /**
  833  * Get threshold violation summary into NXCP message
  834  */
  835 UINT32 DataCollectionTarget::getThresholdSummary(NXCPMessage *msg, UINT32 baseId, UINT32 userId)
  836 {
  837     UINT32 varId = baseId;
  838 
  839     msg->setField(varId++, m_id);
  840     UINT32 countId = varId++;
  841     UINT32 count = 0;
  842 
  843     readLockDciAccess();
  844    for(int i = 0; i < m_dcObjects->size(); i++)
  845     {
  846         DCObject *object = m_dcObjects->get(i);
  847         if (object->hasValue() && (object->getType() == DCO_TYPE_ITEM) && (object->getStatus() == ITEM_STATUS_ACTIVE) && object->hasAccess(userId))
  848         {
  849             if (static_cast<DCItem*>(object)->hasActiveThreshold())
  850             {
  851                static_cast<DCItem*>(object)->fillLastValueMessage(msg, varId);
  852                 varId += 50;
  853                 count++;
  854             }
  855         }
  856     }
  857     unlockDciAccess();
  858     msg->setField(countId, count);
  859    return varId;
  860 }
  861 
  862 /**
  863  * Process new DCI value
  864  */
  865 bool DataCollectionTarget::processNewDCValue(const shared_ptr<DCObject>& dco, time_t currTime, void *value)
  866 {
  867    bool updateStatus;
  868     bool result = dco->processNewValue(currTime, value, &updateStatus);
  869     if (updateStatus)
  870     {
  871       calculateCompoundStatus(FALSE);
  872    }
  873    return result;
  874 }
  875 
  876 /**
  877  * Check if data collection is disabled
  878  */
  879 bool DataCollectionTarget::isDataCollectionDisabled()
  880 {
  881     return false;
  882 }
  883 
  884 /**
  885  * Put items which requires polling into the queue
  886  */
  887 void DataCollectionTarget::queueItemsForPolling()
  888 {
  889    if ((m_status == STATUS_UNMANAGED) || isDataCollectionDisabled() || m_isDeleted)
  890       return;  // Do not collect data for unmanaged objects or if data collection is disabled
  891 
  892    time_t currTime = time(nullptr);
  893 
  894    readLockDciAccess();
  895    for(int i = 0; i < m_dcObjects->size(); i++)
  896    {
  897         DCObject *object = m_dcObjects->get(i);
  898       if (m_dcObjects->get(i)->isReadyForPolling(currTime))
  899       {
  900          object->setBusyFlag();
  901 
  902          if ((object->getDataSource() == DS_NATIVE_AGENT) ||
  903              (object->getDataSource() == DS_WINPERF) ||
  904              (object->getDataSource() == DS_SNMP_AGENT) ||
  905              (object->getDataSource() == DS_SSH) ||
  906              (object->getDataSource() == DS_SMCLP))
  907          {
  908             uint32_t sourceNodeId = getEffectiveSourceNode(object);
  909             TCHAR key[32];
  910             _sntprintf(key, 32, _T("%08X/%s"), (sourceNodeId != 0) ? sourceNodeId : m_id, object->getDataProviderName());
  911             ThreadPoolExecuteSerialized(g_dataCollectorThreadPool, key, DataCollector, m_dcObjects->getShared(i));
  912          }
  913          else
  914          {
  915             ThreadPoolExecute(g_dataCollectorThreadPool, DataCollector, m_dcObjects->getShared(i));
  916          }
  917             nxlog_debug_tag(_T("obj.dc.queue"), 8, _T("DataCollectionTarget(%s)->QueueItemsForPolling(): item %d \"%s\" added to queue"),
  918                      m_name, object->getId(), object->getName().cstr());
  919       }
  920    }
  921    unlockDciAccess();
  922 }
  923 
  924 /**
  925  * Update time intervals in data collection objects
  926  */
  927 void DataCollectionTarget::updateDataCollectionTimeIntervals()
  928 {
  929    readLockDciAccess();
  930    for(int i = 0; i < m_dcObjects->size(); i++)
  931    {
  932       m_dcObjects->get(i)->updateTimeIntervals();
  933    }
  934    unlockDciAccess();
  935 }
  936 
  937 /**
  938  * Get object from parameter
  939  */
  940 shared_ptr<NetObj> DataCollectionTarget::objectFromParameter(const TCHAR *param) const
  941 {
  942    TCHAR *eptr, arg[256];
  943    AgentGetParameterArg(param, 1, arg, 256);
  944    UINT32 objectId = _tcstoul(arg, &eptr, 0);
  945    if (*eptr != 0)
  946    {
  947       // Argument is object's name
  948       objectId = 0;
  949    }
  950 
  951    // Find child object with requested ID or name
  952    shared_ptr<NetObj> object;
  953    readLockChildList();
  954    for(int i = 0; i < getChildList().size(); i++)
  955    {
  956       NetObj *curr = getChildList().get(i);
  957       if (((objectId == 0) && (!_tcsicmp(curr->getName(), arg))) ||
  958           (objectId == curr->getId()))
  959       {
  960          object = getChildList().getShared(i);
  961          break;
  962       }
  963    }
  964    unlockChildList();
  965    return object;
  966 }
  967 
  968 /**
  969  * Get value for server's internal table parameter
  970  */
  971 DataCollectionError DataCollectionTarget::getInternalTable(const TCHAR *name, Table **result)
  972 {
  973    return DCE_NOT_SUPPORTED;
  974 }
  975 
  976 /**
  977  * Get value for server's internal parameter
  978  */
  979 DataCollectionError DataCollectionTarget::getInternalMetric(const TCHAR *name, TCHAR *buffer, size_t size)
  980 {
  981    DataCollectionError error = DCE_SUCCESS;
  982 
  983    if (!_tcsicmp(name, _T("PollTime.Configuration.Average")))
  984    {
  985       _sntprintf(buffer, size, INT64_FMT, m_configurationPollState.getTimerAverage());
  986    }
  987    else if (!_tcsicmp(name, _T("PollTime.Configuration.Last")))
  988    {
  989       _sntprintf(buffer, size, INT64_FMT, m_configurationPollState.getTimerLast());
  990    }
  991    else if (!_tcsicmp(name, _T("PollTime.Configuration.Max")))
  992    {
  993       _sntprintf(buffer, size, INT64_FMT, m_configurationPollState.getTimerMax());
  994    }
  995    else if (!_tcsicmp(name, _T("PollTime.Configuration.Min")))
  996    {
  997       _sntprintf(buffer, size, INT64_FMT, m_configurationPollState.getTimerMin());
  998    }
  999    else if (!_tcsicmp(name, _T("PollTime.Instance.Average")))
 1000    {
 1001       _sntprintf(buffer, size, INT64_FMT, m_instancePollState.getTimerAverage());
 1002    }
 1003    else if (!_tcsicmp(name, _T("PollTime.Instance.Last")))
 1004    {
 1005       _sntprintf(buffer, size, INT64_FMT, m_instancePollState.getTimerLast());
 1006    }
 1007    else if (!_tcsicmp(name, _T("PollTime.Instance.Max")))
 1008    {
 1009       _sntprintf(buffer, size, INT64_FMT, m_instancePollState.getTimerMax());
 1010    }
 1011    else if (!_tcsicmp(name, _T("PollTime.Instance.Min")))
 1012    {
 1013       _sntprintf(buffer, size, INT64_FMT, m_instancePollState.getTimerMin());
 1014    }
 1015    else if (!_tcsicmp(name, _T("PollTime.Status.Average")))
 1016    {
 1017       _sntprintf(buffer, size, INT64_FMT, m_statusPollState.getTimerAverage());
 1018    }
 1019    else if (!_tcsicmp(name, _T("PollTime.Status.Last")))
 1020    {
 1021       _sntprintf(buffer, size, INT64_FMT, m_statusPollState.getTimerLast());
 1022    }
 1023    else if (!_tcsicmp(name, _T("PollTime.Status.Max")))
 1024    {
 1025       _sntprintf(buffer, size, INT64_FMT, m_statusPollState.getTimerMax());
 1026    }
 1027    else if (!_tcsicmp(name, _T("PollTime.Status.Min")))
 1028    {
 1029       _sntprintf(buffer, size, INT64_FMT, m_statusPollState.getTimerMin());
 1030    }
 1031    else if (!_tcsicmp(name, _T("Status")))
 1032    {
 1033       _sntprintf(buffer, size, _T("%d"), m_status);
 1034    }
 1035    else if (!_tcsicmp(name, _T("Dummy")) || MatchString(_T("Dummy(*)"), name, FALSE))
 1036    {
 1037       _tcscpy(buffer, _T("0"));
 1038    }
 1039    else if (MatchString(_T("ChildStatus(*)"), name, FALSE))
 1040    {
 1041       shared_ptr<NetObj> object = objectFromParameter(name);
 1042       if (object != nullptr)
 1043       {
 1044          _sntprintf(buffer, size, _T("%d"), object->getStatus());
 1045       }
 1046       else
 1047       {
 1048          error = DCE_NOT_SUPPORTED;
 1049       }
 1050    }
 1051    else if (MatchString(_T("ConditionStatus(*)"), name, FALSE))
 1052    {
 1053       TCHAR *pEnd, szArg[256];
 1054       shared_ptr<NetObj> pObject;
 1055 
 1056       AgentGetParameterArg(name, 1, szArg, 256);
 1057       uint32_t dwId = _tcstoul(szArg, &pEnd, 0);
 1058       if (*pEnd == 0)
 1059         {
 1060             pObject = FindObjectById(dwId);
 1061          if ((pObject != nullptr) && (pObject->getObjectClass() != OBJECT_CONDITION))
 1062             pObject.reset();
 1063         }
 1064         else
 1065       {
 1066          // Argument is object's name
 1067             pObject = FindObjectByName(szArg, OBJECT_CONDITION);
 1068       }
 1069 
 1070       if (pObject != nullptr)
 1071       {
 1072             if (pObject->isTrustedNode(m_id))
 1073             {
 1074                 _sntprintf(buffer, size, _T("%d"), pObject->getStatus());
 1075             }
 1076             else
 1077             {
 1078              error = DCE_NOT_SUPPORTED;
 1079             }
 1080       }
 1081       else
 1082       {
 1083          error = DCE_NOT_SUPPORTED;
 1084       }
 1085    }
 1086    else
 1087    {
 1088       error = DCE_NOT_SUPPORTED;
 1089    }
 1090 
 1091    return error;
 1092 }
 1093 
 1094 /**
 1095  * Run data collection script. Returns pointer to NXSL VM after successful run and nullptr on failure.
 1096  */
 1097 NXSL_VM *DataCollectionTarget::runDataCollectionScript(const TCHAR *param, DataCollectionTarget *targetObject)
 1098 {
 1099    TCHAR name[256];
 1100    _tcslcpy(name, param, 256);
 1101    Trim(name);
 1102 
 1103    // Can be in form parameter(arg1, arg2, ... argN)
 1104    TCHAR *p = _tcschr(name, _T('('));
 1105    if (p != nullptr)
 1106    {
 1107       size_t l = _tcslen(name) - 1;
 1108       if (name[l] != _T(')'))
 1109          return nullptr;
 1110       name[l] = 0;
 1111       *p = 0;
 1112    }
 1113 
 1114    NXSL_VM *vm = CreateServerScriptVM(name, self());
 1115    if (vm != nullptr)
 1116    {
 1117       ObjectRefArray<NXSL_Value> args(16, 16);
 1118       if ((p != nullptr) && !ParseValueList(vm, &p, args, true))
 1119       {
 1120          // argument parsing error
 1121          nxlog_debug(6, _T("DataCollectionTarget(%s)->runDataCollectionScript(%s): Argument parsing error"), m_name, param);
 1122          delete vm;
 1123          return nullptr;
 1124       }
 1125 
 1126       if (targetObject != nullptr)
 1127       {
 1128          vm->setGlobalVariable("$targetObject", targetObject->createNXSLObject(vm));
 1129       }
 1130       if (!vm->run(args))
 1131       {
 1132          nxlog_debug(6, _T("DataCollectionTarget(%s)->runDataCollectionScript(%s): Script execution error: %s"), m_name, param, vm->getErrorText());
 1133          time_t now = time(nullptr);
 1134          time_t lastReport = static_cast<time_t>(m_scriptErrorReports.getInt64(param, 0));
 1135          if (lastReport + ConfigReadInt(_T("DataCollection.ScriptErrorReportInterval"), 86400) < now)
 1136          {
 1137             PostSystemEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", name, vm->getErrorText(), m_id);
 1138             m_scriptErrorReports.set(param, static_cast<uint64_t>(now));
 1139          }
 1140          delete_and_null(vm);
 1141       }
 1142    }
 1143    else
 1144    {
 1145       nxlog_debug(6, _T("DataCollectionTarget(%s)->runDataCollectionScript(%s): VM load error"), m_name, param);
 1146    }
 1147    nxlog_debug(7, _T("DataCollectionTarget(%s)->runDataCollectionScript(%s): %s"), m_name, param, (vm != nullptr) ? _T("success") : _T("failure"));
 1148    return vm;
 1149 }
 1150 
 1151 /**
 1152  * Parse list of service call arguments
 1153  */
 1154 static bool ParseCallArgumensList(TCHAR *input, StringList *args)
 1155 {
 1156    TCHAR *p = input;
 1157 
 1158    TCHAR *s = p;
 1159    int state = 1; // normal text
 1160    for(; state > 0; p++)
 1161    {
 1162       switch(*p)
 1163       {
 1164          case '"':
 1165             if (state == 1)
 1166             {
 1167                state = 2;
 1168                s = p + 1;
 1169             }
 1170             else
 1171             {
 1172                state = 3;
 1173                *p = 0;
 1174                args->add(s);
 1175             }
 1176             break;
 1177          case ',':
 1178             if (state == 1)
 1179             {
 1180                *p = 0;
 1181                Trim(s);
 1182                args->add(s);
 1183                s = p + 1;
 1184             }
 1185             else if (state == 3)
 1186             {
 1187                state = 1;
 1188                s = p + 1;
 1189             }
 1190             break;
 1191          case 0:
 1192             if (state == 1)
 1193             {
 1194                Trim(s);
 1195                args->add(s);
 1196                state = 0;
 1197             }
 1198             else if (state == 3)
 1199             {
 1200                state = 0;
 1201             }
 1202             else
 1203             {
 1204                state = -1; // error
 1205             }
 1206             break;
 1207          case ' ':
 1208             break;
 1209          case ')':
 1210             if (state == 1)
 1211             {
 1212                *p = 0;
 1213                Trim(s);
 1214                args->add(s);
 1215                state = 0;
 1216             }
 1217             else if (state == 3)
 1218             {
 1219                state = 0;
 1220             }
 1221             break;
 1222          case '\\':
 1223             if (state == 2)
 1224             {
 1225                memmove(p, p + 1, _tcslen(p) * sizeof(TCHAR));
 1226                switch(*p)
 1227                {
 1228                   case 'r':
 1229                      *p = '\r';
 1230                      break;
 1231                   case 'n':
 1232                      *p = '\n';
 1233                      break;
 1234                   case 't':
 1235                      *p = '\t';
 1236                      break;
 1237                   default:
 1238                      break;
 1239                }
 1240             }
 1241             else if (state == 3)
 1242             {
 1243                state = -1;
 1244             }
 1245             break;
 1246          default:
 1247             if (state == 3)
 1248                state = -1;
 1249             break;
 1250       }
 1251    }
 1252    return (state != -1);
 1253 }
 1254 
 1255 /**
 1256  * Get item or list from web service
 1257  * Parameter is expected in form service:path or service(arguments):path
 1258  */
 1259 DataCollectionError DataCollectionTarget::queryWebService(const TCHAR *param, WebServiceRequestType queryType, TCHAR *buffer,
 1260          size_t bufSize, StringList *list)
 1261 {
 1262    uint32_t proxyId = getEffectiveWebServiceProxy();
 1263    shared_ptr<Node> proxyNode = static_pointer_cast<Node>(FindObjectById(proxyId, OBJECT_NODE));
 1264    if (proxyNode == nullptr)
 1265    {
 1266       nxlog_debug(7, _T("DataCollectionTarget(%s)->queryWebService(%s): cannot find proxy node [%u]"), m_name, param, proxyId);
 1267       return DCE_COMM_ERROR;
 1268    }
 1269 
 1270    TCHAR name[1024];
 1271    _tcslcpy(name, param, 1024);
 1272    Trim(name);
 1273 
 1274    TCHAR *path = nullptr;
 1275    bool openedBraceFound = false;
 1276    for (const TCHAR *curr = name; *curr != 0 && path == nullptr; curr++)
 1277    {
 1278       switch(*curr)
 1279       {
 1280          case ')':
 1281             openedBraceFound = false;
 1282             break;
 1283          case '(':
 1284             openedBraceFound = true;
 1285             break;
 1286          case '\'':
 1287             curr++;
 1288             while (*curr != '\'' && *curr != 0)
 1289             {
 1290                curr++;
 1291             }
 1292             break;
 1293          case '"':
 1294             curr++;
 1295             while (*curr != '"' && *curr != 0)
 1296             {
 1297                curr++;
 1298             }
 1299             break;
 1300          case ':':
 1301             if (!openedBraceFound)
 1302                path = const_cast<TCHAR *>(curr);
 1303             break;
 1304       }
 1305       if (*curr == 0)
 1306          curr--;
 1307    }
 1308    if (path == nullptr)
 1309    {
 1310       nxlog_debug(7, _T("DataCollectionTarget(%s)->queryWebService(%s): missing parameter path"), m_name, param);
 1311       return DCE_NOT_SUPPORTED;
 1312    }
 1313    *path = 0;
 1314    path++;
 1315 
 1316    // Can be in form service(arg1, arg2, ... argN)
 1317    StringList args;
 1318    TCHAR *p = _tcschr(name, _T('('));
 1319    if (p != nullptr)
 1320    {
 1321       size_t l = _tcslen(name) - 1;
 1322       if (name[l] != _T(')'))
 1323       {
 1324          nxlog_debug(7, _T("DataCollectionTarget(%s)->queryWebService(%s): error parsing argument list"), m_name, param);
 1325          return DCE_NOT_SUPPORTED;
 1326       }
 1327       name[l] = 0;
 1328       *p = 0;
 1329       p++;
 1330       if (!ParseCallArgumensList(p, &args))
 1331       {
 1332          nxlog_debug(7, _T("DataCollectionTarget(%s)->queryWebService(%s): error parsing argument list"), m_name, param);
 1333          return DCE_NOT_SUPPORTED;
 1334       }
 1335       Trim(name);
 1336    }
 1337 
 1338    shared_ptr<WebServiceDefinition> d = FindWebServiceDefinition(name);
 1339    if (d == nullptr)
 1340    {
 1341       nxlog_debug(7, _T("DataCollectionTarget(%s)->queryWebService(%s): cannot find web service definition"), m_name, param);
 1342       return DCE_NOT_SUPPORTED;
 1343    }
 1344 
 1345    shared_ptr<AgentConnectionEx> conn = proxyNode->acquireProxyConnection(WEB_SERVICE_PROXY);
 1346    if (conn == nullptr)
 1347    {
 1348       nxlog_debug(7, _T("DataCollectionTarget(%s)->queryWebService(%s): cannot acquire proxy connection"), m_name, param);
 1349       return DCE_COMM_ERROR;
 1350    }
 1351 
 1352    StringBuffer url = expandText(d->getUrl(), nullptr, nullptr, shared_ptr<DCObjectInfo>(), nullptr, nullptr, nullptr, nullptr, &args);
 1353 
 1354    StringMap headers;
 1355    auto it = d->getHeaders().constIterator();
 1356    while(it->hasNext())
 1357    {
 1358       auto h = it->next();
 1359       StringBuffer value = expandText(h->second, nullptr, nullptr, shared_ptr<DCObjectInfo>(), nullptr, nullptr, nullptr, nullptr, &args);
 1360       headers.set(h->first, value);
 1361    }
 1362    delete it;
 1363 
 1364    StringList pathList;
 1365    pathList.add(path);
 1366    StringMap results;
 1367    uint32_t agentStatus = conn->queryWebService(queryType, url, d->getRequestTimeout(), d->getCacheRetentionTime(),
 1368          d->getLogin(), d->getPassword(), d->getAuthType(), headers, pathList, d->isVerifyCertificate(), d->isVerifyHost(),
 1369          d->isForcePlainTextParser(), (queryType == WebServiceRequestType::PARAMETER) ? static_cast<void*>(&results) : static_cast<void*>(list));
 1370 
 1371    DataCollectionError rc;
 1372    if (agentStatus == ERR_SUCCESS)
 1373    {
 1374       if (queryType == WebServiceRequestType::PARAMETER)
 1375       {
 1376          const TCHAR *value = results.get(pathList.get(0));
 1377          if (value != nullptr)
 1378          {
 1379             _tcslcpy(buffer, value, bufSize);
 1380             rc = DCE_SUCCESS;
 1381          }
 1382          else
 1383          {
 1384             rc = DCE_NO_SUCH_INSTANCE;
 1385          }
 1386       }
 1387       else
 1388       {
 1389          rc = DCE_SUCCESS;
 1390       }
 1391    }
 1392    else
 1393    {
 1394       rc = DCE_COMM_ERROR;
 1395    }
 1396    nxlog_debug(7, _T("DataCollectionTarget(%s)->queryWebService(%s): rc=%d"), m_name, param, rc);
 1397    return rc;
 1398 }
 1399 
 1400 /**
 1401  * Get item from web service
 1402  * Parameter is expected in form service:path or service(arguments):path
 1403  */
 1404 DataCollectionError DataCollectionTarget::getMetricFromWebService(const TCHAR *param, TCHAR *buffer, size_t bufSize)
 1405 {
 1406    return queryWebService(param, WebServiceRequestType::PARAMETER, buffer, bufSize, nullptr);
 1407 }
 1408 
 1409 /**
 1410  * Get list from library script
 1411  * Parameter is expected in form service:path or service(arguments):path
 1412  */
 1413 DataCollectionError DataCollectionTarget::getListFromWebService(const TCHAR *param, StringList **list)
 1414 {
 1415    *list = new StringList();
 1416    DataCollectionError rc = queryWebService(param, WebServiceRequestType::LIST, nullptr, 0, *list);
 1417    if (rc != DCE_SUCCESS)
 1418    {
 1419       delete *list;
 1420       *list = nullptr;
 1421    }
 1422    return rc;
 1423 }
 1424 
 1425 /**
 1426  * Get parameter value from NXSL script
 1427  */
 1428 DataCollectionError DataCollectionTarget::getMetricFromScript(const TCHAR *param, TCHAR *buffer, size_t bufSize, DataCollectionTarget *targetObject)
 1429 {
 1430    DataCollectionError rc = DCE_NOT_SUPPORTED;
 1431    NXSL_VM *vm = runDataCollectionScript(param, targetObject);
 1432    if (vm != nullptr)
 1433    {
 1434       NXSL_Value *value = vm->getResult();
 1435       if (value->isNull())
 1436       {
 1437          // nullptr value is an error indicator
 1438          rc = DCE_COLLECTION_ERROR;
 1439       }
 1440       else
 1441       {
 1442          const TCHAR *dciValue = value->getValueAsCString();
 1443          _tcslcpy(buffer, CHECK_NULL_EX(dciValue), bufSize);
 1444          rc = DCE_SUCCESS;
 1445       }
 1446       delete vm;
 1447    }
 1448    nxlog_debug(7, _T("DataCollectionTarget(%s)->getScriptItem(%s): rc=%d"), m_name, param, rc);
 1449    return rc;
 1450 }
 1451 
 1452 /**
 1453  * Get list from library script
 1454  */
 1455 DataCollectionError DataCollectionTarget::getListFromScript(const TCHAR *param, StringList **list, DataCollectionTarget *targetObject)
 1456 {
 1457    DataCollectionError rc = DCE_NOT_SUPPORTED;
 1458    NXSL_VM *vm = runDataCollectionScript(param, targetObject);
 1459    if (vm != nullptr)
 1460    {
 1461       rc = DCE_SUCCESS;
 1462       NXSL_Value *value = vm->getResult();
 1463       if (value->isArray())
 1464       {
 1465          *list = value->getValueAsArray()->toStringList();
 1466       }
 1467       else if (value->isString())
 1468       {
 1469          *list = new StringList;
 1470          (*list)->add(value->getValueAsCString());
 1471       }
 1472       else if (value->isNull())
 1473       {
 1474          rc = DCE_COLLECTION_ERROR;
 1475       }
 1476       else
 1477       {
 1478          *list = new StringList;
 1479       }
 1480       delete vm;
 1481    }
 1482    nxlog_debug(7, _T("DataCollectionTarget(%s)->getListFromScript(%s): rc=%d"), m_name, param, rc);
 1483    return rc;
 1484 }
 1485 
 1486 /**
 1487  * Get table from NXSL script
 1488  */
 1489 DataCollectionError DataCollectionTarget::getTableFromScript(const TCHAR *param, Table **result, DataCollectionTarget *targetObject)
 1490 {
 1491    DataCollectionError rc = DCE_NOT_SUPPORTED;
 1492    NXSL_VM *vm = runDataCollectionScript(param, targetObject);
 1493    if (vm != nullptr)
 1494    {
 1495       NXSL_Value *value = vm->getResult();
 1496       if (value->isObject(_T("Table")))
 1497       {
 1498          *result = (Table *)value->getValueAsObject()->getData();
 1499          (*result)->incRefCount();
 1500          rc = DCE_SUCCESS;
 1501       }
 1502       else
 1503       {
 1504          rc = DCE_COLLECTION_ERROR;
 1505       }
 1506       delete vm;
 1507    }
 1508    nxlog_debug(7, _T("DataCollectionTarget(%s)->getScriptTable(%s): rc=%d"), m_name, param, rc);
 1509    return rc;
 1510 }
 1511 
 1512 /**
 1513  * Get string map from library script
 1514  */
 1515 DataCollectionError DataCollectionTarget::getStringMapFromScript(const TCHAR *param, StringMap **map, DataCollectionTarget *targetObject)
 1516 {
 1517    DataCollectionError rc = DCE_NOT_SUPPORTED;
 1518    NXSL_VM *vm = runDataCollectionScript(param, targetObject);
 1519    if (vm != nullptr)
 1520    {
 1521       rc = DCE_SUCCESS;
 1522       NXSL_Value *value = vm->getResult();
 1523       if (value->isHashMap())
 1524       {
 1525          *map = value->getValueAsHashMap()->toStringMap();
 1526       }
 1527       else if (value->isArray())
 1528       {
 1529          *map = new StringMap();
 1530          NXSL_Array *a = value->getValueAsArray();
 1531          for(int i = 0; i < a->size(); i++)
 1532          {
 1533             NXSL_Value *v = a->getByPosition(i);
 1534             if (v->isString())
 1535             {
 1536                (*map)->set(v->getValueAsCString(), v->getValueAsCString());
 1537             }
 1538          }
 1539       }
 1540       else if (value->isString())
 1541       {
 1542          *map = new StringMap();
 1543          (*map)->set(value->getValueAsCString(), value->getValueAsCString());
 1544       }
 1545       else if (value->isNull())
 1546       {
 1547          rc = DCE_COLLECTION_ERROR;
 1548       }
 1549       else
 1550       {
 1551          *map = new StringMap();
 1552       }
 1553       delete vm;
 1554    }
 1555    nxlog_debug(7, _T("DataCollectionTarget(%s)->getListFromScript(%s): rc=%d"), m_name, param, rc);
 1556    return rc;
 1557 }
 1558 
 1559 /**
 1560  * Get last (current) DCI values for summary table.
 1561  */
 1562 void DataCollectionTarget::getDciValuesSummary(SummaryTable *tableDefinition, Table *tableData, UINT32 userId)
 1563 {
 1564    if (tableDefinition->isTableDciSource())
 1565       getTableDciValuesSummary(tableDefinition, tableData, userId);
 1566    else
 1567       getItemDciValuesSummary(tableDefinition, tableData, userId);
 1568 }
 1569 
 1570 /**
 1571  * Get last (current) DCI values for summary table using single-value DCIs
 1572  */
 1573 void DataCollectionTarget::getItemDciValuesSummary(SummaryTable *tableDefinition, Table *tableData, UINT32 userId)
 1574 {
 1575    int offset = tableDefinition->isMultiInstance() ? 2 : 1;
 1576    int baseRow = tableData->getNumRows();
 1577    bool rowAdded = false;
 1578    readLockDciAccess();
 1579    for(int i = 0; i < tableDefinition->getNumColumns(); i++)
 1580    {
 1581       SummaryTableColumn *tc = tableDefinition->getColumn(i);
 1582       for(int j = 0; j < m_dcObjects->size(); j++)
 1583        {
 1584            DCObject *object = m_dcObjects->get(j);
 1585          if ((object->getType() == DCO_TYPE_ITEM) && object->hasValue() &&
 1586              (object->getStatus() == ITEM_STATUS_ACTIVE) &&
 1587              ((tc->m_flags & COLUMN_DEFINITION_REGEXP_MATCH) ?
 1588                RegexpMatch(object->getName(), tc->m_dciName, FALSE) :
 1589                !_tcsicmp(object->getName(), tc->m_dciName)
 1590              ) && object->hasAccess(userId))
 1591          {
 1592             int row;
 1593             if (tableDefinition->isMultiInstance())
 1594             {
 1595                // Find instance
 1596                const TCHAR *instance = object->getInstance();
 1597                for(row = baseRow; row < tableData->getNumRows(); row++)
 1598                {
 1599                   const TCHAR *v = tableData->getAsString(row, 1);
 1600                   if (!_tcscmp(CHECK_NULL_EX(v), instance))
 1601                      break;
 1602                }
 1603                if (row == tableData->getNumRows())
 1604                {
 1605                   tableData->addRow();
 1606                   tableData->set(0, m_name);
 1607                   tableData->set(1, instance);
 1608                   tableData->setObjectId(m_id);
 1609                }
 1610             }
 1611             else
 1612             {
 1613                if (!rowAdded)
 1614                {
 1615                   tableData->addRow();
 1616                   tableData->set(0, m_name);
 1617                   tableData->setObjectId(m_id);
 1618                   rowAdded = true;
 1619                }
 1620                row = tableData->getNumRows() - 1;
 1621             }
 1622             tableData->setStatusAt(row, i + offset, ((DCItem *)object)->getThresholdSeverity());
 1623             tableData->setCellObjectIdAt(row, i + offset, object->getId());
 1624             tableData->getColumnDefinitions()->get(i + offset)->setDataType(((DCItem *)object)->getDataType());
 1625             if (tableDefinition->getAggregationFunction() == F_LAST)
 1626             {
 1627                if (tc->m_flags & COLUMN_DEFINITION_MULTIVALUED)
 1628                {
 1629                   StringList *values = String(((DCItem *)object)->getLastValue()).split(tc->m_separator);
 1630                   tableData->setAt(row, i + offset, values->get(0));
 1631                   for(int r = 1; r < values->size(); r++)
 1632                   {
 1633                      if (row + r >= tableData->getNumRows())
 1634                      {
 1635                         tableData->addRow();
 1636                         tableData->setObjectId(m_id);
 1637                         tableData->setBaseRow(row);
 1638                      }
 1639                      tableData->setAt(row + r, i + offset, values->get(r));
 1640                      tableData->setStatusAt(row + r, i + offset, ((DCItem *)object)->getThresholdSeverity());
 1641                      tableData->setCellObjectIdAt(row + r, i + offset, object->getId());
 1642                   }
 1643                }
 1644                else
 1645                {
 1646                   tableData->setAt(row, i + offset, ((DCItem *)object)->getLastValue());
 1647                }
 1648             }
 1649             else
 1650             {
 1651                tableData->setPreallocatedAt(row, i + offset,
 1652                   ((DCItem *)object)->getAggregateValue(
 1653                      tableDefinition->getAggregationFunction(),
 1654                      tableDefinition->getPeriodStart(),
 1655                      tableDefinition->getPeriodEnd()));
 1656             }
 1657 
 1658             if (!tableDefinition->isMultiInstance())
 1659                break;
 1660          }
 1661       }
 1662    }
 1663    unlockDciAccess();
 1664 }
 1665 
 1666 /**
 1667  * Get last (current) DCI values for summary table using table DCIs
 1668  */
 1669 void DataCollectionTarget::getTableDciValuesSummary(SummaryTable *tableDefinition, Table *tableData, UINT32 userId)
 1670 {
 1671    readLockDciAccess();
 1672    for(int i = 0; i < m_dcObjects->size(); i++)
 1673    {
 1674       DCObject *o = m_dcObjects->get(i);
 1675       if ((o->getType() == DCO_TYPE_TABLE) && o->hasValue() &&
 1676            (o->getStatus() == ITEM_STATUS_ACTIVE) &&
 1677            !_tcsicmp(o->getName(), tableDefinition->getTableDciName()) &&
 1678            o->hasAccess(userId))
 1679       {
 1680          Table *lastValue = ((DCTable*)o)->getLastValue();
 1681          if (lastValue == nullptr)
 1682             continue;
 1683 
 1684          for(int j = 0; j < lastValue->getNumRows(); j++)
 1685          {
 1686             tableData->addRow();
 1687             tableData->setObjectId(m_id);
 1688             tableData->set(0, m_name);
 1689             for(int k = 0; k < lastValue->getNumColumns(); k++)
 1690             {
 1691                int columnIndex = tableData->getColumnIndex(lastValue->getColumnName(k));
 1692                if (columnIndex == -1)
 1693                   columnIndex = tableData->addColumn(lastValue->getColumnDefinition(k));
 1694                tableData->set(columnIndex, lastValue->getAsString(j, k));
 1695             }
 1696          }
 1697       }
 1698    }
 1699    unlockDciAccess();
 1700 }
 1701 
 1702 /**
 1703  * Must return true if object is a possible event source
 1704  */
 1705 bool DataCollectionTarget::isEventSource() const
 1706 {
 1707    return true;
 1708 }
 1709 
 1710 /**
 1711  * Returns most critical status of DCI used for
 1712  * status calculation
 1713  */
 1714 int DataCollectionTarget::getMostCriticalDCIStatus()
 1715 {
 1716    int status = -1;
 1717    readLockDciAccess();
 1718    for(int i = 0; i < m_dcObjects->size(); i++)
 1719     {
 1720         DCObject *curr = m_dcObjects->get(i);
 1721       if (curr->isStatusDCO() && (curr->getType() == DCO_TYPE_ITEM) &&
 1722           curr->hasValue() && (curr->getStatus() == ITEM_STATUS_ACTIVE))
 1723       {
 1724          if (getObjectClass() == OBJECT_CLUSTER && !curr->isAggregateOnCluster())
 1725             continue; // Calculated only on those that are aggregated on cluster
 1726 
 1727          ItemValue *value = static_cast<DCItem*>(curr)->getInternalLastValue();
 1728          if (value != nullptr && (INT32)*value >= STATUS_NORMAL && (INT32)*value <= STATUS_CRITICAL)
 1729             status = std::max(status, (INT32)*value);
 1730          delete value;
 1731       }
 1732     }
 1733    unlockDciAccess();
 1734    return (status == -1) ? STATUS_UNKNOWN : status;
 1735 }
 1736 
 1737 /**
 1738  * Set object's management status
 1739  */
 1740 bool DataCollectionTarget::setMgmtStatus(bool isManaged)
 1741 {
 1742    return super::setMgmtStatus(isManaged);
 1743 }
 1744 
 1745 /**
 1746  * Calculate compound status
 1747  */
 1748 void DataCollectionTarget::calculateCompoundStatus(BOOL bForcedRecalc)
 1749 {
 1750    super::calculateCompoundStatus(bForcedRecalc);
 1751 }
 1752 
 1753 /**
 1754  * Enter maintenance mode
 1755  */
 1756 void DataCollectionTarget::enterMaintenanceMode(uint32_t userId, const TCHAR *comments)
 1757 {
 1758    TCHAR userName[MAX_USER_NAME];
 1759    ResolveUserId(userId, userName, true);
 1760 
 1761    DbgPrintf(4, _T("Entering maintenance mode for %s [%d] (initiated by %s)"), m_name, m_id, userName);
 1762    UINT64 eventId = PostSystemEvent2(EVENT_MAINTENANCE_MODE_ENTERED, m_id, "sds", CHECK_NULL_EX(comments), userId, userName);
 1763 
 1764    readLockDciAccess();
 1765    for(int i = 0; i < m_dcObjects->size(); i++)
 1766    {
 1767       DCObject *dco = m_dcObjects->get(i);
 1768       if (dco->getStatus() == ITEM_STATUS_DISABLED)
 1769          continue;
 1770 
 1771       dco->updateThresholdsBeforeMaintenanceState();
 1772    }
 1773    unlockDciAccess();
 1774 
 1775    lockProperties();
 1776    m_maintenanceEventId = eventId;
 1777    m_maintenanceInitiator = userId;
 1778    m_stateBeforeMaintenance = m_state;
 1779    setModified(MODIFY_COMMON_PROPERTIES | MODIFY_DATA_COLLECTION);
 1780    unlockProperties();
 1781 }
 1782 
 1783 /**
 1784  * Leave maintenance mode
 1785  */
 1786 void DataCollectionTarget::leaveMaintenanceMode(uint32_t userId)
 1787 {
 1788    TCHAR userName[MAX_USER_NAME];
 1789    ResolveUserId(userId, userName, true);
 1790 
 1791    DbgPrintf(4, _T("Leaving maintenance mode for %s [%d] (initiated by %s)"), m_name, m_id, userName);
 1792    PostSystemEvent(EVENT_MAINTENANCE_MODE_LEFT, m_id, "ds", userId, userName);
 1793 
 1794    readLockDciAccess();
 1795    for(int i = 0; i < m_dcObjects->size(); i++)
 1796    {
 1797       DCObject *dco = m_dcObjects->get(i);
 1798       if (dco->getStatus() == ITEM_STATUS_DISABLED)
 1799       {
 1800          continue;
 1801       }
 1802 
 1803       dco->generateEventsBasedOnThrDiff();
 1804    }
 1805    unlockDciAccess();
 1806 
 1807    lockProperties();
 1808    m_maintenanceEventId = 0;
 1809    m_maintenanceInitiator = 0;
 1810    bool forcePoll = m_state != m_stateBeforeMaintenance;
 1811    m_state = m_stateBeforeMaintenance;
 1812    setModified(MODIFY_COMMON_PROPERTIES);
 1813    unlockProperties();
 1814 
 1815    if (forcePoll)
 1816    {
 1817       startForcedStatusPoll();
 1818       TCHAR threadKey[32];
 1819       _sntprintf(threadKey, 32, _T("POLL_%u"), getId());
 1820       ThreadPoolExecuteSerialized(g_pollerThreadPool, threadKey, self(), &DataCollectionTarget::statusPollWorkerEntry, RegisterPoller(PollerType::STATUS, self()));
 1821    }
 1822 }
 1823 
 1824 /**
 1825  * Update cache size for given data collection item
 1826  */
 1827 void DataCollectionTarget::updateDCItemCacheSize(UINT32 dciId, UINT32 conditionId)
 1828 {
 1829    readLockDciAccess();
 1830    shared_ptr<DCObject> dci = getDCObjectById(dciId, 0, false);
 1831    if ((dci != nullptr) && (dci->getType() == DCO_TYPE_ITEM))
 1832    {
 1833       static_cast<DCItem*>(dci.get())->updateCacheSize(conditionId);
 1834    }
 1835    unlockDciAccess();
 1836 }
 1837 
 1838 /**
 1839  * Reload DCI cache
 1840  */
 1841 void DataCollectionTarget::reloadDCItemCache(UINT32 dciId)
 1842 {
 1843    readLockDciAccess();
 1844    shared_ptr<DCObject> dci = getDCObjectById(dciId, 0, false);
 1845    if ((dci != nullptr) && (dci->getType() == DCO_TYPE_ITEM))
 1846    {
 1847       nxlog_debug_tag(_T("obj.dc.cache"), 6, _T("Reload DCI cache for \"%s\" [%d] on %s [%d]"),
 1848                dci->getName().cstr(), dci->getId(), m_name, m_id);
 1849       static_cast<DCItem*>(dci.get())->reloadCache(true);
 1850    }
 1851    unlockDciAccess();
 1852 }
 1853 
 1854 /**
 1855  * Returns true if object is data collection target
 1856  */
 1857 bool DataCollectionTarget::isDataCollectionTarget() const
 1858 {
 1859    return true;
 1860 }
 1861 
 1862 /**
 1863  * Add data collection element to proxy info structure
 1864  */
 1865 void DataCollectionTarget::addProxyDataCollectionElement(ProxyInfo *info, const DCObject *dco, UINT32 primaryProxyId)
 1866 {
 1867    info->msg->setField(info->baseInfoFieldId++, dco->getId());
 1868    info->msg->setField(info->baseInfoFieldId++, static_cast<int16_t>(dco->getType()));
 1869    info->msg->setField(info->baseInfoFieldId++, static_cast<int16_t>(dco->getDataSource()));
 1870    info->msg->setField(info->baseInfoFieldId++, dco->getName());
 1871    info->msg->setField(info->baseInfoFieldId++, dco->getEffectivePollingInterval());
 1872    info->msg->setFieldFromTime(info->baseInfoFieldId++, dco->getLastPollTime());
 1873    info->msg->setField(info->baseInfoFieldId++, m_guid);
 1874    info->msg->setField(info->baseInfoFieldId++, dco->getSnmpPort());
 1875    if (dco->getType() == DCO_TYPE_ITEM)
 1876       info->msg->setField(info->baseInfoFieldId++, static_cast<const DCItem*>(dco)->getSnmpRawValueType());
 1877    else
 1878       info->msg->setField(info->baseInfoFieldId++, (INT16)0);
 1879    info->msg->setField(info->baseInfoFieldId++, primaryProxyId);
 1880 
 1881    if (dco->getDataSource() == DS_SNMP_AGENT)
 1882    {
 1883       info->msg->setField(info->extraInfoFieldId++, static_cast<int16_t>(dco->getSnmpVersion()));
 1884       if (dco->getType() == DCO_TYPE_TABLE)
 1885       {
 1886          info->extraInfoFieldId += 8;
 1887          const ObjectArray<DCTableColumn> &columns = static_cast<const DCTable*>(dco)->getColumns();
 1888          int count = std::min(columns.size(), 99);
 1889          info->msg->setField(info->extraInfoFieldId++, count);
 1890          for(int i = 0; i < count; i++)
 1891          {
 1892             columns.get(i)->fillMessage(info->msg, info->extraInfoFieldId);
 1893             info->extraInfoFieldId += 10;
 1894          }
 1895          info->extraInfoFieldId += (99 - count) * 10;
 1896       }
 1897       else
 1898       {
 1899          info->extraInfoFieldId += 999;
 1900       }
 1901    }
 1902    else
 1903    {
 1904       info->extraInfoFieldId += 1000;
 1905    }
 1906 
 1907    if (dco->isAdvancedSchedule())
 1908    {
 1909       dco->fillSchedulingDataMessage(info->msg, info->extraInfoFieldId);
 1910    }
 1911    info->extraInfoFieldId += 100;
 1912 
 1913    info->count++;
 1914 }
 1915 
 1916 /**
 1917  * Add SNMP target to proxy info structure
 1918  */
 1919 void DataCollectionTarget::addProxySnmpTarget(ProxyInfo *info, const Node *node)
 1920 {
 1921    info->msg->setField(info->nodeInfoFieldId++, m_guid);
 1922    info->msg->setField(info->nodeInfoFieldId++, node->getIpAddress());
 1923    info->msg->setField(info->nodeInfoFieldId++, node->getSNMPVersion());
 1924    info->msg->setField(info->nodeInfoFieldId++, node->getSNMPPort());
 1925    SNMP_SecurityContext *snmpSecurity = node->getSnmpSecurityContext();
 1926    info->msg->setField(info->nodeInfoFieldId++, (INT16)snmpSecurity->getAuthMethod());
 1927    info->msg->setField(info->nodeInfoFieldId++, (INT16)snmpSecurity->getPrivMethod());
 1928    info->msg->setFieldFromMBString(info->nodeInfoFieldId++, snmpSecurity->getUser());
 1929    info->msg->setFieldFromMBString(info->nodeInfoFieldId++, snmpSecurity->getAuthPassword());
 1930    info->msg->setFieldFromMBString(info->nodeInfoFieldId++, snmpSecurity->getPrivPassword());
 1931    delete snmpSecurity;
 1932    info->nodeInfoFieldId += 41;
 1933    info->nodeInfoCount++;
 1934 }
 1935 
 1936 /**
 1937  * Collect info for SNMP proxy and DCI source (proxy) nodes
 1938  * Default implementation adds only agent based DCIs with source node set to requesting node
 1939  */
 1940 void DataCollectionTarget::collectProxyInfo(ProxyInfo *info)
 1941 {
 1942    if ((m_status == STATUS_UNMANAGED) || (m_state & DCSF_UNREACHABLE))
 1943       return;
 1944 
 1945    readLockDciAccess();
 1946    for(int i = 0; i < m_dcObjects->size(); i++)
 1947    {
 1948       DCObject *dco = m_dcObjects->get(i);
 1949       if (dco->getStatus() == ITEM_STATUS_DISABLED)
 1950          continue;
 1951 
 1952       if ((dco->getDataSource() == DS_NATIVE_AGENT) && (dco->getSourceNode() == info->proxyId) &&
 1953           dco->hasValue() && (dco->getAgentCacheMode() == AGENT_CACHE_ON))
 1954       {
 1955          addProxyDataCollectionElement(info, dco, 0);
 1956       }
 1957    }
 1958    unlockDciAccess();
 1959 }
 1960 
 1961 /**
 1962  * Callback for collecting proxied SNMP DCIs
 1963  */
 1964 void DataCollectionTarget::collectProxyInfoCallback(NetObj *object, void *data)
 1965 {
 1966    static_cast<DataCollectionTarget*>(object)->collectProxyInfo(static_cast<ProxyInfo*>(data));
 1967 }
 1968 
 1969 /**
 1970  * Get effective source node for given data collection object
 1971  */
 1972 uint32_t DataCollectionTarget::getEffectiveSourceNode(DCObject *dco)
 1973 {
 1974    return dco->getSourceNode();
 1975 }
 1976 
 1977 /**
 1978  * Filter for selecting templates from objects
 1979  */
 1980 static bool TemplateSelectionFilter(NetObj *object, void *userData)
 1981 {
 1982    return (object->getObjectClass() == OBJECT_TEMPLATE) && !object->isDeleted() && static_cast<Template*>(object)->isAutoBindEnabled();
 1983 }
 1984 
 1985 /**
 1986  * Apply templates
 1987  */
 1988 void DataCollectionTarget::applyTemplates()
 1989 {
 1990    if (IsShutdownInProgress())
 1991       return;
 1992 
 1993    sendPollerMsg(_T("Processing template autoapply rules\r\n"));
 1994    SharedObjectArray<NetObj> *templates = g_idxObjectById.getObjects(TemplateSelectionFilter);
 1995    for(int i = 0; i < templates->size(); i++)
 1996    {
 1997       Template *templateObject = static_cast<Template*>(templates->get(i));
 1998       AutoBindDecision decision = templateObject->isApplicable(self());
 1999       if (decision == AutoBindDecision_Bind)
 2000       {
 2001          if (!templateObject->isDirectChild(m_id))
 2002          {
 2003             sendPollerMsg(_T("   Applying template %s\r\n"), templateObject->getName());
 2004             nxlog_debug_tag(_T("obj.bind"), 4, _T("DataCollectionTarget::applyUserTemplates(): applying template %d \"%s\" to object %d \"%s\""),
 2005                       templateObject->getId(), templateObject->getName(), m_id, m_name);
 2006             templateObject->applyToTarget(self());
 2007             PostSystemEvent(EVENT_TEMPLATE_AUTOAPPLY, g_dwMgmtNode, "isis", m_id, m_name, templateObject->getId(), templateObject->getName());
 2008          }
 2009       }
 2010       else if (decision == AutoBindDecision_Unbind)
 2011       {
 2012          if (templateObject->isAutoUnbindEnabled() && templateObject->isDirectChild(m_id))
 2013          {
 2014             sendPollerMsg(_T("   Removing template %s\r\n"), templateObject->getName());
 2015             nxlog_debug_tag(_T("obj.bind"), 4, _T("DataCollectionTarget::applyUserTemplates(): removing template %d \"%s\" from object %d \"%s\""),
 2016                       templateObject->getId(), templateObject->getName(), m_id, m_name);
 2017             templateObject->deleteChild(*this);
 2018             deleteParent(*templateObject);
 2019             templateObject->queueRemoveFromTarget(m_id, true);
 2020             PostSystemEvent(EVENT_TEMPLATE_AUTOREMOVE, g_dwMgmtNode, "isis", m_id, m_name, templateObject->getId(), templateObject->getName());
 2021          }
 2022       }
 2023    }
 2024    delete templates;
 2025 }
 2026 
 2027 /**
 2028  * Filter for selecting containers from objects
 2029  */
 2030 static bool ContainerSelectionFilter(NetObj *object, void *userData)
 2031 {
 2032    return (object->getObjectClass() == OBJECT_CONTAINER) && !object->isDeleted() && ((Container *)object)->isAutoBindEnabled();
 2033 }
 2034 
 2035 /**
 2036  * Update container membership
 2037  */
 2038 void DataCollectionTarget::updateContainerMembership()
 2039 {
 2040    if (IsShutdownInProgress())
 2041       return;
 2042 
 2043    sendPollerMsg(_T("Processing container autobind rules\r\n"));
 2044    SharedObjectArray<NetObj> *containers = g_idxObjectById.getObjects(ContainerSelectionFilter);
 2045    for(int i = 0; i < containers->size(); i++)
 2046    {
 2047       Container *container = static_cast<Container*>(containers->get(i));
 2048       AutoBindDecision decision = container->isApplicable(self());
 2049       if (decision == AutoBindDecision_Bind)
 2050       {
 2051          if (!container->isDirectChild(m_id))
 2052          {
 2053             sendPollerMsg(_T("   Binding to container %s\r\n"), container->getName());
 2054             nxlog_debug_tag(_T("obj.bind"), 4, _T("DataCollectionTarget::updateContainerMembership(): binding object %d \"%s\" to container %d \"%s\""),
 2055                       m_id, m_name, container->getId(), container->getName());
 2056             container->addChild(self());
 2057             addParent(container->self());
 2058             PostSystemEvent(EVENT_CONTAINER_AUTOBIND, g_dwMgmtNode, "isis", m_id, m_name, container->getId(), container->getName());
 2059             container->calculateCompoundStatus();
 2060          }
 2061       }
 2062       else if (decision == AutoBindDecision_Unbind)
 2063       {
 2064          if (container->isAutoUnbindEnabled() && container->isDirectChild(m_id))
 2065          {
 2066             sendPollerMsg(_T("   Removing from container %s\r\n"), container->getName());
 2067             nxlog_debug_tag(_T("obj.bind"), 4, _T("DataCollectionTarget::updateContainerMembership(): removing object %d \"%s\" from container %d \"%s\""),
 2068                       m_id, m_name, container->getId(), container->getName());
 2069             container->deleteChild(*this);
 2070             deleteParent(*container);
 2071             PostSystemEvent(EVENT_CONTAINER_AUTOUNBIND, g_dwMgmtNode, "isis", m_id, m_name, container->getId(), container->getName());
 2072             container->calculateCompoundStatus();
 2073          }
 2074       }
 2075    }
 2076    delete containers;
 2077 }
 2078 
 2079 /**
 2080  * Serialize object to JSON
 2081  */
 2082 json_t *DataCollectionTarget::toJson()
 2083 {
 2084    return super::toJson();
 2085 }
 2086 
 2087 /**
 2088  * Entry point for status poll worker thread
 2089  */
 2090 void DataCollectionTarget::statusPollWorkerEntry(PollerInfo *poller)
 2091 {
 2092    statusPollWorkerEntry(poller, nullptr, 0);
 2093 }
 2094 
 2095 /**
 2096  * Entry point for status poll worker thread
 2097  */
 2098 void DataCollectionTarget::statusPollWorkerEntry(PollerInfo *poller, ClientSession *session, UINT32 rqId)
 2099 {
 2100    poller->startExecution();
 2101    statusPoll(poller, session, rqId);
 2102    delete poller;
 2103 }
 2104 
 2105 /**
 2106  * Entry point for second level status poll (called by parent object)
 2107  */
 2108 void DataCollectionTarget::statusPollPollerEntry(PollerInfo *poller, ClientSession *session, UINT32 rqId)
 2109 {
 2110    poller->setStatus(_T("child poll"));
 2111    statusPoll(poller, session, rqId);
 2112 }
 2113 
 2114 /**
 2115  * Perform status poll on this data collection target. Default implementation do nothing.
 2116  */
 2117 void DataCollectionTarget::statusPoll(PollerInfo *poller, ClientSession *session, UINT32 rqId)
 2118 {
 2119 }
 2120 
 2121 /**
 2122  * Entry point for configuration poll worker thread
 2123  */
 2124 void DataCollectionTarget::configurationPollWorkerEntry(PollerInfo *poller)
 2125 {
 2126    configurationPollWorkerEntry(poller, nullptr, 0);
 2127 }
 2128 
 2129 /**
 2130  * Entry point for configuration poll worker thread
 2131  */
 2132 void DataCollectionTarget::configurationPollWorkerEntry(PollerInfo *poller, ClientSession *session, UINT32 rqId)
 2133 {
 2134    poller->startExecution();
 2135    poller->startObjectTransaction();
 2136    configurationPoll(poller, session, rqId);
 2137    poller->endObjectTransaction();
 2138    delete poller;
 2139 }
 2140 
 2141 /**
 2142  * Perform configuration poll on this data collection target. Default implementation do nothing.
 2143  */
 2144 void DataCollectionTarget::configurationPoll(PollerInfo *poller, ClientSession *session, UINT32 rqId)
 2145 {
 2146 }
 2147 
 2148 /**
 2149  * Entry point for instance discovery poll worker thread
 2150  */
 2151 void DataCollectionTarget::instanceDiscoveryPollWorkerEntry(PollerInfo *poller)
 2152 {
 2153    instanceDiscoveryPollWorkerEntry(poller, nullptr, 0);
 2154 }
 2155 
 2156 /**
 2157  * Entry point for instance discovery poll worker thread
 2158  */
 2159 void DataCollectionTarget::instanceDiscoveryPollWorkerEntry(PollerInfo *poller, ClientSession *session, UINT32 requestId)
 2160 {
 2161    poller->startExecution();
 2162    poller->startObjectTransaction();
 2163    instanceDiscoveryPoll(poller, session, requestId);
 2164    poller->endObjectTransaction();
 2165    delete poller;
 2166 }
 2167 
 2168 /**
 2169  * Perform instance discovery poll on data collection target
 2170  */
 2171 void DataCollectionTarget::instanceDiscoveryPoll(PollerInfo *poller, ClientSession *session, UINT32 requestId)
 2172 {
 2173    lockProperties();
 2174    if (m_isDeleteInitiated || IsShutdownInProgress())
 2175    {
 2176       m_instancePollState.complete(0);
 2177       unlockProperties();
 2178       return;
 2179    }
 2180    unlockProperties();
 2181 
 2182    poller->setStatus(_T("wait for lock"));
 2183    pollerLock(instance);
 2184 
 2185    if (IsShutdownInProgress())
 2186    {
 2187       pollerUnlock();
 2188       return;
 2189    }
 2190 
 2191    m_pollRequestor = session;
 2192    m_pollRequestId = requestId;
 2193 
 2194    if (m_status == STATUS_UNMANAGED)
 2195    {
 2196       sendPollerMsg(POLLER_WARNING _T("%s %s is unmanaged, instance discovery  poll aborted\r\n"), getObjectClassName(), m_name);
 2197       nxlog_debug(5, _T("%s %s [%u] is unmanaged, instance discovery poll aborted"), getObjectClassName(), m_name, m_id);
 2198       pollerUnlock();
 2199       return;
 2200    }
 2201 
 2202    sendPollerMsg(_T("Starting instance discovery poll for %s %s\r\n"), getObjectClassName(), m_name);
 2203    DbgPrintf(4, _T("Starting instance discovery poll for %s %s (ID: %d)"), getObjectClassName(), m_name, m_id);
 2204 
 2205    // Check if DataCollectionTarget is marked as unreachable
 2206    if (!(m_state & DCSF_UNREACHABLE))
 2207    {
 2208       poller->setStatus(_T("instance discovery"));
 2209       doInstanceDiscovery(requestId);
 2210 
 2211       // Update time intervals in data collection objects
 2212       updateDataCollectionTimeIntervals();
 2213 
 2214       // Execute hook script
 2215       poller->setStatus(_T("hook"));
 2216       executeHookScript(_T("InstancePoll"), requestId);
 2217    }
 2218    else
 2219    {
 2220       sendPollerMsg(POLLER_WARNING _T("%s is marked as unreachable, instance discovery poll aborted\r\n"), getObjectClassName());
 2221       DbgPrintf(4, _T("%s is marked as unreachable, instance discovery poll aborted"), getObjectClassName());
 2222    }
 2223 
 2224    // Finish instance discovery poll
 2225    poller->setStatus(_T("cleanup"));
 2226    pollerUnlock();
 2227    DbgPrintf(4, _T("Finished instance discovery poll for %s %s (ID: %d)"), getObjectClassName(), m_name, m_id);
 2228 }
 2229 
 2230 /**
 2231  * Get list of instances for given data collection object. Default implementation always returns nullptr.
 2232  */
 2233 StringMap *DataCollectionTarget::getInstanceList(DCObject *dco)
 2234 {
 2235    return nullptr;
 2236 }
 2237 
 2238 /**
 2239  * Cancellation checkpoint for instance discovery loop
 2240  */
 2241 #define INSTANCE_DISCOVERY_CANCELLATION_CHECKPOINT \
 2242 if (g_flags & AF_SHUTDOWN) \
 2243 { \
 2244    object->clearBusyFlag(); \
 2245    for(i++; i < rootObjects.size(); i++) \
 2246       rootObjects.get(i)->clearBusyFlag(); \
 2247    delete instances; \
 2248    changed = false; \
 2249    break; \
 2250 }
 2251 
 2252 /**
 2253  * Do instance discovery
 2254  */
 2255 void DataCollectionTarget::doInstanceDiscovery(UINT32 requestId)
 2256 {
 2257    sendPollerMsg(_T("Running DCI instance discovery\r\n"));
 2258 
 2259    // collect instance discovery DCIs
 2260    SharedObjectArray<DCObject> rootObjects;
 2261    readLockDciAccess();
 2262    for(int i = 0; i < m_dcObjects->size(); i++)
 2263    {
 2264       shared_ptr<DCObject> object = m_dcObjects->getShared(i);
 2265       if (object->getInstanceDiscoveryMethod() != IDM_NONE)
 2266       {
 2267          object->setBusyFlag();
 2268          rootObjects.add(object);
 2269       }
 2270    }
 2271    unlockDciAccess();
 2272 
 2273    // process instance discovery DCIs
 2274    // it should be done that way to prevent DCI list lock for long time
 2275    bool changed = false;
 2276    for(int i = 0; i < rootObjects.size(); i++)
 2277    {
 2278       DCObject *object = rootObjects.get(i);
 2279       DbgPrintf(5, _T("DataCollectionTarget::doInstanceDiscovery(%s [%u]): Updating instances for instance discovery DCO %s [%d]"),
 2280                 m_name, m_id, object->getName().cstr(), object->getId());
 2281       sendPollerMsg(_T("   Updating instances for %s [%d]\r\n"), object->getName().cstr(), object->getId());
 2282       StringMap *instances = getInstanceList(object);
 2283       INSTANCE_DISCOVERY_CANCELLATION_CHECKPOINT;
 2284       if (instances != nullptr)
 2285       {
 2286          DbgPrintf(5, _T("DataCollectionTarget::doInstanceDiscovery(%s [%u]): read %d values"), m_name, m_id, instances->size());
 2287          StringObjectMap<InstanceDiscoveryData> *filteredInstances = object->filterInstanceList(instances);
 2288          INSTANCE_DISCOVERY_CANCELLATION_CHECKPOINT;
 2289          if (updateInstances(object, filteredInstances, requestId))
 2290             changed = true;
 2291          delete filteredInstances;
 2292          delete instances;
 2293       }
 2294       else
 2295       {
 2296          DbgPrintf(5, _T("DataCollectionTarget::doInstanceDiscovery(%s [%u]): failed to get instance list for DCO %s [%d]"),
 2297                    m_name, m_id, object->getName().cstr(), object->getId());
 2298          sendPollerMsg(POLLER_ERROR _T("      Failed to get instance list\r\n"));
 2299       }
 2300       object->clearBusyFlag();
 2301    }
 2302 
 2303    if (changed)
 2304    {
 2305       onDataCollectionChange();
 2306 
 2307       lockProperties();
 2308       setModified(MODIFY_DATA_COLLECTION);
 2309       unlockProperties();
 2310    }
 2311 }
 2312 
 2313 /**
 2314  * Callback for finding instance
 2315  */
 2316 static EnumerationCallbackResult FindInstanceCallback(const TCHAR *key, const InstanceDiscoveryData *value, const TCHAR *data)
 2317 {
 2318    return !_tcscmp(data, key) ? _STOP : _CONTINUE;
 2319 }
 2320 
 2321 /**
 2322  * Data for CreateInstanceDCI
 2323  */
 2324 struct CreateInstanceDCOData
 2325 {
 2326    DCObject *root;
 2327    shared_ptr<DataCollectionTarget> object;
 2328 
 2329    CreateInstanceDCOData(DCObject *_root, const shared_ptr<DataCollectionTarget>& _object) : object(_object)
 2330    {
 2331       root = _root;
 2332    }
 2333 };
 2334 
 2335 /**
 2336  * Callback for creating instance DCIs
 2337  */
 2338 static EnumerationCallbackResult CreateInstanceDCI(const TCHAR *key, const InstanceDiscoveryData *value, CreateInstanceDCOData *data)
 2339 {
 2340    auto object = data->object;
 2341    auto root = data->root;
 2342 
 2343    DbgPrintf(5, _T("DataCollectionTarget::updateInstances(%s [%u], %s [%u]): creating new DCO for instance \"%s\""),
 2344              object->getName(), object->getId(), root->getName().cstr(), root->getId(), key);
 2345    object->sendPollerMsg(_T("      Creating new DCO for instance \"%s\"\r\n"), key);
 2346 
 2347    DCObject *dco = root->clone();
 2348 
 2349    dco->setTemplateId(object->getId(), root->getId());
 2350    dco->setInstance(value->getInstance());
 2351    dco->setInstanceDiscoveryMethod(IDM_NONE);
 2352    dco->setInstanceDiscoveryData(key);
 2353    dco->setInstanceFilter(nullptr);
 2354    dco->expandInstance();
 2355    dco->setRelatedObject(value->getRelatedObject());
 2356    dco->changeBinding(CreateUniqueId(IDG_ITEM), object, false);
 2357    object->addDCObject(dco, true);
 2358    return _CONTINUE;
 2359 }
 2360 
 2361 /**
 2362  * Update instance DCIs created from instance discovery DCI
 2363  */
 2364 bool DataCollectionTarget::updateInstances(DCObject *root, StringObjectMap<InstanceDiscoveryData> *instances, UINT32 requestId)
 2365 {
 2366    bool changed = false;
 2367 
 2368    writeLockDciAccess();
 2369 
 2370    // Delete DCIs for missing instances and update existing
 2371    IntegerArray<UINT32> deleteList;
 2372    for(int i = 0; i < m_dcObjects->size(); i++)
 2373    {
 2374       DCObject *object = m_dcObjects->get(i);
 2375       if ((object->getTemplateId() != m_id) || (object->getTemplateItemId() != root->getId()))
 2376          continue;
 2377 
 2378       SharedString dcoInstance = object->getInstanceDiscoveryData();
 2379       if (instances->forEach(FindInstanceCallback, dcoInstance.cstr()) == _STOP)
 2380       {
 2381          // found, remove value from instances
 2382          nxlog_debug(5, _T("DataCollectionTarget::updateInstances(%s [%u], %s [%u]): instance \"%s\" found"),
 2383                    m_name, m_id, root->getName().cstr(), root->getId(), dcoInstance.cstr());
 2384          InstanceDiscoveryData *instanceObject = instances->get(dcoInstance);
 2385          const TCHAR *name = instanceObject->getInstance();
 2386          bool notify = false;
 2387          if (_tcscmp(name, object->getInstance()))
 2388          {
 2389             object->setInstance(name);
 2390             object->updateFromTemplate(root);
 2391             changed = true;
 2392             notify = true;
 2393          }
 2394          if (object->getInstanceGracePeriodStart() > 0)
 2395          {
 2396             object->setInstanceGracePeriodStart(0);
 2397             object->setStatus(ITEM_STATUS_ACTIVE, false);
 2398          }
 2399          if(instanceObject->getRelatedObject() != object->getRelatedObject())
 2400          {
 2401             object->setRelatedObject(instanceObject->getRelatedObject());
 2402             changed = true;
 2403             notify = true;
 2404          }
 2405          instances->remove(dcoInstance);
 2406          if (notify)
 2407             NotifyClientsOnDCIUpdate(*this, object);
 2408       }
 2409       else
 2410       {
 2411          time_t retentionTime = ((object->getInstanceRetentionTime() != -1) ? object->getInstanceRetentionTime() : g_instanceRetentionTime) * 86400;
 2412 
 2413          if ((object->getInstanceGracePeriodStart() == 0) && (retentionTime > 0))
 2414          {
 2415             object->setInstanceGracePeriodStart(time(nullptr));
 2416             object->setStatus(ITEM_STATUS_DISABLED, false);
 2417             nxlog_debug(5, _T("DataCollectionTarget::updateInstances(%s [%u], %s [%u]): instance \"%s\" not found, grace period started"),
 2418                       m_name, m_id, root->getName().cstr(), root->getId(), dcoInstance.cstr());
 2419             sendPollerMsg(_T("      Existing instance \"%s\" not found, grace period started\r\n"), dcoInstance.cstr());
 2420             changed = true;
 2421          }
 2422 
 2423          if ((retentionTime == 0) || ((time(nullptr) - object->getInstanceGracePeriodStart()) > retentionTime))
 2424          {
 2425             // not found, delete DCO
 2426             nxlog_debug(5, _T("DataCollectionTarget::updateInstances(%s [%u], %s [%u]): instance \"%s\" not found, instance DCO will be deleted"),
 2427                       m_name, m_id, root->getName().cstr(), root->getId(), dcoInstance.cstr());
 2428             sendPollerMsg(_T("      Existing instance \"%s\" not found and will be deleted\r\n"), dcoInstance.cstr());
 2429             deleteList.add(object->getId());
 2430             changed = true;
 2431          }
 2432       }
 2433    }
 2434 
 2435    for(int i = 0; i < deleteList.size(); i++)
 2436       deleteDCObject(deleteList.get(i), false, 0);
 2437 
 2438    // Create new instances
 2439    if (instances->size() > 0)
 2440    {
 2441       CreateInstanceDCOData data(root, self());
 2442       instances->forEach(CreateInstanceDCI, &data);
 2443       changed = true;
 2444    }
 2445 
 2446    unlockDciAccess();
 2447    return changed;
 2448 }
 2449 
 2450 /**
 2451  * Get last (current) DCI values.
 2452  */
 2453 UINT32 DataCollectionTarget::getLastValues(NXCPMessage *msg, bool objectTooltipOnly, bool overviewOnly, bool includeNoValueObjects, UINT32 userId)
 2454 {
 2455    readLockDciAccess();
 2456 
 2457    UINT32 dwId = VID_DCI_VALUES_BASE, dwCount = 0;
 2458    for(int i = 0; i < m_dcObjects->size(); i++)
 2459    {
 2460       DCObject *object = m_dcObjects->get(i);
 2461       if ((object->hasValue() || includeNoValueObjects) &&
 2462           (!objectTooltipOnly || object->isShowOnObjectTooltip()) &&
 2463           (!overviewOnly || object->isShowInObjectOverview()) &&
 2464           object->hasAccess(userId))
 2465       {
 2466          if (object->getType() == DCO_TYPE_ITEM)
 2467          {
 2468             ((DCItem *)object)->fillLastValueMessage(msg, dwId);
 2469             dwId += 50;
 2470             dwCount++;
 2471          }
 2472          else if (object->getType() == DCO_TYPE_TABLE)
 2473          {
 2474             ((DCTable *)object)->fillLastValueSummaryMessage(msg, dwId);
 2475             dwId += 50;
 2476             dwCount++;
 2477          }
 2478       }
 2479    }
 2480    msg->setField(VID_NUM_ITEMS, dwCount);
 2481 
 2482    unlockDciAccess();
 2483    return RCC_SUCCESS;
 2484 }
 2485 
 2486 /**
 2487  * Hook for data collection load
 2488  */
 2489 void DataCollectionTarget::onDataCollectionLoad()
 2490 {
 2491    super::onDataCollectionLoad();
 2492    calculateProxyLoad();
 2493 }
 2494 
 2495 /**
 2496  * Hook for data collection change
 2497  */
 2498 void DataCollectionTarget::onDataCollectionChange()
 2499 {
 2500    super::onDataCollectionChange();
 2501    calculateProxyLoad();
 2502 }
 2503 
 2504 /**
 2505  * Hook for instance discovery configuration change
 2506  */
 2507 void DataCollectionTarget::onInstanceDiscoveryChange()
 2508 {
 2509    super::onInstanceDiscoveryChange();
 2510    nxlog_debug_tag(_T("obj.dc"), 5, _T("Instance discovery configuration change for %s [%u]"), m_name, m_id);
 2511    lockProperties();
 2512    m_instanceDiscoveryPending = true;
 2513    unlockProperties();
 2514 }
 2515 
 2516 /**
 2517  * Calculate proxy load factor
 2518  */
 2519 void DataCollectionTarget::calculateProxyLoad()
 2520 {
 2521    double loadFactor = 0;
 2522    readLockDciAccess();
 2523    for(int i = 0; i < m_dcObjects->size(); i++)
 2524    {
 2525       DCObject *object = m_dcObjects->get(i);
 2526       if ((object->getDataSource() == DS_SNMP_AGENT) && (object->getStatus() == ITEM_STATUS_ACTIVE))
 2527       {
 2528          if (object->isAdvancedSchedule())
 2529             loadFactor += 12;  // assume 5 minutes interval for custom schedule
 2530          else
 2531             loadFactor += 3600.0 / static_cast<double>(object->getEffectivePollingInterval());
 2532       }
 2533    }
 2534    unlockDciAccess();
 2535 
 2536    MutexLock(m_mutexProxyLoadFactor);
 2537    m_proxyLoadFactor = loadFactor;
 2538    MutexUnlock(m_mutexProxyLoadFactor);
 2539 }
 2540 
 2541 /**
 2542  * Reset poll timers
 2543  */
 2544 void DataCollectionTarget::resetPollTimers()
 2545 {
 2546    m_statusPollState.resetTimer();
 2547    m_configurationPollState.resetTimer();
 2548    m_instancePollState.resetTimer();
 2549 }
 2550 
 2551 /**
 2552  * Get list of template type parent objects for NXSL script
 2553  */
 2554 NXSL_Array *DataCollectionTarget::getTemplatesForNXSL(NXSL_VM *vm)
 2555 {
 2556    NXSL_Array *parents = new NXSL_Array(vm);
 2557    int index = 0;
 2558 
 2559    readLockParentList();
 2560    for(int i = 0; i < getParentList().size(); i++)
 2561    {
 2562       NetObj *object = getParentList().get(i);
 2563       if ((object->getObjectClass() == OBJECT_TEMPLATE) && object->isTrustedNode(m_id))
 2564       {
 2565          parents->set(index++, object->createNXSLObject(vm));
 2566       }
 2567    }
 2568    unlockParentList();
 2569 
 2570    return parents;
 2571 }
 2572 
 2573 /**
 2574  * Get cache memory usage
 2575  */
 2576 uint64_t DataCollectionTarget::getCacheMemoryUsage()
 2577 {
 2578    uint64_t cacheSize = 0;
 2579    readLockDciAccess();
 2580    for(int i = 0; i < m_dcObjects->size(); i++)
 2581    {
 2582       DCObject *object = m_dcObjects->get(i);
 2583       if (object->getType() == DCO_TYPE_ITEM)
 2584       {
 2585          cacheSize += static_cast<DCItem*>(object)->getCacheMemoryUsage();
 2586       }
 2587    }
 2588    unlockDciAccess();
 2589    return cacheSize;
 2590 }
 2591 
 2592 /**
 2593  * Get effective web service proxy for this node
 2594  */
 2595 uint32_t DataCollectionTarget::getEffectiveWebServiceProxy()
 2596 {
 2597    uint32_t webServiceProxy = 0;
 2598    int32_t zoneUIN = getZoneUIN();
 2599    if (IsZoningEnabled() && (webServiceProxy == 0) && (zoneUIN != 0))
 2600    {
 2601       // Use zone default proxy if set
 2602       shared_ptr<Zone> zone = FindZoneByUIN(zoneUIN);
 2603       if (zone != nullptr)
 2604       {
 2605          webServiceProxy = zone->isProxyNode(m_id) ? m_id : zone->getProxyNodeId(this);
 2606       }
 2607    }
 2608    return (webServiceProxy != 0) ? webServiceProxy : g_dwMgmtNode;
 2609 }
 2610 
 2611 /**
 2612  * Update geolocation for device. Object properties assumed to be locked when this method is called.
 2613  */
 2614 void DataCollectionTarget::updateGeoLocation(const GeoLocation& geoLocation)
 2615 {
 2616    if ((m_flags & DCF_LOCATION_CHANGE_EVENT) && m_geoLocation.isValid() && geoLocation.isValid() && !m_geoLocation.equals(geoLocation))
 2617    {
 2618       PostSystemEvent(EVENT_GEOLOCATION_CHANGED, m_id, "ffssffss", geoLocation.getLatitude(), geoLocation.getLongitude(),
 2619             geoLocation.getLatitudeAsString(), geoLocation.getLongitudeAsString(), m_geoLocation.getLatitude(), m_geoLocation.getLongitude(),
 2620             m_geoLocation.getLatitudeAsString(), m_geoLocation.getLongitudeAsString());
 2621    }
 2622    m_geoLocation = geoLocation;
 2623    setModified(MODIFY_COMMON_PROPERTIES);
 2624 
 2625    TCHAR key[32];
 2626    _sntprintf(key, 32, _T("GLupd-%u"), m_id);
 2627    ThreadPoolExecuteSerialized(g_mainThreadPool, key, self(), &NetObj::updateGeoLocationHistory, m_geoLocation);
 2628 
 2629    nxlog_debug_tag(DEBUG_TAG_GEOLOCATION, 4, _T("Location for device %s [%u] set to %s %s)"),
 2630             m_name, m_id, geoLocation.getLatitude(), geoLocation.getLongitude());
 2631 
 2632    if (m_geoLocationControlMode == GEOLOCATION_RESTRICTED_AREAS)
 2633    {
 2634       bool insideArea = false;
 2635       for(int i = 0; i < m_geoAreas.size(); i++)
 2636       {
 2637          shared_ptr<GeoArea> area = GetGeoArea(m_geoAreas.get(i));
 2638          if ((area != nullptr) && area->containsLocation(geoLocation))
 2639          {
 2640             bool insideArea = true;
 2641             if (!m_geoLocationRestrictionsViolated)
 2642             {
 2643                nxlog_debug_tag(DEBUG_TAG_GEOLOCATION, 4, _T("Device %s [%u] is within restricted area %s [%u] (current coordinates %s %s)"),
 2644                         m_name, m_id, area->getName(), area->getId(), geoLocation.getLatitude(), geoLocation.getLongitude());
 2645                PostSystemEvent(EVENT_GEOLOCATION_INSIDE_RESTRICTED_AREA, m_id, "ffsssd", geoLocation.getLatitude(), geoLocation.getLongitude(),
 2646                      geoLocation.getLatitudeAsString(), geoLocation.getLongitudeAsString(), area->getName(), area->getId());
 2647                m_geoLocationRestrictionsViolated = true;
 2648             }
 2649             break;
 2650          }
 2651       }
 2652       if (!insideArea && m_geoLocationRestrictionsViolated)
 2653       {
 2654          nxlog_debug_tag(DEBUG_TAG_GEOLOCATION, 4, _T("Device %s [%u] is outside restricted area (current coordinates %s %s)"),
 2655                   m_name, m_id, geoLocation.getLatitude(), geoLocation.getLongitude());
 2656          m_geoLocationRestrictionsViolated = false;
 2657       }
 2658    }
 2659    else if (m_geoLocationControlMode == GEOLOCATION_ALLOWED_AREAS)
 2660    {
 2661       bool insideArea = false;
 2662       for(int i = 0; i < m_geoAreas.size(); i++)
 2663       {
 2664          shared_ptr<GeoArea> area = GetGeoArea(m_geoAreas.get(i));
 2665          if ((area != nullptr) && area->containsLocation(geoLocation))
 2666          {
 2667             insideArea = true;
 2668             break;
 2669          }
 2670       }
 2671       if (!insideArea && !m_geoLocationRestrictionsViolated)
 2672       {
 2673          nxlog_debug_tag(DEBUG_TAG_GEOLOCATION, 4, _T("Device %s [%u] is outside allowed area (current coordinates %s %s)"),
 2674                   m_name, m_id, geoLocation.getLatitude(), geoLocation.getLongitude());
 2675          PostSystemEvent(EVENT_GEOLOCATION_OUTSIDE_ALLOWED_AREA, m_id, "ffss", geoLocation.getLatitude(), geoLocation.getLongitude(),
 2676                geoLocation.getLatitudeAsString(), geoLocation.getLongitudeAsString());
 2677          m_geoLocationRestrictionsViolated = true;
 2678       }
 2679       else if (insideArea && m_geoLocationRestrictionsViolated)
 2680       {
 2681          nxlog_debug_tag(DEBUG_TAG_GEOLOCATION, 4, _T("Device %s [%u] is inside allowed area (current coordinates %s %s)"),
 2682                   m_name, m_id, geoLocation.getLatitude(), geoLocation.getLongitude());
 2683          m_geoLocationRestrictionsViolated = false;
 2684       }
 2685    }
 2686 }