Browse Source

Merge #2930

Sending notifications over msgq.

No receiving end yet.
Michal 'vorner' Vaner 12 years ago
parent
commit
bf2a2aab27

+ 2 - 2
doc/design/ipc-high.txt

@@ -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).

+ 3 - 0
src/lib/cc/proto_defs.cc

@@ -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";
 
 }
 }

+ 16 - 0
src/lib/config/ccsession.cc

@@ -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);
+}
+
 }
 }

+ 13 - 5
src/lib/config/ccsession.h

@@ -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 =

+ 40 - 0
src/lib/config/tests/ccsession_unittests.cc

@@ -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();

+ 36 - 0
src/lib/python/isc/config/ccsession.py

@@ -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

+ 26 - 0
src/lib/python/isc/config/tests/ccsession_test.py

@@ -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)