table dci export/import working; fixed bugs in table dci db save/load
authorVictor Kirhenshtein <victor@netxms.org>
Fri, 23 Aug 2013 09:35:02 +0000 (09:35 +0000)
committerVictor Kirhenshtein <victor@netxms.org>
Fri, 23 Aug 2013 09:35:02 +0000 (09:35 +0000)
12 files changed:
include/netxmsdb.h
include/nxconfig.h
sql/schema.in
src/libnetxms/config.cpp
src/server/core/dcithreshold.cpp
src/server/core/dctable.cpp
src/server/core/dctcolumn.cpp
src/server/core/dctthreshold.cpp
src/server/core/import.cpp
src/server/core/template.cpp
src/server/include/nms_dcoll.h
src/server/tools/nxdbmgr/upgrade.cpp

index 2b15c06..d33dfc9 100644 (file)
@@ -23,6 +23,6 @@
 #ifndef _netxmsdb_h
 #define _netxmsdb_h
 
-#define DB_FORMAT_VERSION   286
+#define DB_FORMAT_VERSION   287
 
 #endif
index 161c202..fafd2c7 100644 (file)
@@ -40,14 +40,15 @@ private:
        TCHAR *m_name;
        ConfigEntry *m_parent;
        ConfigEntry *m_next;
-       ConfigEntry *m_childs;
+       ConfigEntry *m_first;
+   ConfigEntry *m_last;
        int m_valueCount;
        TCHAR **m_values;
        TCHAR *m_file;
        int m_line;
        int m_id;
 
-       void addEntry(ConfigEntry *entry) { entry->m_parent = this; entry->m_next = m_childs; m_childs = entry; }
+       void addEntry(ConfigEntry *entry);
        void linkEntry(ConfigEntry *entry) { entry->m_next = m_next; m_next = entry; }
 
 public:
@@ -96,11 +97,9 @@ public:
        void createXml(String &xml, int level = 0);
 };
 
-
-//
-// List of config entries
-//
-
+/**
+ * List of config entries
+ */
 class ConfigEntryList
 {
 private:
@@ -117,11 +116,9 @@ public:
        void sortById();
 };
 
