...
Some people might want to asynchronously retrieve requested values. An intuitial approach could read as followSfollows:
Code Block |
---|
void myMibObject::get_request(Request* req, int ind) { //--AgentGen BEGIN=myMibObject::get_request // Callback ptr = &SnmpDisplayString::get_request(req, ind); // Put req, pdu and callback_ptr into a message structure and send that in a message to the queue of another task. // do not return any value now (won't be up-to-date) if (FALSE) //--AgentGen END SnmpDisplayString::get_request(req, ind); } |
The problem with the above approach is: It will not work with AGENT++ (at least not without additional coding)!
Solution
OK, so how can asynchronous or pseudo-synchronous processing a SNMP request be implemented with AGENT++?
The answer is, there are at least two approaches:
- Asynchronous (concurrent) processing of all sub-requests with reprocessing of the request when a sub-task associated with sub-request has completed.
- Pseudo-synchronous processing of each sub-request sequentially.
Asynchronous
The real asynchronous approach has been implemented by AgentX++ in order to be able to concurrently process sub-requests on several sub-agents (or a single multi-threaded sub-agent).
Pseudo-Synchronous
The pseudo-synchronous approach allows queued instrumentation while answering the SNMP request is done synchronously. That means, all sub-requests are processed in the order as they occur in the original SNMP request.
To implement this solution you need:
- An OidList<RequestID> to associate the processed Request...
- The Request pointer which extends Synchronized and can therefore by used to wait with timeout until another thread notifies it.
- A pointer to the RequestList to be able to lookup the pending Request to be notified when the instrumentation code finishes.
Code Block |
---|
int agentppTestRowCreation::commit_set_request(Request* req, int ind)
{
//--AgentGen BEGIN=agentppTestRowCreation::commit_set_request
unsigned long val;
Vbx vb(req->get_value(ind));
vb.get_value(val);
Oidx rowIndex;
rowIndex += val;
if (agentppTestSharedEntry::instance->
pending_row_ops.find(&rowIndex)) {
return SNMP_ERROR_COMITFAIL;
}
// double check for duplicate row (has already been checked in prepare
agentppTestSharedEntry::instance->start_synch();
if (agentppTestSharedEntry::instance->find_index(rowIndex)) {
agentppTestSharedEntry::instance->end_synch();
return SNMP_ERROR_COMITFAIL;
}
agentppTestSharedEntry::instance->end_synch();
// allocate index and then register and add the row in a background thread
if (!agentppTestSharedEntry::instance->allocate_index(rowIndex)) {
*((Gauge32*)value) = 0;
return SNMP_ERROR_COMITFAIL;
}
else {
agentppTestSharedEntry::instance->
pending_row_ops.add(new RequestID(req->get_request_id(), rowIndex));
// wait until row has been registered at master agent
if (req->wait(AGENTX_DEFAULT_TIMEOUT*1000)) {
agentppTestSharedEntry::instance->start_synch();
agentppTestSharedEntry::instance->remove_row(rowIndex);
agentppTestSharedEntry::instance->end_synch();
return SNMP_ERROR_COMITFAIL;
}
}
//--AgentGen END
return MibLeaf::commit_set_request(req, ind);
}
|
The background thread creates the new row in the agentppTestSharedEntry table and then calls the row_added method listed below:
Code Block |
---|
void agentppTestSharedEntry::row_added(MibTableRow* row, const Oidx& index, MibTable* src)
{
// The row 'row' with 'index' has been added to the table.
//--AgentGen BEGIN=agentppTestSharedEntry::row_added
Oidx rowIndex(index);
RequestID* req_id = pending_row_ops.find(&rowIndex);
if (req_id) {
RequestList* req_list =
backReference->get_request_list();
if (req_list) {
Request* req = req_list->get_request(req_id->get_request_id());
if (req) {
// signal success
LOG_BEGIN(EVENT_LOG | 3);
LOG("agentppTestSharedEntry: row_added: AgentX row successfully registered (index)(request_id)");
LOG(rowIndex.get_printable());
LOG(req_id->get_request_id());
LOG_END;
req->notify();
}
}
pending_row_ops.remove(req_id);
}
//--AgentGen END
}
|