source: fact/Evidence/Alarm.cc@ 10071

Last change on this file since 10071 was 10068, checked in by ogrimm, 14 years ago
Alarm has 5 s minimum period, updates to Edd
File size: 7.2 KB
Line 
1/********************************************************************\
2
3 Alarm handler of the Evidence Control System
4
5 - Checks periodically if all required servers are up
6 - Listens to the 'Message' service of each server and generates new service for
7 each observed server indicating the maximum Severity in the past.
8 - Maximum severity may be reset by a command 'Alarm/ResetAlarm' for a server.
9 - A text describing the current state of all servers is published as DIM service.
10 The states are described in LevelStr[].
11 - A master alarm (indicating most severe of individual alarms) is published.
12 - The server can be switched on/off with the command 'Alarm/Switch'.
13
14 A mutex is used because UpdateAlarmSummary() may be called from DIM handler thread and
15 from main thread.
16
17 Oliver Grimm, June 2010
18
19\********************************************************************/
20
21#define SERVER_NAME "Alarm"
22#include "Evidence.h"
23
24#include <sstream>
25
26using namespace std;
27
28const char* LevelStr[] = {"OK", "WARN", "ERROR", "FATAL", "UNAVAILABLE"};
29const int MIN_PERIOD = 5; // Minimum period in seconds for checking servers are alive
30
31//
32// Class declaration
33//
34class AlarmHandler: public DimClient, public EvidenceServer {
35
36 DimCommand *ResetCommand;
37 DimCommand *SwitchCommand;
38 DimService *Summary, *Master;
39 char *AlarmText;
40 int MasterAlarm;
41
42 void infoHandler();
43 void commandHandler();
44
45 public:
46 AlarmHandler();
47 ~AlarmHandler();
48
49 struct Item {
50 string Server;
51 string Email;
52 DimStampedInfo *Subscription;
53 DimService *AlarmLevel;
54 int WarnedLevel;
55 int Level;
56 };
57 vector<struct Item> List;
58 bool Active;
59
60 void UpdateAlarmSummary();
61};
62
63// Constructor
64AlarmHandler::AlarmHandler(): EvidenceServer(SERVER_NAME) {
65
66 struct Item N;
67 static int InitLevel = -1; // static for DIM service below
68
69 // Initialise
70 MasterAlarm = 0;
71 AlarmText = NULL;
72 Active = true;
73
74 // Handling of servies will only start after start()
75 autoStartOff();
76
77 // Create DIM services
78 Summary = new DimService(SERVER_NAME"/Summary", (char *) "not yet available");
79 Master = new DimService(SERVER_NAME"/MasterAlarm", MasterAlarm);
80
81 // Get DIM servers to observe
82 vector<string> Token = Tokenize(GetConfig("servers"));
83
84 for (int i=0; i<Token.size(); i++) {
85 // Extract server name and email
86 vector<string> A = Tokenize(Token[i], ":");
87 N.Server = A[0];
88 if (A.size() == 2) N.Email = A[1];
89 else N.Email = string();
90
91 // DIS_DNS has no Message service
92 if (N.Server == "DIS_DNS") N.Subscription = NULL;
93 else N.Subscription = new DimStampedInfo((N.Server+"/Message").c_str(), NO_LINK, this);
94
95 // Alarm service for server (reference to variable will be updated in UpdateAlarmSummary())
96 N.WarnedLevel = 0;
97 N.Level = -1;
98 N.AlarmLevel = new DimService((N.Server+"/AlarmLevel").c_str(), InitLevel);
99
100 List.push_back(N);
101 }
102
103 // Provide command to reset Level
104 ResetCommand = new DimCommand(SERVER_NAME"/ResetAlarm", (char *) "C", this);
105 SwitchCommand = new DimCommand(SERVER_NAME"/Switch", (char *) "C", this);
106
107 // List set up, can start handling
108 start(SERVER_NAME);
109}
110
111
112// Destructor
113AlarmHandler::~AlarmHandler() {
114
115 delete SwitchCommand;
116 delete ResetCommand;
117
118 for (int i=0; i<List.size(); i++) {
119 delete List[i].Subscription;
120 delete List[i].AlarmLevel;
121 }
122 delete Master;
123 delete Summary;
124 delete[] AlarmText;
125}
126
127
128// Print messages of status changes to screen and update status string
129void AlarmHandler::infoHandler() {
130
131 // Check if alarm server active
132 if (!Active) return;
133
134 // Identify status service
135 for (int i=0; i<List.size(); i++) if (getInfo() == List[i].Subscription) {
136 // Update level: unavailable or current severity of status (safely extracted)
137 if (!ServiceOK(getInfo())) List[i].Level = 4;
138 else {
139 int Severity = atoi(ToString(getInfo()->getFormat(), getInfo()->getData(), getInfo()->getSize()).c_str());
140 if ((Severity>List[i].Level) || (List[i].Level==4 && Severity==0)) List[i].Level = Severity;
141 }
142 }
143
144 UpdateAlarmSummary();
145}
146
147
148// Handle commands
149void AlarmHandler::commandHandler() {
150
151 string Text = ToString((char *) "C", getCommand()->getData(), getCommand()->getSize());
152
153 // Reset alarm level, publish/log action and reset server message severity
154 if (getCommand() == ResetCommand) {
155 for (int i=0; i<List.size(); i++) if (List[i].Server == Text) {
156 Message(INFO, "Alarm level of server %s reset by %s (ID %d)", Text.c_str(), getClientName(), getClientId());
157 List[i].Level = 0;
158 List[i].WarnedLevel = 0;
159 sendCommandNB((Text+"/ResetMessage").c_str(), (int) 0);
160 }
161 }
162
163 // Switch Alarm server on/off and publish/log action
164 if (getCommand() == SwitchCommand) {
165 if (Text == "off") Active = false;
166 else Active = true;
167
168 Message(INFO, "Alarm server switched %s by %s (ID %d)", Active ? "ON":"OFF", getClientName(), getClientId());
169 }
170
171 UpdateAlarmSummary();
172}
173
174
175// Update alarm status summary (locking since access can be from main thread and DIM handler threads)
176void AlarmHandler::UpdateAlarmSummary() {
177
178 ostringstream Buf;
179 int Alarm = -1, Ret;
180
181 Lock();
182
183 if (!Active) Buf << "Alarm server inactive";
184 else for (int i=0; i<List.size(); i++) {
185 // Alarm level description
186 Buf << List[i].Server << ": " << (List[i].Level>=0 && List[i].Level<=4 ? LevelStr[List[i].Level] : "unknown");
187 Buf << " (" << List[i].Level << ")" << endl;
188
189 // Adjust master alarm and update server alarm level
190 if (List[i].Level > Alarm) Alarm = List[i].Level;
191 List[i].AlarmLevel->updateService(List[i].Level);
192
193 // Check if alarm level raised, then send alarm message once
194 if (List[i].WarnedLevel < List[i].Level && !List[i].Email.empty()) {
195 List[i].WarnedLevel = List[i].Level;
196
197 // Prepare email message
198 char *Text;
199 time_t Time = time(NULL);
200 if (asprintf(&Text, "echo \"Server alarm level '%s' at %s\"|"
201 "mail -s \"Evidence Alarm for '%s'\" %s",
202 List[i].Level>=0 && List[i].Level<=4 ? LevelStr[List[i].Level] : "unknown",
203 ctime(&Time), List[i].Server.c_str(), List[i].Email.c_str()) != -1) {
204 system(Text); // Return value depending on OS
205 free(Text);
206 }
207 else Message(ERROR, "Could not send alarm email, asprintf() failed");
208 }
209 }
210
211 // Update master alarm services
212 MasterAlarm = Alarm;
213 Master->updateService();
214
215 // Update alarm description (DIM requires variables to be valid until update)
216 char *Tmp = new char[Buf.str().size()+1];
217 strcpy(Tmp, Buf.str().c_str());
218 Summary->updateService(Tmp);
219
220 delete[] AlarmText;
221 AlarmText = Tmp;
222
223 Unlock();
224}
225
226//
227// Main program
228//
229int main() {
230
231 DimBrowser B;
232 char *Server, *Node;
233 bool Exist;
234
235 // Static declaration ensures calling of destructor by exit()
236 static AlarmHandler A;
237
238 // Verify periodically that servers exist (if Alarm is active)
239 while(!A.ExitRequest) {
240 for (int i=0; i<A.List.size() && A.Active; i++) {
241 // Check if server exists
242 Exist = false;
243 B.getServers();
244 while (B.getNextServer(Server, Node) == 1) {
245 if (A.List[i].Server == Server) Exist = true;
246 }
247 if (!Exist) A.List[i].Level = 4;
248
249 // Check if standard service available in case server not yet checked (Level is -1)
250 if (B.getServices((A.List[i].Server+"/VERSION_NUMBER").c_str())>0 && A.List[i].Level==-1) A.List[i].Level = 0;
251 }
252
253 A.UpdateAlarmSummary();
254 sleep(max(atoi(A.GetConfig("period").c_str()), MIN_PERIOD));
255 }
256}
Note: See TracBrowser for help on using the repository browser.