-
-//
-// Config class
-//
-
+/**
+ * Hierarchical config
+ */
 class LIBNETXMS_EXPORTABLE Config
 {
 private:
index 30aba83..1888806 100644 (file)
@@ -640,6 +640,7 @@ CREATE INDEX idx_dc_tables_node_id ON dc_tables(node_id);
 CREATE TABLE dc_table_columns
 (
        table_id integer not null,
+       sequence_number integer not null,
        column_name varchar(63) not null,
        snmp_oid varchar(1023) null,            // SNMP OID for this column, valid only for SNMP tables
        flags integer not null,
index e9fd619..7813ebb 100644 (file)
@@ -31,7 +31,8 @@
 ConfigEntry::ConfigEntry(const TCHAR *name, ConfigEntry *parent, const TCHAR *file, int line, int id)
 {
        m_name = _tcsdup(CHECK_NULL(name));
-       m_childs = NULL;
+       m_first = NULL;
+   m_last = NULL;
        m_next = NULL;
        if (parent != NULL)
                parent->addEntry(this);
@@ -49,7 +50,7 @@ ConfigEntry::~ConfigEntry()
 {
        ConfigEntry *entry, *next;
 
-       for(entry = m_childs; entry != NULL; entry = next)
+       for(entry = m_first; entry != NULL; entry = next)
        {
                next = entry->getNext();
                delete entry;
@@ -78,38 +79,48 @@ ConfigEntry *ConfigEntry::findEntry(const TCHAR *name)
 {
        ConfigEntry *e;
 
-       for(e = m_childs; e != NULL; e = e->getNext())
+       for(e = m_first; e != NULL; e = e->getNext())
                if (!_tcsicmp(e->getName(), name))
                        return e;
        return NULL;
 }
 
-
-//
-// Create (or find existing) subentry
-//
-
+/**
+ * Create (or find existing) subentry
+ */
 ConfigEntry *ConfigEntry::createEntry(const TCHAR *name)
 {
        ConfigEntry *e;
 
-       for(e = m_childs; e != NULL; e = e->getNext())
+       for(e = m_first; e != NULL; e = e->getNext())
                if (!_tcsicmp(e->getName(), name))
                        return e;
 
        return new ConfigEntry(name, this, _T("<memory>"), 0, 0);
 }
 
+/**
+ * Add sub-entry
+ */
+void ConfigEntry::addEntry(ConfigEntry *entry)
+{ 
+   entry->m_parent = this; 
+   entry->m_next = NULL;
+   if (m_last != NULL)
+      m_last->m_next = entry;
+   m_last = entry;
+   if (m_first == NULL)
+      m_first = entry;
+}
 
-//
-// Unlink subentry
-//
-
+/**
+ * Unlink subentry
+ */
 void ConfigEntry::unlinkEntry(ConfigEntry *entry)
 {
        ConfigEntry *curr, *prev;
 
-       for(curr = m_childs, prev = NULL; curr != NULL; curr = curr->m_next)
+       for(curr = m_first, prev = NULL; curr != NULL; curr = curr->m_next)
        {
                if (curr == entry)
                {
@@ -119,25 +130,26 @@ void ConfigEntry::unlinkEntry(ConfigEntry *entry)
                        }
                        else
                        {
-                               m_childs = curr->m_next;
+                               m_first = curr->m_next;
                        }
+         if (m_last == curr)
+            m_last = prev;
+         curr->m_next = NULL;
                        break;
                }
                prev = curr;
        }
 }
 
-
-//
-// Get all subentries with names matched to mask
-//
-
+/**
+ * Get all subentries with names matched to mask
+ */
 ConfigEntryList *ConfigEntry::getSubEntries(const TCHAR *mask)
 {
        ConfigEntry *e, **list = NULL;
        int count = 0, allocated = 0;
 
-       for(e = m_childs; e != NULL; e = e->getNext())
+       for(e = m_first; e != NULL; e = e->getNext())
                if ((mask == NULL) || MatchString(mask, e->getName(), FALSE))
                {
                        if (count == allocated)
@@ -150,11 +162,9 @@ ConfigEntryList *ConfigEntry::getSubEntries(const TCHAR *mask)
        return new ConfigEntryList(list, count);
 }
 
-
-//
-// Get all subentries with names matched to mask ordered by id
-//
-
+/**
+ * Get all subentries with names matched to mask ordered by id
+ */
 ConfigEntryList *ConfigEntry::getOrderedSubEntries(const TCHAR *mask)
 {
        ConfigEntryList *list = getSubEntries(mask);
@@ -265,11 +275,14 @@ int ConfigEntry::getConcatenatedValuesLength()
        return len + m_valueCount;
 }
 
-
-//
-// Get value for given subentry
-//
-
+/**
+ * Get value for given subentry
+ *
+ * @param name sub-entry name
+ * @param index sub-entry index (0 if omited)
+ * @param defaultValue value to be returned if requested sub-entry is missing (NULL if omited)
+ * @return sub-entry value or default value if requested sub-entry does not exist
+ */
 const TCHAR *ConfigEntry::getSubEntryValue(const TCHAR *name, int index, const TCHAR *defaultValue)
 {
        ConfigEntry *e = findEntry(name);
@@ -330,15 +343,13 @@ bool ConfigEntry::getSubEntryValueUUID(const TCHAR *name, uuid_t uuid, int index
        }
 }
 
-
-//
-// Print config entry
-//
-
+/**
+ * Print config entry
+ */
 void ConfigEntry::print(FILE *file, int level)
 {
        _tprintf(_T("%*s%s\n"), level * 4, _T(""), m_name);
-       for(ConfigEntry *e = m_childs; e != NULL; e = e->getNext())
+       for(ConfigEntry *e = m_first; e != NULL; e = e->getNext())
                e->print(file, level + 1);
 
        for(int i = 0, len = 0; i < m_valueCount; i++)
@@ -360,10 +371,10 @@ void ConfigEntry::createXml(String &xml, int level)
        else
                xml.addFormattedString(_T("%*s<%s id=\"%d\">"), level * 4, _T(""), name, m_id);
 
-       if (m_childs != NULL)
+       if (m_first != NULL)
        {
                xml += _T("\n");
-               for(ConfigEntry *e = m_childs; e != NULL; e = e->getNext())
+               for(ConfigEntry *e = m_first; e != NULL; e = e->getNext())
                        e->createXml(xml, level + 1);
                xml.addFormattedString(_T("%*s"), level * 4, _T(""));
        }
index d8fa8fc..c45eeb2 100644 (file)
@@ -119,11 +119,9 @@ Threshold::Threshold(DB_RESULT hResult, int iRow, DCItem *pRelatedItem)
        m_numMatches = 0;
 }
 
-
-//
-// Create threshold from import file
-//
-
+/**
+ * Create threshold from import file
+ */
 Threshold::Threshold(ConfigEntry *config, DCItem *parentItem)
 {
    createId();
index d6352b1..35685e2 100644 (file)
@@ -187,7 +187,7 @@ DCTable::DCTable(DB_RESULT hResult, int iRow, Template *pNode) : DCObject()
        m_columns = new ObjectArray<DCTableColumn>(8, 8, true);
        m_lastValue = NULL;
 
-       DB_STATEMENT hStmt = DBPrepare(g_hCoreDB, _T("SELECT column_name,flags,snmp_oid,display_name FROM dc_table_columns WHERE table_id=?"));
+       DB_STATEMENT hStmt = DBPrepare(g_hCoreDB, _T("SELECT column_name,flags,snmp_oid,display_name FROM dc_table_columns WHERE table_id=? ORDER BY sequence_number"));
        if (hStmt != NULL)
        {
                DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_dwId);
@@ -208,6 +208,46 @@ DCTable::DCTable(DB_RESULT hResult, int iRow, Template *pNode) : DCObject()
    loadThresholds();
 }
 
+/**
+ * Create DCTable from import file
+ */
+DCTable::DCTable(ConfigEntry *config, Template *owner) : DCObject(config, owner)
+{
+       ConfigEntry *columnsRoot = config->findEntry(_T("columns"));
+       if (columnsRoot != NULL)
+       {
+               ConfigEntryList *columns = columnsRoot->getSubEntries(_T("column#*"));
+               m_columns = new ObjectArray<DCTableColumn>(columns->getSize(), 8, true);
+               for(int i = 0; i < columns->getSize(); i++)
+               {
+                       m_columns->add(new DCTableColumn(columns->getEntry(i)));
+               }
+               delete columns;
+       }
+       else
+       {
+       m_columns = new ObjectArray<DCTableColumn>(8, 8, true);
+       }
+
+       ConfigEntry *thresholdsRoot = config->findEntry(_T("thresholds"));
+       if (thresholdsRoot != NULL)
+       {
+               ConfigEntryList *thresholds = thresholdsRoot->getSubEntries(_T("threshold#*"));
+               m_thresholds = new ObjectArray<DCTableThreshold>(thresholds->getSize(), 8, true);
+               for(int i = 0; i < thresholds->getSize(); i++)
+               {
+                       m_thresholds->add(new DCTableThreshold(thresholds->getEntry(i)));
+               }
+               delete thresholds;
+       }
+       else
+       {
+      m_thresholds = new ObjectArray<DCTableThreshold>(0, 4, true);
+       }
+
+   m_lastValue = NULL;
+}
+
 /**
  * Destructor
  */
@@ -477,18 +517,19 @@ BOOL DCTable::saveToDB(DB_HANDLE hdb)
 
                if (result && (m_columns->size() > 0))
                {
-                       hStmt = DBPrepare(hdb, _T("INSERT INTO dc_table_columns (table_id,column_name,snmp_oid,flags,display_name) VALUES (?,?,?,?,?)"));
+                       hStmt = DBPrepare(hdb, _T("INSERT INTO dc_table_columns (table_id,sequence_number,column_name,snmp_oid,flags,display_name) VALUES (?,?,?,?,?,?)"));
                        if (hStmt != NULL)
                        {
                                DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_dwId);
                                for(int i = 0; i < m_columns->size(); i++)
                                {
                                        DCTableColumn *column = m_columns->get(i);
-                                       DBBind(hStmt, 2, DB_SQLTYPE_VARCHAR, column->getName(), DB_BIND_STATIC);
+               DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, (INT32)(i + 1));
+                                       DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, column->getName(), DB_BIND_STATIC);
                                        SNMP_ObjectId *oid = column->getSnmpOid();
-                                       DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, (oid != NULL) ? oid->getValueAsText() : NULL, DB_BIND_STATIC);
-                                       DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, (INT32)column->getFlags());
-                                       DBBind(hStmt, 5, DB_SQLTYPE_VARCHAR, column->getDisplayName(), DB_BIND_STATIC);
+                                       DBBind(hStmt, 4, DB_SQLTYPE_VARCHAR, (oid != NULL) ? oid->getValueAsText() : NULL, DB_BIND_STATIC);
+                                       DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, (INT32)column->getFlags());
+                                       DBBind(hStmt, 6, DB_SQLTYPE_VARCHAR, column->getDisplayName(), DB_BIND_STATIC);
 
                                        result = DBExecute(hStmt);
                                        if (!result)
index 91f9d69..5a3327f 100644 (file)
@@ -95,6 +95,35 @@ DCTableColumn::DCTableColumn(DB_RESULT hResult, int row)
        }
 }
 
