2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2016 Raden Solutions
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.
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.
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.
19 ** File: svccontainer.cpp
25 #define QUERY_LENGTH (512)
28 * Service log record ID
30 INT32 ServiceContainer
::logRecordId
= -1;
33 * Default constructor for service service object
35 ServiceContainer
::ServiceContainer() : Container()
37 initServiceContainer();
41 * Create new service container object
43 ServiceContainer
::ServiceContainer(const TCHAR
*pszName
) : Container(pszName
, 0)
45 initServiceContainer();
48 void ServiceContainer
::initServiceContainer()
50 m_prevUptimeUpdateStatus
= STATUS_UNKNOWN
;
51 m_prevUptimeUpdateTime
= time(NULL
);
54 m_uptimeMonth
= 100.0;
64 * Create object from database data
66 bool ServiceContainer
::loadFromDatabase(DB_HANDLE hdb
, UINT32 id
)
68 if (!Container
::loadFromDatabase(hdb
, id
))
76 * Save object to database
78 bool ServiceContainer
::saveToDatabase(DB_HANDLE hdb
)
80 return Container
::saveToDatabase(hdb
);
84 * Delete object from database
86 bool ServiceContainer
::deleteFromDatabase(DB_HANDLE hdb
)
88 return Container
::deleteFromDatabase(hdb
);
92 * Create NXCP message with object's data
94 void ServiceContainer
::fillMessageInternal(NXCPMessage
*pMsg
)
96 Container
::fillMessageInternal(pMsg
);
97 pMsg
->setField(VID_UPTIME_DAY
, m_uptimeDay
);
98 pMsg
->setField(VID_UPTIME_WEEK
, m_uptimeWeek
);
99 pMsg
->setField(VID_UPTIME_MONTH
, m_uptimeMonth
);
103 * Modify object from message
105 UINT32 ServiceContainer
::modifyFromMessageInternal(NXCPMessage
*pRequest
)
107 return Container
::modifyFromMessageInternal(pRequest
);
111 * Calculate status for compound object based on childs status
113 void ServiceContainer
::calculateCompoundStatus(BOOL bForcedRecalc
)
115 int i
, iCount
, iMostCriticalStatus
;
116 int iOldStatus
= m_status
;
118 DbgPrintf(7, _T("ServiceContainer::calculateCompoundStatus() for %s [%d]"), m_name
, m_id
);
120 // Calculate own status by selecting the most critical status of the kids
121 lockChildList(false);
122 for(i
= 0, iCount
= 0, iMostCriticalStatus
= -1; i
< m_childList
->size(); i
++)
124 int iChildStatus
= m_childList
->get(i
)->getStatus();
125 if ((iChildStatus
< STATUS_UNKNOWN
) &&
126 (iChildStatus
> iMostCriticalStatus
))
128 iMostCriticalStatus
= iChildStatus
;
132 // Set status and update uptime counters
133 setStatus((iCount
> 0) ? iMostCriticalStatus
: STATUS_UNKNOWN
);
136 // Cause parent object(s) to recalculate it's status
137 if ((iOldStatus
!= m_status
) || bForcedRecalc
)
139 lockParentList(false);
140 for(i
= 0; i
< m_parentList
->size(); i
++)
141 m_parentList
->get(i
)->calculateCompoundStatus();
144 setModified(MODIFY_COMMON_PROPERTIES
);
148 DbgPrintf(6, _T("ServiceContainer::calculateCompoundStatus(%s [%d]): old_status=%d new_status=%d"), m_name
, m_id
, iOldStatus
, m_status
);
150 if (iOldStatus
!= STATUS_UNKNOWN
&& iOldStatus
!= m_status
)
155 * Set service status - use this instead of direct assignment;
157 void ServiceContainer
::setStatus(int newStatus
)
159 m_status
= newStatus
;
163 * Add a record to slm_service_history table
165 BOOL ServiceContainer
::addHistoryRecord()
170 DB_HANDLE hdb
= DBConnectionPoolAcquireConnection();
172 if (ServiceContainer
::logRecordId
< 0)
174 hResult
= DBSelect(hdb
, _T("SELECT max(record_id) FROM slm_service_history"));
177 DBConnectionPoolReleaseConnection(hdb
);
180 ServiceContainer
::logRecordId
= DBGetNumRows(hResult
) > 0 ?
DBGetFieldLong(hResult
, 0, 0) : 0;
181 DBFreeResult(hResult
);
184 ServiceContainer
::logRecordId
++;
186 hStmt
= DBPrepare(hdb
, _T("INSERT INTO slm_service_history (record_id,service_id,change_timestamp,new_status) ")
187 _T("VALUES (?,?,?,?)"));
190 DBBind(hStmt
, 1, DB_SQLTYPE_INTEGER
, ServiceContainer
::logRecordId
);
191 DBBind(hStmt
, 2, DB_SQLTYPE_INTEGER
, m_id
);
192 DBBind(hStmt
, 3, DB_SQLTYPE_INTEGER
, UINT32(time(NULL
)));
193 DBBind(hStmt
, 4, DB_SQLTYPE_INTEGER
, UINT32(m_status
));
194 if (!DBExecute(hStmt
))
196 DBFreeStatement(hStmt
);
197 DBConnectionPoolReleaseConnection(hdb
);
200 DbgPrintf(9, _T("ServiceContainer::addHistoryRecord() ok with id %d"), ServiceContainer
::logRecordId
);
204 DBConnectionPoolReleaseConnection(hdb
);
208 DBFreeStatement(hStmt
);
209 DBConnectionPoolReleaseConnection(hdb
);
214 * Initialize uptime statistics (daily, weekly, monthly) by examining slm_service_history
216 void ServiceContainer
::initUptimeStats()
219 m_prevUptimeUpdateStatus
= m_status
;
220 m_uptimeDay
= getUptimeFromDBFor(DAY
, &m_downtimeDay
);
221 m_uptimeWeek
= getUptimeFromDBFor(WEEK
, &m_downtimeWeek
);
222 m_uptimeMonth
= getUptimeFromDBFor(MONTH
, &m_downtimeMonth
);
224 DbgPrintf(6, _T("ServiceContainer::initUptimeStats() %s [%d] %lf %lf %lf"), m_name
, m_id
, m_uptimeDay
, m_uptimeWeek
, m_uptimeMonth
);
228 * Calculate uptime for given period using data in database
230 double ServiceContainer
::getUptimeFromDBFor(Period period
, INT32
*downtime
)
233 INT32 timediffTillNow
= ServiceContainer
::getSecondsSinceBeginningOf(period
, &beginTime
);
234 double percentage
= 0;
236 DB_HANDLE hdb
= DBConnectionPoolAcquireConnection();
237 DB_STATEMENT hStmt
= DBPrepare(hdb
, _T("SELECT change_timestamp,new_status FROM slm_service_history ")
238 _T("WHERE service_id=? AND change_timestamp>?"));
241 DBBind(hStmt
, 1, DB_SQLTYPE_INTEGER
, m_id
);
242 DBBind(hStmt
, 2, DB_SQLTYPE_INTEGER
, (UINT32
)beginTime
);
243 DB_RESULT hResult
= DBSelectPrepared(hStmt
);
246 DBFreeStatement(hStmt
);
247 DBConnectionPoolReleaseConnection(hdb
);
251 time_t changeTimestamp
, prevChangeTimestamp
= beginTime
;
252 int newStatus
= STATUS_UNKNOWN
, i
, realRows
;
253 int numRows
= DBGetNumRows(hResult
);
255 for (i
= 0, realRows
= 0; i
< numRows
; i
++)
257 changeTimestamp
= DBGetFieldLong(hResult
, i
, 0);
258 newStatus
= DBGetFieldLong(hResult
, i
, 1);
259 if (newStatus
== STATUS_UNKNOWN
) // Malawi hotfix - ignore unknown status
261 if (newStatus
== STATUS_NORMAL
)
262 *downtime
+= (LONG
)(changeTimestamp
- prevChangeTimestamp
);
264 prevChangeTimestamp
= changeTimestamp
;
267 if (newStatus
== STATUS_CRITICAL
) // the service is still down, add period till now
268 *downtime
+= LONG(time(NULL
) - prevChangeTimestamp
);
269 // no rows for period && critical status -> downtime from beginning till now
270 if (realRows
== 0 && m_status
== STATUS_CRITICAL
)
271 *downtime
= timediffTillNow
;
272 percentage
= 100.0 - (double)(*downtime
* 100) / (double)getSecondsInPeriod(period
);
273 DbgPrintf(7, _T("++++ ServiceContainer::getUptimeFromDBFor(), downtime %ld"), *downtime
);
275 DBFreeResult(hResult
);
276 DBFreeStatement(hStmt
);
279 DBConnectionPoolReleaseConnection(hdb
);
284 * Update uptime counters
286 void ServiceContainer
::updateUptimeStats(time_t currentTime
, BOOL updateChilds
)
288 LONG timediffTillNow
;
289 LONG downtimeBetweenPolls
= 0;
291 if (currentTime
== 0)
292 currentTime
= time(NULL
);
296 double prevUptimeDay
= m_uptimeDay
;
297 double prevUptimeWeek
= m_uptimeWeek
;
298 double prevUptimeMonth
= m_uptimeMonth
;
300 if (m_status
== STATUS_CRITICAL
&& m_prevUptimeUpdateStatus
== STATUS_CRITICAL
)
302 downtimeBetweenPolls
= LONG(currentTime
- m_prevUptimeUpdateTime
);
303 DbgPrintf(7, _T("++++ ServiceContainer::updateUptimeStats() both statuses critical"));
306 timediffTillNow
= ServiceContainer
::getSecondsSinceBeginningOf(DAY
, NULL
);
307 m_downtimeDay
+= downtimeBetweenPolls
;
308 if (timediffTillNow
< m_prevDiffDay
)
310 m_uptimeDay
= 100.0 - (double)(m_downtimeDay
* 100) / (double)ServiceContainer
::getSecondsInPeriod(DAY
);
311 m_prevDiffDay
= timediffTillNow
;
312 DbgPrintf(7, _T("++++ ServiceContainer::updateUptimeStats() m_downtimeDay %ld, timediffTillNow %ld, downtimeBetweenPolls %ld"),
313 m_downtimeDay
, timediffTillNow
, downtimeBetweenPolls
);
315 timediffTillNow
= ServiceContainer
::getSecondsSinceBeginningOf(WEEK
, NULL
);
316 m_downtimeWeek
+= downtimeBetweenPolls
;
317 if (timediffTillNow
< m_prevDiffWeek
)
319 m_uptimeWeek
= 100.0 - (double)(m_downtimeWeek
* 100) / (double)ServiceContainer
::getSecondsInPeriod(WEEK
);
320 m_prevDiffWeek
= timediffTillNow
;
322 timediffTillNow
= ServiceContainer
::getSecondsSinceBeginningOf(MONTH
, NULL
);
323 m_downtimeMonth
+= downtimeBetweenPolls
;
324 if (timediffTillNow
< m_prevDiffMonth
)
326 m_uptimeMonth
= 100.0 - (double)(m_downtimeMonth
* 100) / (double)ServiceContainer
::getSecondsInPeriod(MONTH
);
327 m_prevDiffMonth
= timediffTillNow
;
329 if ((prevUptimeDay
!= m_uptimeDay
) || (prevUptimeWeek
!= m_uptimeWeek
) || (prevUptimeMonth
!= m_uptimeMonth
))
331 setModified(MODIFY_OTHER
);
335 m_prevUptimeUpdateStatus
= m_status
;
336 m_prevUptimeUpdateTime
= currentTime
;
338 DbgPrintf(7, _T("++++ ServiceContainer::updateUptimeStats() [%d] %lf %lf %lf"), int(m_id
), m_uptimeDay
, m_uptimeWeek
, m_uptimeMonth
);
342 lockChildList(false);
343 for(int i
= 0; i
< m_childList
->size(); i
++)
345 NetObj
*child
= m_childList
->get(i
);
346 if (child
->getObjectClass() == OBJECT_BUSINESSSERVICE
|| child
->getObjectClass() == OBJECT_NODELINK
)
347 ((ServiceContainer
*)child
)->updateUptimeStats(currentTime
, TRUE
);
354 * Calculate number of seconds since the beginning of given period
356 INT32 ServiceContainer
::getSecondsSinceBeginningOf(Period period
, time_t *beginTime
)
358 time_t curTime
= time(NULL
);
362 localtime_r(&curTime
, &tmBuffer
);
364 struct tm
*tms
= localtime(&curTime
);
365 memcpy(&tmBuffer
, tms
, sizeof(struct tm
));
368 tmBuffer
.tm_hour
= 0;
372 tmBuffer
.tm_mday
= 1;
373 time_t beginTimeL
= mktime(&tmBuffer
);
376 if (tmBuffer
.tm_wday
== 0)
377 tmBuffer
.tm_wday
= 7;
379 beginTimeL
-= 3600 * 24 * tmBuffer
.tm_wday
;
382 if (beginTime
!= NULL
)
383 *beginTime
= beginTimeL
;
385 return (INT32
)(curTime
- beginTimeL
);
389 * Calculate number of seconds in the current month
391 INT32 ServiceContainer
::getSecondsInMonth()
393 time_t curTime
= time(NULL
);
398 tms
= localtime_r(&curTime
, &tmBuffer
);
400 tms
= localtime(&curTime
);
403 int& month
= tms
->tm_mon
;
404 int year
= tms
->tm_year
+ 1900;
407 if (month
== 3 || month
== 5 || month
== 8 || month
== 10)
409 else if (month
== 1) /* February */
410 days
= (year
% 4 == 0 && year
% 100 != 0) || (year
% 400 == 0) ?
29 : 28;
412 return (INT32
)(days
* 24 * 3600);
416 * Called by client session handler to check if threshold summary should be shown for this object.
418 bool ServiceContainer
::showThresholdSummary()