1 // ANNA - Anna is Not Nothingness Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // http://redmine.teslayout.com/projects/anna-suite
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
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
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.
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.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
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>
48 #include <anna/core/tracing/Logger.hpp>
49 #include <anna/core/functions.hpp>
50 #include <anna/core/tracing/TraceMethod.hpp>
54 using namespace anna::diameter;
55 using namespace anna::diameter::comm;
59 void Entity::initialize() throw() {
61 a_servers.clear(); // importante (el recycler creo que no lo tocaba)
64 a_socketListLiteral = "";
65 a_primarySocketLiteral = "";
66 a_secondarySocketLiteral = "";
69 a_lastUsedResource = NULL;
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);
82 void Entity::addServer(const socket_t & serverId)
83 throw(anna::RuntimeException) {
84 if(a_servers.size() == a_maxServers) {
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);
96 throw anna::RuntimeException("Invalid engine reference (NULL)", ANNA_FILE_LOCATION);
98 Server *s = a_engine->createServer(this, serverId);
99 a_servers.push_back(s);
100 a_deliveryIterator = begin();
104 int Entity::readSocketId(const Message* message, int maxClientSessions) const throw() {
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);
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;
118 anna::diameter::helpers::base::functions::decodeSessionId(sid, diameterIdentity, high, low /* context-teid */, optional);
119 return (low % maxClientSessions);
121 //case anna::diameter::helpers::dcca::ChargingContext::SMS:
122 //case anna::diameter::helpers::dcca::ChargingContext::MMS:
124 // return -1; // IEC model and Unknown traffic types
126 } catch(anna::RuntimeException &ex) {
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);
138 bool Entity::send(const Message* message, bool balance) throw(anna::RuntimeException) {
139 LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Entity", "send", ANNA_FILE_LOCATION));
141 // Carried socket id (forwarding/proxy features):
142 std::string carriedSocketId = message->getRequestClientSessionKey();
144 if(carriedSocketId != "") {
145 // Send (it was a request, we forward the answer):
147 ClientSession * fixedClientSession = a_engine->findClientSession(carriedSocketId); // exception if not found
148 fixedClientSession->send(message);
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);
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 ////////////////////////////////////////////////////////////////////////////////////////
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();
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
180 if(a_lastUsedResource->send(message, socketId)) // exception only possible at findClientSession within server send procedure
184 // Standard (no balance) // start at begining, try secondary, and so on until end.
185 std::vector<Server*>::iterator it = begin();
188 a_lastUsedResource = (*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
196 if(a_lastUsedResource->send(message, socketId)) // exception only possible at findClientSession within server send procedure
201 // END BALANCE AND TRY ALGORITHM or STANDARD ///////////////////////////////////////////
202 ////////////////////////////////////////////////////////////////////////////////////////
203 // Here, sent has failed:
205 OamModule &oamModule = OamModule::instantiate();
207 if(a_maxServers != 2) {
208 oamModule.activateAlarm(OamModule::Alarm::UnableToDeliverDiameterMessageToEntityDefinedAs__s__, a_socketListLiteral.c_str());
209 oamModule.count(OamModule::Counter::UnableToDeliverOverEntity);
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);
221 bool Entity::broadcast(const Message* message) throw(anna::RuntimeException) {
222 LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Entity", "broadcast", ANNA_FILE_LOCATION));
227 for(std::vector<Server*>::iterator it = begin(); it != end(); it++) {
229 ok = (*it)->broadcast(message);
231 if(!ok) allok = false;
232 } catch(anna::RuntimeException &ex) {
241 bool Entity::bind() throw(anna::RuntimeException) {
242 LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Entity", "bind", ANNA_FILE_LOCATION));
244 bool result = true; // all OK return
246 for(std::vector<Server*>::iterator it = begin(); it != end(); it++) {
249 } catch(anna::RuntimeException &ex) {
258 void Entity::raiseAutoRecovery(bool autoRecovery) throw(anna::RuntimeException) {
259 LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Entity", "raiseAutoRecovery", ANNA_FILE_LOCATION));
262 for(std::vector<Server*>::iterator it = begin(); it != end(); it++)
263 (*it)->raiseAutoRecovery(autoRecovery);
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));
270 for(std::vector<Server*>::iterator it = begin(); it != end(); it++) {
272 (*it)->setClassCodeTimeout(v, millisecond);
273 } catch(anna::RuntimeException &ex) {
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));
285 throw anna::RuntimeException("Invalid engine reference (NULL)", ANNA_FILE_LOCATION);
289 for(std::vector<Server*>::iterator it = begin(); it != end(); it++)
290 a_engine->closeServer(*it, destroy);
294 socket_v Entity::getAddressPortList() const throw() {
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);
305 int Entity::getOTARequests() const throw() {
308 for(std::vector<Server*>::const_iterator it = begin(); it != end(); it++)
309 result += (*it)->getOTARequests();
314 void Entity::childIdle() const throw() {
315 // Check father engine idleness:
316 if(idle()) a_engine->eraseDeprecatedIdleEntities();
320 void Entity::hide() throw() {
321 for(std::vector<Server*>::iterator it = begin(); it != end(); it++)
325 void Entity::show() throw() {
326 for(std::vector<Server*>::iterator it = begin(); it != end(); it++)
330 bool Entity::hidden() const throw() {
331 for(std::vector<Server*>::const_iterator it = begin(); it != end(); it++)
332 if((*it)->shown()) return false;
336 bool Entity::shown() const throw() {
337 for(std::vector<Server*>::const_iterator it = begin(); it != end(); it++)
338 if((*it)->hidden()) return false;
343 void Entity::eventPeerShutdown(const ClientSession* clientSession) throw() {
345 std::string msg(clientSession->asString());
346 msg += " | eventPeerShutdown";
347 anna::Logger::warning(msg, ANNA_FILE_LOCATION);
351 std::string Entity::asString() const throw() {
352 std::string result("diameter::comm::Entity { ");
353 std::string realm = a_engine->getRealm();
356 result += "Parent Engine (realm): ";
360 result += " | Category: ";
361 result += anna::functions::asString(a_category);
363 if(a_description != "") {
364 result += " | Description: '";
365 result += a_description;
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");
385 for(std::vector<Server*>::const_iterator it = begin(); it != end(); it++) {
387 result += (*it)->asString();
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();
397 if(realm != "") result->createAttribute("ParentEngineRealm", realm);
399 result->createAttribute("Category", anna::functions::asString(a_category));
401 if(a_description != "") result->createAttribute("Description", a_description);
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");
413 for(std::vector<Server*>::const_iterator it = begin(); it != end(); it++)
414 (*it)->asXML(servers);
419 void Entity::availabilityLost() throw() {
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);
427 OamModule &oamModule = OamModule::instantiate();
429 if(a_maxServers != 2) {
430 oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverEntityDefinedAs__s__, a_socketListLiteral.c_str());
431 oamModule.count(OamModule::Counter::LostAvailabilityOverEntity);
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);
439 a_engine->availabilityLost(this);
440 a_engine->refreshAvailabilityForEntities();
444 void Entity::availabilityRecovered() throw() {
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);
452 OamModule &oamModule = OamModule::instantiate();
454 if(a_maxServers != 2) {
455 oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverEntityDefinedAs__s__, a_socketListLiteral.c_str());
456 oamModule.count(OamModule::Counter::RecoveredAvailabilityOverEntity);
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);
464 a_engine->availabilityRecovered(this);
465 a_engine->refreshAvailabilityForEntities();
469 bool Entity::refreshAvailability() throw() {
471 if(a_available) { // check not-bound state for all servers:
474 for(std::vector<Server*>::const_iterator it = begin(); it != end(); it++)
475 if((*it)->isAvailable()) { isolate = false; break; }
485 // Here not available
486 for(std::vector<Server*>::const_iterator it = begin(); it != end(); it++)
487 if((*it)->isAvailable()) {
488 availabilityRecovered();
495 //------------------------------------------------------------------------------
496 //----------------------------------------- Entity::updateIncomingActivityTime()
497 //------------------------------------------------------------------------------
498 void Entity::updateIncomingActivityTime() throw() {
499 a_lastIncomingActivityTime = anna::functions::millisecond();
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);
509 //------------------------------------------------------------------------------
510 //----------------------------------------- Entity::updateOutgoingActivityTime()
511 //------------------------------------------------------------------------------
512 void Entity::updateOutgoingActivityTime(void) throw() {
513 a_lastOutgoingActivityTime = anna::functions::millisecond();
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);