+/**
+ * Create from NXMP record
+ */
+DCTableColumn::DCTableColumn(ConfigEntry *e)
+{
+   nx_strncpy(m_name, e->getSubEntryValue(_T("name"), 0, _T("")), MAX_COLUMN_NAME);
+   m_flags = (UINT16)e->getSubEntryValueUInt(_T("flags"));
+   m_displayName = _tcsdup(e->getSubEntryValue(_T("displayName"), 0, _T("")));
+
+   const TCHAR *oid = e->getSubEntryValue(_T("snmpOid"));
+   if ((oid != NULL) && (*oid != 0))
+   {
+               UINT32 oidBin[256];
+               UINT32 len = SNMPParseOID(oid, oidBin, 256);
+               if (len > 0)
+               {
+                       m_snmpOid = new SNMP_ObjectId(len, oidBin);
+               }
+               else
+               {
+                       m_snmpOid = NULL;
+               }
+   }
+       else
+       {
+               m_snmpOid = NULL;
+       }
+}
+
 /**
  * Destructor
  */
index da2ab58..3436d97 100644 (file)
@@ -238,6 +238,32 @@ DCTableConditionGroup::DCTableConditionGroup(CSCPMessage *msg, UINT32 *baseId)
    *baseId = varId;
 }
 
+/**
+ * Create condition group from NXMP record
+ */
+DCTableConditionGroup::DCTableConditionGroup(ConfigEntry *e)
+{
+       ConfigEntry *root = e->findEntry(_T("conditions"));
+       if (root != NULL)
+       {
+               ConfigEntryList *conditions = root->getSubEntries(_T("condition#*"));
+      m_conditions = new ObjectArray<DCTableCondition>(conditions->getSize(), 4, true);
+               for(int i = 0; i < conditions->getSize(); i++)
+               {
+         ConfigEntry *c = conditions->getEntry(i);
+         const TCHAR *column = c->getSubEntryValue(_T("column"), 0, _T(""));
+         const TCHAR *value = c->getSubEntryValue(_T("value"), 0, _T(""));
+         int op = c->getSubEntryValueInt(_T("operation"));
+                       m_conditions->add(new DCTableCondition(column, op, value));
+               }
+               delete conditions;
+       }
+       else
+       {
+       m_conditions = new ObjectArray<DCTableCondition>(8, 8, true);
+       }
+}
+
 /**
  * Condition group destructor
  */
