Commit bf2a2aab authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge #2930

Sending notifications over msgq.

No receiving end yet.
parents 26f8bf35 1248e88a
......@@ -210,7 +210,7 @@ about changes to zone data, they'd subscribe to the
`Notifications/ZoneUpdates` group. Then, other client (let's say
`XfrIn`, with session ID `s12345`) would send something like:
s12345 -> Notifications/ZoneUpdates
s12345 -> notifications/ZoneUpdates
{"notification": ["zone-update", {
"class": "IN",
"origin": "example.org.",
......@@ -221,7 +221,7 @@ Both receivers would receive the message and know that the
`example.org` zone is now at version 123456. Note that multiple users
may produce the same kind of notification. Also, single group may be
used to send multiple notification names (but they should be related;
in our example, the `Notifications/ZoneUpdates` could be used for
in our example, the `notifications/ZoneUpdates` could be used for
`zone-update`, `zone-available` and `zone-unavailable` notifications
for change in zone data, configuration of new zone in the system and
removal of a zone from configuration).
......
......@@ -43,6 +43,8 @@ const char* const CC_COMMAND_STOP = "stop";
// The wildcards of some headers
const char* const CC_TO_WILDCARD = "*";
const char* const CC_INSTANCE_WILDCARD = "*";
// Prefixes for groups
const char* const CC_GROUP_NOTIFICATION_PREFIX = "notifications/";
// Reply codes
const int CC_REPLY_NO_RECPT = -1;
const int CC_REPLY_SUCCESS = 0;
......@@ -50,6 +52,7 @@ const int CC_REPLY_SUCCESS = 0;
const char *const CC_PAYLOAD_LNAME = "lname";
const char *const CC_PAYLOAD_RESULT = "result";
const char *const CC_PAYLOAD_COMMAND = "command";
const char *const CC_PAYLOAD_NOTIFICATION = "notification";
}
}
......@@ -884,5 +884,21 @@ ModuleCCSession::rpcCall(const std::string &command, const std::string &group,
}
}
void
ModuleCCSession::notify(const std::string& group, const std::string& name,
const ConstElementPtr& params)
{
const ElementPtr message(Element::createMap());
const ElementPtr notification(Element::createList());
notification->add(Element::create(name));
if (params) {
notification->add(params);
}
message->set(isc::cc::CC_PAYLOAD_NOTIFICATION, notification);
groupSendMsg(message, isc::cc::CC_GROUP_NOTIFICATION_PREFIX + group,
isc::cc::CC_INSTANCE_WILDCARD,
isc::cc::CC_TO_WILDCARD, false);
}
}
}
......@@ -425,18 +425,26 @@ public:
params =
isc::data::ConstElementPtr());
/// \brief Send a notification to subscribed clients
/// \brief Send a notification to subscribed users
///
/// Send a notification message to all clients subscribed to the given
/// Send a notification message to all users subscribed to the given
/// notification group.
///
/// This method does not not block.
///
/// See docs/design/ipc-high.txt for details about notifications and
/// the format of messages sent.
///
/// \throw CCSessionError for low-level communication errors.
/// \param notification_group This parameter (indirectly) signifies what
/// clients should receive the notification. Only the clients that
/// users should receive the notification. Only the users that
/// subscribed to notifications on the same group receive it.
/// \param name The name of the event to notify about (for example
/// `config_changed`).
/// `new_group_member`).
/// \param params Other parameters that describe the event. This might
/// be, for example, the new configuration value.
/// be, for example, the ID of the new member and the name of the
/// group. This can be any data element, but it is common for it to be
/// map.
void notify(const std::string& notification_group,
const std::string& name,
const isc::data::ConstElementPtr& params =
......
......@@ -117,6 +117,46 @@ TEST_F(CCSessionTest, rpcNoRecpt) {
RPCRecipientMissing);
}
// Test sending a notification
TEST_F(CCSessionTest, notify) {
ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL, false,
false);
mccs.notify("group", "event", el("{\"param\": true}"));
const ConstElementPtr notification(el(
"["
" \"notifications/group\","
" \"*\","
" {"
" \"notification\": ["
" \"event\", {"
" \"param\": true"
" }"
" ]"
" },"
" -1"
"]"));
EXPECT_TRUE(notification->equals(*session.getMsgQueue()->get(1))) <<
session.getMsgQueue()->get(1)->toWire();
}
// Test sending a notification
TEST_F(CCSessionTest, notifyNoParams) {
ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL, false,
false);
mccs.notify("group", "event");
const ConstElementPtr notification(el(
"["
" \"notifications/group\","
" \"*\","
" {"
" \"notification\": [\"event\"]"
" },"
" -1"
"]"));
EXPECT_TRUE(notification->equals(*session.getMsgQueue()->get(1))) <<
session.getMsgQueue()->get(1)->toWire();
}
TEST_F(CCSessionTest, createAnswer) {
ConstElementPtr answer;
answer = createAnswer();
......
......@@ -539,6 +539,42 @@ class ModuleCCSession(ConfigData):
raise RPCError(code, value)
return value
def notify(self, notification_group, event_name, params=None):
"""
Send a notification to subscribed users.
Send a notification message to all users subscribed to the given
notification group.
This method does not block.
See docs/design/ipc-high.txt for details about notifications
and the format of messages sent.
Throws:
- CCSessionError: for low-level communication errors.
Params:
- notification_group (string): This parameter (indirectly) signifies
what users should receive the notification. Only users that
subscribed to notifications on the same group receive it.
- event_name (string): The name of the event to notify about (for
example `new_group_member`).
- params: Other parameters that describe the event. This might be, for
example, the ID of the new member and the name of the group. This can
be any data that can be sent over the isc.cc.Session, but it is
common for it to be dict.
Returns: Nothing
"""
notification = [event_name]
if params is not None:
notification.append(params)
self._session.group_sendmsg({CC_PAYLOAD_NOTIFICATION: notification},
CC_GROUP_NOTIFICATION_PREFIX +
notification_group,
instance=CC_INSTANCE_WILDCARD,
to=CC_TO_WILDCARD,
want_answer=False)
class UIModuleCCSession(MultiConfigData):
"""This class is used in a configuration user interface. It contains
specific functions for getting, displaying, and sending
......
......@@ -350,6 +350,32 @@ class TestModuleCCSession(unittest.TestCase):
self.assertRaises(RPCRecipientMissing, self.rpc_check,
{"result": [-1, "Error"]})
def test_notify(self):
"""
Test the sent notification has the right format.
"""
fake_session = FakeModuleCCSession()
mccs = self.create_session("spec1.spec", None, None, fake_session)
mccs.notify("group", "event", {"param": True})
self.assertEqual([
["notifications/group", "*", {"notification": ["event", {
"param": True
}]}, False]], fake_session.message_queue)
def test_notify_no_params(self):
"""
Test the sent notification has the right format, this time
without passing parameters.
"""
fake_session = FakeModuleCCSession()
mccs = self.create_session("spec1.spec", None, None, fake_session)
mccs.notify("group", "event")
self.assertEqual([
["notifications/group", "*", {"notification": ["event"]},
False]
],
fake_session.message_queue)
def my_config_handler_ok(self, new_config):
return isc.config.ccsession.create_answer(0)
......
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