Implemented object caching on client side
[public/netxms.git] / src / server / core / node.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003, 2004 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 ** $module: node.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25
26 //
27 // Node class default constructor
28 //
29
30 Node::Node()
31 :Template()
32 {
33 m_dwFlags = 0;
34 m_dwDiscoveryFlags = 0;
35 m_dwDynamicFlags = 0;
36 m_dwNodeType = NODE_TYPE_GENERIC;
37 m_wAgentPort = AGENT_LISTEN_PORT;
38 m_wAuthMethod = AUTH_NONE;
39 m_szSharedSecret[0] = 0;
40 m_iStatusPollType = POLL_ICMP_PING;
41 m_iSNMPVersion = SNMP_VERSION_1;
42 strcpy(m_szCommunityString, "public");
43 m_szObjectId[0] = 0;
44 m_tLastDiscoveryPoll = 0;
45 m_tLastStatusPoll = 0;
46 m_tLastConfigurationPoll = 0;
47 m_iSnmpAgentFails = 0;
48 m_iNativeAgentFails = 0;
49 m_hPollerMutex = MutexCreate();
50 m_hAgentAccessMutex = MutexCreate();
51 m_pAgentConnection = NULL;
52 m_szAgentVersion[0] = 0;
53 m_szPlatformName[0] = 0;
54 m_dwNumParams = 0;
55 m_pParamList = NULL;
56 m_dwPollerNode = 0;
57 memset(m_qwLastEvents, 0, sizeof(QWORD) * MAX_LAST_EVENTS);
58 }
59
60
61 //
62 // Constructor for new node object
63 //
64
65 Node::Node(DWORD dwAddr, DWORD dwFlags, DWORD dwDiscoveryFlags)
66 :Template()
67 {
68 m_dwIpAddr = dwAddr;
69 m_dwFlags = dwFlags;
70 m_dwDynamicFlags = 0;
71 m_dwNodeType = NODE_TYPE_GENERIC;
72 m_dwDiscoveryFlags = dwDiscoveryFlags;
73 m_wAgentPort = AGENT_LISTEN_PORT;
74 m_wAuthMethod = AUTH_NONE;
75 m_szSharedSecret[0] = 0;
76 m_iStatusPollType = POLL_ICMP_PING;
77 m_iSNMPVersion = SNMP_VERSION_1;
78 strcpy(m_szCommunityString, "public");
79 IpToStr(dwAddr, m_szName); // Make default name from IP address
80 m_szObjectId[0] = 0;
81 m_tLastDiscoveryPoll = 0;
82 m_tLastStatusPoll = 0;
83 m_tLastConfigurationPoll = 0;
84 m_iSnmpAgentFails = 0;
85 m_iNativeAgentFails = 0;
86 m_hPollerMutex = MutexCreate();
87 m_hAgentAccessMutex = MutexCreate();
88 m_pAgentConnection = NULL;
89 m_szAgentVersion[0] = 0;
90 m_szPlatformName[0] = 0;
91 m_dwNumParams = 0;
92 m_pParamList = NULL;
93 m_dwPollerNode = 0;
94 memset(m_qwLastEvents, 0, sizeof(QWORD) * MAX_LAST_EVENTS);
95 }
96
97
98 //
99 // Node destructor
100 //
101
102 Node::~Node()
103 {
104 MutexDestroy(m_hPollerMutex);
105 MutexDestroy(m_hAgentAccessMutex);
106 if (m_pAgentConnection != NULL)
107 delete m_pAgentConnection;
108 safe_free(m_pParamList);
109 }
110
111
112 //
113 // Create object from database data
114 //
115
116 BOOL Node::CreateFromDB(DWORD dwId)
117 {
118 char szQuery[512];
119 DB_RESULT hResult;
120 int i, iNumRows;
121 DWORD dwSubnetId;
122 NetObj *pObject;
123 BOOL bResult = FALSE;
124
125 m_dwId = dwId;
126
127 if (!LoadCommonProperties())
128 return FALSE;
129
130 _sntprintf(szQuery, 512, "SELECT primary_ip,node_flags,"
131 "snmp_version,discovery_flags,auth_method,secret,"
132 "agent_port,status_poll_type,community,snmp_oid,"
133 "description,node_type,agent_version,"
134 "platform_name,poller_node_id FROM nodes WHERE id=%ld", dwId);
135 hResult = DBSelect(g_hCoreDB, szQuery);
136 if (hResult == 0)
137 return FALSE; // Query failed
138
139 if (DBGetNumRows(hResult) == 0)
140 {
141 DBFreeResult(hResult);
142 return FALSE;
143 }
144
145 m_dwIpAddr = DBGetFieldIPAddr(hResult, 0, 0);
146 m_dwFlags = DBGetFieldULong(hResult, 0, 1);
147 m_iSNMPVersion = DBGetFieldLong(hResult, 0, 2);
148 m_dwDiscoveryFlags = DBGetFieldULong(hResult, 0, 3);
149 m_wAuthMethod = (WORD)DBGetFieldLong(hResult, 0, 4);
150 strncpy(m_szSharedSecret, DBGetField(hResult, 0, 5), MAX_SECRET_LENGTH);
151 m_wAgentPort = (WORD)DBGetFieldLong(hResult, 0, 6);
152 m_iStatusPollType = DBGetFieldLong(hResult, 0, 7);
153 strncpy(m_szCommunityString, DBGetField(hResult, 0, 8), MAX_COMMUNITY_LENGTH);
154 strncpy(m_szObjectId, DBGetField(hResult, 0, 9), MAX_OID_LEN * 4);
155 m_pszDescription = _tcsdup(CHECK_NULL_EX(DBGetField(hResult, 0, 10)));
156 DecodeSQLString(m_pszDescription);
157 m_dwNodeType = DBGetFieldULong(hResult, 0, 11);
158 _tcsncpy(m_szAgentVersion, CHECK_NULL_EX(DBGetField(hResult, 0, 12)), MAX_AGENT_VERSION_LEN);
159 DecodeSQLString(m_szAgentVersion);
160 _tcsncpy(m_szPlatformName, CHECK_NULL_EX(DBGetField(hResult, 0, 13)), MAX_PLATFORM_NAME_LEN);
161 DecodeSQLString(m_szPlatformName);
162 m_dwPollerNode = DBGetFieldULong(hResult, 0, 14);
163
164 DBFreeResult(hResult);
165
166 if (!m_bIsDeleted)
167 {
168 // Link node to subnets
169 sprintf(szQuery, "SELECT subnet_id FROM nsmap WHERE node_id=%ld", dwId);
170 hResult = DBSelect(g_hCoreDB, szQuery);
171 if (hResult == NULL)
172 return FALSE; // Query failed
173
174 iNumRows = DBGetNumRows(hResult);
175 if (iNumRows == 0)
176 {
177 DBFreeResult(hResult);
178 return FALSE; // No parents - it shouldn't happen if database isn't corrupted
179 }
180
181 for(i = 0; i < iNumRows; i++)
182 {
183 dwSubnetId = DBGetFieldULong(hResult, i, 0);
184 pObject = FindObjectById(dwSubnetId);
185 if (pObject == NULL)
186 {
187 WriteLog(MSG_INVALID_SUBNET_ID, EVENTLOG_ERROR_TYPE, "dd", dwId, dwSubnetId);
188 break;
189 }
190 else if (pObject->Type() != OBJECT_SUBNET)
191 {
192 WriteLog(MSG_SUBNET_NOT_SUBNET, EVENTLOG_ERROR_TYPE, "dd", dwId, dwSubnetId);
193 break;
194 }
195 else
196 {
197 pObject->AddChild(this);
198 AddParent(pObject);
199 bResult = TRUE;
200 }
201 }
202
203 DBFreeResult(hResult);
204 LoadItemsFromDB();
205 LoadACLFromDB();
206
207 // Walk through all items in the node and load appropriate thresholds
208 for(i = 0; i < (int)m_dwNumItems; i++)
209 if (!m_ppItems[i]->LoadThresholdsFromDB())
210 bResult = FALSE;
211 }
212 else
213 {
214 bResult = TRUE;
215 }
216
217 return bResult;
218 }
219
220
221 //
222 // Save object to database
223 //
224
225 BOOL Node::SaveToDB(void)
226 {
227 TCHAR *pszEscDescr, *pszEscVersion, *pszEscPlatform;
228 TCHAR szQuery[4096], szIpAddr[16];
229 DB_RESULT hResult;
230 BOOL bNewObject = TRUE;
231 BOOL bResult;
232
233 // Lock object's access
234 Lock();
235
236 SaveCommonProperties();
237
238 // Check for object's existence in database
239 sprintf(szQuery, "SELECT id FROM nodes WHERE id=%ld", m_dwId);
240 hResult = DBSelect(g_hCoreDB, szQuery);
241 if (hResult != 0)
242 {
243 if (DBGetNumRows(hResult) > 0)
244 bNewObject = FALSE;
245 DBFreeResult(hResult);
246 }
247
248 // Form and execute INSERT or UPDATE query
249 pszEscDescr = EncodeSQLString(CHECK_NULL_EX(m_pszDescription));
250 pszEscVersion = EncodeSQLString(m_szAgentVersion);
251 pszEscPlatform = EncodeSQLString(m_szPlatformName);
252 if (bNewObject)
253 snprintf(szQuery, 4096,
254 "INSERT INTO nodes (id,primary_ip,"
255 "node_flags,snmp_version,community,discovery_flags,status_poll_type,"
256 "agent_port,auth_method,secret,snmp_oid,"
257 "description,node_type,agent_version,platform_name,"
258 "poller_node_id) VALUES (%ld,'%s',%ld,%d,'%s',%ld,%d,%d,%d,"
259 "'%s','%s','%s',%ld,'%s','%s',%ld)",
260 m_dwId, IpToStr(m_dwIpAddr, szIpAddr), m_dwFlags,
261 m_iSNMPVersion, m_szCommunityString, m_dwDiscoveryFlags, m_iStatusPollType,
262 m_wAgentPort, m_wAuthMethod, m_szSharedSecret, m_szObjectId,
263 pszEscDescr, m_dwNodeType, pszEscVersion, pszEscPlatform, m_dwPollerNode);
264 else
265 snprintf(szQuery, 4096,
266 "UPDATE nodes SET primary_ip='%s',"
267 "node_flags=%ld,snmp_version=%d,community='%s',discovery_flags=%d,"
268 "status_poll_type=%d,agent_port=%d,auth_method=%d,secret='%s',"
269 "snmp_oid='%s',description='%s',node_type=%ld,"
270 "agent_version='%s',platform_name='%s',poller_node_id=%ld WHERE id=%ld",
271 IpToStr(m_dwIpAddr, szIpAddr),
272 m_dwFlags, m_iSNMPVersion, m_szCommunityString, m_dwDiscoveryFlags,
273 m_iStatusPollType, m_wAgentPort, m_wAuthMethod, m_szSharedSecret,
274 m_szObjectId, pszEscDescr, m_dwNodeType,
275 pszEscVersion, pszEscPlatform, m_dwPollerNode, m_dwId);
276 bResult = DBQuery(g_hCoreDB, szQuery);
277 free(pszEscDescr);
278 free(pszEscVersion);
279 free(pszEscPlatform);
280
281 // Save data collection items
282 if (bResult)
283 {
284 DWORD i;
285
286 for(i = 0; i < m_dwNumItems; i++)
287 m_ppItems[i]->SaveToDB();
288 }
289
290 // Save access list
291 SaveACLToDB();
292
293 // Clear modifications flag and unlock object
294 m_bIsModified = FALSE;
295 Unlock();
296
297 return bResult;
298 }
299
300
301 //
302 // Delete object from database
303 //
304
305 BOOL Node::DeleteFromDB(void)
306 {
307 char szQuery[256];
308 BOOL bSuccess;
309
310 bSuccess = Template::DeleteFromDB();
311 if (bSuccess)
312 {
313 sprintf(szQuery, "DELETE FROM nodes WHERE id=%ld", m_dwId);
314 QueueSQLRequest(szQuery);
315 sprintf(szQuery, "DELETE FROM nsmap WHERE node_id=%ld", m_dwId);
316 QueueSQLRequest(szQuery);
317 sprintf(szQuery, "DROP TABLE idata_%ld", m_dwId);
318 QueueSQLRequest(szQuery);
319 }
320 return bSuccess;
321 }
322
323
324 //
325 // Poll newly discovered node
326 // Usually called once by node poller thread when new node is discovered
327 // and object for it is created
328 //
329
330 void Node::NewNodePoll(DWORD dwNetMask)
331 {
332 AgentConnection *pAgentConn;
333
334 PollerLock();
335
336 // Determine node's capabilities
337 if (SnmpGet(SNMP_VERSION_2C, m_dwIpAddr, m_szCommunityString, ".1.3.6.1.2.1.1.2.0", NULL, 0,
338 m_szObjectId, MAX_OID_LEN * 4, FALSE, FALSE) == SNMP_ERR_SUCCESS)
339 {
340 DWORD dwNodeFlags;
341
342 m_dwNodeType = OidToType(m_szObjectId, &dwNodeFlags);
343 m_dwFlags |= NF_IS_SNMP | dwNodeFlags;
344 m_iSNMPVersion = SNMP_VERSION_2C;
345 }
346 else
347 {
348 if (SnmpGet(SNMP_VERSION_1, m_dwIpAddr, m_szCommunityString, ".1.3.6.1.2.1.1.2.0", NULL, 0,
349 m_szObjectId, MAX_OID_LEN * 4, FALSE, FALSE) == SNMP_ERR_SUCCESS)
350 {
351 DWORD dwNodeFlags;
352
353 m_dwNodeType = OidToType(m_szObjectId, &dwNodeFlags);
354 m_dwFlags |= NF_IS_SNMP | dwNodeFlags;
355 m_iSNMPVersion = SNMP_VERSION_1;
356 }
357 }
358
359 // Check OSPF capabilities
360 if (m_dwFlags & NF_IS_SNMP)
361 {
362 CheckOSPFSupport();
363 }
364
365 pAgentConn = new AgentConnection(htonl(m_dwIpAddr), m_wAgentPort, m_wAuthMethod,
366 m_szSharedSecret);
367 if (pAgentConn->Connect())
368 {
369 m_dwFlags |= NF_IS_NATIVE_AGENT;
370 pAgentConn->GetParameter("Agent.Version", MAX_AGENT_VERSION_LEN, m_szAgentVersion);
371 pAgentConn->GetParameter("System.PlatformName", MAX_PLATFORM_NAME_LEN, m_szPlatformName);
372
373 pAgentConn->GetSupportedParameters(&m_dwNumParams, &m_pParamList);
374 }
375
376 // Get interface list
377 if ((m_dwFlags & NF_IS_SNMP) || (m_dwFlags & NF_IS_NATIVE_AGENT) ||
378 (m_dwFlags & NF_IS_LOCAL_MGMT))
379 {
380 INTERFACE_LIST *pIfList = NULL;
381 int i;
382
383 if (m_dwFlags & NF_IS_LOCAL_MGMT) // For local machine
384 pIfList = GetLocalInterfaceList();
385 else if (m_dwFlags & NF_IS_NATIVE_AGENT) // For others prefer native agent
386 {
387 pIfList = pAgentConn->GetInterfaceList();
388 CleanInterfaceList(pIfList);
389 }
390 if ((pIfList == NULL) && (m_dwFlags & NF_IS_SNMP)) // Use SNMP if we cannot get interfaces via agent
391 pIfList = SnmpGetInterfaceList(m_iSNMPVersion, m_dwIpAddr, m_szCommunityString, m_dwNodeType);
392
393 if (pIfList != NULL)
394 {
395 for(i = 0; i < pIfList->iNumEntries; i++)
396 CreateNewInterface(pIfList->pInterfaces[i].dwIpAddr,
397 pIfList->pInterfaces[i].dwIpNetMask,
398 pIfList->pInterfaces[i].szName,
399 pIfList->pInterfaces[i].dwIndex,
400 pIfList->pInterfaces[i].dwType,
401 pIfList->pInterfaces[i].bMacAddr);
402
403 // Check if address we are using to communicate with node
404 // is configured on one of node's interfaces
405 for(i = 0; i < pIfList->iNumEntries; i++)
406 if (pIfList->pInterfaces[i].dwIpAddr == m_dwIpAddr)
407 break;
408
409 if (i == pIfList->iNumEntries)
410 {
411 char szBuffer[MAX_OBJECT_NAME];
412
413 // Node is behind NAT
414 m_dwFlags |= NF_BEHIND_NAT;
415
416 // Create pseudo interface for NAT
417 ConfigReadStr("NATAdapterName", szBuffer, MAX_OBJECT_NAME, "NetXMS NAT Adapter");
418 CreateNewInterface(m_dwIpAddr, 0, szBuffer,
419 0x7FFFFFFF, IFTYPE_NETXMS_NAT_ADAPTER);
420 }
421
422 DestroyInterfaceList(pIfList);
423 }
424 else
425 {
426 // We cannot get interface list from node for some reasons, create dummy one
427 CreateNewInterface(m_dwIpAddr, dwNetMask);
428 }
429 }
430 else // No SNMP, no native agent - create pseudo interface object
431 {
432 CreateNewInterface(m_dwIpAddr, dwNetMask);
433 }
434
435 // Clean up agent connection
436 if (m_dwFlags & NF_IS_NATIVE_AGENT)
437 pAgentConn->Disconnect();
438 delete pAgentConn;
439
440 PollerUnlock();
441 }
442
443
444 //
445 // Get ARP cache from node
446 //
447
448 ARP_CACHE *Node::GetArpCache(void)
449 {
450 ARP_CACHE *pArpCache = NULL;
451
452 if (m_dwFlags & NF_IS_LOCAL_MGMT)
453 {
454 pArpCache = GetLocalArpCache();
455 }
456 else if (m_dwFlags & NF_IS_NATIVE_AGENT)
457 {
458 AgentLock();
459 if (ConnectToAgent())
460 pArpCache = m_pAgentConnection->GetArpCache();
461 AgentUnlock();
462 }
463 else if (m_dwFlags & NF_IS_SNMP)
464 {
465 pArpCache = SnmpGetArpCache(m_iSNMPVersion, m_dwIpAddr, m_szCommunityString);
466 }
467
468 return pArpCache;
469 }
470
471
472 //
473 // Get list of interfaces from node
474 //
475
476 INTERFACE_LIST *Node::GetInterfaceList(void)
477 {
478 INTERFACE_LIST *pIfList = NULL;
479
480 if (m_dwFlags & NF_IS_LOCAL_MGMT)
481 {
482 pIfList = GetLocalInterfaceList();
483 }
484 else if (m_dwFlags & NF_IS_NATIVE_AGENT)
485 {
486 AgentLock();
487 if (ConnectToAgent())
488 {
489 pIfList = m_pAgentConnection->GetInterfaceList();
490 CleanInterfaceList(pIfList);
491 }
492 AgentUnlock();
493 }
494 else if (m_dwFlags & NF_IS_SNMP)
495 {
496 pIfList = SnmpGetInterfaceList(m_iSNMPVersion, m_dwIpAddr, m_szCommunityString, m_dwNodeType);
497 }
498
499 return pIfList;
500 }
501
502
503 //
504 // Find interface by index and node IP
505 // Returns pointer to interface object or NULL if appropriate interface couldn't be found
506 //
507
508 Interface *Node::FindInterface(DWORD dwIndex, DWORD dwHostAddr)
509 {
510 DWORD i;
511 Interface *pInterface;
512
513 Lock();
514 for(i = 0; i < m_dwChildCount; i++)
515 if (m_pChildList[i]->Type() == OBJECT_INTERFACE)
516 {
517 pInterface = (Interface *)m_pChildList[i];
518 if (pInterface->IfIndex() == dwIndex)
519 {
520 if ((pInterface->IpAddr() & pInterface->IpNetMask()) ==
521 (dwHostAddr & pInterface->IpNetMask()))
522 {
523 Unlock();
524 return pInterface;
525 }
526 }
527 }
528 Unlock();
529 return NULL;
530 }
531
532
533 //
534 // Create new interface
535 //
536
537 void Node::CreateNewInterface(DWORD dwIpAddr, DWORD dwNetMask, char *szName,
538 DWORD dwIndex, DWORD dwType, BYTE *pbMacAddr)
539 {
540 Interface *pInterface;
541 Subnet *pSubnet = NULL;
542
543 // Find subnet to place interface object to
544 if (dwIpAddr != 0)
545 {
546 pSubnet = FindSubnetForNode(dwIpAddr);
547 if (pSubnet == NULL)
548 {
549 // Check if netmask is 0 (detect), and if yes, create
550 // new subnet with class mask
551 if (dwNetMask == 0)
552 {
553 if (dwIpAddr < 0x80000000)
554 dwNetMask = 0xFF000000; // Class A
555 else if (dwIpAddr < 0xC0000000)
556 dwNetMask = 0xFFFF0000; // Class B
557 else if (dwIpAddr < 0xE0000000)
558 dwNetMask = 0xFFFFFF00; // Class C
559 else
560 {
561 TCHAR szBuffer[16];
562
563 // Multicast address??
564 DbgPrintf(AF_DEBUG_DISCOVERY,
565 "Attempt to create interface object with multicast address %s",
566 IpToStr(dwIpAddr, szBuffer));
567 }
568 }
569
570 // Create new subnet object
571 if (dwIpAddr < 0xE0000000)
572 {
573 pSubnet = new Subnet(dwIpAddr & dwNetMask, dwNetMask);
574 NetObjInsert(pSubnet, TRUE);
575 g_pEntireNet->AddSubnet(pSubnet);
576 }
577 }
578 else
579 {
580 // Set correct netmask if we was asked for it
581 if (dwNetMask == 0)
582 {
583 dwNetMask = pSubnet->IpNetMask();
584 }
585 }
586 }
587
588 // Create interface object
589 if (szName != NULL)
590 pInterface = new Interface(szName, dwIndex, dwIpAddr, dwNetMask, dwType);
591 else
592 pInterface = new Interface(dwIpAddr, dwNetMask);
593 if (pbMacAddr != NULL)
594 pInterface->SetMacAddr(pbMacAddr);
595
596 // Insert to objects' list and generate event
597 NetObjInsert(pInterface, TRUE);
598 AddInterface(pInterface);
599 PostEvent(EVENT_INTERFACE_ADDED, m_dwId, "dsaad", pInterface->Id(),
600 pInterface->Name(), pInterface->IpAddr(),
601 pInterface->IpNetMask(), pInterface->IfIndex());
602
603 // Bind node to appropriate subnet
604 if (pSubnet != NULL)
605 {
606 pSubnet->AddNode(this);
607
608 // Check if subnet mask is correct on interface
609 if (pSubnet->IpNetMask() != dwNetMask)
610 PostEvent(EVENT_INCORRECT_NETMASK, m_dwId, "idsaa", pInterface->Id(),
611 pInterface->IfIndex(), pInterface->Name(),
612 pInterface->IpNetMask(), pSubnet->IpNetMask());
613 }
614 }
615
616
617 //
618 // Delete interface from node
619 //
620
621 void Node::DeleteInterface(Interface *pInterface)
622 {
623 DWORD i;
624
625 // Check if we should unlink node from interface's subnet
626 if (pInterface->IpAddr() != 0)
627 {
628 for(i = 0; i < m_dwChildCount; i++)
629 if (m_pChildList[i]->Type() == OBJECT_INTERFACE)
630 if (m_pChildList[i] != pInterface)
631 if ((((Interface *)m_pChildList[i])->IpAddr() & ((Interface *)m_pChildList[i])->IpNetMask()) ==
632 (pInterface->IpAddr() & pInterface->IpNetMask()))
633 break;
634 if (i == m_dwChildCount)
635 {
636 // Last interface in subnet, should unlink node
637 Subnet *pSubnet = FindSubnetByIP(pInterface->IpAddr() & pInterface->IpNetMask());
638 DeleteParent(pSubnet);
639 if (pSubnet != NULL)
640 {
641 pSubnet->DeleteChild(this);
642 if ((pSubnet->IsEmpty()) && (g_dwFlags & AF_DELETE_EMPTY_SUBNETS))
643 {
644 PostEvent(EVENT_SUBNET_DELETED, pSubnet->Id(), NULL);
645 pSubnet->Delete(FALSE);
646 }
647 }
648 }
649 }
650 pInterface->Delete(FALSE);
651 }
652
653
654 //
655 // Calculate node status based on child objects status
656 //
657
658 void Node::CalculateCompoundStatus(void)
659 {
660 int iOldStatus = m_iStatus;
661 static DWORD dwEventCodes[] = { EVENT_NODE_NORMAL, EVENT_NODE_MINOR,
662 EVENT_NODE_WARNING, EVENT_NODE_MAJOR, EVENT_NODE_CRITICAL,
663 EVENT_NODE_UNKNOWN, EVENT_NODE_UNMANAGED };
664
665 NetObj::CalculateCompoundStatus();
666 if (m_iStatus != iOldStatus)
667 PostEvent(dwEventCodes[m_iStatus], m_dwId, "d", iOldStatus);
668 }
669
670
671 //
672 // Perform status poll on node
673 //
674
675 void Node::StatusPoll(ClientSession *pSession, DWORD dwRqId)
676 {
677 DWORD i, dwPollListSize;
678 NetObj *pPollerNode = NULL, **ppPollList;
679
680 PollerLock();
681 m_pPollRequestor = pSession;
682 SendPollerMsg(dwRqId, _T("Starting status poll for node %s\r\n"), m_szName);
683 Lock();
684
685 // Find service poller node object
686 if (m_dwPollerNode != 0)
687 {
688 pPollerNode = FindObjectById(m_dwPollerNode);
689 if (pPollerNode != NULL)
690 {
691 if (pPollerNode->Type() != OBJECT_NODE)
692 pPollerNode = NULL;
693 }
694 }
695
696 // If nothing found, use management server
697 if (pPollerNode == NULL)
698 {
699 pPollerNode = FindObjectById(g_dwMgmtNode);
700 if (pPollerNode != NULL)
701 pPollerNode->IncRefCount();
702 }
703 else
704 {
705 pPollerNode->IncRefCount();
706 }
707
708 // Create polling list
709 ppPollList = (NetObj **)malloc(sizeof(NetObj *) * m_dwChildCount);
710 for(i = 0, dwPollListSize = 0; i < m_dwChildCount; i++)
711 if (m_pChildList[i]->Status() != STATUS_UNMANAGED)
712 {
713 m_pChildList[i]->IncRefCount();
714 ppPollList[dwPollListSize++] = m_pChildList[i];
715 }
716 Unlock();
717
718 // Poll interfaces and services
719 for(i = 0; i < dwPollListSize; i++)
720 {
721 switch(ppPollList[i]->Type())
722 {
723 case OBJECT_INTERFACE:
724 ((Interface *)ppPollList[i])->StatusPoll(pSession, dwRqId);
725 break;
726 case OBJECT_NETWORKSERVICE:
727 ((NetworkService *)ppPollList[i])->StatusPoll(pSession, dwRqId, (Node *)pPollerNode);
728 break;
729 default:
730 break;
731 }
732 ppPollList[i]->DecRefCount();
733 }
734 safe_free(ppPollList);
735
736 if (pPollerNode != NULL)
737 pPollerNode->DecRefCount();
738
739 CalculateCompoundStatus();
740 m_tLastStatusPoll = time(NULL);
741 SendPollerMsg(dwRqId, "Finished status poll for node %s\r\n"
742 "Node status after poll is %s\r\n", m_szName, g_pszStatusName[m_iStatus]);
743 m_pPollRequestor = NULL;
744 if (dwRqId == 0)
745 m_dwDynamicFlags &= ~NDF_QUEUED_FOR_STATUS_POLL;
746 PollerUnlock();
747 }
748
749
750 //
751 // Perform configuration poll on node
752 //
753
754 void Node::ConfigurationPoll(ClientSession *pSession, DWORD dwRqId)
755 {
756 DWORD dwOldFlags = m_dwFlags;
757 AgentConnection *pAgentConn;
758 INTERFACE_LIST *pIfList;
759 char szBuffer[4096];
760 BOOL bHasChanges = FALSE;
761
762 PollerLock();
763 m_pPollRequestor = pSession;
764 SendPollerMsg(dwRqId, _T("Starting configuration poll for node %s\r\n"), m_szName);
765 DbgPrintf(AF_DEBUG_DISCOVERY, "Starting configuration poll for node %s (ID: %d)", m_szName, m_dwId);
766
767 // Check node's capabilities
768 SendPollerMsg(dwRqId, _T("Checking node's capabilities...\r\n"));
769 if (SnmpGet(m_iSNMPVersion, m_dwIpAddr, m_szCommunityString, ".1.3.6.1.2.1.1.2.0", NULL, 0,
770 szBuffer, 4096, FALSE, FALSE) == SNMP_ERR_SUCCESS)
771 {
772 DWORD dwNodeFlags, dwNodeType;
773
774 if (strcmp(m_szObjectId, szBuffer))
775 {
776 strncpy(m_szObjectId, szBuffer, MAX_OID_LEN * 4);
777 bHasChanges = TRUE;
778 }
779
780 m_dwFlags |= NF_IS_SNMP;
781 m_dwDynamicFlags &= ~NDF_SNMP_UNREACHEABLE;
782 m_iSnmpAgentFails = 0;
783 SendPollerMsg(dwRqId, _T(" SNMP agent is active\r\n"));
784
785 // Check node type
786 dwNodeType = OidToType(m_szObjectId, &dwNodeFlags);
787 if (m_dwNodeType != dwNodeType)
788 {
789 m_dwFlags |= dwNodeFlags;
790 m_dwNodeType = dwNodeType;
791 SendPollerMsg(dwRqId, _T(" Node type has been changed to %d\r\n"), m_dwNodeType);
792 bHasChanges = TRUE;
793 }
794
795 CheckOSPFSupport();
796 }
797 else
798 {
799 if (m_dwFlags & NF_IS_SNMP)
800 {
801 if (m_iSnmpAgentFails == 0)
802 PostEvent(EVENT_SNMP_FAIL, m_dwId, NULL);
803 m_iSnmpAgentFails++;
804 m_dwDynamicFlags |= NDF_SNMP_UNREACHEABLE;
805 }
806 SendPollerMsg(dwRqId, _T(" SNMP agent is not responding\r\n"));
807 }
808
809 pAgentConn = new AgentConnection(htonl(m_dwIpAddr), m_wAgentPort, m_wAuthMethod, m_szSharedSecret);
810 if (pAgentConn->Connect())
811 {
812 m_dwFlags |= NF_IS_NATIVE_AGENT;
813 m_dwDynamicFlags &= ~NDF_AGENT_UNREACHEABLE;
814 m_iNativeAgentFails = 0;
815
816 Lock();
817
818 if (pAgentConn->GetParameter("Agent.Version", MAX_AGENT_VERSION_LEN, szBuffer) == ERR_SUCCESS)
819 {
820 if (strcmp(m_szAgentVersion, szBuffer))
821 {
822 strcpy(m_szAgentVersion, szBuffer);
823 bHasChanges = TRUE;
824 SendPollerMsg(dwRqId, _T(" NetXMS agent version changed to %s\r\n"), m_szAgentVersion);
825 }
826 }
827
828 if (pAgentConn->GetParameter("System.PlatformName", MAX_PLATFORM_NAME_LEN, szBuffer) == ERR_SUCCESS)
829 {
830 if (strcmp(m_szPlatformName, szBuffer))
831 {
832 strcpy(m_szPlatformName, szBuffer);
833 bHasChanges = TRUE;
834 SendPollerMsg(dwRqId, _T(" Platform name changed to %s\r\n"), m_szAgentVersion);
835 }
836 }
837
838 safe_free(m_pParamList);
839 pAgentConn->GetSupportedParameters(&m_dwNumParams, &m_pParamList);
840
841 Unlock();
842 pAgentConn->Disconnect();
843 SendPollerMsg(dwRqId, _T(" NetXMS native agent is active\r\n"));
844 }
845 else
846 {
847 if (m_dwFlags & NF_IS_NATIVE_AGENT)
848 {
849 if (m_iNativeAgentFails == 0)
850 PostEvent(EVENT_AGENT_FAIL, m_dwId, NULL);
851 m_iNativeAgentFails++;
852 m_dwDynamicFlags |= NDF_AGENT_UNREACHEABLE;
853 }
854 SendPollerMsg(dwRqId, _T(" NetXMS native agent is not responding\r\n"));
855 }
856 delete pAgentConn;
857
858 // Generate event if node flags has been changed
859 if (dwOldFlags != m_dwFlags)
860 {
861 PostEvent(EVENT_NODE_FLAGS_CHANGED, m_dwId, "xx", dwOldFlags, m_dwFlags);
862 bHasChanges = TRUE;
863 }
864
865 // Retrieve interface list
866 SendPollerMsg(dwRqId, _T("Capability check finished\r\n"
867 "Checking interface configuration...\r\n"));
868 pIfList = GetInterfaceList();
869 if (pIfList != NULL)
870 {
871 DWORD i;
872 int j;
873
874 // Find non-existing interfaces
875 for(i = 0; i < m_dwChildCount; i++)
876 if (m_pChildList[i]->Type() == OBJECT_INTERFACE)
877 {
878 Interface *pInterface = (Interface *)m_pChildList[i];
879
880 if (pInterface->IfType() != IFTYPE_NETXMS_NAT_ADAPTER)
881 {
882 for(j = 0; j < pIfList->iNumEntries; j++)
883 if ((pIfList->pInterfaces[j].dwIndex == pInterface->IfIndex()) &&
884 (pIfList->pInterfaces[j].dwIpAddr == pInterface->IpAddr()) &&
885 (pIfList->pInterfaces[j].dwIpNetMask == pInterface->IpNetMask()))
886 break;
887 if (j == pIfList->iNumEntries)
888 {
889 // No such interface in current configuration, delete it
890 SendPollerMsg(dwRqId, _T(" Interface \"%s\" is no longer exist\r\n"),
891 pInterface->Name());
892 PostEvent(EVENT_INTERFACE_DELETED, m_dwId, "dsaa", pInterface->IfIndex(),
893 pInterface->Name(), pInterface->IpAddr(), pInterface->IpNetMask());
894 DeleteInterface(pInterface);
895 i = 0xFFFFFFFF; // Restart loop
896 bHasChanges = TRUE;
897 }
898 }
899 }
900
901 // Add new interfaces and check configuration of existing
902 for(j = 0; j < pIfList->iNumEntries; j++)
903 {
904 for(i = 0; i < m_dwChildCount; i++)
905 if (m_pChildList[i]->Type() == OBJECT_INTERFACE)
906 {
907 Interface *pInterface = (Interface *)m_pChildList[i];
908
909 if ((pIfList->pInterfaces[j].dwIndex == pInterface->IfIndex()) &&
910 (pIfList->pInterfaces[j].dwIpAddr == pInterface->IpAddr()) &&
911 (pIfList->pInterfaces[j].dwIpNetMask == pInterface->IpNetMask()))
912 {
913 // Existing interface, check configuration
914 if (memcmp(pIfList->pInterfaces[j].bMacAddr, pInterface->MacAddr(), MAC_ADDR_LENGTH))
915 {
916 char szOldMac[16], szNewMac[16];
917
918 BinToStr((BYTE *)pInterface->MacAddr(), MAC_ADDR_LENGTH, szOldMac);
919 BinToStr(pIfList->pInterfaces[j].bMacAddr, MAC_ADDR_LENGTH, szNewMac);
920 PostEvent(EVENT_MAC_ADDR_CHANGED, m_dwId, "idsss",
921 pInterface->Id(), pInterface->IfIndex(),
922 pInterface->Name(), szOldMac, szNewMac);
923 pInterface->SetMacAddr(pIfList->pInterfaces[j].bMacAddr);
924 }
925 break;
926 }
927 }
928 if (i == m_dwChildCount)
929 {
930 // New interface
931 SendPollerMsg(dwRqId, _T(" Found new interface \"%s\"\r\n"),
932 pIfList->pInterfaces[j].szName);
933 CreateNewInterface(pIfList->pInterfaces[j].dwIpAddr,
934 pIfList->pInterfaces[j].dwIpNetMask,
935 pIfList->pInterfaces[j].szName,
936 pIfList->pInterfaces[j].dwIndex,
937 pIfList->pInterfaces[j].dwType,
938 pIfList->pInterfaces[j].bMacAddr);
939 bHasChanges = TRUE;
940 }
941 }
942
943 // Check if address we are using to communicate with node
944 // is configured on one of node's interfaces
945 for(i = 0; i < (DWORD)pIfList->iNumEntries; i++)
946 if (pIfList->pInterfaces[i].dwIpAddr == m_dwIpAddr)
947 break;
948
949 if (i == (DWORD)pIfList->iNumEntries)
950 {
951 // Node is behind NAT
952 m_dwFlags |= NF_BEHIND_NAT;
953
954 // Check if we already have NAT interface
955 Lock();
956 for(i = 0; i < m_dwChildCount; i++)
957 if (m_pChildList[i]->Type() == OBJECT_INTERFACE)
958 {
959 if (((Interface *)m_pChildList[i])->IfType() == IFTYPE_NETXMS_NAT_ADAPTER)
960 break;
961 }
962 Unlock();
963
964 if (i == m_dwChildCount)
965 {
966 char szBuffer[MAX_OBJECT_NAME];
967
968 // Create pseudo interface for NAT
969 ConfigReadStr("NATAdapterName", szBuffer, MAX_OBJECT_NAME, "NetXMS NAT Adapter");
970 CreateNewInterface(m_dwIpAddr, 0, szBuffer,
971 0x7FFFFFFF, IFTYPE_NETXMS_NAT_ADAPTER);
972 bHasChanges = TRUE;
973 }
974 }
975 else
976 {
977 // Check if NF_BEHIND_NAT flag set incorrectly
978 if (m_dwFlags & NF_BEHIND_NAT)
979 {
980 Interface *pIfNat;
981
982 // Remove NAT interface
983 Lock();
984 for(i = 0, pIfNat = NULL; i < m_dwChildCount; i++)
985 if (m_pChildList[i]->Type() == OBJECT_INTERFACE)
986 {
987 if (((Interface *)m_pChildList[i])->IfType() == IFTYPE_NETXMS_NAT_ADAPTER)
988 {
989 pIfNat = (Interface *)m_pChildList[i];
990 break;
991 }
992 }
993 Unlock();
994
995 if (pIfNat != NULL)
996 DeleteInterface(pIfNat);
997
998 m_dwFlags &= ~NF_BEHIND_NAT;
999 bHasChanges = TRUE;
1000 }
1001 }
1002
1003 DestroyInterfaceList(pIfList);
1004 }
1005 else
1006 {
1007 SendPollerMsg(dwRqId, _T(" Unable to get interface list from node\r\n"));
1008 }
1009
1010 m_tLastConfigurationPoll = time(NULL);
1011 SendPollerMsg(dwRqId, _T("Interface configuration check finished\r\n"
1012 "Finished configuration poll for node %s\r\n"
1013 "Node configuration was%schanged after poll\r\n"),
1014 m_szName, bHasChanges ? _T(" ") : _T(" not "));
1015
1016 // Finish configuration poll
1017 if (dwRqId == 0)
1018 m_dwDynamicFlags &= ~NDF_QUEUED_FOR_CONFIG_POLL;
1019 PollerUnlock();
1020 DbgPrintf(AF_DEBUG_DISCOVERY, "Finished configuration poll for node %s (ID: %d)", m_szName, m_dwId);
1021
1022 if (bHasChanges)
1023 {
1024 Lock();
1025 Modify();
1026 Unlock();
1027 }
1028 }
1029
1030
1031 //
1032 // Connect to native agent
1033 //
1034
1035 BOOL Node::ConnectToAgent(void)
1036 {
1037 // Create new agent connection object if needed
1038 if (m_pAgentConnection == NULL)
1039 m_pAgentConnection = new AgentConnection(htonl(m_dwIpAddr), m_wAgentPort, m_wAuthMethod, m_szSharedSecret);
1040
1041 // Check if we already connected
1042 if (m_pAgentConnection->Nop() == ERR_SUCCESS)
1043 return TRUE;
1044
1045 // Close current connection or clean up after broken connection
1046 m_pAgentConnection->Disconnect();
1047 return m_pAgentConnection->Connect();
1048 }
1049
1050
1051 //
1052 // Get item's value via SNMP
1053 //
1054
1055 DWORD Node::GetItemFromSNMP(const char *szParam, DWORD dwBufSize, char *szBuffer)
1056 {
1057 DWORD dwResult;
1058
1059 dwResult = SnmpGet(m_iSNMPVersion, m_dwIpAddr, m_szCommunityString, szParam, NULL, 0,
1060 szBuffer, dwBufSize, FALSE, TRUE);
1061 return (dwResult == SNMP_ERR_SUCCESS) ? DCE_SUCCESS :
1062 ((dwResult == SNMP_ERR_NO_OBJECT) ? DCE_NOT_SUPPORTED : DCE_COMM_ERROR);
1063 }
1064
1065
1066 //
1067 // Get item's value via native agent
1068 //
1069
1070 DWORD Node::GetItemFromAgent(const char *szParam, DWORD dwBufSize, char *szBuffer)
1071 {
1072 DWORD dwError, dwResult = DCE_COMM_ERROR;
1073 DWORD dwTries = 5;
1074
1075 AgentLock();
1076
1077 // Establish connection if needed
1078 if (m_pAgentConnection == NULL)
1079 if (!ConnectToAgent())
1080 goto end_loop;
1081
1082 // Get parameter from agent
1083 while(dwTries-- > 0)
1084 {
1085 dwError = m_pAgentConnection->GetParameter((char *)szParam, dwBufSize, szBuffer);
1086 switch(dwError)
1087 {
1088 case ERR_SUCCESS:
1089 dwResult = DCE_SUCCESS;
1090 goto end_loop;
1091 case ERR_UNKNOWN_PARAMETER:
1092 dwResult = DCE_NOT_SUPPORTED;
1093 goto end_loop;
1094 case ERR_NOT_CONNECTED:
1095 case ERR_CONNECTION_BROKEN:
1096 if (!ConnectToAgent())
1097 goto end_loop;
1098 break;
1099 case ERR_REQUEST_TIMEOUT:
1100 break;
1101 }
1102 }
1103
1104 end_loop:
1105 AgentUnlock();
1106 DbgPrintf(AF_DEBUG_DC, "Node(%s)->GetItemFromAgent(%s): dwError=%d dwResult=%d",
1107 m_szName, szParam, dwError, dwResult);
1108 return dwResult;
1109 }
1110
1111
1112 //
1113 // Get value for server's internal parameter
1114 //
1115
1116 DWORD Node::GetInternalItem(const char *szParam, DWORD dwBufSize, char *szBuffer)
1117 {
1118 DWORD dwError = DCE_SUCCESS;
1119
1120 if (!stricmp(szParam, "status"))
1121 {
1122 sprintf(szBuffer, "%d", m_iStatus);
1123 }
1124 else if (!stricmp(szParam, "AgentStatus"))
1125 {
1126 if (m_dwFlags & NF_IS_NATIVE_AGENT)
1127 {
1128 szBuffer[0] = (m_dwDynamicFlags & NDF_AGENT_UNREACHEABLE) ? '1' : '0';
1129 szBuffer[1] = 0;
1130 }
1131 else
1132 {
1133 dwError = DCE_NOT_SUPPORTED;
1134 }
1135 }
1136 else if (MatchString("ChildStatus(*)", szParam, FALSE))
1137 {
1138 char *pEnd, szArg[256];
1139 DWORD i, dwId;
1140 NetObj *pObject = NULL;
1141
1142 NxGetParameterArg((char *)szParam, 1, szArg, 256);
1143 dwId = strtoul(szArg, &pEnd, 0);
1144 if (*pEnd != 0)
1145 {
1146 // Argument is object's name
1147 dwId = 0;
1148 }
1149
1150 // Find child object with requested ID or name
1151 Lock();
1152 for(i = 0; i < m_dwChildCount; i++)
1153 {
1154 if (((dwId == 0) && (!stricmp(m_pChildList[i]->Name(), szArg))) ||
1155 (dwId == m_pChildList[i]->Id()))
1156 {
1157 pObject = m_pChildList[i];
1158 break;
1159 }
1160 }
1161 Unlock();
1162
1163 if (pObject != NULL)
1164 {
1165 sprintf(szBuffer, "%d", pObject->Status());
1166 }
1167 else
1168 {
1169 dwError = DCE_NOT_SUPPORTED;
1170 }
1171 }
1172 else if (m_dwFlags & NF_IS_LOCAL_MGMT)
1173 {
1174 if (!stricmp(szParam, "Server.AverageDCPollerQueueSize"))
1175 {
1176 sprintf(szBuffer, "%f", g_dAvgPollerQueueSize);
1177 }
1178 else if (!stricmp(szParam, "Server.AverageDBWriterQueueSize"))
1179 {
1180 sprintf(szBuffer, "%f", g_dAvgDBWriterQueueSize);
1181 }
1182 else if (!stricmp(szParam, "Server.AverageStatusPollerQueueSize"))
1183 {
1184 sprintf(szBuffer, "%f", g_dAvgStatusPollerQueueSize);
1185 }
1186 else if (!stricmp(szParam, "Server.AverageConfigurationPollerQueueSize"))
1187 {
1188 sprintf(szBuffer, "%f", g_dAvgConfigPollerQueueSize);
1189 }
1190 else if (!stricmp(szParam, "Server.AverageDCIQueuingTime"))
1191 {
1192 sprintf(szBuffer, "%lu", g_dwAvgDCIQueuingTime);
1193 }
1194 else
1195 {
1196 dwError = DCE_NOT_SUPPORTED;
1197 }
1198 }
1199 else
1200 {
1201 dwError = DCE_NOT_SUPPORTED;
1202 }
1203
1204 return dwError;
1205 }
1206
1207
1208 //
1209 // Get item's value for client
1210 //
1211
1212 DWORD Node::GetItemForClient(int iOrigin, const char *pszParam, char *pszBuffer, DWORD dwBufSize)
1213 {
1214 DWORD dwResult = 0, dwRetCode;
1215
1216 // Get data from node
1217 switch(iOrigin)
1218 {
1219 case DS_INTERNAL:
1220 dwRetCode = GetInternalItem(pszParam, dwBufSize, pszBuffer);
1221 break;
1222 case DS_NATIVE_AGENT:
1223 dwRetCode = GetItemFromAgent(pszParam, dwBufSize, pszBuffer);
1224 break;
1225 case DS_SNMP_AGENT:
1226 dwRetCode = GetItemFromSNMP(pszParam, dwBufSize, pszBuffer);
1227 break;
1228 default:
1229 dwResult = RCC_INVALID_ARGUMENT;
1230 break;
1231 }
1232
1233 // Translate return code to RCC
1234 if (dwResult != RCC_INVALID_ARGUMENT)
1235 {
1236 switch(dwRetCode)
1237 {
1238 case DCE_SUCCESS:
1239 dwResult = RCC_SUCCESS;
1240 break;
1241 case DCE_COMM_ERROR:
1242 dwResult = RCC_COMM_FAILURE;
1243 break;
1244 case DCE_NOT_SUPPORTED:
1245 dwResult = RCC_DCI_NOT_SUPPORTED;
1246 break;
1247 default:
1248 dwResult = RCC_SYSTEM_FAILURE;
1249 break;
1250 }
1251 }
1252
1253 return dwResult;
1254 }
1255
1256
1257 //
1258 // Put items which requires polling into the queue
1259 //
1260
1261 void Node::QueueItemsForPolling(Queue *pPollerQueue)
1262 {
1263 DWORD i;
1264 time_t currTime;
1265
1266 currTime = time(NULL);
1267
1268 Lock();
1269 for(i = 0; i < m_dwNumItems; i++)
1270 {
1271 if (m_ppItems[i]->ReadyForPolling(currTime))
1272 {
1273 m_ppItems[i]->SetBusyFlag(TRUE);
1274 m_dwRefCount++; // Increment reference count for each queued DCI
1275 pPollerQueue->Put(m_ppItems[i]);
1276 }
1277 }
1278 Unlock();
1279 }
1280
1281
1282 //
1283 // Create CSCP message with object's data
1284 //
1285
1286 void Node::CreateMessage(CSCPMessage *pMsg)
1287 {
1288 Template::CreateMessage(pMsg);
1289 pMsg->SetVariable(VID_FLAGS, m_dwFlags);
1290 pMsg->SetVariable(VID_DISCOVERY_FLAGS, m_dwDiscoveryFlags);
1291 pMsg->SetVariable(VID_AGENT_PORT, m_wAgentPort);
1292 pMsg->SetVariable(VID_AUTH_METHOD, m_wAuthMethod);
1293 pMsg->SetVariable(VID_SHARED_SECRET, m_szSharedSecret);
1294 pMsg->SetVariable(VID_COMMUNITY_STRING, m_szCommunityString);
1295 pMsg->SetVariable(VID_SNMP_OID, m_szObjectId);
1296 pMsg->SetVariable(VID_NODE_TYPE, m_dwNodeType);
1297 pMsg->SetVariable(VID_SNMP_VERSION, (WORD)m_iSNMPVersion);
1298 pMsg->SetVariable(VID_AGENT_VERSION, m_szAgentVersion);
1299 pMsg->SetVariable(VID_PLATFORM_NAME, m_szPlatformName);
1300 pMsg->SetVariable(VID_POLLER_NODE_ID, m_dwPollerNode);
1301 }
1302
1303
1304 //
1305 // Modify object from message
1306 //
1307
1308 DWORD Node::ModifyFromMessage(CSCPMessage *pRequest, BOOL bAlreadyLocked)
1309 {
1310 if (!bAlreadyLocked)
1311 Lock();
1312
1313 // Change primary IP address
1314 if (pRequest->IsVariableExist(VID_IP_ADDRESS))
1315 {
1316 DWORD i, dwIpAddr;
1317
1318 dwIpAddr = pRequest->GetVariableLong(VID_IP_ADDRESS);
1319
1320 // Check if received IP address is one of node's interface addresses
1321 for(i = 0; i < m_dwChildCount; i++)
1322 if ((m_pChildList[i]->Type() == OBJECT_INTERFACE) &&
1323 (m_pChildList[i]->IpAddr() == dwIpAddr))
1324 break;
1325 if (i == m_dwChildCount)
1326 {
1327 Unlock();
1328 return RCC_INVALID_IP_ADDR;
1329 }
1330
1331 m_dwIpAddr = dwIpAddr;
1332 }
1333
1334 // Poller node ID
1335 if (pRequest->IsVariableExist(VID_POLLER_NODE_ID))
1336 {
1337 DWORD dwNodeId;
1338 NetObj *pObject;
1339
1340 dwNodeId = pRequest->GetVariableLong(VID_POLLER_NODE_ID);
1341 pObject = FindObjectById(dwNodeId);
1342
1343 // Check if received id is a valid node id
1344 if (pObject == NULL)
1345 {
1346 Unlock();
1347 return RCC_INVALID_OBJECT_ID;
1348 }
1349 if (pObject->Type() != OBJECT_NODE)
1350 {
1351 Unlock();
1352 return RCC_INVALID_OBJECT_ID;
1353 }
1354
1355 m_dwPollerNode = dwNodeId;
1356 }
1357
1358 // Change listen port of native agent
1359 if (pRequest->IsVariableExist(VID_AGENT_PORT))
1360 m_wAgentPort = pRequest->GetVariableShort(VID_AGENT_PORT);
1361
1362 // Change authentication method of native agent
1363 if (pRequest->IsVariableExist(VID_AUTH_METHOD))
1364 m_wAuthMethod = pRequest->GetVariableShort(VID_AUTH_METHOD);
1365
1366 // Change shared secret of native agent
1367 if (pRequest->IsVariableExist(VID_SHARED_SECRET))
1368 pRequest->GetVariableStr(VID_SHARED_SECRET, m_szSharedSecret, MAX_SECRET_LENGTH);
1369
1370 // Change SNMP protocol version
1371 if (pRequest->IsVariableExist(VID_SNMP_VERSION))
1372 m_iSNMPVersion = pRequest->GetVariableShort(VID_SNMP_VERSION);
1373
1374 // Change SNMP community string
1375 if (pRequest->IsVariableExist(VID_COMMUNITY_STRING))
1376 pRequest->GetVariableStr(VID_COMMUNITY_STRING, m_szCommunityString, MAX_COMMUNITY_LENGTH);
1377
1378 return Template::ModifyFromMessage(pRequest, TRUE);
1379 }
1380
1381
1382 //
1383 // Wakeup node using magic packet
1384 //
1385
1386 DWORD Node::WakeUp(void)
1387 {
1388 DWORD i, dwResult = RCC_NO_WOL_INTERFACES;
1389
1390 Lock();
1391
1392 for(i = 0; i < m_dwChildCount; i++)
1393 if ((m_pChildList[i]->Type() == OBJECT_INTERFACE) &&
1394 (m_pChildList[i]->Status() != STATUS_UNMANAGED) &&
1395 (m_pChildList[i]->IpAddr() != 0))
1396 {
1397 dwResult = ((Interface *)m_pChildList[i])->WakeUp();
1398 break;
1399 }
1400
1401 Unlock();
1402 return dwResult;
1403 }
1404
1405
1406 //
1407 // Get status of interface with given index from SNMP agent
1408 //
1409
1410 int Node::GetInterfaceStatusFromSNMP(DWORD dwIndex)
1411 {
1412 return SnmpGetInterfaceStatus(m_dwIpAddr, m_iSNMPVersion, m_szCommunityString, dwIndex);
1413 }
1414
1415
1416 //
1417 // Get status of interface with given index from native agent
1418 //
1419
1420 int Node::GetInterfaceStatusFromAgent(DWORD dwIndex)
1421 {
1422 char szParam[128], szBuffer[32];
1423 DWORD dwAdminStatus, dwLinkState;
1424 int iStatus;
1425
1426 // Get administrative status
1427 sprintf(szParam, "Net.Interface.AdminStatus(%lu)", dwIndex);
1428 if (GetItemFromAgent(szParam, 32, szBuffer) == DCE_SUCCESS)
1429 {
1430 dwAdminStatus = strtoul(szBuffer, NULL, 0);
1431
1432 switch(dwAdminStatus)
1433 {
1434 case 3:
1435 iStatus = STATUS_TESTING;
1436 break;
1437 case 2:
1438 iStatus = STATUS_DISABLED;
1439 break;
1440 case 1: // Interface administratively up, check link state
1441 sprintf(szParam, "Net.Interface.Link(%lu)", dwIndex);
1442 if (GetItemFromAgent(szParam, 32, szBuffer) == DCE_SUCCESS)
1443 {
1444 dwLinkState = strtoul(szBuffer, NULL, 0);
1445 iStatus = (dwLinkState == 0) ? STATUS_CRITICAL : STATUS_NORMAL;
1446 }
1447 else
1448 {
1449 iStatus = STATUS_UNKNOWN;
1450 }
1451 break;
1452 default:
1453 iStatus = STATUS_UNKNOWN;
1454 break;
1455 }
1456 }
1457 else
1458 {
1459 iStatus = STATUS_UNKNOWN;
1460 }
1461
1462 return iStatus;
1463 }
1464
1465
1466 //
1467 // Put list of supported parameters into CSCP message
1468 //
1469
1470 void Node::WriteParamListToMessage(CSCPMessage *pMsg)
1471 {
1472 DWORD i, dwId;
1473
1474 Lock();
1475 if (m_pParamList != NULL)
1476 {
1477 pMsg->SetVariable(VID_NUM_PARAMETERS, m_dwNumParams);
1478 for(i = 0, dwId = VID_PARAM_LIST_BASE; i < m_dwNumParams; i++)
1479 {
1480 pMsg->SetVariable(dwId++, m_pParamList[i].szName);
1481 pMsg->SetVariable(dwId++, m_pParamList[i].szDescription);
1482 pMsg->SetVariable(dwId++, (WORD)m_pParamList[i].iDataType);
1483 }
1484 }
1485 else
1486 {
1487 pMsg->SetVariable(VID_NUM_PARAMETERS, (DWORD)0);
1488 }
1489 Unlock();
1490 }
1491
1492
1493 //
1494 // Check status of network service
1495 //
1496
1497 DWORD Node::CheckNetworkService(DWORD *pdwStatus, DWORD dwIpAddr, int iServiceType,
1498 WORD wPort, WORD wProto, TCHAR *pszRequest,
1499 TCHAR *pszResponce)
1500 {
1501 DWORD dwError = ERR_NOT_CONNECTED;
1502 DWORD dwTries = 3;
1503 AgentConnection conn(htonl(m_dwIpAddr), m_wAgentPort, m_wAuthMethod, m_szSharedSecret);
1504
1505 // Establish connection with agent
1506 if (conn.Connect())
1507 {
1508 dwError = conn.CheckNetworkService(pdwStatus, dwIpAddr, iServiceType,
1509 wPort, wProto, pszRequest, pszResponce);
1510 conn.Disconnect();
1511 }
1512 return dwError;
1513 }
1514
1515
1516 //
1517 // Handler for object deletion
1518 //
1519
1520 void Node::OnObjectDelete(DWORD dwObjectId)
1521 {
1522 Lock();
1523 if (dwObjectId == m_dwPollerNode)
1524 {
1525 // If deleted object is our poller node, change it to default
1526 m_dwPollerNode = 0;
1527 Modify();
1528 DbgPrintf(AF_DEBUG_MISC, _T("Node \"%s\": poller node %ld deleted"), m_szName, dwObjectId);
1529 }
1530 Unlock();
1531 }
1532
1533
1534 //
1535 // Check node for OSPF support
1536 //
1537
1538 void Node::CheckOSPFSupport(void)
1539 {
1540 long nAdminStatus;
1541
1542 if (SnmpGet(m_iSNMPVersion, m_dwIpAddr, m_szCommunityString, ".1.3.6.1.2.1.14.1.2.0",
1543 NULL, 0, &nAdminStatus, sizeof(long), FALSE, FALSE) == SNMP_ERR_SUCCESS)
1544 {
1545 if (nAdminStatus)
1546 {
1547 m_dwFlags |= NF_IS_OSPF;
1548 }
1549 else
1550 {
1551 m_dwFlags &= ~NF_IS_OSPF;
1552 }
1553 }
1554 }
1555
1556
1557 //
1558 // Create ready to use agent connection
1559 //
1560
1561 AgentConnection *Node::CreateAgentConnection(void)
1562 {
1563 AgentConnection *pConn;
1564
1565 if (!(m_dwFlags & NF_IS_NATIVE_AGENT))
1566 return NULL;
1567
1568 pConn = new AgentConnection(htonl(m_dwIpAddr), m_wAgentPort, m_wAuthMethod, m_szSharedSecret);
1569 if (!pConn->Connect())
1570 {
1571 delete pConn;
1572 pConn = NULL;
1573 }
1574 return pConn;
1575 }
1576
1577
1578 //
1579 // Get last collected values of all DCIs
1580 //
1581
1582 DWORD Node::GetLastValues(CSCPMessage *pMsg)
1583 {
1584 DWORD i, dwId;
1585
1586 Lock();
1587
1588 pMsg->SetVariable(VID_NUM_ITEMS, m_dwNumItems);
1589 for(i = 0, dwId = VID_DCI_VALUES_BASE; i < m_dwNumItems; i++, dwId += 6)
1590 m_ppItems[i]->GetLastValue(pMsg, dwId);
1591
1592 Unlock();
1593 return RCC_SUCCESS;
1594 }
1595
1596
1597 //
1598 // Clean expired DCI data
1599 //
1600
1601 void Node::CleanDCIData(void)
1602 {
1603 DWORD i;
1604
1605 Lock();
1606 for(i = 0; i < m_dwNumItems; i++)
1607 m_ppItems[i]->CleanData();
1608 Unlock();
1609 }
1610
1611
1612 //
1613 // Apply DCI from template
1614 // pItem passed to this method should be new DCI copied from template's DCI
1615 // with unique ID and correct template id and template item id.
1616 //
1617
1618 BOOL Node::ApplyTemplateItem(DCItem *pItem)
1619 {
1620 BOOL bResult = TRUE;
1621 DWORD i;
1622 int iStatus;
1623
1624 Lock();
1625
1626 DbgPrintf(AF_DEBUG_DC, "Applying item \"%s\" to node \"%s\"", pItem->Name(), m_szName);
1627
1628 // Check if that template item exists
1629 for(i = 0; i < m_dwNumItems; i++)
1630 if ((m_ppItems[i]->TemplateId() == pItem->TemplateId()) &&
1631 (m_ppItems[i]->TemplateItemId() == pItem->TemplateItemId()))
1632 break; // Item with specified id already exist
1633
1634 if (i == m_dwNumItems)
1635 {
1636 // New item from template, just add it
1637 bResult = AddItem(pItem, TRUE);
1638 }
1639 else
1640 {
1641 // Replace existing item
1642 iStatus = m_ppItems[i]->Status();
1643 m_ppItems[i]->PrepareForDeletion();
1644 pItem->PrepareForReplacement(m_ppItems[i], iStatus);
1645 delete m_ppItems[i];
1646 m_ppItems[i] = pItem;
1647 Modify();
1648 }
1649
1650 Unlock();
1651 return bResult;
1652 }
1653
1654
1655 //
1656 // Clean deleted template items from node's DCI list
1657 // Arguments is template id and list of valid template item ids.
1658 // all items related to given template and not presented in list should be deleted.
1659 //
1660
1661 void Node::CleanDeletedTemplateItems(DWORD dwTemplateId, DWORD dwNumItems, DWORD *pdwItemList)
1662 {
1663 DWORD i, j, dwNumDeleted, *pdwDeleteList;
1664
1665 pdwDeleteList = (DWORD *)malloc(sizeof(DWORD) * m_dwNumItems);
1666 dwNumDeleted = 0;
1667
1668 Lock();
1669
1670 for(i = 0; i < m_dwNumItems; i++)
1671 if (m_ppItems[i]->TemplateId() == dwTemplateId)
1672 {
1673 for(j = 0; j < dwNumItems; j++)
1674 if (m_ppItems[i]->TemplateItemId() == pdwItemList[j])
1675 break;
1676
1677 // Delete DCI if it's not in list
1678 if (j == dwNumItems)
1679 pdwDeleteList[dwNumDeleted++] = m_ppItems[i]->Id();
1680 }
1681
1682 Unlock();
1683
1684 for(i = 0; i < dwNumDeleted; i++)
1685 DeleteItem(pdwDeleteList[i]);
1686 free(pdwDeleteList);
1687 }