@@ -333,6 +359,31 @@ DCTableThreshold::DCTableThreshold(CSCPMessage *msg, UINT32 *baseId)
       m_groups->add(new DCTableConditionGroup(msg, baseId));
 }
 
+/**
+ * Create from NXMP record
+ */
+DCTableThreshold::DCTableThreshold(ConfigEntry *e)
+{
+   m_activationEvent = EventCodeFromName(e->getSubEntryValue(_T("activationEvent"), 0, _T("SYS_THRESHOLD_REACHED")));
+   m_deactivationEvent = EventCodeFromName(e->getSubEntryValue(_T("deactivationEvent"), 0, _T("SYS_THRESHOLD_REARMED")));
+
+       ConfigEntry *groupsRoot = e->findEntry(_T("groups"));
+       if (groupsRoot != NULL)
+       {
+               ConfigEntryList *groups = groupsRoot->getSubEntries(_T("group#*"));
+      m_groups = new ObjectArray<DCTableConditionGroup>(groups->getSize(), 4, true);
+               for(int i = 0; i < groups->getSize(); i++)
+               {
+                       m_groups->add(new DCTableConditionGroup(groups->getEntry(i)));
+               }
+               delete groups;
+       }
+       else
+       {
+       m_groups = new ObjectArray<DCTableConditionGroup>(4, 4, true);
+       }
+}
+
 /**
  * Load conditions from database
  */
