#include <TestCase.hpp>
-#define SIGUSR2_TASKS_INPUT_FILENAME "./sigusr2.tasks.input"
-#define SIGUSR2_TASKS_OUTPUT_FILENAME "./sigusr2.tasks.output"
+#define SIGUSR2_TASKS_INPUT_FILENAME "./sigusr2.in"
+#define SIGUSR2_TASKS_OUTPUT_FILENAME "./sigusr2.out"
<!--\n\
Stack record\n\
\n\
- id: Normally the id corresponds to the Application-Id for which the dictionary provided is designed.\n\
+ id: Normally the id corresponds to the Application-Id for which the dictionary provided is designed\n\
(in multistack applications, it shall be mandatory respect such association to know the stack used\n\
for processed messages).\n\
dictionary: Path to the dictionary file\n\
-->\n\
\n\
<!ELEMENT node EMPTY>\n\
-<!ATTLIST node originRealm CDATA #REQUIRED applicationId CDATA #REQUIRED originHost CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>\n\
+<!ATTLIST node originRealm CDATA #REQUIRED originHost CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>\n\
<!--\n\
Node record\n\
\n\
originRealm: Node identifier (Origin-Realm name).\n\
- applicationId: The Application-Id provided must exists as a registered 'stack id'.\n\
originHost: Diameter application host name (system name). If missing, process sets o.s. hostname\n\
Note that if you have two or more realms, the names must be different.\n\
cer: User defined CER path file to be encoded to establish diameter connections.\n\
Launcher::Launcher() : anna::comm::Application("launcher", "DiameterLauncher", "1.1"), a_communicator(NULL) {
a_codecEngine = new anna::diameter::codec::Engine("MyCodecEngine");
+ a_baseProtocolDictionary = NULL;
a_timeEngine = NULL;
a_counterRecorder = NULL;
a_admlMinResolution = 2 * anna::timex::Engine::minResolution; // 2*10 = 20 ms; 1000/20 = 50 ticks per second;
//<!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED>
const anna::xml::Attribute *id, *dictionary;
- // <!ATTLIST node originRealm CDATA #REQUIRED applicationId CDATA #REQUIRED originHost CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>
- const anna::xml::Attribute *originRealm, *appId, *originHost, *cer, *dwr, *allowedInactivityTime, *tcpConnectDelay,
+ // <!ATTLIST node originRealm CDATA #REQUIRED originHost CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>
+ const anna::xml::Attribute *originRealm, *originHost, *cer, *dwr, *allowedInactivityTime, *tcpConnectDelay,
*answersTimeout, *ceaTimeout, *watchdogPeriod, *entity, *entityServerSessions,
*diameterServer, *diameterServerSessions, *balance, *sessionBasedModelsClientSocketSelection,
*retries, *log, *splitLog, *detailedLog, *dumpLog, *burstLog;
anna::diameter::comm::ApplicationMessageOamModule & appMsgOamModule = anna::diameter::comm::ApplicationMessageOamModule::instantiate();
appMsgOamModule.enableCounters(); // this special module is disabled by default (the only)
static int scope_id = 3;
+ bool id_0_registered = false;
+ unsigned int id_value;
for(anna::xml::Node::const_child_iterator it = servicesNode->child_begin(); it != servicesNode->child_end(); it++) {
std::string nodeName = (*it)->getName();
// Input data:
id = (*it)->getAttribute("id");
dictionary = (*it)->getAttribute("dictionary");
+ id_value = id->getIntegerValue();
try {
- d = stackEngine.createDictionary(id->getIntegerValue(), dictionary->getValue());
+ d = stackEngine.createDictionary(id_value, dictionary->getValue());
getCodecEngine()->setDictionary(d);
// OAM module for counters:
- appMsgOamModule.createStackCounterScope(scope_id, id->getIntegerValue() /* application-id */);
+ appMsgOamModule.createStackCounterScope(scope_id, id_value /* application-id */);
scope_id++;
} catch(anna::RuntimeException &ex) {
//_exit(ex.asString());
throw ex;
}
+
+ if (id_value == 0)
+ id_0_registered = true;
+ a_baseProtocolDictionary = d;
}
}
// Show loaded stacks:
std::cout << "Stacks currently loaded:" << std::endl;
- std::cout << anna::functions::tab(stackEngine.asString(false /* light */));
- std::cout << std::endl;
+ std::cout << anna::functions::tab(stackEngine.asString(false /* light */)) << std::endl;
// Codec engine adjustments:
// Auto stack selection based on Application-ID:
bool multistack = (stackEngine.stack_size() > 1);
- if (multistack) getCodecEngine()->selectStackWithApplicationId(true);
+ if (multistack) {
+ getCodecEngine()->selectStackWithApplicationId(true);
+ // In multistack, id = 0 MUST be registered:
+ if (!id_0_registered)
+ throw anna::RuntimeException("In multistack applications is mandatory register a stack id = 0 using a dictionary which contains the needed elements to build base protocol messages (CER/A, DWR/A, DPR/A, STR/A, etc.)", ANNA_FILE_LOCATION);
+ }
+ else {
+ a_baseProtocolDictionary = d;
+ }
for(anna::xml::Node::const_child_iterator it = servicesNode->child_begin(); it != servicesNode->child_end(); it++) {
std::string nodeName = (*it)->getName();
if(nodeName == "node") {
// Input data:
originRealm = (*it)->getAttribute("originRealm");
- appId = (*it)->getAttribute("applicationId");
originHost = (*it)->getAttribute("originHost", false /* no exception */);
cer = (*it)->getAttribute("cer", false /* no exception */);
dwr = (*it)->getAttribute("dwr", false /* no exception */);
burstLog = (*it)->getAttribute("burstLog", false /* no exception */); // (yes | no)
// Basic checkings:
- if (stackEngine.getDictionary(appId->getIntegerValue()) == NULL) {
- std::string msg = "Cannot found a registered stack id with the value of applicationId provided: "; msg += appId->getValue();
- throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
- }
realm_nodes_it nodeIt = a_nodes.find(originRealm->getValue());
if (nodeIt != a_nodes.end()) {
std::string msg = "Already registered node name (Origin-Realm): "; msg += originRealm->getValue();
}
// Create new Node instance /////////////////////////////////////////////////////////////////
- a_workingNode = new RealmNode(originRealm->getValue(), appId->getIntegerValue(), a_codecEngine);
+ a_workingNode = new RealmNode(originRealm->getValue(), a_codecEngine, a_baseProtocolDictionary);
MyDiameterEngine *commEngine = a_workingNode->getMyDiameterEngine();
/////////////////////////////////////////////////////////////////////////////////////////////
msg += "')";
anna::Logger::notice(msg, ANNA_FILE_LOCATION);
);
+
// Operation:
std::string line;
std::string response_content;
ex.trace();
}
- out_file << response_content;
+ out_file << response_content << "\n";
}
in_file.close();
result += "\n";
result += "\nStart the launcher process without arguments in order to see all the startup configuration";
result += "\n posibilities, many of which could be modified on the air through the management interface";
- result += "\n (we will talk later about this great feature). Some of the more common parameters are:";
+ result += "\n (we will talk later about this great feature). There is only one mandatory parameter which";
+ result += "\n is the services definition: --services <services xml file>. You must follow the dtd schema";
+ result += "\n to build a valid services xml file. Some basic examples are:";
+ result += "\n";
+ result += "\nClient configuration:";
+ result += "\n";
+ result += "\n<services>";
+ result += "\n <!-- Stacks -->";
+ result += "\n <stack id=\"0\" dictionary=\"dictionary.xml\"/>";
result += "\n";
- result += "\nAs mandatory, the stacks enabled given through the applicationId and the xml dictionary:";
- result += "\n --stacks <appid1,dictionary1#appid2,dictionary2#...#appidN,dictionaryN>";
+ result += "\n <!-- Nodes -->";
+ result += "\n <node originRealm=\"ADML-client\" entity=\"localhost:3868\"/>";
+ result += "\n</services>";
result += "\n";
- result += "\nActing as a diameter server (accepting i.e. 10 connections), you would have:";
- result += "\n --diameterServer localhost:3868 --diameterServerSessions 10 --entityServerSessions 0";
+ result += "\nServer configuration:";
result += "\n";
- result += "\nActing as a diameter client (launching i.e. 10 connections to each entity server), you would have:";
- result += "\n --entity 192.168.12.11:3868,192.168.12.21:3868 --entityServerSessions 10 --diameterServerSessions 0";
+ result += "\n<services>";
+ result += "\n <!-- Stacks -->";
+ result += "\n <stack id=\"0\" dictionary=\"dictionary.xml\"/>";
+ result += "\n";
+ result += "\n <!-- Nodes -->";
+ result += "\n <node originRealm=\"ADML-server\" diameterServer=\"localhost:3868\"/>";
+ result += "\n</services>";
result += "\n";
result += "\nIf you act as a proxy or a translation agent, you need to combine both former setups, and probably";
result += "\n will need to program the answers to be replied through the operations interface. To balance the";
result += "\n traffic at your client side you shall use '--balance' and '--sessionBasedModelsClientSocketSelection'";
- result += "\n arguments in order to define the balancing behaviour.";
+ result += "\n arguments in order to define the balancing behaviour. To make hybrid setups you only must mix the realms:";
result += "\n";
- result += "\nThe process builds automatically CER and DWR messages as a client, but you could specify your own";
- result += "\n customized ones using '--cer <xml message file>' and '--dwr <xml message file>'.";
- result += "\nThe process builds automatically CEA and DWA messages as a server, but you could program your own";
- result += "\n customized ones using operations interface.";
+ result += "\nClient and server configuration:";
+ result += "\n";
+ result += "\n<services>";
+ result += "\n <!-- Stacks -->";
+ result += "\n <stack id=\"16777236\" dictionary=\"dictionary_Rx.xml\"/>";
+ result += "\n <stack id=\"16777238\" dictionary=\"dictionary_Gx.xml\"/>";
+ result += "\n <stack id=\"0\" dictionary=\"dictionary_base.xml\"/>";
result += "\n";
+ result += "\n <!-- Nodes -->";
+ result += "\n <node originRealm=\"ADML-Rx-client\" entity=\"localhost:3868\" cer=\"cer_Rx.xml\"/>";
+ result += "\n <node originRealm=\"ADML-Gx-client\" entity=\"localhost:3868\" cer=\"cer_Gx.xml\"/>";
+ result += "\n</services>";
+ result += "\n";
+ result += "\n";
+ result += "\nThe process builds automatically CER and DWR messages as a client, but you could specify your own";
+ result += "\n as shown in the hybrid former example. Note that the base protocol stack must be registered because";
+ result += "\n the configuration corresponds to a multistack process which change the stack using the application-id";
+ result += "\n processed (0 in the case of base protocol messages: CER, CEA, DWR, DWA, DPR, DPA).";
result += "\n";
result += "\nDYNAMIC OPERATIONS";
result += "\n------------------";
result += "\n";
result += "\n--------------------------------------------------------------------------------------- General purpose";
result += "\n";
- result += "\nhelp This help. Startup information-level traces also dump this help.";
+ result += "\nhelp This help.";
result += "\n";
result += "\n---------------------------------------------------------------------------------------- Node selection";
result += "\n";
result += "\nnode[|<name>] Select current working node by mean the registered name.";
result += "\n All the subsequent operations will be referred to this node.";
- result += "\n Without argument, the current node is dumped on stdout.";
+ result += "\n Without argument, the current node information is retrieved.";
result += "\n";
result += "\n------------------------------------------------------------------------------------ Parsing operations";
result += "\n";
result += "\n interface.";
result += "\n";
result += "\n";
+
return result;
}
void Launcher::eventOperation(const std::string &operation, std::string &response_content) throw(anna::RuntimeException) {
LOGMETHOD(anna::TraceMethod tm("Launcher", "eventOperation", ANNA_FILE_LOCATION));
+ if (operation == "") return; // ignore
+
CommandLine& cl(anna::CommandLine::instantiate());
TestManager &testManager = TestManager::instantiate();
LOGDEBUG(anna::Logger::debug(operation, ANNA_FILE_LOCATION));
// Default response:
response_content = "Operation processed with exception (see traces): ";
response_content += operation;
- response_content += "\n";
- std::string result_msg = "";
- anna::DataBlock db_aux(true);
+ std::string opt_response_content = ""; // aditional response content
+ anna::DataBlock db_aux(true);
///////////////////////////////////////////////////////////////////
// Simple operations without arguments:
// Help:
if(operation == "help") {
- std::string s_help = help();
- std::cout << s_help << std::endl;
- LOGINFORMATION(anna::Logger::information(s_help, ANNA_FILE_LOCATION));
- response_content = "Help dumped on stdout and information-level traces\n";
+ response_content = help();
return;
}
if(operation == "collect") {
resetCounters();
resetStatistics();
- response_content = "All process counters & statistic information have been reset\n";
+ response_content = "All process counters & statistic information have been reset";
return;
}
// Counters dump on demand:
if(operation == "forceCountersRecord") {
forceCountersRecord();
- response_content = "Current counters have been dump to disk\n";
+ response_content = "Current counters have been dump to disk";
return;
}
if(opType == "context") {
std::string contextFile = ((numParams == 1) ? param1 : anna::functions::asString("/var/tmp/anna.context.%05d", getPid()));
writeContext(contextFile);
- response_content = anna::functions::asString("Context dumped on file '%s'\n", contextFile.c_str());
+ response_content = anna::functions::asString("Context dumped on file '%s'", contextFile.c_str());
return;
}
}
catch(anna::RuntimeException &ex) {
ex.trace();
- response_content = anna::functions::asString("Loaded services from file '%s' with some problems (ignored ones)\n", servicesFile.c_str());
+ response_content = anna::functions::asString("Loaded services from file '%s' with some problems (ignored ones)", servicesFile.c_str());
return;
}
- response_content = anna::functions::asString("Loaded services from file '%s'\n", servicesFile.c_str());
+ response_content = anna::functions::asString("Loaded services from file '%s'", servicesFile.c_str());
return;
}
// Realm switch:
if(opType == "node") {
if (param1 != "") {
- if (setWorkingNode(param1)) response_content = anna::functions::asString("Current node is now '%s'\n", param1.c_str());
+ if (setWorkingNode(param1)) response_content = anna::functions::asString("Current node is now '%s'", param1.c_str());
}
else {
- std::cout << getWorkingNode()->asXMLString() << std::endl;
+ response_content = getWorkingNode()->asXMLString();
}
return;
}
if(opType == "show") commEngine->findClientSession(key)->show();
- if(opType == "hidden") result_msg = commEngine->findClientSession(key)->hidden() ? "true" : "false";
+ if(opType == "hidden") opt_response_content = commEngine->findClientSession(key)->hidden() ? "true" : "false";
- if(opType == "shown") result_msg = commEngine->findClientSession(key)->shown() ? "true" : "false";
+ if(opType == "shown") opt_response_content = commEngine->findClientSession(key)->shown() ? "true" : "false";
} else {
std::string address;
int port;
if(opType == "show") commEngine->findServer(address, port)->show();
- if(opType == "hidden") result_msg = commEngine->findServer(address, port)->hidden() ? "true" : "false";
+ if(opType == "hidden") opt_response_content = commEngine->findServer(address, port)->hidden() ? "true" : "false";
- if(opType == "shown") result_msg = commEngine->findServer(address, port)->shown() ? "true" : "false";
+ if(opType == "shown") opt_response_content = commEngine->findServer(address, port)->shown() ? "true" : "false";
}
} else {
if(opType == "hide") entity->hide();
if(opType == "show") entity->show();
- if(opType == "hidden") result_msg = entity->hidden() ? "true" : "false";
+ if(opType == "hidden") opt_response_content = entity->hidden() ? "true" : "false";
- if(opType == "shown") result_msg = entity->shown() ? "true" : "false";
+ if(opType == "shown") opt_response_content = entity->shown() ? "true" : "false";
}
} else if((opType == "sendxml") || (opType == "sendxml2e") || (opType == "sendhex") || (opType == "sendhex2e")) {
if(!entity) throw anna::RuntimeException("No entity configured to send the message", ANNA_FILE_LOCATION);
// burst|look|<order> Show programmed burst message for order provided, current when missing.
if(param1 == "clear") {
- result_msg = "removed ";
- result_msg += anna::functions::asString(getWorkingNode()->clearBurst());
- result_msg += " elements";
+ opt_response_content = "removed ";
+ opt_response_content += anna::functions::asString(getWorkingNode()->clearBurst());
+ opt_response_content += " elements";
} else if(param1 == "load") {
if(param2 == "") throw anna::RuntimeException("Missing xml path file for burst load operation", ANNA_FILE_LOCATION);
try { codecMsg.valid(); } catch(anna::RuntimeException &ex) { ex.trace(); } // at least we need to see validation errors although it will continue loading (see validation mode configured in launcher)
int position = getWorkingNode()->loadBurstMessage(codecMsg.code());
- result_msg = "loaded '";
- result_msg += param2;
- result_msg += "' file into burst list position ";
- result_msg += anna::functions::asString(position);
+ opt_response_content = "loaded '";
+ opt_response_content += param2;
+ opt_response_content += "' file into burst list position ";
+ opt_response_content += anna::functions::asString(position);
} else if(param1 == "start") {
if(param2 == "") throw anna::RuntimeException("Missing initial load for burst start operation", ANNA_FILE_LOCATION);
int processed = getWorkingNode()->startBurst(initialLoad);
if(processed > 0) {
- result_msg = "initial load completed for ";
- result_msg += anna::functions::entriesAsString(processed, "message");
+ opt_response_content = "initial load completed for ";
+ opt_response_content += anna::functions::entriesAsString(processed, "message");
}
} else if(param1 == "push") {
if(param2 == "") throw anna::RuntimeException("Missing load amount for burst push operation", ANNA_FILE_LOCATION);
int pushed = getWorkingNode()->pushBurst(atoi(param2.c_str()));
if(pushed > 0) {
- result_msg = "pushed ";
- result_msg += anna::functions::entriesAsString(pushed, "message");
+ opt_response_content = "pushed ";
+ opt_response_content += anna::functions::entriesAsString(pushed, "message");
}
} else if(param1 == "pop") {
if(param2 == "") throw anna::RuntimeException("Missing amount for burst pop operation", ANNA_FILE_LOCATION);
int popped = getWorkingNode()->popBurst(releaseLoad);
if(popped > 0) {
- result_msg = "burst popped for ";
- result_msg += anna::functions::entriesAsString(popped, "message");
+ opt_response_content = "burst popped for ";
+ opt_response_content += anna::functions::entriesAsString(popped, "message");
}
} else if(param1 == "stop") {
int left = getWorkingNode()->stopBurst();
if(left != -1) {
- result_msg += anna::functions::entriesAsString(left, "message");
- result_msg += " left to the end of the cycle";
+ opt_response_content += anna::functions::entriesAsString(left, "message");
+ opt_response_content += " left to the end of the cycle";
}
} else if(param1 == "repeat") {
if(param2 == "") param2 = "yes";
bool repeat = (param2 == "yes");
getWorkingNode()->repeatBurst(repeat);
- result_msg += (repeat ? "repeat enabled" : "repeat disabled");
+ opt_response_content += (repeat ? "repeat enabled" : "repeat disabled");
} else if(param1 == "send") {
if(param2 == "") throw anna::RuntimeException("Missing amount for burst send operation", ANNA_FILE_LOCATION);
int sent = getWorkingNode()->sendBurst(atoi(param2.c_str()));
if(sent > 0) {
- result_msg = "sent ";
- result_msg += anna::functions::entriesAsString(sent, "message");
+ opt_response_content = "sent ";
+ opt_response_content += anna::functions::entriesAsString(sent, "message");
}
} else if(param1 == "goto") {
if(param2 == "") throw anna::RuntimeException("Missing order position for burst goto operation", ANNA_FILE_LOCATION);
- result_msg = getWorkingNode()->gotoBurst(atoi(param2.c_str()));
+ opt_response_content = getWorkingNode()->gotoBurst(atoi(param2.c_str()));
} else if(param1 == "look") {
int order = ((param2 != "") ? atoi(param2.c_str()) : -1);
- result_msg = "\n\n";
- result_msg += getWorkingNode()->lookBurst(order);
+ opt_response_content = "\n\n";
+ opt_response_content += getWorkingNode()->lookBurst(order);
} else {
throw anna::RuntimeException("Wrong body content format on HTTP Request for 'burst' operation (unexpected action parameter). See help", ANNA_FILE_LOCATION);
}
bool success = ((param2 != "") ? testManager.configureTTPS(atoi(param2.c_str())) : false);
if (success) {
- result_msg = "assigned new test launch rate to ";
- result_msg += anna::functions::asString(atoi(param2.c_str()));
- result_msg += " events per second";
+ opt_response_content = "assigned new test launch rate to ";
+ opt_response_content += anna::functions::asString(atoi(param2.c_str()));
+ opt_response_content += " events per second";
}
else {
- result_msg += "unable to configure the test rate provided";
+ opt_response_content += "unable to configure the test rate provided";
}
}
else if(param1 == "ip-limit") {
if (param2 != "") {
limit = atoi(param2.c_str());
testManager.setInProgressLimit(limit);
- result_msg = "new in-progress limit: ";
- result_msg += (limit != UINT_MAX) ? anna::functions::asString(limit) : "<no limit>";
+ opt_response_content = "new in-progress limit: ";
+ opt_response_content += (limit != UINT_MAX) ? anna::functions::asString(limit) : "<no limit>";
}
else {
- result_msg = "in-progress limit amount: ";
+ opt_response_content = "in-progress limit amount: ";
limit = testManager.getInProgressLimit();
- result_msg += (limit != UINT_MAX) ? anna::functions::asString(limit) : "<no limit>";
- result_msg += "; currently there are ";
- result_msg += anna::functions::asString(testManager.getInProgressCount());
- result_msg += " test cases running";
+ opt_response_content += (limit != UINT_MAX) ? anna::functions::asString(limit) : "<no limit>";
+ opt_response_content += "; currently there are ";
+ opt_response_content += anna::functions::asString(testManager.getInProgressCount());
+ opt_response_content += " test cases running";
}
}
else if(param1 == "repeat") {
if(param2 == "") param2 = "yes";
testManager.setPoolRepeat((param2 == "yes"));
- result_msg += (testManager.getPoolRepeat() ? "repeat enabled" : "repeat disabled");
+ opt_response_content += (testManager.getPoolRepeat() ? "repeat enabled" : "repeat disabled");
}
else if(param1 == "report") {
if (numParams > 2)
if(param2 == "") param2 = "yes";
testManager.setDumpReports((param2 == "yes"));
- result_msg += (testManager.getDumpReports() ? "report enabled" : "report disabled");
+ opt_response_content += (testManager.getDumpReports() ? "report enabled" : "report disabled");
}
else if(param1 == "goto") {
if (numParams > 2)
if(param2 == "") throw anna::RuntimeException("Missing id for test goto operation", ANNA_FILE_LOCATION);
int id = atoi(param2.c_str());
if (testManager.gotoTestCase(id)) {
- result_msg = "position updated for id provided (";
+ opt_response_content = "position updated for id provided (";
}
else {
- result_msg = "cannot found test id (";
+ opt_response_content = "cannot found test id (";
}
- result_msg += anna::functions::asString(id);
- result_msg += ")";
+ opt_response_content += anna::functions::asString(id);
+ opt_response_content += ")";
}
else if(param1 == "look") {
if (numParams > 2)
TestCase *testCase = testManager.findTestCase(id);
if (testCase) {
- result_msg = "\n\n";
- result_msg += testCase->asXMLString();
+ response_content = testCase->asXMLString();
+ return;
}
else {
if (id == -1) {
- result_msg = "no current test case detected (testing started ?)";
+ opt_response_content = "no current test case detected (testing started ?)";
}
else {
- result_msg = "cannot found test id (";
- result_msg += anna::functions::asString(id);
- result_msg += ")";
+ opt_response_content = "cannot found test id (";
+ opt_response_content += anna::functions::asString(id);
+ opt_response_content += ")";
}
}
}
if (testCase) {
bool done = testCase->reset((param2 == "hard") ? true:false);
- result_msg = "test ";
- result_msg += param2;
- result_msg += " reset for id ";
- result_msg += anna::functions::asString(id);
- result_msg += done ? ": done": ": not done";
+ opt_response_content = "test ";
+ opt_response_content += param2;
+ opt_response_content += " reset for id ";
+ opt_response_content += anna::functions::asString(id);
+ opt_response_content += done ? ": done": ": not done";
}
else {
if (id == -1) {
bool anyReset = testManager.resetPool((param2 == "hard") ? true:false);
- result_msg = "reset have been sent to all programmed tests: "; result_msg += anyReset ? "some/all was actually reset" : "nothing was reset";
+ opt_response_content = "reset have been sent to all programmed tests: "; opt_response_content += anyReset ? "some/all was actually reset" : "nothing was reset";
}
else {
- result_msg = "cannot found test id (";
- result_msg += anna::functions::asString(id);
- result_msg += ")";
+ opt_response_content = "cannot found test id (";
+ opt_response_content += anna::functions::asString(id);
+ opt_response_content += ")";
}
}
}
throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
if (testManager.clearPool()) {
- result_msg = "all the programmed test cases have been dropped";
+ opt_response_content = "all the programmed test cases have been dropped";
}
else {
- result_msg = "there are not programmed test cases to be removed";
+ opt_response_content = "there are not programmed test cases to be removed";
}
}
else {
else {
throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
}
-
- result_msg = "new step added to test id ";
- result_msg += anna::functions::asString(id);
}
} else if((opType == "sendxml2c") || (opType == "sendhex2c")) {
}
} else if(opType == "loadxml") {
codecMsg.loadXML(param1);
- std::string xmlString = codecMsg.asXMLString();
- std::cout << xmlString << std::endl;
+ response_content = codecMsg.asXMLString();
+ return;
} else if(opType == "diameterServerSessions") {
int diameterServerSessions = atoi(param1.c_str());
throw anna::RuntimeException("Operation not applicable (no own diameter server has been configured)", ANNA_FILE_LOCATION);
if(param1 == "") { // programmed answers FIFO's to stdout
- std::cout << localServer->getReactingAnswers()->asString("ANSWERS TO CLIENT") << std::endl;
- response_content = "Programmed answers dumped on stdout\n";
+ response_content = localServer->getReactingAnswers()->asString("ANSWERS TO CLIENT");
return;
} else if (param1 == "rotate") {
localServer->getReactingAnswers()->rotate(true);
throw anna::RuntimeException("Operation not applicable (no diameter entity has been configured)", ANNA_FILE_LOCATION);
if(param1 == "") { // programmed answers FIFO's to stdout
- std::cout << entity->getReactingAnswers()->asString("ANSWERS TO ENTITY") << std::endl;
- response_content = "Programmed answers dumped on stdout\n";
+ response_content = entity->getReactingAnswers()->asString("ANSWERS TO ENTITY");
return;
} else if (param1 == "rotate") {
entity->getReactingAnswers()->rotate(true);
}
// HTTP response
- response_content = "Operation correctly processed: "; response_content += operation; response_content += " => ";
- response_content += result_msg;
+ response_content = "Operation correctly processed: "; response_content += operation;
+ if (opt_response_content != "") {
+ response_content += " => ";
+ response_content += opt_response_content;
+ }
}
anna::xml::Node* Launcher::asXML(anna::xml::Node* parent) const
// Core engines:
MyCommunicator *a_communicator;
anna::diameter::codec::Engine *a_codecEngine;
+ anna::diameter::stack::Dictionary *a_baseProtocolDictionary;
anna::timex::Engine* a_timeEngine;
MyCounterRecorder *a_counterRecorder;
anna::Millisecond a_admlMinResolution;
void startServices() throw(anna::RuntimeException);
anna::diameter::codec::Engine *getCodecEngine() const throw() { return a_codecEngine; }
+ //anna::diameter::stack::Dictionary *getBaseProtocolDictionary() const throw() { return a_baseProtocolDictionary; }
RealmNode *getWorkingNode() const throw(anna::RuntimeException); // management operations working node
bool setWorkingNode(const std::string &name) throw(); // we could update ignoreFlagsOnValidation/integrationAndDebugging over the global codec engine
// but finally, that configuration issues will be global to the process.
#include <MyLocalServer.hpp>
+namespace anna {
+ namespace diameter {
+ namespace stack {
+ class Dictionary;
+ }
+ }
+}
+
class MyDiameterEngine : public anna::diameter::comm::Engine {
public:
- MyDiameterEngine(const char *className = "MyDiameterEngine") : Engine(className, NULL /* we will assign the base protocol codec engine later*/) {;}
+ MyDiameterEngine(const char *className, const anna::diameter::stack::Dictionary *baseProtocolDictionary) : Engine(className, baseProtocolDictionary) {;}
// Default implementation is enough
// void readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) throw() {;} // DPA is not replied
#include <RealmNode.hpp>
#include <MyDiameterEngine.hpp>
+namespace anna {
+ namespace diameter {
+ namespace stack {
+ class Dictionary;
+ }
+ }
+}
-
-RealmNode::RealmNode(const std::string &originRealm, unsigned int applicationId, anna::diameter::codec::Engine *codecEngine) :
- a_originRealm(originRealm), a_applicationId(applicationId), a_codecEngine(codecEngine) {
+RealmNode::RealmNode(const std::string &originRealm, anna::diameter::codec::Engine *codecEngine, const anna::diameter::stack::Dictionary *baseProtocolDictionary) :
+ a_originRealm(originRealm), a_codecEngine(codecEngine) {
std::string commEngineName = a_originRealm + "_DiameterCommEngine";
- a_commEngine = new MyDiameterEngine(commEngineName.c_str());
+ a_commEngine = new MyDiameterEngine(commEngineName.c_str(), baseProtocolDictionary);
a_commEngine->setAutoBind(false); // allow to create client-sessions without binding them, in order to set timeouts.
- a_commEngine->setBaseProtocolCodecEngine(getCodecEngine());
a_logFile = "";
a_burstLogFile = "";
}
-void RealmNode::createEntity(const std::string &entityRepresentation, const anna::Millisecond &bindTimeout, const anna::Millisecond &applicationTimeout) throw() {
+void RealmNode::createEntity(const std::string &entityRepresentation, const anna::Millisecond &bindTimeout, const anna::Millisecond &applicationTimeout) throw(anna::RuntimeException) {
anna::socket_v servers = anna::functions::getSocketVectorFromString(entityRepresentation);
std::string entityDescription = "Launcher diameter entity for "; entityDescription += a_originRealm;
anna::xml::Node* result = parent->createChild("RealmNode");
result->createAttribute("OriginRealm", a_originRealm);
- result->createAttribute("ApplicationId", a_applicationId);
result->createAttribute("LogFile", a_logFile);
result->createAttribute("SplitLog", a_splitLog ? "yes" : "no");
result->createAttribute("DetailedLog", a_detailedLog ? "yes" : "no");
namespace codec {
class Engine;
}
+ namespace stack {
+ class Dictionary;
+ }
namespace comm {
class Message;
}
// main
std::string a_originRealm;
- unsigned int a_applicationId;
// Timming
anna::Millisecond a_allowedInactivityTime;
int a_burstPopCounter;
public:
- RealmNode(const std::string &originRealm, unsigned int applicationId, anna::diameter::codec::Engine *codecEngine);
+ RealmNode(const std::string &originRealm, anna::diameter::codec::Engine *codecEngine, const anna::diameter::stack::Dictionary *baseProtocolDictionary);
~RealmNode() {;}
// Core resources:
MyDiameterEngine* getMyDiameterEngine() const throw() { return a_commEngine; }
anna::diameter::codec::Engine *getCodecEngine() const throw() { return a_codecEngine; }
- void createEntity(const std::string &entityRepresentation, const anna::Millisecond &bindTimeout, const anna::Millisecond &applicationTimeout) throw();
+ void createEntity(const std::string &entityRepresentation, const anna::Millisecond &bindTimeout, const anna::Millisecond &applicationTimeout) throw(anna::RuntimeException);
MyDiameterEntity *getEntity() const throw() { return a_entity; }
void startDiameterServer(const std::string &serverRepresentation, int sessions, const anna::Millisecond &inactivityTimeout) throw(anna::RuntimeException);
MyLocalServer* getDiameterServer() throw() { return a_diameterServer; }
# Send operation:
[ "$1" = "" ] && _exit "Usage: $0 <operation string>; i.e.: $0 help"
-echo $1 > sigusr2.tasks.input
+echo $1 > sigusr2.in
kill -s SIGUSR2 $PID
-#echo "You could see results on '`pwd`/sigusr2.tasks.output' file."
-cat `pwd`/sigusr2.tasks.output 2>/dev/null
+#echo "You could see results on '`pwd`/sigusr2.out' file."
+cat `pwd`/sigusr2.out 2>/dev/null
echo
<stack id="0" dictionary="dictionary.xml"/>
<!-- Nodes -->
- <node originRealm="ADML-balancer" applicationId="0" entity="192.168.12.11:3868,192.168.12.21:3868" diameterServer="localhost:3868" balance="yes"/>
- <!-- <node originRealm="ADML-proxy" applicationId="0" entity="localhost:3868" diameterServer="localhost:3870"/> -->
+ <node originRealm="ADML-balancer" entity="192.168.12.11:3868,192.168.12.21:3868" diameterServer="localhost:3868" balance="yes"/>
+ <!-- <node originRealm="ADML-proxy" entity="localhost:3868" diameterServer="localhost:3870"/> -->
</services>
<stack id="0" dictionary="dictionary.xml"/>
<!-- Nodes -->
- <node originRealm="ADML-client" applicationId="0" entity="localhost:3868"/>
+ <node originRealm="ADML-client" entity="localhost:3868"/>
</services>
<stack id="0" dictionary="dictionary.xml"/>
<!-- Nodes -->
- <node originRealm="ADML-dummy" applicationId="0"/>
+ <node originRealm="ADML-dummy"/>
</services>
<stack id="16777236" dictionary="dictionaryRx.xml"/>
<stack id="16777238" dictionary="dictionaryGx.xml"/>
<stack id="16777302" dictionary="dictionarySy.xml"/>
+ <stack id="0" dictionary="dictionaryBase.xml"/>
<!-- Nodes -->
- <node originRealm="afNode" applicationId="16777236" entity="192.168.12.11:3868,192.168.12.21:3868"/>
- <node originRealm="ggsnNode" applicationId="16777238" entity="192.168.12.11:3868,192.168.12.21:3868"/>
- <node originRealm="ggsn2Node" applicationId="16777238" entity="192.168.12.11:3868,192.168.12.21:3868"/>
- <node originRealm="ocsNode" applicationId="16777302" entity="192.168.12.11:3868,192.168.12.21:3868"/>
+ <node originRealm="afNode" entity="192.168.12.11:3868,192.168.12.21:3868"/>
+ <node originRealm="ggsnNode" entity="192.168.12.11:3868,192.168.12.21:3868"/>
+ <node originRealm="ggsn2Node" entity="192.168.12.11:3868,192.168.12.21:3868"/>
+ <node originRealm="ocsNode" entity="192.168.12.11:3868,192.168.12.21:3868"/>
</services>
<stack id="0" dictionary="dictionary.xml"/>
<!-- Nodes -->
- <node originRealm="ADML-server" applicationId="0" diameterServer="localhost:3868"/>
+ <node originRealm="ADML-server" diameterServer="localhost:3868"/>
</services>
<!--
Stack record
- id: Normally the id corresponds to the Application-Id for which the dictionary provided is designed.
+ id: Normally the id corresponds to the Application-Id for which the dictionary provided is designed
(in multistack applications, it shall be mandatory respect such association to know the stack used
for processed messages).
dictionary: Path to the dictionary file
-->
<!ELEMENT node EMPTY>
-<!ATTLIST node originRealm CDATA #REQUIRED applicationId CDATA #REQUIRED originHost CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>
+<!ATTLIST node originRealm CDATA #REQUIRED originHost CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>
<!--
Node record
originRealm: Node identifier (Origin-Realm name).
- applicationId: The Application-Id provided must exists as a registered 'stack id'.
originHost: Diameter application host name (system name). If missing, process sets o.s. hostname
Note that if you have two or more realms, the names must be different.
cer: User defined CER path file to be encoded to establish diameter connections.
TestStepWait *result;
for (std::vector<TestStep*>::const_iterator it = a_stepsIt /* current */; it != a_steps.end(); it++) {
if ((*it)->getType() != TestStep::Type::Wait) continue;
+ if ((*it)->isCompleted()) continue;
result = (TestStepWait*)(*it);
if ((result->getCondition().receivedFromEntity() == waitFromEntity) && (result->fulfilled(message)))
return result;
return false;
}
+ // Synchronous sendings per tick:
int count = a_synchronousAmount;
while (count > 0) {
if (!nextTestCase()) return false; // stop the clock
}
}
- // Hard reset, because normally a cycle takes more time that a single test case lifetime. We can consider that never
- // going to break a in-progress test case due to cycle repeat
+ // Soft reset to initialize already finished (in previous cycle) test cases:
a_currentTestIt->second->reset(false);
// Process test case:
LOGDEBUG(anna::Logger::debug(anna::functions::asString("Processing test case id = %llu, currently '%s' state", a_currentTestIt->first, TestCase::asText(a_currentTestIt->second->getState())), ANNA_FILE_LOCATION));
if (a_currentTestIt->second->getState() != TestCase::State::InProgress) {
a_currentTestIt->second->process();
- return true;
+ return true; // is not probably to reach still In-Progress test cases from previous cycles due to the whole
+ // time for complete the test cases pool regarding the single test case lifetime. You shouldn't
+ // forget to programm a test case timeout with a reasonable value
}
}
}
-TestCase *TestManager::getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw(anna::RuntimeException) {
- sessionId = anna::diameter::helpers::base::functions::getSessionId(message);
+TestCase *TestManager::getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw() {
+ try {
+ sessionId = anna::diameter::helpers::base::functions::getSessionId(message);
+ }
+ catch (anna::RuntimeException &ex) {
+ //ex.trace();
+ LOGWARNING(anna::Logger::warning("Cannot get the Session-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
+ return NULL;
+ }
std::map<std::string /* session id's */, TestCase*>::const_iterator sessionIdIt = a_sessionIdTestCaseMap.find(sessionId);
if (sessionIdIt != a_sessionIdTestCaseMap.end())
return sessionIdIt->second;
TestCase *getTestCase(unsigned int id) throw(); // creates/reuses a test case
// Main logic
- TestCase *getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw(anna::RuntimeException);
+ TestCase *getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw();
void receiveMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException);
void receiveMessage(const anna::DataBlock &message, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException);
}
bool TestStep::execute() throw() {
- LOGDEBUG(anna::Logger::debug(anna::functions::asString("EXECUTING %s for Test Case %llu (%p) (%p)", asText(a_type), a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION));
+ LOGDEBUG(anna::Logger::debug(anna::functions::asString("EXECUTING %s (step number %d) for Test Case %llu (%p) (%p)", asText(a_type), a_number, a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION));
setBeginTimestamp(anna::functions::millisecond());
return do_execute();
}
void TestStep::complete() throw() {
- LOGDEBUG(anna::Logger::debug(anna::functions::asString("COMPLETE %s for Test Case %llu (%p) (%p)", asText(a_type), a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION));
+ LOGDEBUG(anna::Logger::debug(anna::functions::asString("COMPLETE %s (step number %d) for Test Case %llu (%p) (%p)", asText(a_type), a_number, a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION));
a_completed = true;
setEndTimestamp(anna::functions::millisecond());
do_complete();
}
void TestStep::reset() throw() {
- LOGDEBUG(anna::Logger::debug(anna::functions::asString("RESET %s for Test Case %llu (%p) (%p)", asText(a_type), a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION));
+ LOGDEBUG(anna::Logger::debug(anna::functions::asString("RESET %s (step number %d) for Test Case %llu (%p) (%p)", asText(a_type), a_number, a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION));
// type and testCase kept
a_completed = false;
a_beginTimestamp = 0;
bool success = false;
std::string failReason, s_warn;
- // Update sequence for answers:
- if (a_waitForRequestStepNumber != -1) { // is an answer: try to copy sequence information; alert about Session-Id discrepance
- // Request which was received:
- const TestStepWait *tsw = (const TestStepWait*)(a_testCase->getStep(a_waitForRequestStepNumber));
- const anna::DataBlock &request = tsw->getMsgDataBlock();
- anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(request);
- anna::diameter::EndToEnd ete = anna::diameter::codec::functions::getEndToEnd(request);
- // Update sequence:
- anna::diameter::codec::functions::setHopByHop(a_message, hbh);
- anna::diameter::codec::functions::setEndToEnd(a_message, ete);
-
- // Check Session-Id for warning ...
- std::string sessionIdAnswer = anna::diameter::helpers::base::functions::getSessionId(a_message);
- std::string sessionIdRequest = anna::diameter::helpers::base::functions::getSessionId(request);
- if (sessionIdRequest != sessionIdAnswer) {
- s_warn = anna::functions::asString("Sending an answer which Session-Id (%s) is different than supposed corresponding request (%s)", sessionIdAnswer.c_str(), sessionIdRequest.c_str());
- LOGWARNING(anna::Logger::warning(s_warn, ANNA_FILE_LOCATION));
- a_testCase->addDebugSummaryHint(s_warn);
+ try {
+ // Update sequence for answers:
+ if (a_waitForRequestStepNumber != -1) { // is an answer: try to copy sequence information; alert about Session-Id discrepance
+ // Request which was received:
+ const TestStepWait *tsw = (const TestStepWait*)(a_testCase->getStep(a_waitForRequestStepNumber));
+ const anna::DataBlock &request = tsw->getMsgDataBlock();
+ anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(request);
+ anna::diameter::EndToEnd ete = anna::diameter::codec::functions::getEndToEnd(request);
+ // Update sequence:
+ anna::diameter::codec::functions::setHopByHop(a_message, hbh);
+ anna::diameter::codec::functions::setEndToEnd(a_message, ete);
+
+ // Check Session-Id for warning ...
+ std::string sessionIdAnswer = anna::diameter::helpers::base::functions::getSessionId(a_message);
+ std::string sessionIdRequest = anna::diameter::helpers::base::functions::getSessionId(request);
+ if (sessionIdRequest != sessionIdAnswer) {
+ s_warn = anna::functions::asString("Sending an answer which Session-Id (%s) is different than supposed corresponding request (%s)", sessionIdAnswer.c_str(), sessionIdRequest.c_str());
+ LOGWARNING(anna::Logger::warning(s_warn, ANNA_FILE_LOCATION));
+ a_testCase->addDebugSummaryHint(s_warn);
+ }
}
- }
- if (getType() == Type::Sendxml2e) {
- MyDiameterEntity *entity = a_realmNode->getEntity();
- if (entity) {
- try {
+ if (getType() == Type::Sendxml2e) {
+ MyDiameterEntity *entity = a_realmNode->getEntity();
+ if (entity) {
//msg->clearBody();
msg->setBody(a_message);
/* response = NULL =*/entity->send(msg);
success = true;
- } catch(anna::RuntimeException &ex) {
- ex.trace();
- failReason = ex.asString();
+ }
+ else {
+ failReason = "There is no diameter entity currently configured. Unable to send the message";
+ LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION));
}
}
- else {
- failReason = "There is no diameter entity currently configured. Unable to send the message";
- LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION));
- }
- }
- else if (getType() == Type::Sendxml2c) {
- MyLocalServer *localServer = a_realmNode->getDiameterServer();
- if (localServer) {
- try {
+ else if (getType() == Type::Sendxml2c) {
+ MyLocalServer *localServer = a_realmNode->getDiameterServer();
+ if (localServer) {
//msg->clearBody();
msg->setBody(a_message);
/* response = NULL =*/localServer->send(msg);
success = true;
- } catch(anna::RuntimeException &ex) {
- ex.trace();
- failReason = ex.asString();
+ }
+ else {
+ failReason = "There is no diameter local server currently configured. Unable to send the message";
+ LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION));
}
}
- else {
- failReason = "There is no diameter local server currently configured. Unable to send the message";
- LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION));
- }
+ } catch(anna::RuntimeException &ex) {
+ failReason = ex.asString();
}
// release msg
return success; // go next if sent was OK
}
-void TestStepSendxml::do_complete() throw() {
- next();
-}
-
void TestStepSendxml::do_reset() throw() {
a_expired = false;
//a_message.clear();
void TestStepDelay::do_complete() throw() {
a_timer = NULL;
- next();
+ next(); // next() invoked here because execute() is always false for delay and never dvance the iterator
}
void TestStepDelay::do_reset() throw() {
}
void TestStepWait::do_complete() throw() {
- next();
+ next(); // next() invoked here because execute() never do this.
}
bool TestStepWait::fulfilled(const anna::DataBlock &db/*, bool matchSessionId*/) throw() {
// virtuals
bool do_execute() throw();
- void do_complete() throw();
+ void do_complete() throw() {;}
void do_reset() throw();
anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
};
// STL
#include <map>
+#include <vector>
#include <string>
#include <algorithm>
#include <anna/app/Component.hpp>
#include <anna/core/util/Recycler.hpp>
+#include <anna/diameter/codec/Engine.hpp>
#include <anna/diameter.comm/Server.hpp>
#include <anna/diameter.comm/ServerSession.hpp>
#include <anna/config/defines.hpp>
class Engine;
}
-namespace comm {
+namespace stack {
+class Dictionary;
+}
+namespace comm {
+class Response;
class Entity;
class Server;
class LocalServer;
class Engine : public anna::app::Component {
public:
- /**
- * Sets the base protocol codec engine used internally
- *
- * @param baseProtocolCodecEngine This will be used internally during invokation of @readCEA, @readDPA and @readDWA on servers,
- * and also used during base protocol messages tracing (if debug traces are enabled). You could provide NULL but you must be
- * sure that neither of the former situations are going to happen or an exception will be thrown. It is recommended to register
- * a codec engine pointed to a base protocol stack (you can use the files 'avps_ietf.xml' and 'commands_baseProtocol.xml'
- * located on ANNA suite project under 'source/diameter/stack/setups', or perhaps you can create your own dictionary from
- * file or directly with the dictionay creation API. Even you can use a greater dictionary (application dictionary), the
- * only condition is that must contain the resources to build base protocol messages. You could provide this in engine constructor,
- * but don't forget it.
- */
- void setBaseProtocolCodecEngine(codec::Engine *baseProtocolCodecEngine) throw() { a_baseProtocolCodecEngine = baseProtocolCodecEngine; }
-
- /**
- * Gets the base protocol codec engine used internally
- *
- * @see setBaseProtocolCodecEngine
- */
- codec::Engine * getBaseProtocolCodecEngine() const throw() { return a_baseProtocolCodecEngine; }
-
/**
Diameter application node realm name (used to be the site domain name).
*/
virtual void readDWA(anna::DataBlock &dwa, const anna::DataBlock & dwr) throw();
+ /**
+ * DRA basics: CER information is gathered on every server session managed by the diameter comm engine. You could send the message to a
+ * specific realm, and optionally you could restrict a host inside it. This is common for requests (answers are normally sent through
+ * the same source server session where the request was received). Exception will be thrown if not found an available server session
+ * for the Destination-Realm and/or Destination-Host provided
+ *
+ * @param destinationRealm If empty, NULL is returned, because is nonsense to specify a host out of realm context
+ * @param destinationHost If empty, no restriction is applied within the target realm node. Random delivery is applied for the available server sessions
+ *
+ * @return transactional response reference, or NULL if answer is sent
+ */
+ const Response* sendRealmHost(const Message* message, const std::string &destinationRealm, const std::string &destinationHost = "") throw(anna::RuntimeException);
+
/**
Reset engine statistics.
At the moment, only diameter servers processing time is observed.
Constructor.
@param className Component class name
- @param baseProtocolCodecEngine This will be used internally during invokation of @readCEA, @readDPA and @readDWA on servers,
- and also used during base protocol messages tracing (if debug traces are enabled). You could provide NULL but you must be
- sure that neither of the former situations are going to happen or an exception will be thrown. It is recommended to register
- a codec engine pointed to a base protocol stack (you can use the files 'avps_ietf.xml' and 'commands_baseProtocol.xml'
- located on ANNA suite project under 'source/diameter/stack/setups', or perhaps you can create your own dictionary from
- file or directly with the dictionay creation API. Even you can use a greater dictionary (application dictionary), the
- only condition is that must contain the resources to build base protocol messages. You could use @setBaseProtocolCodecEngine
- to set this reference later; don't forget it.
+ @param baseProtocolDictionary This will be used internally when calling \@readCEA, \@readDPA and \@readDWA on
+ servers, and also used during base protocol messages tracing (if debug traces are enabled). You could provide
+ NULL, but you must be sure that neither of the former situations are going to happen or an exception will be
+ thrown (using setClientCERandDWR with DataBlock arguments, expects externally encoded messages and could help).
+ It is recommended to set a base protocol dictionary loading 'source/diameter/stack/setups' dictionaries (for
+ example 'avps_ietf.xml' plus 'commands_baseProtocol.xml'), or using the dictionary creation API. The dictionary
+ could also be an application stack, the only condition is containing the resources to build base protocol messages.
*/
- Engine(const char *className, codec::Engine *baseProtocolCodecEngine);
-
+ Engine(const char *className, const stack::Dictionary *baseProtocolDictionary);
// INTERNAL CREATORS AND CLOSE METHODS
Server *createServer(Entity*, const socket_t&) throw(anna::RuntimeException);
virtual void releaseLocalServer(LocalServer*) throw() {;}
-
private:
// Internal use: tracing and readCEA/DPA/DWA
- codec::Engine *a_baseProtocolCodecEngine;
+ codec::Engine a_baseProtocolCodecEngine;
std::string a_realm;
std::string a_host;
// Integrity:
void checkEntityCollision(const socket_v &) throw(anna::RuntimeException);
+ void assertBaseProtocolHealth() throw(anna::RuntimeException); // checks the dictionary
+ // Gets the base protocol codec engine used internally.
+ // This engine is initializaed on constructor with the base protocol dictionary.
+ // The reason to not reuse any other codec engine from the application is to have this one isolated with no interference
+ // regarding configuration changes (validation depth/mode, fix mode, etc.).
+ //
+ // @return Pointer to the internal base protocol codec engine
+ codec::Engine *getBaseProtocolCodecEngine() const throw() { return const_cast<codec::Engine *>(&a_baseProtocolCodecEngine); }
+
//////////////////////////
// CLIENT FUNCTIONALITY //
//////////////////////////
const_localServer_iterator localServer_end() const throw() { return a_localServers.end(); }
static const LocalServer* localServer(const_localServer_iterator ii) throw() { return ii->second; }
- // Server sessions are managed within LocalServer (not at engine) due to dynamic cration nature
-
+ // Server sessions are managed within LocalServer (not at engine) due to dynamic creation nature
+ // Here we maintain the Destination-Realm / Destination-Host maps for DRA basics:
+ typedef std::vector<ServerSession*> server_sessions_vector_t;
+ typedef server_sessions_vector_t::const_iterator server_sessions_it_t;
+ typedef server_sessions_vector_t::iterator server_sessions_nc_it_t;
+ typedef std::map <std::string /* Destination-Host */, server_sessions_vector_t> dh_server_sessions_map_t;
+ typedef dh_server_sessions_map_t::const_iterator dh_server_sessions_it_t;
+ typedef dh_server_sessions_map_t::iterator dh_server_sessions_nc_it_t;
+ typedef std::map <std::string /* Destination-Realm */, dh_server_sessions_map_t> dr_dh_server_sessions_map_t;
+ typedef dr_dh_server_sessions_map_t::const_iterator dr_dh_server_sessions_it_t;
+ typedef dr_dh_server_sessions_map_t::iterator dr_dh_server_sessions_nc_it_t;
+ dr_dh_server_sessions_map_t a_dr_dh_server_sessions;
+ void manageDrDhServerSession(ServerSession *ss, bool register_or_desregister) throw();
friend class Session;
+ friend class ClientSession;
friend class ServerSession;
friend class ServerSocket;
friend class Server;
* this to have the commonly recommended way to choose the stack: using the Application-Id value.
*
* @warning do not activate in case of multithreaded applications.
+ * @warning must register the base protocol stack (with id = 0 = application-id) to manage base protocol messages.
* @param enable Activates/deactivates the stack selection from the Application-Id value within the message header.
- * False by default.
+ * False by default on engine construction.
*/
void selectStackWithApplicationId (bool enable) throw() { a_selectStackWithApplicationId = enable; }
Sets the message application id.
The codec engine could be configured to force a stack selection based in this field value: see #selectStackWithApplicationId.
- In multistack applications (which also shall be monothreaded), you only have to take care about how to apply this method: the thing
+ In multistack applications (in case of being monothread), you only have to take care about how to apply this method: the thing
is that you must not interleave message builds which belongs to different stacks. For example, you could think about setting the
message header for message A using stack A. Then, start to add the message header fields for a second message B using another stack B.
Following you would add the message A avps, but then, the stack is not going to be automatically changed (this is only done through this
// See project site at http://redmine.teslayout.com/projects/anna-suite //
// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
+// Standard
+#include <stdlib.h> // rand()
-#include <anna/diameter.comm/Engine.hpp>
+#include <anna/diameter.comm/Engine.hpp>
#include <anna/core/tracing/Logger.hpp>
#include <anna/core/tracing/TraceMethod.hpp>
#include <anna/xml/Node.hpp>
#include <anna/diameter/helpers/helpers.hpp>
#include <anna/diameter/codec/Message.hpp>
#include <anna/diameter/codec/Avp.hpp>
+#include <anna/diameter.comm/Response.hpp>
// STD
#include <map>
using namespace std;
-using namespace anna::diameter::comm;
+using namespace anna::diameter;
+
+namespace anna {
+ namespace diameter {
+ namespace stack {
+ class Dictionary;
+ }
+ }
+}
-Engine::Engine(const char *className, codec::Engine *baseProtocolCodecEngine) :
+comm::Engine::Engine(const char *className, const stack::Dictionary *baseProtocolDictionary) :
anna::app::Component(className),
- a_baseProtocolCodecEngine(baseProtocolCodecEngine),
a_autoBind(true),
a_availableForEntities(false),
a_availableForLocalServers(false),
// a_dwa(true),
a_watchdogPeriod(ClientSession::DefaultWatchdogPeriod),
a_maxConnectionDelay(anna::comm::ClientSocket::DefaultMaxConnectionDelay /* 200 ms*/),
- a_numberOfClientSessionsPerServer(1) {
+ a_numberOfClientSessionsPerServer(1),
+ a_baseProtocolCodecEngine((std::string("baseProtocolCodecEngine_for_") + std::string(className)).c_str())
+{
anna::diameter::sccs::activate();
a_realm = anna::functions::getDomainname();
a_host = anna::functions::getHostname();
+
+ // Internal base protocol codec engine:
+ a_baseProtocolCodecEngine.setDictionary(baseProtocolDictionary);
+ a_baseProtocolCodecEngine.setValidationMode(anna::diameter::codec::Engine::ValidationMode::Always); // default was: after decoding
}
-Server* Engine::allocateServer() throw() { return a_serversRecycler.create(); }
-void Engine::releaseServer(Server *server) throw() { a_serversRecycler.release(server); }
-ClientSession* Engine::allocateClientSession() throw() { return a_clientSessionsRecycler.create(); }
-void Engine::releaseClientSession(ClientSession *clientSession) throw() { a_clientSessionsRecycler.release(clientSession); }
+
+void comm::Engine::assertBaseProtocolHealth() throw(anna::RuntimeException) {
+ if (!getBaseProtocolCodecEngine()->getDictionary())
+ throw anna::RuntimeException("Invalid diameter::comm::Engine object: base protocol dictionary provided on constructor was NULL", ANNA_FILE_LOCATION);
+ // it would be interesting to check and identify certain base protocol elements in the dictionary ...
+ // but these things will be checked in runtime and will fail if they should.
+}
+
+
+comm::Server* comm::Engine::allocateServer() throw() { return a_serversRecycler.create(); }
+void comm::Engine::releaseServer(Server *server) throw() { a_serversRecycler.release(server); }
+comm::ClientSession* comm::Engine::allocateClientSession() throw() { return a_clientSessionsRecycler.create(); }
+void comm::Engine::releaseClientSession(ClientSession *clientSession) throw() { a_clientSessionsRecycler.release(clientSession); }
-void Engine::setClientCERandDWR(const anna::DataBlock & cer, const anna::DataBlock & dwr) throw(anna::RuntimeException) {
+void comm::Engine::setClientCERandDWR(const anna::DataBlock & cer, const anna::DataBlock & dwr) throw(anna::RuntimeException) {
if(codec::functions::getCommandId(cer) != helpers::base::COMMANDID__Capabilities_Exchange_Request) {
throw anna::RuntimeException("The message provided as 'CER' is not a Capabilities-Exchange-Request", ANNA_FILE_LOCATION);
}
a_dwr = dwr;
}
-void Engine::setClientCERandDWR(const std::string & cer, const std::string & dwr) throw(anna::RuntimeException) {
+void comm::Engine::setClientCERandDWR(const std::string & cer, const std::string & dwr) throw(anna::RuntimeException) {
- // Check for base protocol codec engine:
- if (!getBaseProtocolCodecEngine())
- throw anna::RuntimeException("Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow base protocol messages encoding, or use setClientCERandDWR(const anna::DataBlock & cer, const anna::DataBlock & dwr) which expect externally encoded messages", ANNA_FILE_LOCATION);
+ // Check for base protocol codec engine health:
+ assertBaseProtocolHealth();
// Build CER
// <CER> ::= < Diameter Header: 257, REQ >
setClientCERandDWR(diameterCER.code(), diameterDWR.code());
}
-void Engine::setWatchdogPeriod(const anna::Millisecond & wp) throw(anna::RuntimeException) {
+void comm::Engine::setWatchdogPeriod(const anna::Millisecond & wp) throw(anna::RuntimeException) {
if(wp < ClientSession::DefaultWatchdogPeriod) {
throw anna::RuntimeException(anna::functions::asString("Please set watchdog period over %s", ClientSession::DefaultWatchdogPeriod.asString().c_str()), ANNA_FILE_LOCATION);
}
a_watchdogPeriod = wp;
}
-void Engine::checkEntityCollision(const socket_v &v) throw(anna::RuntimeException) {
+void comm::Engine::checkEntityCollision(const socket_v &v) throw(anna::RuntimeException) {
socket_v::const_iterator it;
socket_v::const_iterator it_min(v.begin());
socket_v::const_iterator it_max(v.end());
throw anna::RuntimeException("diameter::comm::Engine::checkEntityCollision: Provided addresses list (sockets) must have all items different", ANNA_FILE_LOCATION);
}
-Entity* Engine::createEntity(const socket_v & socketList, const std::string &description)
+comm::Entity* comm::Engine::createEntity(const socket_v & socketList, const std::string &description)
throw(anna::RuntimeException) {
Entity* result(NULL);
anna::Guard guard(this, "anna::diameter::comm::Engine::createEntity");
}
-LocalServer *Engine::createLocalServer(const std::string & addr, int port, int maxConnections, const anna::Millisecond & allowedInactivityTime, int category, const std::string & description)
+comm::LocalServer *comm::Engine::createLocalServer(const std::string & addr, int port, int maxConnections, const anna::Millisecond & allowedInactivityTime, int category, const std::string & description)
throw(anna::RuntimeException) {
LocalServer* result(NULL);
anna::Guard guard(this, "anna::diameter::comm::Engine::createLocalServer");
}
-Entity* Engine::createEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, const std::string &description)
+comm::Entity* comm::Engine::createEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, const std::string &description)
throw(anna::RuntimeException) {
socket_v dualList;
dualList.push_back(socket_t(addr1, port1));
}
-Server* Engine::createServer(Entity *entity, const socket_t & socket)
+comm::Server* comm::Engine::createServer(Entity *entity, const socket_t & socket)
throw(anna::RuntimeException) {
Server* result(NULL);
anna::Guard guard(this, "anna::diameter::comm::Engine::createServer");
// Lohacemos privado
-ClientSession* Engine::createClientSession(Server *server, int socketId)
+comm::ClientSession* comm::Engine::createClientSession(Server *server, int socketId)
throw(anna::RuntimeException) {
ClientSession* result(NULL);
anna::Guard guard(this, "anna::diameter::comm::Engine::createClientSession");
}
-bool Engine::broadcastEntities(const Message* message) throw(anna::RuntimeException) {
+bool comm::Engine::broadcastEntities(const Message* message) throw(anna::RuntimeException) {
LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "broadcastEntities", ANNA_FILE_LOCATION));
bool allok = true;
bool ok;
return allok;
}
-bool Engine::broadcastLocalServers(const Message* message) throw(anna::RuntimeException) {
+bool comm::Engine::broadcastLocalServers(const Message* message) throw(anna::RuntimeException) {
LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "broadcastLocalServers", ANNA_FILE_LOCATION));
bool allok = true;
bool ok;
return allok;
}
-bool Engine::bind() throw(anna::RuntimeException) {
+bool comm::Engine::bind() throw(anna::RuntimeException) {
LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "bind", ANNA_FILE_LOCATION));
bool result = true; // all OK return
return result;
}
-ClientSession* Engine::findClientSession(const std::string & addr, int port, int socketId, anna::Exception::Mode::_v emode)
+comm::ClientSession* comm::Engine::findClientSession(const std::string & addr, int port, int socketId, anna::Exception::Mode::_v emode)
throw(anna::RuntimeException) {
return findClientSession(ClientSession::getKey(addr, port, socketId), emode);
}
-ClientSession* Engine::findClientSession(const std::string & key, anna::Exception::Mode::_v emode)
+comm::ClientSession* comm::Engine::findClientSession(const std::string & key, anna::Exception::Mode::_v emode)
throw(anna::RuntimeException) {
anna::Guard guard(this, "anna::diameter::comm::Engine::findClientSession");
clientSession_iterator ii = clientSession_find(key);
}
-Server* Engine::findServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
+comm::Server* comm::Engine::findServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
throw(anna::RuntimeException) {
anna::Guard guard(this, "anna::diameter::comm::Engine::findServer");
server_iterator ii = server_find(server_key(addr, port));
return NULL;
}
-Entity* Engine::findEntity(const socket_v & socketList, anna::Exception::Mode::_v emode)
+comm::Entity* comm::Engine::findEntity(const socket_v & socketList, anna::Exception::Mode::_v emode)
throw(anna::RuntimeException) {
anna::Guard guard(this, "anna::diameter::comm::Engine::findEntity");
entity_key key(getEntityKey(socketList));
return NULL;
}
-Entity* Engine::findEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, anna::Exception::Mode::_v emode)
+comm::Entity* comm::Engine::findEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, anna::Exception::Mode::_v emode)
throw(anna::RuntimeException) {
socket_v dualList;
dualList.push_back(socket_t(addr1, port1));
//}
-LocalServer* Engine::findLocalServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
+comm::LocalServer* comm::Engine::findLocalServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
throw(anna::RuntimeException) {
anna::Guard guard(this, "anna::diameter::comm::Engine::findLocalServer");
socket_t key(addr, port);
}
-ServerSession* Engine::findServerSession(int socketId, anna::Exception::Mode::_v emode) throw(anna::RuntimeException) {
+comm::ServerSession* comm::Engine::findServerSession(int socketId, anna::Exception::Mode::_v emode) throw(anna::RuntimeException) {
anna::Guard guard(this, "anna::diameter::comm::Engine::findServerSession");
ServerSession *result;
}
-void Engine::closeClientSession(ClientSession* clientSession, bool destroy)
+void comm::Engine::closeClientSession(comm::ClientSession* clientSession, bool destroy)
throw(anna::RuntimeException) {
if(clientSession == NULL)
return;
-void Engine::closeServer(Server* server, bool destroy)
+void comm::Engine::closeServer(comm::Server* server, bool destroy)
throw(anna::RuntimeException) {
if(server == NULL)
return;
}
-void Engine::closeEntity(Entity* entity, bool destroy)
+void comm::Engine::closeEntity(comm::Entity* entity, bool destroy)
throw(anna::RuntimeException) {
if(entity == NULL)
return;
-void Engine::closeLocalServer(LocalServer* localServer, bool destroy)
+void comm::Engine::closeLocalServer(comm::LocalServer* localServer, bool destroy)
throw(anna::RuntimeException) {
if(localServer == NULL)
return;
-void Engine::closeEntities(bool destroy) throw(anna::RuntimeException) {
+void comm::Engine::closeEntities(bool destroy) throw(anna::RuntimeException) {
LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "closeEntities", ANNA_FILE_LOCATION));
anna::Guard guard(this, "anna::diameter::comm::Engine::closeEntities");
closeEntity(entity(it), destroy);
}
-void Engine::closeLocalServers(bool destroy) throw(anna::RuntimeException) {
+void comm::Engine::closeLocalServers(bool destroy) throw(anna::RuntimeException) {
LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "closeLocalServers", ANNA_FILE_LOCATION));
anna::Guard guard(this, "anna::diameter::comm::Engine::closeLocalServers");
closeLocalServer(localServer(it), destroy);
}
-void Engine::eraseDeprecatedIdleEntities() throw() {
+void comm::Engine::eraseDeprecatedIdleEntities() throw() {
LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "eraseDeprecatedIdleEntities", ANNA_FILE_LOCATION));
Entity *et;
}
}
-int Engine::getOTARequestsForEntities() const throw() {
+int comm::Engine::getOTARequestsForEntities() const throw() {
int result = 0;
for(const_entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
return result;
}
-int Engine::getOTARequestsForLocalServers() const throw() {
+int comm::Engine::getOTARequestsForLocalServers() const throw() {
int result = 0;
for(const_localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++)
}
-void Engine::setRealm(const std::string & name) throw() {
+void comm::Engine::setRealm(const std::string & name) throw() {
a_realm = ((name != "") ? name : anna::functions::getDomainname());
}
-void Engine::setHost(const std::string & name) throw() {
+void comm::Engine::setHost(const std::string & name) throw() {
a_host = ((name != "") ? name : anna::functions::getHostname());
}
-void Engine::raiseAutoRecovery(bool autoRecovery) throw(anna::RuntimeException) {
+void comm::Engine::raiseAutoRecovery(bool autoRecovery) throw(anna::RuntimeException) {
LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "raiseAutoRecovery", ANNA_FILE_LOCATION));
for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
entity(it)->raiseAutoRecovery(autoRecovery);
}
-void Engine::do_stop()
+void comm::Engine::do_stop()
throw() {
LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "do_stop", ANNA_FILE_LOCATION));
close(true /* destroy */);
}
-std::string Engine::asString(void) const throw() {
+std::string comm::Engine::asString(void) const throw() {
std::string trace;
trace = "\n================================";
trace += "\nDiameter comm Engine information";
}
-anna::xml::Node* Engine::asXML(anna::xml::Node* parent) const
+anna::xml::Node* comm::Engine::asXML(anna::xml::Node* parent) const
throw() {
parent = anna::app::Component::asXML(parent);
anna::xml::Node* result = parent->createChild("diameter.comm.Engine");
for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++)
localServer(it)->asXML(localServers);
+ // DRA Basics
+ // Aspect:
+ // <Engine.RemoteRealm Name="afNodeHostRealm.com">
+ // <Engine.RemoteRealmHost Name="afNodeHostname.afNodeHostRealm.com" ServerSession="localhost:3868|ServerSessionId:4"/>
+ // </Engine.RemoteRealm>
+ // <Engine.RemoteRealm Name="ggsnNodeHostRealm.com">
+ // <Engine.RemoteRealmHost Name="ggsnNodeHostname.ggsnNodeHostRealm.com" ServerSession="localhost:3868|ServerSessionId:6"/>
+ // </Engine.RemoteRealm>
+ for (dr_dh_server_sessions_it_t drit = a_dr_dh_server_sessions.begin(); drit != a_dr_dh_server_sessions.end(); drit++) {
+ anna::xml::Node* remoteRealm = result->createChild("Engine.RemoteRealm");
+ remoteRealm->createAttribute("Name", drit->first);
+ dh_server_sessions_map_t *dhServerSessions = (dh_server_sessions_map_t *)&(drit->second);
+ for (dh_server_sessions_it_t dhit = dhServerSessions->begin(); dhit != dhServerSessions->end(); dhit++) {
+ anna::xml::Node* remoteRealmHost = remoteRealm->createChild("Engine.RemoteRealmHost");
+ remoteRealmHost->createAttribute("Name", dhit->first);
+ server_sessions_vector_t *serverSessions = (server_sessions_vector_t *)&(dhit->second);
+ for (server_sessions_it_t ssit = serverSessions->begin(); ssit != serverSessions->end(); ssit++) {
+ std::string socket = anna::functions::socketLiteralAsString((*ssit)->getAddress(), (*ssit)->getPort());
+ std::string ss_desc = socket + anna::functions::asString("|ServerSessionId:%d", (*ssit)->getSocketId());
+ remoteRealmHost->createAttribute("ServerSession", ss_desc);
+ }
+ }
+ }
+
return result;
}
-Engine::clientSession_iterator Engine::clientSession_find(const clientSession_key &key) throw() {
+comm::Engine::clientSession_iterator comm::Engine::clientSession_find(const clientSession_key &key) throw() {
return a_clientSessions.find(key);
}
-Engine::server_iterator Engine::server_find(const server_key &key) throw() {
+comm::Engine::server_iterator comm::Engine::server_find(const server_key &key) throw() {
return a_servers.find(key);
}
-Engine::entity_iterator Engine::entity_find(const entity_key &key) throw() {
+comm::Engine::entity_iterator comm::Engine::entity_find(const entity_key &key) throw() {
return a_entities.find(key);
}
-Engine::localServer_iterator Engine::localServer_find(const socket_t &key) throw() {
+comm::Engine::localServer_iterator comm::Engine::localServer_find(const socket_t &key) throw() {
return a_localServers.find(key);
}
-Engine::entity_key Engine::getEntityKey(const std::string & addr1, int port1, const std::string & addr2, int port2) const throw() {
+comm::Engine::entity_key comm::Engine::getEntityKey(const std::string & addr1, int port1, const std::string & addr2, int port2) const throw() {
socket_v dualList;
dualList.push_back(socket_t(addr1, port1));
dualList.push_back(socket_t(addr2, port2));
return (getEntityKey(dualList));
}
-Engine::entity_key Engine::getEntityKey(const socket_v &v) const throw() {
+comm::Engine::entity_key comm::Engine::getEntityKey(const socket_v &v) const throw() {
std::string result;
socket_v::const_iterator it;
socket_v::const_iterator it_min(v.begin());
}
-void Engine::availabilityLostForEntities() throw() {
+void comm::Engine::availabilityLostForEntities() throw() {
a_availableForEntities = false;
LOGDEBUG(
std::string msg = "diameter::comm::Engine { Realm: ";
}
-void Engine::availabilityRecoveredForEntities() throw() {
+void comm::Engine::availabilityRecoveredForEntities() throw() {
a_availableForEntities = true;
LOGDEBUG(
std::string msg = "diameter::comm::Engine { Realm: ";
}
-void Engine::availabilityLostForLocalServers() throw() {
+void comm::Engine::availabilityLostForLocalServers() throw() {
a_availableForLocalServers = false;
LOGDEBUG(
std::string msg = "diameter::comm::Engine { Realm: ";
}
-void Engine::availabilityRecoveredForLocalServers() throw() {
+void comm::Engine::availabilityRecoveredForLocalServers() throw() {
a_availableForLocalServers = true;
LOGDEBUG(
std::string msg = "diameter::comm::Engine { Realm: ";
}
-bool Engine::refreshAvailabilityForEntities() throw() {
+bool comm::Engine::refreshAvailabilityForEntities() throw() {
// Here available
if(a_availableForEntities) { // check not-bound state for all client-sessions:
bool isolate = true;
return false;
}
-bool Engine::refreshAvailabilityForLocalServers() throw() {
+bool comm::Engine::refreshAvailabilityForLocalServers() throw() {
// Here available
if(a_availableForLocalServers) { // check not-bound state for all client-sessions:
bool isolate = true;
}
-void Engine::readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) throw() {
+void comm::Engine::readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) throw() {
+
+ // Check for base protocol codec engine health:
+ assertBaseProtocolHealth();
+
// Default DPA implementation:
//
// 'Disconnect-Peer-Answer' (282,answer)
}
-void Engine::readCEA(anna::DataBlock &cea, const anna::DataBlock & cer) throw() {
+void comm::Engine::readCEA(anna::DataBlock &cea, const anna::DataBlock & cer) throw() {
+
+ // Check for base protocol codec engine health:
+ assertBaseProtocolHealth();
+
// Default CEA implementation:
//
// 'Capabilities-Exchange-Answer' (257,answer)
}
}
+void comm::Engine::manageDrDhServerSession(ServerSession *ss, bool register_or_desregister) throw() {
+
+ // Decode CER (TODO: use raw buffer helpers)
+ std::string destinationRealm, destinationHost;
+ codec::Message codecMsg(getBaseProtocolCodecEngine());
+ try {
+ codecMsg.decode(ss->a_cer);
+ destinationRealm = codecMsg.getAvp(anna::diameter::helpers::base::AVPID__Origin_Realm)->getDiameterIdentity()->getValue();
+ destinationHost = codecMsg.getAvp(anna::diameter::helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->getValue();
+ }
+ catch(anna::RuntimeException &ex) {
+ ex.trace();
+ return;
+ }
+
+ dr_dh_server_sessions_nc_it_t drit = a_dr_dh_server_sessions.find(destinationRealm);
+ if (drit != a_dr_dh_server_sessions.end()) { // found
+ dh_server_sessions_map_t *dhServerSessions = (dh_server_sessions_map_t *)&(drit->second);
+ dh_server_sessions_nc_it_t dhit = dhServerSessions->find(destinationHost);
+ if (dhit != dhServerSessions->end()) { // found
+ server_sessions_vector_t *serverSessions = (server_sessions_vector_t *)&(dhit->second);
+ if (register_or_desregister) { // REGISTER
+ serverSessions->push_back(ss);
+ }
+ else { // DESREGISTER
+ // Sequential search the specific server session:
+ for (server_sessions_nc_it_t ssit = serverSessions->begin(); ssit != serverSessions->end(); ssit++) {
+ if ((*ssit)->getAddress() != ss->getAddress()) continue;
+ if ((*ssit)->getPort() != ss->getPort()) continue;
+ if ((*ssit)->getSocketId() != ss->getSocketId()) continue;
+ serverSessions->erase(ssit); // if it is the last server session removed in DR-DH path, the XML will show this tree empty
+ // (it could be a hint for past registerings):
+ // <Engine.RemoteRealm Name="afNodeHostRealm.com">
+ // <Engine.RemoteRealmHost Name="afNodeHostname.afNodeHostRealm.com"/>
+ // </Engine.RemoteRealm>
+ // <Engine.RemoteRealm Name="ggsnNodeHostRealm.com">
+ // <Engine.RemoteRealmHost Name="ggsnNodeHostname.ggsnNodeHostRealm.com"/>
+ // </Engine.RemoteRealm>
+
+ break;
+ }
+ }
+ }
+ else {
+ if (!register_or_desregister) return; // strange (host not found)
+ server_sessions_vector_t ssVector;
+ ssVector.push_back(ss);
+ (*dhServerSessions)[destinationHost] = ssVector;
+ }
+ }
+ else {
+ if (!register_or_desregister) return; // strange (realm not found)
+ server_sessions_vector_t ssVector;
+ ssVector.push_back(ss);
+ dh_server_sessions_map_t dhServerSessions;
+ dhServerSessions[destinationHost] = ssVector;
+ a_dr_dh_server_sessions[destinationRealm] = dhServerSessions;
+ }
+}
+
+void comm::Engine::readDWA(anna::DataBlock &dwa, const anna::DataBlock & dwr) throw() {
+
+ // Check for base protocol codec engine health:
+ assertBaseProtocolHealth();
-void Engine::readDWA(anna::DataBlock &dwa, const anna::DataBlock & dwr) throw() {
// Default DWA implementation:
//
// 'Device-Watchdog-Answer' (280,answer)
}
}
-void Engine::resetStatistics() throw() {
+void comm::Engine::resetStatistics() throw() {
for(server_iterator it = server_begin(), maxii = server_end(); it != maxii; it ++)
server(it)->resetStatistics();
localServer(it)->resetStatistics();
}
-void Engine::do_initialize() throw(RuntimeException) {
+void comm::Engine::do_initialize() throw(RuntimeException) {
LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "do_initialize", ANNA_FILE_LOCATION));
LOGDEBUG(anna::Logger::debug("Nothing special done on component initialization", ANNA_FILE_LOCATION));
}
-void Engine::lazyInitialize() throw(RuntimeException) {
+void comm::Engine::lazyInitialize() throw(RuntimeException) {
LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "lazyInitialize", ANNA_FILE_LOCATION));
anna::app::Component::initialize(); // this will invoke do_initialize
}
+
+// Not tested yet
+const comm::Response* comm::Engine::sendRealmHost(const Message* message, const std::string &destinationRealm, const std::string &destinationHost) throw(anna::RuntimeException) {
+
+ if (destinationRealm == "")
+ throw anna::RuntimeException("Unable to resolve the destination: empty provided Destination-Realm name", ANNA_FILE_LOCATION);
+
+ // Get the server sessions which fulfill the restrictions:
+ dr_dh_server_sessions_it_t drit = a_dr_dh_server_sessions.find(destinationRealm);
+ if (drit == a_dr_dh_server_sessions.end())
+ throw anna::RuntimeException(anna::functions::asString("Unable to resolve the destination: Destination-Realm name is not registered (no remote clients have been connected to '%s')", destinationRealm.c_str()), ANNA_FILE_LOCATION);
+
+ dh_server_sessions_map_t *dhServerSessions = (dh_server_sessions_map_t *)&(drit->second);
+ // randomize between all server sessions for all hosts:
+ dh_server_sessions_nc_it_t dhit;
+ int hostsN = dhServerSessions->size();
+ if (hostsN == 0) // avoids next division by cero (rand() % 0)
+ throw anna::RuntimeException(anna::functions::asString("Unable to resolve the destination: neither Destination-Host currently connected to Destination-Realm '%s'", destinationRealm.c_str()), ANNA_FILE_LOCATION);
+
+ if (destinationHost == "") {
+ // in this case, randomize the host:
+ dhit = dhServerSessions->begin();
+ int randomHostIndx = rand() % hostsN; // number between 0 and the number of hosts - 1
+ std::advance (dhit, randomHostIndx);
+ }
+ else {
+ dhit = dhServerSessions->find(destinationHost);
+ if (dhit == dhServerSessions->end())
+ throw anna::RuntimeException(anna::functions::asString("Unable to resolve the destination: Destination-Host '%s' is not registered for Destination-Realm '%s'", destinationHost.c_str(), destinationRealm.c_str()), ANNA_FILE_LOCATION);
+ }
+
+ // Now, randomize the available server sessions:
+ server_sessions_vector_t *serverSessions = (server_sessions_vector_t *)&(dhit->second);
+ int serverSessionN = serverSessions->size();
+ if (serverSessionN == 0) { // avoids next division by cero (rand() % 0)
+ std::string aux = "";
+ if (destinationHost != "") { aux = "to Destination-Host '"; aux += destinationHost; aux += "'"; }
+ std::string msg = anna::functions::asString("Unable to resolve the destination: neither server session currently connected%s within Destination-Realm '%s'", aux.c_str(), destinationRealm.c_str());
+ throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
+ }
+
+ server_sessions_nc_it_t ssit = serverSessions->begin();
+ int randomServerSessionIndx = rand() % serverSessionN; // number between 0 and the number of server sessions - 1
+ std::advance (ssit, randomServerSessionIndx);
+ return (*ssit)->send(message);
+}
if(ii == serverSession_end())
return;
+ // Remove origin-realm / origin-host for server session in delivery map
+ // This is related to http://redmine.teslayout.com/issues/41
+ a_engine->manageDrDhServerSession(serverSession, false /* desregister */);
+
try {
//serverSession->setState(ServerSession::State::Closing); NOT MANAGED WITH SERVER SESSIONS
serverSession->unbind(true /* always forceDisconnect on server sessions ... */);
ServerSession::ServerSession() : Session("diameter::comm::ServerSession", "Diameter Inactivity Detection Timer"),
a_receiverFactory(this),
a_cer(ClassCode::Bind),
- a_dwr(ClassCode::ApplicationMessage) // realmente no es necesario, los Message son por defecto de aplicacion
+ a_dwr(ClassCode::ApplicationMessage) // not actually needed; Message is application type by default
{ initialize(); }
void ServerSession::initialize() throw() {
return a_parent->getKey().second;
}
-
const Response* ServerSession::send(const Message* message) throw(anna::RuntimeException) {
LOGMETHOD(anna::TraceMethod traceMethod(a_className, "send", ANNA_FILE_LOCATION));
}
a_cer.setBody(db);
+ // Basic DRA:
+ getParent()->getEngine()->manageDrDhServerSession(this, true /* register */);
+
sendCEA();
//activateTimer(); // Ya se invoca al inicio de este metodo ::receive
//bool changes = a_parent->refreshAvailability();