cfefec19cc04d2e3035fb535b2a637cfe1fadd2b
[anna.git] / source / diameter.comm / Server.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 #include <anna/diameter.comm/ClientSession.hpp>
10 #include <anna/diameter.comm/Entity.hpp>
11 #include <anna/diameter.comm/Engine.hpp>
12 #include <anna/diameter.comm/OamModule.hpp>
13 #include <anna/diameter/codec/functions.hpp>
14 #include <anna/diameter/helpers/base/defines.hpp>
15 #include <anna/core/functions.hpp>
16 #include <anna/statistics/Engine.hpp>
17 #include <anna/time/functions.hpp>
18
19 #include <anna/core/tracing/Logger.hpp>
20 #include <anna/core/functions.hpp>
21 #include <anna/core/tracing/TraceMethod.hpp>
22
23 // STL
24 #include <vector>
25
26 using namespace anna;
27 using namespace anna::diameter;
28 using namespace anna::diameter::comm;
29
30
31 void Server::initialize() throw() {
32   a_parent = NULL;
33   a_engine = NULL;
34   a_clientSessions.clear(); // importante (el recycler creo que no lo tocaba)
35   a_available = false;
36   a_maxClientSessions = 1; // mono client connection
37   a_lastIncomingActivityTime = (anna::Millisecond)0;
38   a_lastOutgoingActivityTime = (anna::Millisecond)0;
39   a_statisticsAccumulator = NULL;
40   a_lastUsedResource = NULL;
41 }
42
43 void Server::initializeStatisticResources() throw() {
44   std::string accName = "remote server '";
45   accName += anna::functions::socketLiteralAsString(a_socket.first, a_socket.second);
46   accName += "' from origin-realm '";
47   accName += a_engine ? a_engine->getOriginRealm() : "unknown"; // it should be known (createServer)
48   accName += "' and origin-host '";
49   accName += a_engine ? a_engine->getOriginHost() : "unknown"; // it should be known (createServer)
50   accName += "'";
51   a_statisticsAccumulator = anna::statistics::Engine::instantiate().createAccumulator(accName);
52   a_processing_time__StatisticConceptId = a_statisticsAccumulator->addConcept("Diameter requests processing time at", "ms", true/* integer values */);
53   a_received_message_size__StatisticConceptId = a_statisticsAccumulator->addConcept("Diameter message sizes received from", "bytes", true/* integer values */);
54 }
55
56 void Server::resetStatistics() throw() {
57   a_statisticsAccumulator->reset();
58 }
59
60 void Server::updateProcessingTimeStatisticConcept(const double &value) throw() {
61   a_statisticsAccumulator->process(a_processing_time__StatisticConceptId, value);
62   LOGDEBUG(anna::Logger::debug(a_statisticsAccumulator->asString(), ANNA_FILE_LOCATION));
63 }
64
65 void Server::updateReceivedMessageSizeStatisticConcept(const double &value) throw() {
66   a_statisticsAccumulator->process(a_received_message_size__StatisticConceptId, value);
67   //LOGDEBUG(anna::Logger::debug(a_statisticsAccumulator->asString(), ANNA_FILE_LOCATION));
68 }
69
70
71 void Server::assertReady() throw(anna::RuntimeException) {
72   if(a_clientSessions.size() != a_maxClientSessions) {
73     std::string msg(asString());
74     msg += " | Non-configured server: you must add the remaining client-sessions before any operation (bind, send, etc.)";
75     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
76   }
77 }
78
79
80 void Server::addClientSession(int socketId)
81 throw(anna::RuntimeException) {
82   if(a_clientSessions.size() == a_maxClientSessions) {
83     LOGDEBUG
84     (
85       std::string msg = "Maximum number of client-sessions reached for this server (";
86       msg += anna::functions::asString(a_maxClientSessions);
87       msg += ").";
88       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
89     );
90     return;
91   }
92
93   if(!a_engine)
94     throw anna::RuntimeException("Invalid engine reference (NULL)", ANNA_FILE_LOCATION);
95
96   ClientSession *s = a_engine->createClientSession(this, socketId);
97   a_clientSessions.push_back(s);
98 }
99
100 int Server::getOTARequests() const throw() {
101   int result = 0;
102
103   for(std::vector<ClientSession*>::const_iterator it = begin(); it != end(); it++)
104     result += (*it)->getOTARequests();
105
106   return result;
107 }
108
109
110 bool Server::send(const Message* message, int socketId) throw(anna::RuntimeException) {
111   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Server", "send", ANNA_FILE_LOCATION));
112   assertReady();
113   bool fixedSocket = (socketId != -1);
114   int clientSessions = getNumberOfClientSessions();
115
116   for(int k = 0; k < clientSessions; k++) {    // try round-robin only over one cycle,
117     // no matter where you are: don't repeat same socket
118     if(fixedSocket)
119       a_lastUsedResource = a_engine->findClientSession(a_socket.first /*ip*/, a_socket.second /*port*/, socketId); // exception if not found
120     else {
121       if(a_deliveryIterator == end()) a_deliveryIterator = begin();
122
123       a_lastUsedResource = (*a_deliveryIterator);
124
125       if(clientSessions > 1) a_deliveryIterator++;  // Round robin
126     }
127
128     try {
129       // Send:
130       const Response* response = a_lastUsedResource->send(message);
131       return true; // no matter if response is NULL (answers, i.e.) or not.
132     } catch(anna::RuntimeException &ex) {
133       LOGDEBUG(
134         std::string msg = ex.getText();
135         msg += " | Send failed on socket id ";
136         msg += anna::functions::asString(a_lastUsedResource->getSocketId());
137         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
138       );
139     }
140
141     // Specific socket sending doesn't try
142     if(fixedSocket) return false;
143   }
144
145   return false;
146 }
147
148
149 bool Server::broadcast(const Message* message) throw(anna::RuntimeException) {
150   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Server", "broadcast", ANNA_FILE_LOCATION));
151   assertReady();
152   const Response* response;
153   bool allok = true;
154
155   for(std::vector<ClientSession*>::iterator it = begin(); it != end(); it++) {
156     try {
157       response = (*it)->send(message);
158     } catch(anna::RuntimeException &ex) {
159       ex.trace();
160       allok = false;
161     }
162   }
163
164   return allok;
165 }
166
167
168 bool Server::bind() throw(anna::RuntimeException) {
169   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Server", "bind", ANNA_FILE_LOCATION));
170   assertReady();
171   a_deliveryIterator = begin();
172   bool result = true; // all OK return
173
174   for(std::vector<ClientSession*>::iterator it = begin(); it != end(); it++) {
175     try {
176       (*it)->bind();
177     } catch(anna::RuntimeException &ex) {
178       ex.trace();
179       result = false;
180     }
181   }
182
183   return result;
184 }
185
186 void Server::raiseAutoRecovery(bool autoRecovery) throw(anna::RuntimeException) {
187   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Server", "raiseAutoRecovery", ANNA_FILE_LOCATION));
188   assertReady();
189   a_deliveryIterator = begin();
190
191   for(std::vector<ClientSession*>::iterator it = begin(); it != end(); it++)
192     (*it)->setAutoRecovery(autoRecovery);
193 }
194
195 void Server::setClassCodeTimeout(const ClassCode::_v v, const anna::Millisecond & millisecond) throw() {
196   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Server", "setClassCodeTimeout", ANNA_FILE_LOCATION));
197   assertReady();
198
199   for(std::vector<ClientSession*>::iterator it = begin(); it != end(); it++) {
200     try {
201       (*it)->setClassCodeTimeout(v, millisecond);
202     } catch(anna::RuntimeException &ex) {
203       ex.trace();
204     }
205   }
206 }
207
208
209 // Private close/destroy method
210 void Server::close(bool destroy) throw(anna::RuntimeException) {
211   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Server", "close", ANNA_FILE_LOCATION));
212
213   if(!a_engine)
214     throw anna::RuntimeException("Invalid engine reference (NULL)", ANNA_FILE_LOCATION);
215
216   assertReady();
217
218   for(std::vector<ClientSession*>::iterator it = begin(); it != end(); it++)
219     a_engine->closeClientSession(*it, destroy);
220 }
221
222
223 void Server::childIdle() const throw() {
224   // Check father entity idleness:
225   if(idle()) a_parent->childIdle();
226 }
227
228
229 void Server::hide() throw() {
230   for(std::vector<ClientSession*>::iterator it = begin(); it != end(); it++)
231     (*it)->hide();
232 }
233
234 void Server::show() throw() {
235   for(std::vector<ClientSession*>::iterator it = begin(); it != end(); it++)
236     (*it)->show();
237 }
238
239 bool Server::hidden() const throw() {
240   for(std::vector<ClientSession*>::const_iterator it = begin(); it != end(); it++)
241     if((*it)->shown()) return false;
242
243   return true;
244 }
245 bool Server::shown() const throw() {
246   for(std::vector<ClientSession*>::const_iterator it = begin(); it != end(); it++)
247     if((*it)->hidden()) return false;
248
249   return true;
250 }
251
252
253
254 std::string Server::socketAsString() const throw() {
255   std::string result = getAddress();
256   result += ":";
257   result += anna::functions::asString(getPort());
258   return result;
259 }
260
261
262 std::string Server::asString() const throw() {
263   std::string result("diameter::comm::Server { ");
264   result += " | Parent Entity: ";
265   result += a_parent->getDescription();
266   result += " | Server Address: ";
267   result += a_socket.first;
268   result += " | Server Port: ";
269   result += anna::functions::asString(a_socket.second);
270   result += " | Available: ";
271   result += a_available ? "yes" : "no";
272   result += " | Max client-sessions supported: ";
273   result += anna::functions::asString(a_maxClientSessions);
274   result += " | Currently configured client-sessions: ";
275   result += anna::functions::asString(a_clientSessions.size());
276   result += anna::functions::asString(" | OTA requests: %d%s", getOTARequests(), idle() ? " (idle)" : "");
277   result += " | Last Incoming Activity Time: ";
278   result += a_lastIncomingActivityTime.asString();
279   result += " | Last Outgoing Activity Time: ";
280   result += a_lastOutgoingActivityTime.asString();
281   result += " | Hidden: ";
282   result += (hidden() ? "yes" : "no");
283   result += "\n";
284   result += a_statisticsAccumulator->asString();
285
286   for(std::vector<ClientSession*>::const_iterator it = begin(); it != end(); it++) {
287     result += "\n";
288     result += (*it)->asString();
289   }
290
291   return result;
292 }
293
294 anna::xml::Node* Server::asXML(anna::xml::Node* parent) const throw() {
295   anna::xml::Node* result = parent->createChild("diameter.Server");
296   result->createAttribute("ParentEntity", a_parent->getDescription());
297   result->createAttribute("ServerAddress", a_socket.first);
298   result->createAttribute("ServerPort", anna::functions::asString(a_socket.second));
299   result->createAttribute("Available", a_available ? "yes" : "no");
300   result->createAttribute("MaxClientSessionsSupported", anna::functions::asString(a_maxClientSessions));
301   result->createAttribute("CurrentlyConfiguredClientSessions", anna::functions::asString(a_clientSessions.size()));
302   result->createAttribute("OTArequests", anna::functions::asString("%d%s", getOTARequests(), idle() ? " (idle)" : ""));
303   result->createAttribute("LastIncomingActivityTime", a_lastIncomingActivityTime.asString());
304   result->createAttribute("LastOutgoingActivityTime", a_lastOutgoingActivityTime.asString());
305   result->createAttribute("Hidden", hidden() ? "yes" : "no");
306   // Statistics
307   anna::xml::Node* stats = result->createChild("Statistics");
308   a_statisticsAccumulator->asXML(stats);
309   anna::xml::Node* clientSessions = result->createChild("Server.ClientSessions");
310
311   for(std::vector<ClientSession*>::const_iterator it = begin(); it != end(); it++)
312     (*it)->asXML(clientSessions);
313
314   return result;
315 }
316
317
318 void Server::eventPeerShutdown(const ClientSession *clientSession) throw() {
319   // Inform father entity:
320   a_parent->eventPeerShutdown(clientSession);
321 }
322
323 void Server::eventRequestRetransmission(const ClientSession* clientSession, Message *request) throw() {
324   // Inform father entity:
325   a_parent->eventRequestRetransmission(clientSession, request);
326 }
327
328 void Server::eventResponse(const Response& response) throw(anna::RuntimeException) {
329   // Inform father entity:
330   a_parent->eventResponse(response);
331 }
332
333 void Server::eventRequest(ClientSession *clientSession, const anna::DataBlock & request) throw(anna::RuntimeException) {
334   // Inform father entity:
335   a_parent->eventRequest(clientSession, request);
336 }
337
338 void Server::eventUnknownResponse(ClientSession *clientSession, const anna::DataBlock & response) throw(anna::RuntimeException) {
339   // Inform father entity:
340   a_parent->eventUnknownResponse(clientSession, response);
341 }
342
343 void Server::eventDPA(ClientSession *clientSession, const anna::DataBlock & response) throw(anna::RuntimeException) {
344   // Inform father entity:
345   a_parent->eventDPA(clientSession, response);
346 }
347
348
349 void Server::availabilityLost() throw() {
350   a_available = false;
351   std::string socket = anna::functions::socketLiteralAsString(a_socket.first, a_socket.second);
352   LOGDEBUG(
353     std::string msg = "diameter::comm::Server { Socket: ";
354     msg += socket;
355     msg += " } has lost its availability";
356     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
357   );
358   // OAM
359   OamModule &oamModule = OamModule::instantiate();
360   oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverServerDefinedAs__s__, socket.c_str());
361   oamModule.count(OamModule::Counter::LostAvailabilityOverServer);
362   a_engine->availabilityLost(this);
363   a_parent->refreshAvailability();
364 }
365
366
367 void Server::availabilityRecovered() throw() {
368   a_available = true;
369   std::string socket = anna::functions::socketLiteralAsString(a_socket.first, a_socket.second);
370   LOGDEBUG(
371     std::string msg = "diameter::comm::Server { Socket: ";
372     msg += socket;
373     msg += " } has recovered its availability";
374     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
375   );
376   // OAM
377   OamModule &oamModule = OamModule::instantiate();
378   oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverServerDefinedAs__s__, socket.c_str());
379   oamModule.count(OamModule::Counter::RecoveredAvailabilityOverServer);
380   a_engine->availabilityRecovered(this);
381   a_parent->refreshAvailability();
382 }
383
384
385
386 bool Server::refreshAvailability() throw() {
387   // Here available
388   if(a_available) {  // check not-bound state for all client-sessions:
389     bool isolate = true;
390
391     for(std::vector<ClientSession*>::const_iterator it = begin(); it != end(); it++)
392
393       //if ((*it)->getState() == ClientSession::State::Bound) { isolate = false; break; }
394       if((*it)->getState() != ClientSession::State::Closed) { isolate = false; break; }
395
396     if(isolate) {
397       availabilityLost();
398       return true;
399     }
400
401     return false;
402   }
403
404   // Here not available
405   for(std::vector<ClientSession*>::const_iterator it = begin(); it != end(); it++)
406     if((*it)->getState() == ClientSession::State::Bound) {
407       availabilityRecovered();
408       return true;
409     }
410
411   return false;
412 }
413
414
415 //------------------------------------------------------------------------------
416 //---------------------------------------- Server::updateIncomingActivityTime()
417 //------------------------------------------------------------------------------
418 void Server::updateIncomingActivityTime() throw() {
419   a_lastIncomingActivityTime = anna::functions::millisecond();
420   LOGDEBUG
421   (
422     std::string msg = "Updated INCOMING activity on server (milliseconds unix): ";
423     msg += anna::functions::asString(a_lastIncomingActivityTime.getValue());
424     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
425   );
426   a_parent->updateIncomingActivityTime();
427 }
428
429
430 //------------------------------------------------------------------------------
431 //---------------------------------------- Server::updateOutgoingActivityTime()
432 //------------------------------------------------------------------------------
433 void Server::updateOutgoingActivityTime(void) throw() {
434   a_lastOutgoingActivityTime = anna::functions::millisecond();
435   LOGDEBUG
436   (
437     std::string msg = "Updated OUTGOING activity on server (milliseconds unix): "; msg += anna::functions::asString(a_lastOutgoingActivityTime.getValue());
438     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
439   );
440   a_parent->updateOutgoingActivityTime();
441 }