Unverified Commit 95218da8 authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge #2768

The rpcCall method in C++
parents 6067533e c32bfa79
......@@ -37,7 +37,7 @@ socket listed in the output.
This debug message indicates that the connection was successfully made, this
should follow CC_ESTABLISH.
% CC_GROUP_RECEIVE trying to receive a message
% CC_GROUP_RECEIVE trying to receive a message with seq %1
Debug message, noting that a message is expected to come over the command
channel.
......
......@@ -38,8 +38,8 @@ const char* const CC_COMMAND_SEND = "send";
const char* const CC_TO_WILDCARD = "*";
const char* const CC_INSTANCE_WILDCARD = "*";
// Reply codes
const int CC_REPLY_SUCCESS = 0;
const int CC_REPLY_NO_RECPT = -1;
const int CC_REPLY_SUCCESS = 0;
}
}
......@@ -498,7 +498,7 @@ bool
Session::group_recvmsg(ConstElementPtr& envelope, ConstElementPtr& msg,
bool nonblock, int seq)
{
LOG_DEBUG(logger, DBG_TRACE_DETAILED, CC_GROUP_RECEIVE);
LOG_DEBUG(logger, DBG_TRACE_DETAILED, CC_GROUP_RECEIVE).arg(seq);
bool result(recvmsg(envelope, msg, nonblock, seq));
if (result) {
LOG_DEBUG(logger, DBG_TRACE_DETAILED, CC_GROUP_RECEIVED).
......
......@@ -32,7 +32,7 @@
#include <boost/foreach.hpp>
#include <cc/data.h>
#include <module_spec.h>
#include <config/module_spec.h>
#include <cc/session.h>
#include <exceptions/exceptions.h>
......@@ -857,5 +857,27 @@ ModuleCCSession::cancelAsyncRecv(const AsyncRecvRequestID& id) {
async_recv_requests_.erase(id);
}
ConstElementPtr
ModuleCCSession::rpcCall(const std::string &command, const std::string &group,
const std::string &instance, const std::string &to,
const ConstElementPtr &params)
{
ConstElementPtr command_el(createCommand(command, params));
const int seq = groupSendMsg(command_el, group, instance, to, true);
ConstElementPtr env, answer;
LOG_DEBUG(config_logger, DBGLVL_TRACE_DETAIL, CONFIG_RPC_SEQ).arg(command).
arg(group).arg(seq);
groupRecvMsg(env, answer, true, seq);
int rcode;
const ConstElementPtr result(parseAnswer(rcode, answer));
if (rcode == isc::cc::CC_REPLY_NO_RECPT) {
isc_throw(RPCRecipientMissing, result);
} else if (rcode != isc::cc::CC_REPLY_SUCCESS) {
isc_throw_1(RPCError, result, rcode);
} else {
return (result);
}
}
}
}
......@@ -20,6 +20,7 @@
#include <cc/session.h>
#include <cc/data.h>
#include <cc/proto_defs.h>
#include <string>
#include <list>
......@@ -146,6 +147,34 @@ public:
isc::Exception(file, line, what) {}
};
/// \brief Exception thrown when there's a problem with the remote call.
///
/// This usually means either the command couldn't be called or the remote
/// side sent an error as a response.
class RPCError: public CCSessionError {
public:
RPCError(const char* file, size_t line, const char* what, int rcode) :
CCSessionError(file, line, what),
rcode_(rcode)
{}
/// \brief The error code for the error.
int rcode() const {
return (rcode_);
}
private:
const int rcode_;
};
/// \brief Specific version of RPCError for the case the recipient of command
/// doesn't exist.
class RPCRecipientMissing: public RPCError {
public:
RPCRecipientMissing(const char* file, size_t line, const char* what) :
RPCError(file, line, what, isc::cc::CC_REPLY_NO_RECPT)
{}
};
///
/// \brief This module keeps a connection to the command channel,
/// holds configuration information, and handles messages from
......@@ -335,13 +364,15 @@ public:
* \param group see isc::cc::Session::group_sendmsg()
* \param instance see isc::cc::Session::group_sendmsg()
* \param to see isc::cc::Session::group_sendmsg()
* \param want_answer see isc::cc::Session::group_sendmsg()
* \return see isc::cc::Session::group_sendmsg()
*/
int groupSendMsg(isc::data::ConstElementPtr msg,
std::string group,
std::string instance = "*",
std::string to = "*") {
return (session_.group_sendmsg(msg, group, instance, to));
std::string to = "*",
bool want_answer = false) {
return (session_.group_sendmsg(msg, group, instance, to, want_answer));
};
/**
......@@ -361,6 +392,50 @@ public:
return (session_.group_recvmsg(envelope, msg, nonblock, seq));
};
/// \brief Send a command message and wait for the answer.
///
/// This is mostly a convenience wrapper around groupSendMsg
/// and groupRecvMsg, with some error handling.
///
/// \param command Name of the command to call.
/// \param group Name of the remote module to call the command on.
/// \param instance Instance part of recipient address.
/// \param to The lname to send it to. Can be used to override the
/// addressing and use a direct recipient.
/// \param params Parameters for the command. Can be left NULL if
/// no parameters are needed.
/// \return Return value of the successfull remote call. It can be
/// NULL if the remote command is void function (returns nothing).
/// \throw RPCError if the call fails (for example when the other
/// side responds with error code).
/// \throw RPCRecipientMissing if the recipient doesn't exist.
/// \throw CCSessionError if some lower-level error happens (eg.
/// the response was malformed).
isc::data::ConstElementPtr rpcCall(const std::string& command,
const std::string& group,
const std::string& instance =
isc::cc::CC_INSTANCE_WILDCARD,
const std::string& to =
isc::cc::CC_TO_WILDCARD,
const isc::data::ConstElementPtr&
params =
isc::data::ConstElementPtr());
/// \brief Convenience version of rpcCall
///
/// This is exactly the same as the previous version of rpcCall, except
/// that the instance and to parameters are at their default. This
/// allows to sending a command with parameters to a named module
/// without long typing of the parameters.
isc::data::ConstElementPtr rpcCall(const std::string& command,
const std::string& group,
const isc::data::ConstElementPtr&
params)
{
return rpcCall(command, group, isc::cc::CC_INSTANCE_WILDCARD,
isc::cc::CC_TO_WILDCARD, params);
}
/// \brief Forward declaration of internal data structure.
///
/// This holds information about one asynchronous request to receive
......
......@@ -94,3 +94,7 @@ manager.
% CONFIG_OPEN_FAIL error opening %1: %2
There was an error opening the given file. The reason for the failure
is included in the message.
% CONFIG_RPC_SEQ RPC call %1 to %2 with seq %3
Debug message, saying there's a RPC call of given command to given module. It
has internal sequence number as listed in the message.
......@@ -58,6 +58,28 @@ protected:
// ok answer.
session.getMessages()->add(createAnswer());
}
ConstElementPtr rpcCheck(const std::string& reply) {
ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL,
false, false);
// Prepare the answer beforehand, it'll block until it gets one
const ConstElementPtr reply_el(el(reply));
session.getMessages()->add(reply_el);
const ConstElementPtr
result(mccs.rpcCall("test", "Spec2",
el("{\"param1\": \"Param 1\","
"\"param2\": \"Param 2\"}")));
const ConstElementPtr
request(el("[\"Spec2\", \"*\", {"
" \"command\": [\"test\", {"
" \"param1\": \"Param 1\","
" \"param2\": \"Param 2\""
"}]}, -1, true]"));
// The 0th one is from the initialization, to ConfigManager.
// our is the 1st.
EXPECT_TRUE(request->equals(*session.getMsgQueue()->get(1))) <<
session.getMsgQueue()->get(1)->toWire();
return (result);
}
~CCSessionTest() {
isc::log::setRootLoggerName(root_name);
}
......@@ -65,6 +87,36 @@ protected:
const std::string root_name;
};
// Test we can send an RPC (command) and get an answer. The answer is success
// in this case.
TEST_F(CCSessionTest, rpcCallSuccess) {
const ConstElementPtr result =
rpcCheck("{\"result\": [0, {\"Hello\": \"a\"}]}");
EXPECT_TRUE(el("{\"Hello\": \"a\"}")->equals(*result));
}
// Test success of RPC, but the answer is empty (eg. a void function on the
// remote side).
TEST_F(CCSessionTest, rpcCallSuccessNone) {
EXPECT_FALSE(rpcCheck("{\"result\": [0]}"));
}
// Test it successfully raises CCSessionError if the answer is malformed.
TEST_F(CCSessionTest, rpcCallMalformedAnswer) {
EXPECT_THROW(rpcCheck("[\"Nonsense\"]"), CCSessionError);
}
// Test it raises exception when the remote side reports an error
TEST_F(CCSessionTest, rpcCallError) {
EXPECT_THROW(rpcCheck("{\"result\": [1, \"Error\"]}"), RPCError);
}
// Test it raises exception when the remote side doesn't exist
TEST_F(CCSessionTest, rpcNoRecpt) {
EXPECT_THROW(rpcCheck("{\"result\": [-1, \"Error\"]}"),
RPCRecipientMissing);
}
TEST_F(CCSessionTest, createAnswer) {
ConstElementPtr answer;
answer = createAnswer();
......
......@@ -183,12 +183,12 @@ FakeSession::unsubscribe(std::string group, std::string instance) {
int
FakeSession::group_sendmsg(ConstElementPtr msg, std::string group,
std::string to, std::string, bool)
std::string to, std::string, bool want_answer)
{
if (throw_on_send_) {
isc_throw(Exception, "Throw on send is set in FakeSession");
}
addMessage(msg, group, to);
addMessage(msg, group, to, -1, want_answer);
return (1);
}
......@@ -231,13 +231,16 @@ FakeSession::getFirstMessage(std::string& group, std::string& to) const {
void
FakeSession::addMessage(ConstElementPtr msg, const std::string& group,
const std::string& to, int seq)
const std::string& to, int seq, bool want_answer)
{
ElementPtr m_el = Element::createList();
m_el->add(Element::create(group));
m_el->add(Element::create(to));
m_el->add(msg);
m_el->add(Element::create(seq));
if (want_answer) {
m_el->add(Element::create(want_answer));
}
if (!msg_queue_) {
msg_queue_ = Element::createList();
}
......
......@@ -75,7 +75,8 @@ public:
isc::data::ConstElementPtr getFirstMessage(std::string& group,
std::string& to) const;
void addMessage(isc::data::ConstElementPtr, const std::string& group,
const std::string& to, int seq = -1);
const std::string& to, int seq = -1,
bool want_answer = false);
bool haveSubscription(const std::string& group,
const std::string& instance);
bool haveSubscription(const isc::data::ConstElementPtr group,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment