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