ccsession_unittests.cc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. // Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <gtest/gtest.h>
  16. #include <config/tests/fake_session.h>
  17. #include <config/ccsession.h>
  18. #include <fstream>
  19. #include <config/tests/data_def_unittests_config.h>
  20. #include <log/logger_name.h>
  21. using namespace isc::data;
  22. using namespace isc::config;
  23. using namespace isc::cc;
  24. using namespace std;
  25. namespace {
  26. std::string
  27. ccspecfile(const std::string& name) {
  28. return (std::string(TEST_DATA_PATH) + "/" + name);
  29. }
  30. ElementPtr
  31. el(const std::string& str) {
  32. return (Element::fromJSON(str));
  33. }
  34. class CCSessionTest : public ::testing::Test {
  35. protected:
  36. CCSessionTest() : session(el("[]"), el("[]"), el("[]")),
  37. root_name(isc::log::getRootLoggerName())
  38. {
  39. // upon creation of a ModuleCCSession, the class
  40. // sends its specification to the config manager.
  41. // it expects an ok answer back, so everytime we
  42. // create a ModuleCCSession, we must set an initial
  43. // ok answer.
  44. session.getMessages()->add(createAnswer());
  45. }
  46. ~CCSessionTest() {
  47. isc::log::setRootLoggerName(root_name);
  48. }
  49. FakeSession session;
  50. const std::string root_name;
  51. };
  52. TEST_F(CCSessionTest, createAnswer) {
  53. ConstElementPtr answer;
  54. answer = createAnswer();
  55. EXPECT_EQ("{ \"result\": [ 0 ] }", answer->str());
  56. answer = createAnswer(1, "error");
  57. EXPECT_EQ("{ \"result\": [ 1, \"error\" ] }", answer->str());
  58. EXPECT_THROW(createAnswer(1, ElementPtr()), CCSessionError);
  59. EXPECT_THROW(createAnswer(1, Element::create(1)), CCSessionError);
  60. ConstElementPtr arg = el("[ \"just\", \"some\", \"data\" ]");
  61. answer = createAnswer(0, arg);
  62. EXPECT_EQ("{ \"result\": [ 0, [ \"just\", \"some\", \"data\" ] ] }", answer->str());
  63. }
  64. TEST_F(CCSessionTest, parseAnswer) {
  65. ConstElementPtr answer;
  66. ConstElementPtr arg;
  67. int rcode;
  68. EXPECT_THROW(parseAnswer(rcode, ElementPtr()), CCSessionError);
  69. EXPECT_THROW(parseAnswer(rcode, el("1")), CCSessionError);
  70. EXPECT_THROW(parseAnswer(rcode, el("[]")), CCSessionError);
  71. EXPECT_THROW(parseAnswer(rcode, el("{ }")), CCSessionError);
  72. EXPECT_THROW(parseAnswer(rcode, el("{ \"something\": 1 }")), CCSessionError);
  73. EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": 0 }")), CCSessionError);
  74. EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": 1 }")), CCSessionError);
  75. EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": [ 1 ] }")), CCSessionError);
  76. EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": [ 1, 1 ] }")), CCSessionError);
  77. answer = el("{ \"result\": [ 0 ] }");
  78. arg = parseAnswer(rcode, answer);
  79. EXPECT_EQ(0, rcode);
  80. EXPECT_TRUE(isNull(arg));
  81. answer = el("{ \"result\": [ 1, \"error\"] }");
  82. arg = parseAnswer(rcode, answer);
  83. EXPECT_EQ(1, rcode);
  84. EXPECT_EQ("error", arg->stringValue());
  85. answer = el("{ \"result\": [ 0, [ \"just\", \"some\", \"data\" ] ] }");
  86. arg = parseAnswer(rcode, answer);
  87. EXPECT_EQ(0, rcode);
  88. EXPECT_EQ("[ \"just\", \"some\", \"data\" ]", arg->str());
  89. }
  90. TEST_F(CCSessionTest, createCommand) {
  91. ConstElementPtr command;
  92. ConstElementPtr arg;
  93. command = createCommand("my_command");
  94. ASSERT_EQ("{ \"command\": [ \"my_command\" ] }", command->str());
  95. arg = el("1");
  96. command = createCommand("my_command", arg);
  97. ASSERT_EQ("{ \"command\": [ \"my_command\", 1 ] }", command->str());
  98. arg = el("[ \"a\", \"b\" ]");
  99. command = createCommand("my_cmd", arg);
  100. ASSERT_EQ("{ \"command\": [ \"my_cmd\", [ \"a\", \"b\" ] ] }", command->str());
  101. arg = el("{ \"a\": \"map\" }");
  102. command = createCommand("foo", arg);
  103. ASSERT_EQ("{ \"command\": [ \"foo\", { \"a\": \"map\" } ] }", command->str());
  104. }
  105. TEST_F(CCSessionTest, parseCommand) {
  106. ConstElementPtr arg;
  107. std::string cmd;
  108. // should throw
  109. EXPECT_THROW(parseCommand(arg, ElementPtr()), CCSessionError);
  110. EXPECT_THROW(parseCommand(arg, el("1")), CCSessionError);
  111. EXPECT_THROW(parseCommand(arg, el("{ }")), CCSessionError);
  112. EXPECT_THROW(parseCommand(arg, el("{ \"not a command\": 1 }")), CCSessionError);
  113. EXPECT_THROW(parseCommand(arg, el("{ \"command\": 1 }")), CCSessionError);
  114. EXPECT_THROW(parseCommand(arg, el("{ \"command\": [] }")), CCSessionError);
  115. EXPECT_THROW(parseCommand(arg, el("{ \"command\": [ 1 ] }")), CCSessionError);
  116. cmd = parseCommand(arg, el("{ \"command\": [ \"my_command\" ] }"));
  117. EXPECT_EQ("my_command", cmd);
  118. EXPECT_EQ(*arg, *Element::createMap());
  119. cmd = parseCommand(arg, el("{ \"command\": [ \"my_command\", 1 ] }"));
  120. EXPECT_EQ("my_command", cmd);
  121. EXPECT_EQ("1", arg->str());
  122. parseCommand(arg, el("{ \"command\": [ \"my_command\", [ \"some\", \"argument\", \"list\" ] ] }"));
  123. EXPECT_EQ("my_command", cmd);
  124. EXPECT_EQ("[ \"some\", \"argument\", \"list\" ]", arg->str());
  125. }
  126. TEST_F(CCSessionTest, session1) {
  127. EXPECT_FALSE(session.haveSubscription("Spec1", "*"));
  128. ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL,
  129. true, false);
  130. EXPECT_TRUE(session.haveSubscription("Spec1", "*"));
  131. EXPECT_EQ(1, session.getMsgQueue()->size());
  132. ConstElementPtr msg;
  133. std::string group, to;
  134. msg = session.getFirstMessage(group, to);
  135. EXPECT_EQ("{ \"command\": [ \"module_spec\", { \"module_name\": \"Spec1\" } ] }", msg->str());
  136. EXPECT_EQ("ConfigManager", group);
  137. EXPECT_EQ("*", to);
  138. EXPECT_EQ(0, session.getMsgQueue()->size());
  139. // with this argument, the session should not automatically
  140. // subscribe to logging config
  141. EXPECT_FALSE(session.haveSubscription("Logging", "*"));
  142. }
  143. TEST_F(CCSessionTest, session2) {
  144. EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
  145. ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL,
  146. true, false);
  147. EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
  148. EXPECT_EQ(1, session.getMsgQueue()->size());
  149. ConstElementPtr msg;
  150. std::string group, to;
  151. msg = session.getFirstMessage(group, to);
  152. EXPECT_EQ("{ \"command\": [ \"module_spec\", { \"commands\": [ { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }, { \"command_args\": [ ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\" } ], \"config_data\": [ { \"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": false, \"item_type\": \"integer\" }, { \"item_default\": 1.1, \"item_name\": \"item2\", \"item_optional\": false, \"item_type\": \"real\" }, { \"item_default\": true, \"item_name\": \"item3\", \"item_optional\": false, \"item_type\": \"boolean\" }, { \"item_default\": \"test\", \"item_name\": \"item4\", \"item_optional\": false, \"item_type\": \"string\" }, { \"item_default\": [ \"a\", \"b\" ], \"item_name\": \"item5\", \"item_optional\": false, \"item_type\": \"list\", \"list_item_spec\": { \"item_default\": \"\", \"item_name\": \"list_element\", \"item_optional\": false, \"item_type\": \"string\" } }, { \"item_default\": { }, \"item_name\": \"item6\", \"item_optional\": false, \"item_type\": \"map\", \"map_item_spec\": [ { \"item_default\": \"default\", \"item_name\": \"value1\", \"item_optional\": true, \"item_type\": \"string\" }, { \"item_name\": \"value2\", \"item_optional\": true, \"item_type\": \"integer\" } ] } ], \"module_name\": \"Spec2\" } ] }", msg->str());
  153. EXPECT_EQ("ConfigManager", group);
  154. EXPECT_EQ("*", to);
  155. EXPECT_EQ(0, session.getMsgQueue()->size());
  156. }
  157. ConstElementPtr my_config_handler(ConstElementPtr new_config) {
  158. if (new_config && new_config->contains("item1") &&
  159. new_config->get("item1")->intValue() == 5) {
  160. return (createAnswer(6, "I do not like the number 5"));
  161. }
  162. return (createAnswer());
  163. }
  164. ConstElementPtr my_command_handler(const std::string& command,
  165. ConstElementPtr arg)
  166. {
  167. if (command == "good_command") {
  168. return (createAnswer());
  169. } else if (command == "command_with_arg") {
  170. if (arg->contains("number")) {
  171. if (arg->get("number")->getType() == Element::integer) {
  172. return (createAnswer(0, el("2")));
  173. } else {
  174. return (createAnswer(1, "arg bad type"));
  175. }
  176. } else {
  177. return (createAnswer(1, "arg missing"));
  178. }
  179. } else {
  180. return (createAnswer(1, "bad command"));
  181. }
  182. }
  183. TEST_F(CCSessionTest, session3) {
  184. // client will ask for config
  185. session.getMessages()->add(createAnswer(0, el("{}")));
  186. EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
  187. ModuleCCSession mccs(ccspecfile("spec2.spec"), session, my_config_handler,
  188. my_command_handler, true, false);
  189. EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
  190. EXPECT_EQ(2, session.getMsgQueue()->size());
  191. ConstElementPtr msg;
  192. std::string group, to;
  193. msg = session.getFirstMessage(group, to);
  194. EXPECT_EQ("{ \"command\": [ \"module_spec\", { \"commands\": [ { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }, { \"command_args\": [ ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\" } ], \"config_data\": [ { \"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": false, \"item_type\": \"integer\" }, { \"item_default\": 1.1, \"item_name\": \"item2\", \"item_optional\": false, \"item_type\": \"real\" }, { \"item_default\": true, \"item_name\": \"item3\", \"item_optional\": false, \"item_type\": \"boolean\" }, { \"item_default\": \"test\", \"item_name\": \"item4\", \"item_optional\": false, \"item_type\": \"string\" }, { \"item_default\": [ \"a\", \"b\" ], \"item_name\": \"item5\", \"item_optional\": false, \"item_type\": \"list\", \"list_item_spec\": { \"item_default\": \"\", \"item_name\": \"list_element\", \"item_optional\": false, \"item_type\": \"string\" } }, { \"item_default\": { }, \"item_name\": \"item6\", \"item_optional\": false, \"item_type\": \"map\", \"map_item_spec\": [ { \"item_default\": \"default\", \"item_name\": \"value1\", \"item_optional\": true, \"item_type\": \"string\" }, { \"item_name\": \"value2\", \"item_optional\": true, \"item_type\": \"integer\" } ] } ], \"module_name\": \"Spec2\" } ] }", msg->str());
  195. EXPECT_EQ("ConfigManager", group);
  196. EXPECT_EQ("*", to);
  197. EXPECT_EQ(1, session.getMsgQueue()->size());
  198. msg = session.getFirstMessage(group, to);
  199. EXPECT_EQ("{ \"command\": [ \"get_config\", { \"module_name\": \"Spec2\" } ] }", msg->str());
  200. EXPECT_EQ("ConfigManager", group);
  201. EXPECT_EQ("*", to);
  202. EXPECT_EQ(0, session.getMsgQueue()->size());
  203. }
  204. TEST_F(CCSessionTest, checkCommand) {
  205. // client will ask for config
  206. session.getMessages()->add(createAnswer(0, el("{}")));
  207. EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
  208. ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler,
  209. my_command_handler, true, false);
  210. EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
  211. EXPECT_EQ(2, session.getMsgQueue()->size());
  212. ConstElementPtr msg;
  213. std::string group, to;
  214. // checked above, drop em
  215. msg = session.getFirstMessage(group, to);
  216. msg = session.getFirstMessage(group, to);
  217. int result;
  218. result = mccs.checkCommand();
  219. EXPECT_EQ(0, result);
  220. // not a command, should be ignored
  221. session.addMessage(el("1"), "Spec29", "*");
  222. result = mccs.checkCommand();
  223. EXPECT_EQ(0, result);
  224. session.addMessage(el("{ \"command\": [ \"good_command\" ] }"), "Spec29",
  225. "*");
  226. result = mccs.checkCommand();
  227. EXPECT_EQ(1, session.getMsgQueue()->size());
  228. msg = session.getFirstMessage(group, to);
  229. EXPECT_EQ("{ \"result\": [ 0 ] }", msg->str());
  230. EXPECT_EQ(0, result);
  231. session.addMessage(el("{ \"command\": \"bad_command\" }"), "Spec29", "*");
  232. result = mccs.checkCommand();
  233. EXPECT_EQ(0, session.getMsgQueue()->size());
  234. session.addMessage(el("{ \"command\": [ \"bad_command\" ] }"),
  235. "Spec29", "*");
  236. result = mccs.checkCommand();
  237. EXPECT_EQ(1, session.getMsgQueue()->size());
  238. msg = session.getFirstMessage(group, to);
  239. EXPECT_EQ("{ \"result\": [ 1, \"bad command\" ] }", msg->str());
  240. EXPECT_EQ(0, result);
  241. session.addMessage(el("{ \"command\": [ \"command_with_arg\", {\"number\": 1} ] }"),
  242. "Spec29", "*");
  243. result = mccs.checkCommand();
  244. EXPECT_EQ(1, session.getMsgQueue()->size());
  245. msg = session.getFirstMessage(group, to);
  246. EXPECT_EQ("{ \"result\": [ 0, 2 ] }", msg->str());
  247. EXPECT_EQ(0, result);
  248. session.addMessage(el("{ \"command\": [ \"command_with_arg\" ] }"), "Spec29", "*");
  249. result = mccs.checkCommand();
  250. EXPECT_EQ(1, session.getMsgQueue()->size());
  251. msg = session.getFirstMessage(group, to);
  252. EXPECT_EQ("{ \"result\": [ 1, \"arg missing\" ] }", msg->str());
  253. EXPECT_EQ(0, result);
  254. session.addMessage(el("{ \"command\": [ \"command_with_arg\", \"asdf\" ] }"), "Spec29", "*");
  255. result = mccs.checkCommand();
  256. EXPECT_EQ(1, session.getMsgQueue()->size());
  257. msg = session.getFirstMessage(group, to);
  258. EXPECT_EQ("{ \"result\": [ 3, \"Error in command validation: args for command command_with_arg is not a map\" ] }", msg->str());
  259. EXPECT_EQ(0, result);
  260. mccs.setCommandHandler(NULL);
  261. session.addMessage(el("{ \"command\": [ \"whatever\" ] }"), "Spec29", "*");
  262. result = mccs.checkCommand();
  263. EXPECT_EQ(1, session.getMsgQueue()->size());
  264. msg = session.getFirstMessage(group, to);
  265. EXPECT_EQ("{ \"result\": [ 1, \"Command given but no command handler for module\" ] }", msg->str());
  266. EXPECT_EQ(0, result);
  267. }
  268. // A heuristic workaround for clang++: It doesn't seem to compile the whole
  269. // test, probably due to its length. Dividing the tests into two separate
  270. // test cases seems to work.
  271. TEST_F(CCSessionTest, checkCommand2) {
  272. session.getMessages()->add(createAnswer(0, el("{}")));
  273. EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
  274. ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler,
  275. my_command_handler, true, false);
  276. EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
  277. ConstElementPtr msg;
  278. std::string group, to;
  279. // checked above, drop em
  280. msg = session.getFirstMessage(group, to);
  281. msg = session.getFirstMessage(group, to);
  282. EXPECT_EQ(1, mccs.getValue("item1")->intValue());
  283. session.addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": 2 } ] }"), "Spec29", "*");
  284. int result = mccs.checkCommand();
  285. EXPECT_EQ(1, session.getMsgQueue()->size());
  286. msg = session.getFirstMessage(group, to);
  287. EXPECT_EQ("{ \"result\": [ 0 ] }", msg->str());
  288. EXPECT_EQ(0, result);
  289. EXPECT_EQ(2, mccs.getValue("item1")->intValue());
  290. session.addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": \"asdf\" } ] }"), "Spec29", "*");
  291. result = mccs.checkCommand();
  292. EXPECT_EQ(1, session.getMsgQueue()->size());
  293. msg = session.getFirstMessage(group, to);
  294. EXPECT_EQ("{ \"result\": [ 2, \"Error in config validation: Type mismatch\" ] }", msg->str());
  295. EXPECT_EQ(0, result);
  296. EXPECT_EQ(2, mccs.getValue("item1")->intValue());
  297. session.addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": 5 } ] }"), "Spec29", "*");
  298. result = mccs.checkCommand();
  299. EXPECT_EQ(1, session.getMsgQueue()->size());
  300. msg = session.getFirstMessage(group, to);
  301. EXPECT_EQ("{ \"result\": [ 6, \"I do not like the number 5\" ] }", msg->str());
  302. EXPECT_EQ(0, result);
  303. EXPECT_EQ(2, mccs.getValue("item1")->intValue());
  304. }
  305. std::string remote_module_name;
  306. int remote_item1(0);
  307. ConstElementPtr remote_config;
  308. ModuleCCSession *remote_mccs(NULL);
  309. void remoteHandler(const std::string& module_name,
  310. ConstElementPtr config,
  311. const ConfigData&) {
  312. remote_module_name = module_name;
  313. remote_item1 = remote_mccs->getRemoteConfigValue("Spec2", "item1")->
  314. intValue();
  315. remote_config = config;
  316. }
  317. TEST_F(CCSessionTest, remoteConfig) {
  318. std::string module_name;
  319. int item1;
  320. ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL,
  321. false, false);
  322. EXPECT_TRUE(session.haveSubscription("Spec1", "*"));
  323. // first simply connect, with no config values, and see we get
  324. // the default
  325. session.getMessages()->add(createAnswer(0, el("{}")));
  326. EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
  327. module_name = mccs.addRemoteConfig(ccspecfile("spec2.spec"));
  328. EXPECT_EQ("Spec2", module_name);
  329. EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
  330. item1 = mccs.getRemoteConfigValue(module_name, "item1")->intValue();
  331. EXPECT_EQ(1, item1);
  332. // Remove it and see we get an error asking for a config value
  333. mccs.removeRemoteConfig(module_name);
  334. EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
  335. EXPECT_THROW(mccs.getRemoteConfigValue(module_name, "item1"), CCSessionError);
  336. // Now re-add it, with a specific config value, and see we get that
  337. session.getMessages()->add(createAnswer(0, el("{ \"item1\": 2 }")));
  338. module_name = mccs.addRemoteConfig(ccspecfile("spec2.spec"));
  339. item1 = mccs.getRemoteConfigValue(module_name, "item1")->intValue();
  340. EXPECT_EQ(2, item1);
  341. // Try a config_update command
  342. session.addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": 3 } ] }"), module_name, "*");
  343. mccs.checkCommand();
  344. item1 = mccs.getRemoteConfigValue(module_name, "item1")->intValue();
  345. EXPECT_EQ(3, item1);
  346. // remove, re-add, now with a *bad* config request answer
  347. mccs.removeRemoteConfig(module_name);
  348. session.getMessages()->add(el("{}"));
  349. EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")), CCSessionError);
  350. session.getMessages()->add(createAnswer(1, "my_error"));
  351. EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")), CCSessionError);
  352. session.getMessages()->add(createAnswer());
  353. EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")), CCSessionError);
  354. {
  355. SCOPED_TRACE("With module name");
  356. // Try adding it with downloading the spec from config manager
  357. ModuleSpec spec(moduleSpecFromFile(ccspecfile("spec2.spec")));
  358. session.getMessages()->add(createAnswer(0, spec.getFullSpec()));
  359. session.getMessages()->add(createAnswer(0, el("{}")));
  360. EXPECT_NO_THROW(module_name = mccs.addRemoteConfig("Spec2", NULL,
  361. false));
  362. const size_t qsize(session.getMsgQueue()->size());
  363. EXPECT_TRUE(session.getMsgQueue()->get(qsize - 2)->equals(*el(
  364. "[ \"ConfigManager\", \"*\", { \"command\": ["
  365. "\"get_module_spec\", { \"module_name\": \"Spec2\" } ] } ]")));
  366. EXPECT_TRUE(session.getMsgQueue()->get(qsize - 1)->equals(*el(
  367. "[ \"ConfigManager\", \"*\", { \"command\": [ \"get_config\","
  368. "{ \"module_name\": \"Spec2\" } ] } ]")));
  369. EXPECT_EQ("Spec2", module_name);
  370. // Since we returned an empty local config above, the default value
  371. // for "item1", which is 1, should be used.
  372. EXPECT_NO_THROW(item1 =
  373. mccs.getRemoteConfigValue(module_name,
  374. "item1")->intValue());
  375. EXPECT_EQ(1, item1);
  376. mccs.removeRemoteConfig(module_name);
  377. }
  378. {
  379. SCOPED_TRACE("With bad module name");
  380. // It is almost the same as above, but we supply wrong module name.
  381. // It should fail.
  382. // Try adding it with downloading the spec from config manager
  383. ModuleSpec spec(moduleSpecFromFile(ccspecfile("spec2.spec")));
  384. session.getMessages()->add(createAnswer(0, spec.getFullSpec()));
  385. EXPECT_THROW(module_name = mccs.addRemoteConfig("Spec1", NULL, false),
  386. CCSessionError);
  387. }
  388. {
  389. // Try adding it with a handler.
  390. // Pass non-default value to see the handler is called after
  391. // downloading the configuration, not too soon.
  392. SCOPED_TRACE("With handler");
  393. session.getMessages()->add(createAnswer(0, el("{ \"item1\": 2 }")));
  394. remote_mccs = &mccs;
  395. module_name = mccs.addRemoteConfig(ccspecfile("spec2.spec"),
  396. remoteHandler);
  397. {
  398. SCOPED_TRACE("Before update");
  399. EXPECT_EQ("Spec2", module_name);
  400. EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
  401. // Now check the parameters the remote handler stored
  402. // This also checks it was called
  403. EXPECT_EQ("Spec2", remote_module_name);
  404. remote_module_name = "";
  405. EXPECT_EQ(2, remote_item1);
  406. remote_item1 = 0;
  407. if (remote_config) {
  408. EXPECT_EQ(2, remote_config->get("item1")->intValue());
  409. } else {
  410. ADD_FAILURE() << "Remote config not set";
  411. }
  412. remote_config.reset();
  413. // Make sure normal way still works
  414. item1 = mccs.getRemoteConfigValue(module_name,
  415. "item1")->intValue();
  416. EXPECT_EQ(2, item1);
  417. }
  418. {
  419. SCOPED_TRACE("After update");
  420. session.addMessage(el("{ \"command\": [ \"config_update\", "
  421. "{ \"item1\": 3 } ] }"), module_name, "*");
  422. mccs.checkCommand();
  423. EXPECT_EQ("Spec2", remote_module_name);
  424. remote_module_name = "";
  425. EXPECT_EQ(3, remote_item1);
  426. remote_item1 = 0;
  427. if (remote_config) {
  428. EXPECT_EQ(3, remote_config->get("item1")->intValue());
  429. } else {
  430. ADD_FAILURE() << "Remote config not set";
  431. }
  432. remote_config.reset();
  433. // Make sure normal way still works
  434. item1 = mccs.getRemoteConfigValue(module_name,
  435. "item1")->intValue();
  436. EXPECT_EQ(3, item1);
  437. }
  438. remote_mccs = NULL;
  439. mccs.removeRemoteConfig(module_name);
  440. {
  441. SCOPED_TRACE("When removed");
  442. // Make sure nothing is called any more
  443. session.addMessage(el("{ \"command\": [ \"config_update\", "
  444. "{ \"item1\": 4 } ] }"), module_name, "*");
  445. EXPECT_EQ("", remote_module_name);
  446. EXPECT_EQ(0, remote_item1);
  447. EXPECT_FALSE(remote_config);
  448. }
  449. }
  450. }
  451. TEST_F(CCSessionTest, ignoreRemoteConfigCommands) {
  452. // client will ask for config
  453. session.getMessages()->add(createAnswer(0, el("{ }")));
  454. EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
  455. ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler,
  456. my_command_handler, false, false);
  457. EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
  458. EXPECT_EQ(2, session.getMsgQueue()->size());
  459. ConstElementPtr msg;
  460. std::string group, to;
  461. // drop the module_spec and config commands
  462. session.getFirstMessage(group, to);
  463. session.getFirstMessage(group, to);
  464. session.getMessages()->add(createAnswer(0, el("{ }")));
  465. mccs.addRemoteConfig(ccspecfile("spec1.spec"));
  466. EXPECT_EQ(1, session.getMsgQueue()->size());
  467. msg = session.getFirstMessage(group, to);
  468. // Check if commands for the module are handled
  469. session.addMessage(el("{ \"command\": [ \"good_command\" ] }"), "Spec29", "*");
  470. int result = mccs.checkCommand();
  471. EXPECT_EQ(1, session.getMsgQueue()->size());
  472. msg = session.getFirstMessage(group, to);
  473. EXPECT_EQ("{ \"result\": [ 0 ] }", msg->str());
  474. EXPECT_EQ(0, result);
  475. // Check if commands for the other module are ignored
  476. session.addMessage(el("{ \"command\": [ \"good_command\" ] }"), "Spec1", "*");
  477. EXPECT_EQ(1, session.getMsgQueue()->size());
  478. result = mccs.checkCommand();
  479. EXPECT_EQ(0, session.getMsgQueue()->size());
  480. }
  481. TEST_F(CCSessionTest, initializationFail) {
  482. // bad specification
  483. EXPECT_THROW(ModuleCCSession(ccspecfile("spec8.spec"), session,
  484. NULL, NULL), CCSessionInitError);
  485. // file that does not exist
  486. EXPECT_THROW(ModuleCCSession(ccspecfile("does_not_exist_spec"),
  487. session, NULL, NULL),
  488. CCSessionInitError);
  489. session.getMessages()->add(createAnswer(1, el("\"just an error\"")));
  490. EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
  491. EXPECT_THROW(ModuleCCSession(ccspecfile("spec29.spec"), session,
  492. my_config_handler, my_command_handler),
  493. CCSessionInitError);
  494. EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
  495. }
  496. // Test it throws when we try to start it twice (once from the constructor)
  497. TEST_F(CCSessionTest, doubleStartImplicit) {
  498. ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL,
  499. true, false);
  500. EXPECT_THROW(mccs.start(), CCSessionError);
  501. }
  502. // The same, but both starts are explicit
  503. TEST_F(CCSessionTest, doubleStartExplicit) {
  504. ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL,
  505. false, false);
  506. mccs.start();
  507. EXPECT_THROW(mccs.start(), CCSessionError);
  508. }
  509. // Test we can request synchronous receive before we start the session,
  510. // and check there's the mechanism if we do it after
  511. TEST_F(CCSessionTest, delayedStart) {
  512. ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL,
  513. false, false);
  514. session.getMessages()->add(createAnswer());
  515. ConstElementPtr env, answer;
  516. EXPECT_NO_THROW(session.group_recvmsg(env, answer, false, 3));
  517. mccs.start();
  518. session.getMessages()->add(createAnswer());
  519. EXPECT_THROW(session.group_recvmsg(env, answer, false, 3),
  520. FakeSession::DoubleRead);
  521. }
  522. TEST_F(CCSessionTest, loggingStart) {
  523. // provide the logging module spec
  524. ConstElementPtr log_spec = moduleSpecFromFile(LOG_SPEC_FILE).getFullSpec();
  525. session.getMessages()->add(createAnswer(0, log_spec));
  526. // just give an empty config
  527. session.getMessages()->add(createAnswer(0, el("{}")));
  528. ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL,
  529. true, true);
  530. EXPECT_TRUE(session.haveSubscription("Logging", "*"));
  531. }
  532. TEST_F(CCSessionTest, loggingStartBadSpec) {
  533. // provide the logging module spec
  534. session.getMessages()->add(createAnswer(0, el("{}")));
  535. // just give an empty config
  536. session.getMessages()->add(createAnswer(0, el("{}")));
  537. EXPECT_THROW(new ModuleCCSession(ccspecfile("spec2.spec"), session,
  538. NULL, NULL), ModuleSpecError);
  539. EXPECT_FALSE(session.haveSubscription("Logging", "*"));
  540. }
  541. // Similar to the above, but more implicitly by calling addRemoteConfig().
  542. // We should construct ModuleCCSession with start_immediately being false
  543. // if we need to call addRemoteConfig().
  544. // The correct cases are covered in remoteConfig test.
  545. TEST_F(CCSessionTest, doubleStartWithAddRemoteConfig) {
  546. ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL,
  547. true, false);
  548. session.getMessages()->add(createAnswer(0, el("{}")));
  549. EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")),
  550. FakeSession::DoubleRead);
  551. }
  552. namespace {
  553. void doRelatedLoggersTest(const char* input, const char* expected) {
  554. ConstElementPtr all_conf = isc::data::Element::fromJSON(input);
  555. ConstElementPtr expected_conf = isc::data::Element::fromJSON(expected);
  556. EXPECT_EQ(*expected_conf, *isc::config::getRelatedLoggers(all_conf));
  557. }
  558. } // end anonymous namespace
  559. TEST(LogConfigTest, relatedLoggersTest) {
  560. // make sure logger configs for 'other' programs are ignored,
  561. // and that * is substituted correctly
  562. // We'll use a root logger name of "b10-test".
  563. isc::log::setRootLoggerName("b10-test");
  564. doRelatedLoggersTest("[{ \"name\": \"other_module\" }]",
  565. "[]");
  566. doRelatedLoggersTest("[{ \"name\": \"other_module.somelib\" }]",
  567. "[]");
  568. doRelatedLoggersTest("[{ \"name\": \"test_other\" }]",
  569. "[]");
  570. doRelatedLoggersTest("[{ \"name\": \"test_other.somelib\" }]",
  571. "[]");
  572. doRelatedLoggersTest("[ { \"name\": \"other_module\" },"
  573. " { \"name\": \"test\" }]",
  574. "[ { \"name\": \"b10-test\" } ]");
  575. doRelatedLoggersTest("[ { \"name\": \"test\" }]",
  576. "[ { \"name\": \"b10-test\" } ]");
  577. doRelatedLoggersTest("[ { \"name\": \"test.somelib\" }]",
  578. "[ { \"name\": \"b10-test.somelib\" } ]");
  579. doRelatedLoggersTest("[ { \"name\": \"other_module.somelib\" },"
  580. " { \"name\": \"test.somelib\" }]",
  581. "[ { \"name\": \"b10-test.somelib\" } ]");
  582. doRelatedLoggersTest("[ { \"name\": \"other_module.somelib\" },"
  583. " { \"name\": \"test\" },"
  584. " { \"name\": \"test.somelib\" }]",
  585. "[ { \"name\": \"b10-test\" },"
  586. " { \"name\": \"b10-test.somelib\" } ]");
  587. doRelatedLoggersTest("[ { \"name\": \"*\" }]",
  588. "[ { \"name\": \"b10-test\" } ]");
  589. doRelatedLoggersTest("[ { \"name\": \"*.somelib\" }]",
  590. "[ { \"name\": \"b10-test.somelib\" } ]");
  591. doRelatedLoggersTest("[ { \"name\": \"*\", \"severity\": \"DEBUG\" },"
  592. " { \"name\": \"test\", \"severity\": \"WARN\"}]",
  593. "[ { \"name\": \"b10-test\", \"severity\": \"WARN\"} ]");
  594. doRelatedLoggersTest("[ { \"name\": \"*\", \"severity\": \"DEBUG\" },"
  595. " { \"name\": \"some_module\", \"severity\": \"WARN\"}]",
  596. "[ { \"name\": \"b10-test\", \"severity\": \"DEBUG\"} ]");
  597. doRelatedLoggersTest("[ { \"name\": \"b10-test\" }]",
  598. "[]");
  599. // make sure 'bad' things like '*foo.x' or '*lib' are ignored
  600. // (cfgmgr should have already caught it in the logconfig plugin
  601. // check, and is responsible for reporting the error)
  602. doRelatedLoggersTest("[ { \"name\": \"*foo\" }]",
  603. "[ ]");
  604. doRelatedLoggersTest("[ { \"name\": \"*foo.bar\" }]",
  605. "[ ]");
  606. doRelatedLoggersTest("[ { \"name\": \"*foo\" },"
  607. " { \"name\": \"*foo.lib\" },"
  608. " { \"name\": \"test\" } ]",
  609. "[ { \"name\": \"b10-test\" } ]");
  610. }
  611. }