Node class, command line redesign. New xml template for process configuration.
[anna.git] / example / diameter / launcher / MyDiameterEntity.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 // Project
10 #include <anna/core/core.hpp>
11 #include <anna/diameter/functions.hpp>
12 #include <anna/time/functions.hpp>
13 #include <anna/diameter.comm/Response.hpp>
14 #include <anna/diameter.comm/ClientSession.hpp>
15 #include <anna/diameter/helpers/base/functions.hpp>
16 #include <anna/diameter/helpers/dcca/functions.hpp>
17
18 // Process
19 #include "MyDiameterEngine.hpp"
20 #include "MyDiameterEntity.hpp"
21 #include "MyLocalServer.hpp"
22 #include "Launcher.hpp"
23 #include "RealmNode.hpp"
24
25
26 void MyDiameterEntity::eventRequestRetransmission(const anna::diameter::comm::ClientSession* clientSession, anna::diameter::comm::Message *request) throw() {
27
28   LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventRequestRetransmission", ANNA_FILE_LOCATION));
29
30   // Base class:
31   Entity::eventRequestRetransmission(clientSession, request); // warning trace
32
33   // Performance stats:
34   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
35   RealmNode * my_node = my_app.getRealmNode(getEngine()->getRealm());
36   // CommandId:
37   anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(request->getBody());
38   LOGDEBUG
39   (
40     std::string msg = "Request retransmitted: ";
41     msg += anna::diameter::functions::commandIdAsPairString(cid);
42     msg += " | DiameterServer: ";
43     msg += anna::functions::socketLiteralAsString(clientSession->getAddress(), clientSession->getPort());
44     msg += " | EventTime: ";
45     msg += anna::time::functions::currentTimeAsString();
46     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
47   );
48
49   // Write retransmission
50   if(my_node->logEnabled()) my_node->writeLogFile(request->getBody(), "retry", clientSession->asString());
51 }
52
53
54 int MyDiameterEntity::readSocketId(const anna::diameter::comm::Message* message, int maxClientSessions) const throw() {
55
56   if(a_sessionBasedModelsType == "RoundRobin") return -1;  // IEC also would return -1
57
58   try {
59     // Service-Context-Id:
60     anna::diameter::helpers::dcca::ChargingContext::_v chargingContext;
61     std::string scid = anna::diameter::helpers::dcca::functions::getServiceContextId(message->getBody(), chargingContext);
62
63     switch(chargingContext) {
64     case anna::diameter::helpers::dcca::ChargingContext::Data:
65     case anna::diameter::helpers::dcca::ChargingContext::Voice:
66     case anna::diameter::helpers::dcca::ChargingContext::Content: {
67       // Session-Id: '<DiameterIdentity>;<high 32 bits>;<low 32 bits>[;<optional value>="">]'
68       std::string sid = anna::diameter::helpers::base::functions::getSessionId(message->getBody());
69       std::string diameterIdentity, optional;
70       anna::U32 high, low;
71       anna::diameter::helpers::base::functions::decodeSessionId(sid, diameterIdentity, high, low /* context-teid */, optional);
72
73       if(a_sessionBasedModelsType == "SessionIdLowPart") return (low % maxClientSessions);
74
75       if(a_sessionBasedModelsType == "SessionIdHighPart") return (high % maxClientSessions);
76
77       if(a_sessionBasedModelsType == "SessionIdOptionalPart") return (atoi(optional.c_str()) % maxClientSessions);
78     }
79     case anna::diameter::helpers::dcca::ChargingContext::SMS:
80     case anna::diameter::helpers::dcca::ChargingContext::MMS:
81     case anna::diameter::helpers::dcca::ChargingContext::Unknown:
82     default:
83        return -1; // IEC model and Unknown traffic types
84     }
85   } catch(anna::RuntimeException &ex) {
86     LOGDEBUG(
87       std::string msg = ex.getText();
88       msg += " | Round-robin between sessions will be used to send";
89       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
90     );
91   }
92
93   return -1;
94 }
95
96 void MyDiameterEntity::eventRequest(anna::diameter::comm::ClientSession *clientSession, const anna::DataBlock &message)
97 throw(anna::RuntimeException) {
98   LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventRequest", ANNA_FILE_LOCATION));
99   // Performance stats:
100   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
101   RealmNode * my_node = my_app.getRealmNode(getEngine()->getRealm());
102   // CommandId:
103   anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message);
104   LOGDEBUG
105   (
106     std::string msg = "Request received: ";
107     msg += anna::diameter::functions::commandIdAsPairString(cid);
108     msg += " | DiameterServer: ";
109     msg += anna::functions::socketLiteralAsString(clientSession->getAddress(), clientSession->getPort());
110     msg += " | EventTime: ";
111     msg += anna::time::functions::currentTimeAsString();
112     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
113   );
114
115   // Write reception
116   if(my_node->logEnabled()) my_node->writeLogFile(message, "recvfe", clientSession->asString());
117
118   // Lookup reacting answers list:
119   int code = cid.first;
120   anna::diameter::codec::Message *answer_message = a_reactingAnswers.getMessage(code);
121   if (answer_message) {
122     // Prepare answer:
123     my_app.getCommunicator()->prepareAnswer(answer_message, message);
124
125     try {
126       anna::diameter::comm::Message *msg = my_node->createCommMessage();
127       msg->setBody(answer_message->code());
128       /* response = NULL =*/clientSession->send(msg);
129       my_node->releaseCommMessage(msg);
130
131       if(my_node->logEnabled()) my_node->writeLogFile(*answer_message, "sent2e", clientSession->asString());
132     } catch(anna::RuntimeException &ex) {
133       ex.trace();
134
135       if(my_node->logEnabled()) my_node->writeLogFile(*answer_message, "send2eError", clientSession->asString());
136     }
137
138     // Pop front the reacting answer:
139     a_reactingAnswers.nextMessage(code);
140     return;
141   }
142
143   LOGDEBUG
144   (
145     std::string msg = "No answers programmed (maybe sold out) for request coming from entity: ";
146     msg += anna::diameter::functions::commandIdAsPairString(cid);
147     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
148   );
149
150   // not found: forward to client (if exists)
151   // Forward to client:
152   MyLocalServer *localServer = my_node->getDiameterServer();
153
154   if(localServer && (cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) /* don't forward CER */) {
155     try {
156       anna::diameter::comm::Message *msg = my_node->createCommMessage();
157       msg->forwardEndToEnd(); // end-to-end will be kept
158       msg->setBody(message);
159       msg->setRequestClientSessionKey(clientSession->getKey());
160       bool success = localServer->send(msg);
161
162       // Detailed log:
163       if(my_node->logEnabled()) {
164         anna::diameter::comm::ServerSession *usedServerSession = localServer->getLastUsedResource();
165         std::string detail = usedServerSession ? usedServerSession->asString() : "<null server session>"; // esto no deberia ocurrir
166         my_node->writeLogFile(message, (success ? "fwd2c" : "fwd2cError"), detail);
167       }
168     } catch(anna::RuntimeException &ex) {
169       ex.trace();
170     }
171   }
172 }
173
174 void MyDiameterEntity::eventResponse(const anna::diameter::comm::Response &response)
175 throw(anna::RuntimeException) {
176   LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventResponse", ANNA_FILE_LOCATION));
177   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
178   RealmNode * my_node = my_app.getRealmNode(getEngine()->getRealm());
179   anna::diameter::comm::ClassCode::_v code = response.getClassCode();
180   anna::diameter::comm::Response::ResultCode::_v result = response.getResultCode();
181   anna::diameter::comm::Message* request = const_cast<anna::diameter::comm::Message*>(response.getRequest());
182   const anna::DataBlock* message = response.getMessage();
183   const anna::diameter::comm::ClientSession *clientSession = static_cast<const anna::diameter::comm::ClientSession *>(response.getSession());
184   bool isBindResponse = (code == anna::diameter::comm::ClassCode::Bind);
185   bool isApplicationMessage = (code == anna::diameter::comm::ClassCode::ApplicationMessage);
186   bool contextExpired = (result == anna::diameter::comm::Response::ResultCode::Timeout);
187   bool isUnavailable = (result == anna::diameter::comm::Response::ResultCode::DiameterUnavailable);
188   bool isOK = (result == anna::diameter::comm::Response::ResultCode::Success);
189   // CommandId:
190   anna::diameter::CommandId request_cid = request->getCommandId();
191   LOGDEBUG
192   (
193     std::string msg = "Response received for original diameter request: ";
194     msg += anna::diameter::functions::commandIdAsPairString(request_cid);
195     msg += " | Response: ";
196     msg += response.asString();
197     msg += " | DiameterServer: ";
198     msg += anna::functions::socketLiteralAsString(clientSession->getAddress(), clientSession->getPort());
199     msg += " | EventTime: ";
200     msg += anna::time::functions::currentTimeAsString();
201     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
202   );
203
204   if(isUnavailable) {
205     //if (isApplicationMessage)
206     LOGWARNING(anna::Logger::warning("Diameter entity unavailable for Diameter Request", ANNA_FILE_LOCATION));
207   }
208
209   if(contextExpired) {
210     //if (isApplicationMessage)
211     LOGWARNING(anna::Logger::warning("Context Expired for Diameter Request which was sent to the entity", ANNA_FILE_LOCATION));
212
213     if(request_cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) {  // don't trace CEA
214       if(my_node->logEnabled()) my_node->writeLogFile(*request, "req2e-expired", clientSession->asString());
215     }
216   }
217
218   if(isOK) {
219     LOGDEBUG(
220       std::string msg = "Received response for diameter message:  ";
221       msg += anna::diameter::functions::commandIdAsPairString(request_cid);
222       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
223     );
224
225     // Write reception
226     if(request_cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) {  // don't trace CEA
227       if(my_node->logEnabled()) {
228         my_node->writeLogFile(*message, "recvfe", clientSession->asString());
229       }
230     }
231
232     // Forward to client:
233     MyLocalServer *localServer = my_node->getDiameterServer();
234
235     if(localServer && (request_cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) /* don't forward CEA */) {
236       try {
237         anna::diameter::comm::Message *msg = my_node->createCommMessage();
238         msg->forwardEndToEnd(); // end-to-end will be kept
239         msg->setBody(*message);
240         bool success = localServer->send(msg, request->getRequestServerSessionKey());
241         my_node->releaseCommMessage(msg);
242         my_node->releaseCommMessage(request);
243
244         // Detailed log:
245         anna::diameter::comm::ServerSession *usedServerSession = my_node->getMyDiameterEngine()->findServerSession(request->getRequestServerSessionKey());
246         std::string detail = usedServerSession ? usedServerSession->asString() : "<null server session>"; // esto no deberia ocurrir
247
248         if(my_node->logEnabled()) {
249           my_node->writeLogFile(*message, (success ? "fwd2c" : "fwd2cError"), detail);
250         }
251       } catch(anna::RuntimeException &ex) {
252         ex.trace();
253       }
254     }
255   }
256
257   // Triggering burst:
258   if(isOK || contextExpired) my_node->sendBurstMessage();
259 }
260
261 void MyDiameterEntity::eventUnknownResponse(anna::diameter::comm::ClientSession *clientSession, const anna::DataBlock &message)
262 throw(anna::RuntimeException) {
263   LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventUnknownResponse", ANNA_FILE_LOCATION));
264   // Performance stats:
265   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
266   RealmNode * my_node = my_app.getRealmNode(getEngine()->getRealm());
267   // CommandId:
268   anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message);
269   LOGDEBUG
270   (
271     std::string msg = "Out-of-context response received from entity: ";
272     msg += anna::diameter::functions::commandIdAsPairString(cid);
273     msg += " | DiameterServer: ";
274     msg += anna::functions::socketLiteralAsString(clientSession->getAddress(), clientSession->getPort());
275     msg += " | EventTime: ";
276     msg += anna::time::functions::currentTimeAsString();
277     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
278   );
279
280   // Write reception
281   if(my_node->logEnabled()) my_node->writeLogFile(message, "recvfe-ans-unknown", clientSession->asString());
282 }
283
284 void MyDiameterEntity::eventDPA(anna::diameter::comm::ClientSession *clientSession, const anna::DataBlock &message)
285 throw(anna::RuntimeException) {
286   LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventDPA", ANNA_FILE_LOCATION));
287   // Performance stats:
288   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
289   RealmNode * my_node = my_app.getRealmNode(getEngine()->getRealm());
290   // CommandId:
291   anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message);
292   LOGDEBUG
293   (
294     std::string msg = "Disconnect-Peer-Answer received from entity: ";
295     msg += anna::diameter::functions::commandIdAsPairString(cid);
296     msg += " | DiameterServer: ";
297     msg += anna::functions::socketLiteralAsString(clientSession->getAddress(), clientSession->getPort());
298     msg += " | EventTime: ";
299     msg += anna::time::functions::currentTimeAsString();
300     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
301   );
302
303   // Write reception
304   if(my_node->logEnabled()) my_node->writeLogFile(message, "recvfe", clientSession->asString());
305 }