@@ -355,6 +406,7 @@ void DCTableThreshold::loadConditions()
          {
             if (DBGetFieldLong(hResult, i, 0) != groupId)
             {
+               groupId = DBGetFieldLong(hResult, i, 0);
                group = new DCTableConditionGroup();
                m_groups->add(group);
             }
@@ -475,8 +527,8 @@ void DCTableThreshold::createNXMPRecord(String &str, int id)
                                 _T("\t\t\t\t\t\t\t\t\t\t\t<operation>%d</operation>\n")
                                 _T("\t\t\t\t\t\t\t\t\t\t\t<value>%s</value>\n")
                                 _T("\t\t\t\t\t\t\t\t\t\t</condition>\n"),
-                                j + 1, EscapeStringForXML2(c->getColumn()),
-                                c->getOperation(), EscapeStringForXML2(c->getValue()));
+                                j + 1, (const TCHAR *)EscapeStringForXML2(c->getColumn()),
+                                c->getOperation(), (const TCHAR *)EscapeStringForXML2(c->getValue()));
       }
       str += _T("\t\t\t\t\t\t\t\t\t</conditions>\n\t\t\t\t\t\t\t\t</group>\n");
    }
index 3bc5b8a..1db5bd3 100644 (file)
@@ -92,8 +92,9 @@ static bool ValidateTemplate(Config *config, ConfigEntry *root, TCHAR *errorText
                return true;\r
 \r
        bool success = true;\r
-       ConfigEntryList *dcis = dcRoot->getSubEntries(_T("dci#*"));\r
        const TCHAR *name = root->getSubEntryValue(_T("name"), 0, _T("<unnamed>"));\r
+\r
+       ConfigEntryList *dcis = dcRoot->getSubEntries(_T("dci#*"));\r
        for(int i = 0; i < dcis->getSize(); i++)\r
        {\r
                if (!ValidateDci(config, dcis->getEntry(i), name, errorText, errorTextLen))\r
@@ -103,7 +104,22 @@ static bool ValidateTemplate(Config *config, ConfigEntry *root, TCHAR *errorText
                }\r
        }\r
        delete dcis;\r
-       return success;\r
+\r
+   if (success)\r
+   {\r
+          ConfigEntryList *dctables = dcRoot->getSubEntries(_T("dctable#*"));\r
+          for(int i = 0; i < dctables->getSize(); i++)\r
+          {\r
+                  if (!ValidateDci(config, dctables->getEntry(i), name, errorText, errorTextLen))\r
+                  {\r
+                          success = false;\r
+                          break;\r
+                  }\r
+          }\r
+          delete dcis;\r
+   }\r
+   \r
+   return success;\r
 }\r
 \r
 /**\r
index bac49b3..f415e84 100644 (file)
@@ -102,7 +102,14 @@ Template::Template(ConfigEntry *config) : NetObj()
                        m_dcObjects->add(new DCItem(dcis->getEntry(i), this));
                }
                delete dcis;
-       }
+
+               ConfigEntryList *dctables = dcRoot->getSubEntries(_T("dctable#*"));
+               for(int i = 0; i < dctables->getSize(); i++)
+               {
+                       m_dcObjects->add(new DCTable(dctables->getEntry(i), this));
+               }
+               delete dctables;
+   }
 }
 
 /**
index af55d12..8511e67 100644 (file)
@@ -412,6 +412,7 @@ public:
        DCTableColumn(const DCTableColumn *src);
        DCTableColumn(CSCPMessage *msg, UINT32 baseId);
        DCTableColumn(DB_RESULT hResult, int row);
+   DCTableColumn(ConfigEntry *e);
        ~DCTableColumn();
 
        const TCHAR *getName() { return m_name; }
@@ -468,6 +469,7 @@ public:
    DCTableConditionGroup();
    DCTableConditionGroup(CSCPMessage *msg, UINT32 *baseId);
    DCTableConditionGroup(DCTableConditionGroup *src);
+   DCTableConditionGroup(ConfigEntry *e);
    ~DCTableConditionGroup();
 
    bool check(Table *value, int row);
@@ -496,6 +498,7 @@ public:
    DCTableThreshold(DB_RESULT hResult, int row);
    DCTableThreshold(CSCPMessage *msg, UINT32 *baseId);
    DCTableThreshold(DCTableThreshold *src);
+   DCTableThreshold(ConfigEntry *e);
    ~DCTableThreshold();
 
    bool check(Table *value, int row);
@@ -537,6 +540,7 @@ public:
    DCTable(UINT32 id, const TCHAR *name, int source, int pollingInterval, int retentionTime,
                Template *node, const TCHAR *description = NULL, const TCHAR *systemTag = NULL);
    DCTable(DB_RESULT hResult, int iRow, Template *pNode);
+   DCTable(ConfigEntry *config, Template *owner);
        virtual ~DCTable();
 
        virtual int getType() const { return DCO_TYPE_TABLE; }
index fb8f66f..adae144 100644 (file)
@@ -353,6 +353,21 @@ static BOOL RecreateTData(const TCHAR *className)
    return TRUE;
 }
 
+/**
+ * Upgrade from V286 to V287
+ */
+static BOOL H_UpgradeFromV286(int currVersion, int newVersion)
+{
+   static TCHAR batch[] =
+      _T("ALTER TABLE dc_table_columns ADD sequence_number integer\n")
+      _T("UPDATE dc_table_columns SET sequence_number=0\n")
+      _T("<END>");
+   CHK_EXEC(SQLBatch(batch));
+
+   CHK_EXEC(SQLQuery(_T("UPDATE metadata SET var_value='287' WHERE var_name='SchemaVersion'")));
+   return TRUE;
+}
+
 /**
  * Upgrade from V285 to V286
  */
@@ -7077,6 +7092,7 @@ static struct
    { 283, 284, H_UpgradeFromV283 },
    { 284, 285, H_UpgradeFromV284 },
    { 285, 286, H_UpgradeFromV285 },
+   { 286, 287, H_UpgradeFromV286 },
    { 0, 0, NULL }
 };