Suuports clang compiler
[anna.git] / source / diameter.comm / Entity.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // http://redmine.teslayout.com/projects/anna-suite
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     *  Neither the name of the copyright holder nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 #include <anna/diameter.comm/Server.hpp>
38 #include <anna/diameter.comm/Entity.hpp>
39 #include <anna/diameter.comm/Engine.hpp>
40 #include <anna/diameter.comm/OamModule.hpp>
41 #include <anna/diameter/codec/functions.hpp>
42 #include <anna/diameter/helpers/base/defines.hpp>
43 #include <anna/core/functions.hpp>
44 #include <anna/time/functions.hpp>
45 #include <anna/diameter/helpers/base/functions.hpp>
46 #include <anna/diameter/helpers/dcca/functions.hpp>
47
48 #include <anna/core/tracing/Logger.hpp>
49 #include <anna/core/functions.hpp>
50 #include <anna/core/tracing/TraceMethod.hpp>
51
52
53 using namespace anna;
54 using namespace anna::diameter;
55 using namespace anna::diameter::comm;
56
57
58
59 void Entity::initialize() throw() {
60   a_engine = NULL;
61   a_servers.clear(); // importante (el recycler creo que no lo tocaba)
62   a_available = false;
63   a_deprecated = false;
64   a_socketListLiteral = "";
65   a_primarySocketLiteral = "";
66   a_secondarySocketLiteral = "";
67   a_description = "";
68   a_category = 0;
69   a_lastUsedResource = NULL;
70 }
71
72
73 void Entity::assertReady() throw(anna::RuntimeException) {
74   if(a_servers.size() != a_maxServers) {
75     std::string msg(asString());
76     msg += " | Non-configured entity: you must add the remaining servers before any operation (bind, send, etc.)";
77     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
78   }
79 }
80
81
82 void Entity::addServer(const socket_t & serverId)
83 throw(anna::RuntimeException) {
84   if(a_servers.size() == a_maxServers) {
85     LOGDEBUG
86     (
87       std::string msg = "Maximum number of servers reached for this entity (";
88       msg += anna::functions::asString(a_maxServers);
89       msg += "). Please use clear()/close() primitives";
90       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
91     );
92     return;
93   }
94
95   if(!a_engine)
96     throw anna::RuntimeException("Invalid engine reference (NULL)", ANNA_FILE_LOCATION);
97
98   Server *s = a_engine->createServer(this, serverId);
99   a_servers.push_back(s);
100   a_deliveryIterator = begin();
101 }
102
103
104 int Entity::readSocketId(const Message* message, int maxClientSessions) const throw() {
105   try {
106     // Service-Context-Id:
107     anna::diameter::helpers::dcca::ChargingContext::_v chargingContext;
108     std::string scid = anna::diameter::helpers::dcca::functions::getServiceContextId(message->getBody(), chargingContext);
109
110     switch(chargingContext) {
111     case anna::diameter::helpers::dcca::ChargingContext::Data:
112     case anna::diameter::helpers::dcca::ChargingContext::Voice:
113     case anna::diameter::helpers::dcca::ChargingContext::Content: {
114       // Session-Id: '<DiameterIdentity>;<high 32 bits>;<low 32 bits>[;<optional value>="">]'
115       std::string sid = anna::diameter::helpers::base::functions::getSessionId(message->getBody());
116       std::string diameterIdentity, optional;
117       anna::U32 high, low;
118       anna::diameter::helpers::base::functions::decodeSessionId(sid, diameterIdentity, high, low /* context-teid */, optional);
119       return (low % maxClientSessions);
120     }
121     //case anna::diameter::helpers::dcca::ChargingContext::SMS:
122     //case anna::diameter::helpers::dcca::ChargingContext::MMS:
123     //default:
124     //   return -1; // IEC model and Unknown traffic types
125     }
126   } catch(anna::RuntimeException &ex) {
127     LOGDEBUG(
128       std::string msg = ex.getText();
129       msg += " | Round-robin between sessions will be used to send";
130       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
131     );
132   }
133
134   return -1;
135 }
136
137
138 bool Entity::send(const Message* message, bool balance) throw(anna::RuntimeException) {
139   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Entity", "send", ANNA_FILE_LOCATION));
140   assertReady();
141   // Carried socket id (forwarding/proxy features):
142   std::string carriedSocketId = message->getRequestClientSessionKey();
143
144   if(carriedSocketId != "") {
145     // Send (it was a request, we forward the answer):
146     try {
147       ClientSession * fixedClientSession = a_engine->findClientSession(carriedSocketId); // exception if not found
148       fixedClientSession->send(message);
149       return true;
150     } catch(anna::RuntimeException &ex) {
151       std::string msg = "Cannot deliver answer through a fixed client session (";
152       msg += carriedSocketId;
153       msg += "). Perhaps it existed but not now. Ignore";
154       anna::Logger::error(msg, ANNA_FILE_LOCATION);
155       ex.trace();
156     }
157   }
158
159   ////////////////////////////////////////////////////////////////////////////////////////
160   // BALANCE vs STANDARD
161   // Balance algorythm remember last delivery resource used, balancing from start to end,
162   // understanding start as next resource to last used, and end as last used. Standard
163   // algorithm always starts at primary (first defined) server.
164   ////////////////////////////////////////////////////////////////////////////////////////
165
166   // Balance
167   if(balance) {
168     for(int k = 0; k < getMaxServers(); k++) {   // try round-robin only over one cycle,
169       // no matter where you are: don't repeat same server
170       if(a_deliveryIterator == end()) a_deliveryIterator = begin();
171
172       a_lastUsedResource = (*a_deliveryIterator);
173       a_deliveryIterator++;
174       // At 'readSocketId' documentation:
175       //      If server is configured as single session (max client sessions equal to 1), entity will ignore
176       //      this method because it won't affect the session selection.
177       int serverSessions = a_lastUsedResource->getMaxClientSessions();
178       int socketId = (serverSessions > 1) ? readSocketId(message, serverSessions) : -1; // optimization
179
180       if(a_lastUsedResource->send(message, socketId))  // exception only possible at findClientSession within server send procedure
181         return true;
182     }
183   } else {
184     // Standard (no balance) //   start at begining, try secondary, and so on until end.
185     std::vector<Server*>::iterator it = begin();
186
187     while(it != end()) {
188       a_lastUsedResource = (*it);
189       it++;
190       // At 'readSocketId' documentation:
191       //      If server is configured as single session (max client sessions equal to 1), entity will ignore
192       //      this method because it won't affect the session selection.
193       int serverSessions = a_lastUsedResource->getMaxClientSessions();
194       int socketId = (serverSessions > 1) ? readSocketId(message, serverSessions) : -1; // optimization
195
196       if(a_lastUsedResource->send(message, socketId))  // exception only possible at findClientSession within server send procedure
197         return true;
198     }
199   }
200
201   // END BALANCE AND TRY ALGORITHM or STANDARD ///////////////////////////////////////////
202   ////////////////////////////////////////////////////////////////////////////////////////
203   // Here, sent has failed:
204   // OAM
205   OamModule &oamModule = OamModule::instantiate();
206
207   if(a_maxServers != 2) {
208     oamModule.activateAlarm(OamModule::Alarm::UnableToDeliverDiameterMessageToEntityDefinedAs__s__, a_socketListLiteral.c_str());
209     oamModule.count(OamModule::Counter::UnableToDeliverOverEntity);
210   } else {
211     OamModule &oamModule = OamModule::instantiate();
212     oamModule.activateAlarm(OamModule::Alarm::UnableToDeliverDiameterMessageToEntityDefinedAsPrimary__s__AndSecondary__s__,
213                             a_primarySocketLiteral.c_str(),
214                             a_secondarySocketLiteral.c_str());
215     oamModule.count(OamModule::Counter::UnableToDeliverOverEntity);
216   }
217
218   return false;
219 }
220
221 bool Entity::broadcast(const Message* message) throw(anna::RuntimeException) {
222   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Entity", "broadcast", ANNA_FILE_LOCATION));
223   assertReady();
224   bool allok = true;
225   bool ok;
226
227   for(std::vector<Server*>::iterator it = begin(); it != end(); it++) {
228     try {
229       ok = (*it)->broadcast(message);
230
231       if(!ok) allok = false;
232     } catch(anna::RuntimeException &ex) {
233       ex.trace();
234       allok = false;
235     }
236   }
237
238   return allok;
239 }
240
241 bool Entity::bind() throw(anna::RuntimeException) {
242   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Entity", "bind", ANNA_FILE_LOCATION));
243   assertReady();
244   bool result = true; // all OK return
245
246   for(std::vector<Server*>::iterator it = begin(); it != end(); it++) {
247     try {
248       (*it)->bind();
249     } catch(anna::RuntimeException &ex) {
250       ex.trace();
251       result = false;
252     }
253   }
254
255   return result;
256 }
257
258 void Entity::raiseAutoRecovery(bool autoRecovery) throw(anna::RuntimeException) {
259   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Entity", "raiseAutoRecovery", ANNA_FILE_LOCATION));
260   assertReady();
261
262   for(std::vector<Server*>::iterator it = begin(); it != end(); it++)
263     (*it)->raiseAutoRecovery(autoRecovery);
264 }
265
266 void Entity::setClassCodeTimeout(const ClassCode::_v v, const anna::Millisecond & millisecond) throw() {
267   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Entity", "setClassCodeTimeout", ANNA_FILE_LOCATION));
268   assertReady();
269
270   for(std::vector<Server*>::iterator it = begin(); it != end(); it++) {
271     try {
272       (*it)->setClassCodeTimeout(v, millisecond);
273     } catch(anna::RuntimeException &ex) {
274       ex.trace();
275     }
276   }
277 }
278
279
280 // Private close/destroy method
281 void Entity::close(bool destroy) throw(anna::RuntimeException) {
282   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Entity", "close", ANNA_FILE_LOCATION));
283
284   if(!a_engine)
285     throw anna::RuntimeException("Invalid engine reference (NULL)", ANNA_FILE_LOCATION);
286
287   assertReady();
288
289   for(std::vector<Server*>::iterator it = begin(); it != end(); it++)
290     a_engine->closeServer(*it, destroy);
291 }
292
293
294 socket_v Entity::getAddressPortList() const throw() {
295   socket_v result;
296
297   for(std::vector<Server*>::const_iterator it = begin(); it != end(); it++) {
298     socket_t address((*it)->getAddress(), (*it)->getPort());
299     result.push_back(address);
300   }
301
302   return result;
303 }
304
305 int Entity::getOTARequests() const throw() {
306   int result = 0;
307
308   for(std::vector<Server*>::const_iterator it = begin(); it != end(); it++)
309     result += (*it)->getOTARequests();
310
311   return result;
312 }
313
314 void Entity::childIdle() const throw() {
315   // Check father engine idleness:
316   if(idle()) a_engine->eraseDeprecatedIdleEntities();
317 }
318
319
320 void Entity::hide() throw() {
321   for(std::vector<Server*>::iterator it = begin(); it != end(); it++)
322     (*it)->hide();
323 }
324
325 void Entity::show() throw() {
326   for(std::vector<Server*>::iterator it = begin(); it != end(); it++)
327     (*it)->show();
328 }
329
330 bool Entity::hidden() const throw() {
331   for(std::vector<Server*>::const_iterator it = begin(); it != end(); it++)
332     if((*it)->shown()) return false;
333
334   return true;
335 }
336 bool Entity::shown() const throw() {
337   for(std::vector<Server*>::const_iterator it = begin(); it != end(); it++)
338     if((*it)->hidden()) return false;
339
340   return true;
341 }
342
343 void Entity::eventPeerShutdown(const ClientSession* clientSession) throw() {
344   LOGWARNING(
345     std::string msg(clientSession->asString());
346     msg += " | eventPeerShutdown";
347     anna::Logger::warning(msg, ANNA_FILE_LOCATION);
348   );
349 }
350
351 std::string Entity::asString() const throw() {
352   std::string result("diameter::comm::Entity { ");
353   std::string realm = a_engine->getRealm();
354
355   if(realm != "") {
356     result += "Parent Engine (realm): ";
357     result += realm;
358   }
359
360   result += " | Category: ";
361   result += anna::functions::asString(a_category);
362
363   if(a_description != "") {
364     result += " | Description: '";
365     result += a_description;
366   }
367
368   result += " | Available: ";
369   result += a_available ? "yes" : "no";
370   result += " | Deprecated: ";
371   result += a_deprecated ? "yes" : "no";
372   result += " | Max servers supported: ";
373   result += anna::functions::asString(a_maxServers);
374   result += " | Currently configured servers: ";
375   result += anna::functions::asString(a_servers.size());
376   result += anna::functions::asString(" | OTA requests: %d%s", getOTARequests(), idle() ? " (idle)" : "");
377   result += " | Last Incoming Activity Time: ";
378   result += a_lastIncomingActivityTime.asString();
379   result += " | Last Outgoing Activity Time: ";
380   result += a_lastOutgoingActivityTime.asString();
381   result += " | Hidden: ";
382   result += (hidden() ? "yes" : "no");
383   result += "\n";
384
385   for(std::vector<Server*>::const_iterator it = begin(); it != end(); it++) {
386     result += "\n";
387     result += (*it)->asString();
388   }
389
390   return result;
391 }
392
393 anna::xml::Node* Entity::asXML(anna::xml::Node* parent) const throw() {
394   anna::xml::Node* result = parent->createChild("diameter.Entity");
395   std::string realm = a_engine->getRealm();
396
397   if(realm != "") result->createAttribute("ParentEngineRealm", realm);
398
399   result->createAttribute("Category", anna::functions::asString(a_category));
400
401   if(a_description != "") result->createAttribute("Description", a_description);
402
403   result->createAttribute("Available", a_available ? "yes" : "no");
404   result->createAttribute("Deprecated", a_deprecated ? "yes" : "no");
405   result->createAttribute("MaxServersSupported", anna::functions::asString(a_maxServers));
406   result->createAttribute("CurrentlyConfiguredServers", anna::functions::asString(a_servers.size()));
407   result->createAttribute("OTArequests", anna::functions::asString("%d%s", getOTARequests(), idle() ? " (idle)" : ""));
408   result->createAttribute("LastIncomingActivityTime", a_lastIncomingActivityTime.asString());
409   result->createAttribute("LastOutgoingActivityTime", a_lastOutgoingActivityTime.asString());
410   result->createAttribute("Hidden", hidden() ? "yes" : "no");
411   anna::xml::Node* servers = result->createChild("Entity.Servers");
412
413   for(std::vector<Server*>::const_iterator it = begin(); it != end(); it++)
414     (*it)->asXML(servers);
415
416   return result;
417 }
418
419 void Entity::availabilityLost() throw() {
420   a_available = false;
421   LOGDEBUG(
422     std::string msg = "diameter::comm::Entity { Description: ";
423     msg += getDescription();
424     msg += " } has lost its availability";
425     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
426   );
427   OamModule &oamModule = OamModule::instantiate();
428
429   if(a_maxServers != 2) {
430     oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverEntityDefinedAs__s__, a_socketListLiteral.c_str());
431     oamModule.count(OamModule::Counter::LostAvailabilityOverEntity);
432   } else {
433     oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverEntityDefinedAsPrimary__s__AndSecondary__s__,
434                             a_primarySocketLiteral.c_str(),
435                             a_secondarySocketLiteral.c_str());
436     oamModule.count(OamModule::Counter::LostAvailabilityOverEntity);
437   }
438
439   a_engine->availabilityLost(this);
440   a_engine->refreshAvailabilityForEntities();
441 }
442
443
444 void Entity::availabilityRecovered() throw() {
445   a_available = true;
446   LOGDEBUG(
447     std::string msg = "diameter::comm::Entity { Description: ";
448     msg += getDescription();
449     msg += " } has recovered its availability";
450     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
451   );
452   OamModule &oamModule = OamModule::instantiate();
453
454   if(a_maxServers != 2) {
455     oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverEntityDefinedAs__s__, a_socketListLiteral.c_str());
456     oamModule.count(OamModule::Counter::RecoveredAvailabilityOverEntity);
457   } else {
458     oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverEntityDefinedAsPrimary__s__AndSecondary__s__,
459                           a_primarySocketLiteral.c_str(),
460                           a_secondarySocketLiteral.c_str());
461     oamModule.count(OamModule::Counter::RecoveredAvailabilityOverEntity);
462   }
463
464   a_engine->availabilityRecovered(this);
465   a_engine->refreshAvailabilityForEntities();
466 }
467
468
469 bool Entity::refreshAvailability() throw() {
470   // Here available
471   if(a_available) {  // check not-bound state for all servers:
472     bool isolate = true;
473
474     for(std::vector<Server*>::const_iterator it = begin(); it != end(); it++)
475       if((*it)->isAvailable()) { isolate = false; break; }
476
477     if(isolate) {
478       availabilityLost();
479       return true;
480     }
481
482     return false;
483   }
484
485   // Here not available
486   for(std::vector<Server*>::const_iterator it = begin(); it != end(); it++)
487     if((*it)->isAvailable()) {
488       availabilityRecovered();
489       return true;
490     }
491
492   return false;
493 }
494
495 //------------------------------------------------------------------------------
496 //----------------------------------------- Entity::updateIncomingActivityTime()
497 //------------------------------------------------------------------------------
498 void Entity::updateIncomingActivityTime() throw() {
499   a_lastIncomingActivityTime = anna::functions::millisecond();
500   LOGDEBUG
501   (
502     std::string msg = "Updated INCOMING activity on entity (milliseconds unix): ";
503     msg += anna::functions::asString(a_lastIncomingActivityTime.getValue());
504     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
505   );
506 }
507
508
509 //------------------------------------------------------------------------------
510 //----------------------------------------- Entity::updateOutgoingActivityTime()
511 //------------------------------------------------------------------------------
512 void Entity::updateOutgoingActivityTime(void) throw() {
513   a_lastOutgoingActivityTime = anna::functions::millisecond();
514   LOGDEBUG
515   (
516     std::string msg = "Updated OUTGOING activity on entity (milliseconds unix): ";
517     msg += anna::functions::asString(a_lastOutgoingActivityTime.getValue());
518     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
519   );
520 }
521