- uptime related fixes
[public/netxms.git] / src / server / core / svccontainer.cpp
CommitLineData
3e2bf4aa
AK
1/*
2** NetXMS - Network Management System
3** Copyright (C) 2003-2011 NetXMS Team
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: svccontainer.cpp
20**
21**/
22
23#include "nxcore.h"
24
25#define QUERY_LENGTH (512)
26
27LONG ServiceContainer::logRecordId = -1;
28
4a1e58e5 29
3e2bf4aa
AK
30//
31// Constructor for service service object
32//
33
4a1e58e5 34ServiceContainer::ServiceContainer() : Container()
3e2bf4aa
AK
35{
36 initServiceContainer();
37}
38
4a1e58e5 39ServiceContainer::ServiceContainer(const TCHAR *pszName) : Container(pszName, 0)
3e2bf4aa
AK
40{
41 initServiceContainer();
42}
43
3e2bf4aa
AK
44void ServiceContainer::initServiceContainer()
45{
46 m_prevUptimeUpdateStatus = STATUS_UNKNOWN;
47 m_prevUptimeUpdateTime = time(NULL);
48 m_uptimeDay = 100.0;
49 m_uptimeWeek = 100.0;
50 m_uptimeMonth = 100.0;
51 m_downtimeDay = 0;
52 m_downtimeWeek = 0;
53 m_downtimeMonth = 0;
54 m_prevDiffDay = 0;
55 m_prevDiffWeek = 0;
56 m_prevDiffMonth = 0;
57}
58
4a1e58e5
VK
59
60//
61// Create object from database data
62//
63
64BOOL ServiceContainer::CreateFromDB(DWORD id)
65{
66 if (!Container::CreateFromDB(id))
67 return FALSE;
68
69 initUptimeStats();
70 return TRUE;
71}
72
73
74//
75// Save object to database
76//
77
78BOOL ServiceContainer::SaveToDB(DB_HANDLE hdb)
79{
80 return Container::SaveToDB(hdb);
81}
82
83
84//
85// Delete object from database
86//
87
88BOOL ServiceContainer::DeleteFromDB()
89{
90 return Container::DeleteFromDB();
91}
92
93
94//
95// Create CSCP message with object's data
96//
97
98void ServiceContainer::CreateMessage(CSCPMessage *pMsg)
99{
100 Container::CreateMessage(pMsg);
101 pMsg->SetVariable(VID_UPTIME_DAY, m_uptimeDay);
102 pMsg->SetVariable(VID_UPTIME_WEEK, m_uptimeWeek);
103 pMsg->SetVariable(VID_UPTIME_MONTH, m_uptimeMonth);
104}
105
106
107//
108// Modify object from message
109//
110
111DWORD ServiceContainer::ModifyFromMessage(CSCPMessage *pRequest, BOOL bAlreadyLocked)
112{
113 if (!bAlreadyLocked)
114 LockData();
115
116 return Container::ModifyFromMessage(pRequest, TRUE);
117}
118
119
3e2bf4aa
AK
120//
121// Calculate status for compound object based on childs status
122//
123
124void ServiceContainer::calculateCompoundStatus(BOOL bForcedRecalc)
125{
126 int i, iCount, iMostCriticalStatus;
127 int iOldStatus = m_iStatus;
128
5e7a466e
AK
129 DbgPrintf(7, _T("#### CalculateCompoundStatus for id %d"), m_dwId);
130
3e2bf4aa
AK
131 // Calculate own status by selecting the most critical status of the kids
132 LockChildList(FALSE);
133 for(i = 0, iCount = 0, iMostCriticalStatus = -1; i < int(m_dwChildCount); i++)
134 {
135 int iChildStatus = m_pChildList[i]->Status();
136 if ((iChildStatus < STATUS_UNKNOWN) &&
137 (iChildStatus > iMostCriticalStatus))
138 {
139 iMostCriticalStatus = iChildStatus;
140 iCount++;
141 }
142 }
143 // Set status and update uptime counters
144 setStatus((iCount > 0) ? iMostCriticalStatus : STATUS_UNKNOWN);
145 UnlockChildList();
146
5e7a466e
AK
147 // A hack to make service root to recalculate uptime properly
148 for(i = 0; iOldStatus == m_iStatus && i < int(m_dwParentCount); i++)
149 {
150 if (m_pParentList[i]->Type() == OBJECT_BUSINESSSERVICEROOT)
151 {
152 ((BusinessServiceRoot*)m_pParentList[i])->updateUptimeStats();
153 break;
154 }
155 }
156
3e2bf4aa
AK
157 // Cause parent object(s) to recalculate it's status
158 if ((iOldStatus != m_iStatus) || bForcedRecalc)
159 {
160 LockParentList(FALSE);
161 for(i = 0; i < int(m_dwParentCount); i++)
162 m_pParentList[i]->calculateCompoundStatus();
163 UnlockParentList();
164 Modify(); /* LOCK? */
165 }
166
167 if (iOldStatus != STATUS_UNKNOWN && iOldStatus != m_iStatus)
168 addHistoryRecord();
169}
170
4a1e58e5 171
3e2bf4aa
AK
172//
173// Set service status - use this instead of direct assignment;
174//
175
176void ServiceContainer::setStatus(int newStatus)
177{
178 m_iStatus = newStatus;
179 updateUptimeStats();
180}
181
4a1e58e5 182
3e2bf4aa
AK
183//
184// Add a record to slm_service_history table
185//
186
187BOOL ServiceContainer::addHistoryRecord()
188{
189 DB_RESULT hResult;
190 DB_STATEMENT hStmt;
191
192 if (ServiceContainer::logRecordId < 0)
193 {
194 hResult = DBSelect(g_hCoreDB, _T("SELECT max(record_id) FROM slm_service_history"));
195 if (hResult == NULL)
196 return FALSE;
197 ServiceContainer::logRecordId = DBGetNumRows(hResult) > 0 ? DBGetFieldLong(hResult, 0, 0) : 0;
198 DBFreeResult(hResult);
199 }
200
201 ServiceContainer::logRecordId++;
202
203 hStmt = DBPrepare(g_hCoreDB, _T("INSERT INTO slm_service_history (record_id,service_id,change_timestamp,new_status) ")
204 _T("VALUES (?,?,?,?)"));
205 if (hStmt != NULL)
206 {
207 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, ServiceContainer::logRecordId);
208 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_dwId);
209 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, DWORD(time(NULL)));
210 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, DWORD(m_iStatus));
211 if (!DBExecute(hStmt))
212 {
213 DBFreeStatement(hStmt);
214 return FALSE;
215 }
216 DbgPrintf(9, _T("ServiceContainer::addHistoryRecord() ok with id %ld"), ServiceContainer::logRecordId);
217 }
218 else
219 {
220 return FALSE;
221 }
222
223 DBFreeStatement(hStmt);
224 return TRUE;
225}
226
227
228//
229// Initialize uptime statistics (daily, weekly, monthly) by examining slm_service_history
230//
231
232void ServiceContainer::initUptimeStats()
233{
234 m_prevUptimeUpdateStatus = m_iStatus;
235 m_uptimeDay = getUptimeFromDBFor(DAY, &m_downtimeDay);
236 m_uptimeWeek = getUptimeFromDBFor(WEEK, &m_downtimeWeek);
237 m_uptimeMonth = getUptimeFromDBFor(MONTH, &m_downtimeMonth);
238 DbgPrintf(7, _T("++++ ServiceContainer::initUptimeStats() id=%d %lf %lf %lf"), m_dwId, m_uptimeDay, m_uptimeWeek, m_uptimeMonth);
239}
240
241//
242// Calculate uptime for given period using data in database
243//
244
245double ServiceContainer::getUptimeFromDBFor(Period period, LONG *downtime)
246{
247 time_t beginTime;
248 LONG timediffTillNow = ServiceContainer::getSecondsSinceBeginningOf(period, &beginTime);
249 double percentage = 0;
250
251 DB_STATEMENT hStmt = DBPrepare(g_hCoreDB, _T("SELECT change_timestamp,new_status FROM slm_service_history ")
252 _T("WHERE service_id=? AND change_timestamp>?"));
253 if (hStmt != NULL)
254 {
3e2bf4aa
AK
255 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_dwId);
256 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, (DWORD)beginTime);
257 DB_RESULT hResult = DBSelectPrepared(hStmt);
258 if (hResult == NULL)
259 {
260 DBFreeStatement(hStmt);
261 return percentage;
262 }
263
5e7a466e
AK
264 time_t changeTimestamp, prevChangeTimestamp = beginTime;
265 int newStatus = STATUS_UNKNOWN, i, realRows;
3e2bf4aa
AK
266 int numRows = DBGetNumRows(hResult);
267 *downtime = 0;
5e7a466e 268 for (i = 0, realRows = 0; i < numRows; i++)
3e2bf4aa 269 {
5e7a466e
AK
270 changeTimestamp = DBGetFieldLong(hResult, i, 0);
271 newStatus = DBGetFieldLong(hResult, i, 1);
272 if (newStatus == STATUS_UNKNOWN) // Malawi hotfix - ignore unknown status
273 continue;
274 if (newStatus == STATUS_NORMAL)
275 *downtime += (LONG)(changeTimestamp - prevChangeTimestamp);
276 else
277 prevChangeTimestamp = changeTimestamp;
278 realRows++;
3e2bf4aa 279 }
5e7a466e
AK
280 if (newStatus == STATUS_CRITICAL) // the service is still down, add period till now
281 *downtime += LONG(time(NULL) - prevChangeTimestamp);
282 // now rows for period && critical status -> downtime from beginning till now
283 if (realRows == 0 && m_iStatus == STATUS_CRITICAL)
284 *downtime = timediffTillNow;
3e2bf4aa
AK
285 percentage = 100.0 - (double)(*downtime * 100) / (double)getSecondsInPeriod(period);
286 DbgPrintf(7, _T("++++ ServiceContainer::getUptimeFromDBFor(), downtime %ld"), *downtime);
287
288 DBFreeResult(hResult);
289 DBFreeStatement(hStmt);
290 }
291
292 return percentage;
293}
294
295
296//
297// Update uptime counters
298//
299
300void ServiceContainer::updateUptimeStats()
301{
302 LONG timediffTillNow;
303 LONG downtimeBetweenPolls = 0;
304 time_t curTime = time(NULL);
305
306 LockData();
307
308 double prevUptimeDay = m_uptimeDay;
309 double prevUptimeWeek = m_uptimeWeek;
310 double prevUptimeMonth = m_uptimeMonth;
311
312 if (m_iStatus == STATUS_CRITICAL && m_prevUptimeUpdateStatus == STATUS_CRITICAL)
313 {
314 downtimeBetweenPolls = LONG(curTime - m_prevUptimeUpdateTime);
315 DbgPrintf(7, _T("++++ ServiceContainer::updateUptimeStats() both statuses critical"));
316 }
317
318 timediffTillNow = ServiceContainer::getSecondsSinceBeginningOf(DAY, NULL);
319 m_downtimeDay += downtimeBetweenPolls;
320 if (timediffTillNow < m_prevDiffDay)
321 m_downtimeDay = 0;
322 m_uptimeDay = 100.0 - (double)(m_downtimeDay * 100) / (double)ServiceContainer::getSecondsInPeriod(DAY);
323 m_prevDiffDay = timediffTillNow;
324 DbgPrintf(7, _T("++++ ServiceContainer::updateUptimeStats() m_downtimeDay %ld, timediffTillNow %ld, downtimeBetweenPolls %ld"),
325 m_downtimeDay, timediffTillNow, downtimeBetweenPolls);
326
327 timediffTillNow = ServiceContainer::getSecondsSinceBeginningOf(WEEK, NULL);
328 m_downtimeWeek += downtimeBetweenPolls;
329 if (timediffTillNow < m_prevDiffWeek)
330 m_downtimeWeek = 0;
331 m_uptimeWeek = 100.0 - (double)(m_downtimeWeek * 100) / (double)ServiceContainer::getSecondsInPeriod(WEEK);
332 m_prevDiffWeek = timediffTillNow;
333
334 timediffTillNow = ServiceContainer::getSecondsSinceBeginningOf(MONTH, NULL);
335 m_downtimeMonth += downtimeBetweenPolls;
336 if (timediffTillNow < m_prevDiffMonth)
337 m_downtimeMonth = 0;
338 m_uptimeMonth = 100.0 - (double)(m_downtimeMonth * 100) / (double)ServiceContainer::getSecondsInPeriod(MONTH);
339 m_prevDiffMonth = timediffTillNow;
340
341 if ((prevUptimeDay != m_uptimeDay) || (prevUptimeWeek != m_uptimeWeek) || (prevUptimeMonth != m_uptimeMonth))
342 {
343 Modify();
344 }
345 UnlockData();
346
347 m_prevUptimeUpdateStatus = m_iStatus;
348 m_prevUptimeUpdateTime = curTime;
349
350 DbgPrintf(7, _T("++++ ServiceContainer::updateUptimeStats() %lf %lf %lf"), m_uptimeDay, m_uptimeWeek, m_uptimeMonth);
351}
352
353
354//
355// Calculate number of seconds since the beginning of given period
356//
357
358LONG ServiceContainer::getSecondsSinceBeginningOf(Period period, time_t *beginTime)
359{
360 time_t curTime = time(NULL);
361 struct tm *tms;
362 struct tm tmBuffer;
363
364#if HAVE_LOCALTIME_R
365 tms = localtime_r(&curTime, &tmBuffer);
366#else
367 tms = localtime(&curTime);
368 memcpy((void*)&tmBuffer, (void*)tms, sizeof(struct tm));
369#endif
370
371 tmBuffer.tm_hour = 0;
372 tmBuffer.tm_min = 0;
373 tmBuffer.tm_sec = 0;
374 if (period == MONTH)
375 tmBuffer.tm_mday = 1;
376 time_t beginTimeL = mktime(&tmBuffer);
377 if (period == WEEK)
378 {
379 if (tmBuffer.tm_wday == 0)
380 tmBuffer.tm_wday = 7;
381 tmBuffer.tm_wday--;
382 beginTimeL -= 3600 * 24 * tmBuffer.tm_wday;
383 }
384
385 if (beginTime != NULL)
386 *beginTime = beginTimeL;
387
388 return LONG(curTime - beginTimeL);
389}
390
391
392//
393// Calculate number of seconds in the current month
394//
395
396LONG ServiceContainer::getSecondsInMonth()
397{
398 time_t curTime = time(NULL);
399 struct tm *tms;
400
401#if HAVE_LOCALTIME_R
402 struct tm tmBuffer;
403 tms = localtime_r(&curTime, &tmBuffer);
404#else
405 tms = localtime(&curTime);
406#endif
407
408 int& month = tms->tm_mon;
409 int year = tms->tm_year + 1900;
410 int days = 31;
411
412 if (month == 3 || month == 5 || month == 8 || month == 10)
413 days = 30;
414 else if (month == 1) /* February */
415 days = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) ? 29 : 28;
416
417 return LONG(days * 24 * 3600);
418}