Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / confutils.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4  * Ceph - scalable distributed file system
5  *
6  * Copyright (C) 2011 New Dream Network
7  *
8  * This is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License version 2.1, as published by the Free Software
11  * Foundation.  See file COPYING.
12  *
13  */
14 #include "common/ConfUtils.h"
15 #include "common/config.h"
16 #include "common/errno.h"
17 #include "gtest/gtest.h"
18 #include "include/buffer.h"
19
20 #include <errno.h>
21 #include <iostream>
22 #include <stdlib.h>
23 #include <sstream>
24 #include <stdint.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include "include/memory.h"
28
29 using ceph::bufferlist;
30 using std::cerr;
31 using std::ostringstream;
32
33 #define MAX_FILES_TO_DELETE 1000UL
34
35 static size_t config_idx = 0;
36 static size_t unlink_idx = 0;
37 static char *to_unlink[MAX_FILES_TO_DELETE];
38
39 static std::string get_temp_dir()
40 {
41   static std::string temp_dir;
42
43   if (temp_dir.empty()) {
44     const char *tmpdir = getenv("TMPDIR");
45     if (!tmpdir)
46       tmpdir = "/tmp";
47     srand(time(NULL));
48     ostringstream oss;
49     oss << tmpdir << "/confutils_test_dir." << rand() << "." << getpid();
50     umask(022);
51     int res = mkdir(oss.str().c_str(), 01777);
52     if (res) {
53       cerr << "failed to create temp directory '" << temp_dir << "'" << std::endl;
54       return "";
55     }
56     temp_dir = oss.str();
57   }
58   return temp_dir;
59 }
60
61 static void unlink_all(void)
62 {
63   for (size_t i = 0; i < unlink_idx; ++i) {
64     unlink(to_unlink[i]);
65   }
66   for (size_t i = 0; i < unlink_idx; ++i) {
67     free(to_unlink[i]);
68   }
69   rmdir(get_temp_dir().c_str());
70 }
71
72 static int create_tempfile(const std::string &fname, const char *text)
73 {
74   FILE *fp = fopen(fname.c_str(), "w");
75   if (!fp) {
76     int err = errno;
77     cerr << "Failed to write file '" << fname << "' to temp directory '"
78          << get_temp_dir() << "'. " << cpp_strerror(err) << std::endl;
79     return err;
80   }
81   ceph::shared_ptr<FILE> fpp(fp, fclose);
82   if (unlink_idx >= MAX_FILES_TO_DELETE)
83     return -ENOBUFS;
84   if (unlink_idx == 0) {
85     memset(to_unlink, 0, sizeof(to_unlink));
86     atexit(unlink_all);
87   }
88   to_unlink[unlink_idx++] = strdup(fname.c_str());
89   size_t strlen_text = strlen(text);
90   size_t res = fwrite(text, 1, strlen_text, fp);
91   if (res != strlen_text) {
92     int err = errno;
93     cerr << "fwrite error while writing to " << fname 
94          << ": " << cpp_strerror(err) << std::endl;
95     return err;
96   }
97   return 0;
98 }
99
100 static std::string next_tempfile(const char *text)
101 {
102   ostringstream oss;
103   std::string temp_dir(get_temp_dir());
104   if (temp_dir.empty())
105     return "";
106   oss << temp_dir << "/test_config." << config_idx++ << ".config";
107   int ret = create_tempfile(oss.str(), text);
108   if (ret)
109     return "";
110   return oss.str();
111 }
112
113 const char * const trivial_conf_1 = "";
114
115 const char * const trivial_conf_2 = "log dir = foobar";
116
117 const char * const trivial_conf_3 = "log dir = barfoo\n";
118
119 const char * const trivial_conf_4 = "log dir = \"barbaz\"\n";
120
121 const char * const simple_conf_1 = "\
122 ; here's a comment\n\
123 [global]\n\
124         keyring = .my_ceph_keyring\n\
125 \n\
126 [mds]\n\
127         log dir = out\n\
128         log per instance = true\n\
129         log sym history = 100\n\
130         profiling logger = true\n\
131         profiling logger dir = wowsers\n\
132         chdir = ""\n\
133         pid file = out/$name.pid\n\
134 \n\
135         mds debug frag = true\n\
136 [osd]\n\
137         pid file = out/$name.pid\n\
138         osd scrub load threshold = 5.0\n\
139 \n\
140         lockdep = 1\n\
141 [osd0]\n\
142         osd data = dev/osd0\n\
143         osd journal size = 100\n\
144 [mds.a]\n\
145 [mds.b]\n\
146 [mds.c]\n\
147 ";
148
149 // we can add whitespace at odd locations and it will get stripped out.
150 const char * const simple_conf_2 = "\
151 [mds.a]\n\
152         log dir = special_mds_a\n\
153 [mds]\n\
154         log sym history = 100\n\
155         log dir = out # after a comment, anything # can ### happen ;;; right?\n\
156         log per instance = true\n\
157         profiling logger = true\n\
158         profiling                 logger dir = log\n\
159         chdir = ""\n\
160         pid file\t=\tfoo2\n\
161 [osd0]\n\
162         keyring   =       osd_keyring          ; osd's keyring\n\
163 \n\
164            \n\
165 [global]\n\
166         # I like pound signs as comment markers.\n\
167         ; Do you like pound signs as comment markers?\n\
168         keyring = shenanigans          ; The keyring of a leprechaun\n\
169 \n\
170         # Let's just have a line with a lot of whitespace and nothing else.\n\
171                          \n\
172         lockdep = 1\n\
173 ";
174
175 // test line-combining
176 const char * const conf3 = "\
177 [global]\n\
178         log file = /quite/a/long/path\\\n\
179 /for/a/log/file\n\
180         pid file = \\\n\
181                            spork\\\n\
182 \n\
183 [mon] #nothing here \n\
184 ";
185
186 const char * const escaping_conf_1 = "\
187 [global]\n\
188         log file = the \"scare quotes\"\n\
189         pid file = a \\\n\
190 pid file\n\
191 [mon]\n\
192         keyring = \"nested \\\"quotes\\\"\"\n\
193 ";
194
195 const char * const escaping_conf_2 = "\
196 [apple \\]\\[]\n\
197         log file = floppy disk\n\
198 [mon]\n\
199         keyring = \"backslash\\\\\"\n\
200 ";
201
202 // illegal because it contains an invalid utf8 sequence.
203 const char illegal_conf1[] = "\
204 [global]\n\
205         log file = foo\n\
206         pid file = invalid-utf-\xe2\x28\xa1\n\
207 [osd0]\n\
208         keyring = osd_keyring          ; osd's keyring\n\
209 ";
210
211 // illegal because it contains a malformed section header.
212 const char illegal_conf2[] = "\
213 [global\n\
214         log file = foo\n\
215 [osd0]\n\
216         keyring = osd_keyring          ; osd's keyring\n\
217 ";
218
219 // illegal because it contains a line that doesn't parse
220 const char illegal_conf3[] = "\
221 [global]\n\
222         who_what_where\n\
223 [osd0]\n\
224         keyring = osd_keyring          ; osd's keyring\n\
225 ";
226
227 // illegal because it has unterminated quotes
228 const char illegal_conf4[] = "\
229 [global]\n\
230         keyring = \"unterminated quoted string\n\
231 [osd0]\n\
232         keyring = osd_keyring          ; osd's keyring\n\
233 ";
234
235 // illegal because it has a backslash at the very end
236 const char illegal_conf5[] = "\
237 [global]\n\
238         keyring = something awful\\\\\n\
239 ";
240
241 // unicode config file
242 const char unicode_config_1[] = "\
243 [global]\n\
244         log file =           \x66\xd1\x86\xd1\x9d\xd3\xad\xd3\xae     \n\
245         pid file =           foo-bar\n\
246 [osd0]\n\
247 ";
248
249 const char override_config_1[] = "\
250 [global]\n\
251         log file =           global_log\n\
252 [mds]\n\
253         log file =           mds_log\n\
254 [osd]\n\
255         log file =           osd_log\n\
256 [osd.0]\n\
257         log file =           osd0_log\n\
258 ";
259
260 const char dup_key_config_1[] = "\
261 [mds.a]\n\
262         log_file = 1\n\
263         log_file = 3\n\
264 ";
265
266 TEST(ConfUtils, Whitespace) {
267   std::string test0("");
268   ConfFile::trim_whitespace(test0, false);
269   ASSERT_EQ(test0, "");
270
271   std::string test0a("");
272   ConfFile::trim_whitespace(test0a, true);
273   ASSERT_EQ(test0a, "");
274
275   std::string test0b("          ");
276   ConfFile::trim_whitespace(test0b, false);
277   ASSERT_EQ(test0b, "");
278
279   std::string test0c("          ");
280   ConfFile::trim_whitespace(test0c, true);
281   ASSERT_EQ(test0c, "");
282
283   std::string test1(" abc             ");
284   ConfFile::trim_whitespace(test1, false);
285   ASSERT_EQ(test1, "abc");
286
287   std::string test2(" abc        d     ");
288   ConfFile::trim_whitespace(test2, true);
289   ASSERT_EQ(test2, "abc d");
290
291   std::string test3(" abc        d     ");
292   ConfFile::trim_whitespace(test3, false);
293   ASSERT_EQ(test3, "abc        d");
294
295   std::string test4("abcd");
296   ConfFile::trim_whitespace(test4, false);
297   ASSERT_EQ(test4, "abcd");
298
299   std::string test5("abcd");
300   ConfFile::trim_whitespace(test5, true);
301   ASSERT_EQ(test5, "abcd");
302 }
303
304 TEST(ConfUtils, ParseFiles0) {
305   std::deque<std::string> err;
306   std::string val;
307   std::ostringstream warn;
308
309   std::string trivial_conf_1_f(next_tempfile(trivial_conf_1));
310   ConfFile cf1;
311   ASSERT_EQ(cf1.parse_file(trivial_conf_1_f.c_str(), &err, &warn), 0);
312   ASSERT_EQ(err.size(), 0U);
313
314   std::string trivial_conf_2_f(next_tempfile(trivial_conf_2));
315   ConfFile cf2;
316   ASSERT_EQ(cf2.parse_file(trivial_conf_2_f.c_str(), &err, &warn), 0);
317   ASSERT_EQ(err.size(), 1U);
318
319   bufferlist bl3;
320   bl3.append(trivial_conf_3, strlen(trivial_conf_3));
321   ConfFile cf3;
322   ASSERT_EQ(cf3.parse_bufferlist(&bl3, &err, &warn), 0);
323   ASSERT_EQ(err.size(), 0U);
324   ASSERT_EQ(cf3.read("global", "log dir", val), 0);
325   ASSERT_EQ(val, "barfoo");
326
327   std::string trivial_conf_4_f(next_tempfile(trivial_conf_4));
328   ConfFile cf4;
329   ASSERT_EQ(cf4.parse_file(trivial_conf_4_f.c_str(), &err, &warn), 0);
330   ASSERT_EQ(err.size(), 0U);
331   ASSERT_EQ(cf4.read("global", "log dir", val), 0);
332   ASSERT_EQ(val, "barbaz");
333 }
334
335 TEST(ConfUtils, ParseFiles1) {
336   std::deque<std::string> err;
337   std::ostringstream warn;
338   std::string simple_conf_1_f(next_tempfile(simple_conf_1));
339   ConfFile cf1;
340   ASSERT_EQ(cf1.parse_file(simple_conf_1_f.c_str(), &err, &warn), 0);
341   ASSERT_EQ(err.size(), 0U);
342
343   std::string simple_conf_2_f(next_tempfile(simple_conf_1));
344   ConfFile cf2;
345   ASSERT_EQ(cf2.parse_file(simple_conf_2_f.c_str(), &err, &warn), 0);
346   ASSERT_EQ(err.size(), 0U);
347
348   bufferlist bl3;
349   bl3.append(simple_conf_1, strlen(simple_conf_1));
350   ConfFile cf3;
351   ASSERT_EQ(cf3.parse_bufferlist(&bl3, &err, &warn), 0);
352   ASSERT_EQ(err.size(), 0U);
353
354   bufferlist bl4;
355   bl4.append(simple_conf_2, strlen(simple_conf_2));
356   ConfFile cf4;
357   ASSERT_EQ(cf4.parse_bufferlist(&bl4, &err, &warn), 0);
358   ASSERT_EQ(err.size(), 0U);
359 }
360
361 TEST(ConfUtils, ReadFiles1) {
362   std::deque<std::string> err;
363   std::ostringstream warn;
364   std::string simple_conf_1_f(next_tempfile(simple_conf_1));
365   ConfFile cf1;
366   ASSERT_EQ(cf1.parse_file(simple_conf_1_f.c_str(), &err, &warn), 0);
367   ASSERT_EQ(err.size(), 0U);
368
369   std::string val;
370   ASSERT_EQ(cf1.read("global", "keyring", val), 0);
371   ASSERT_EQ(val, ".my_ceph_keyring");
372
373   ASSERT_EQ(cf1.read("mds", "profiling logger dir", val), 0);
374   ASSERT_EQ(val, "wowsers");
375
376   ASSERT_EQ(cf1.read("mds", "something that does not exist", val), -ENOENT);
377
378   // exists in mds section, but not in global
379   ASSERT_EQ(cf1.read("global", "profiling logger dir", val), -ENOENT);
380
381   bufferlist bl2;
382   bl2.append(simple_conf_2, strlen(simple_conf_2));
383   ConfFile cf2;
384   ASSERT_EQ(cf2.parse_bufferlist(&bl2, &err, &warn), 0);
385   ASSERT_EQ(err.size(), 0U);
386   ASSERT_EQ(cf2.read("osd0", "keyring", val), 0);
387   ASSERT_EQ(val, "osd_keyring");
388
389   ASSERT_EQ(cf2.read("mds", "pid file", val), 0);
390   ASSERT_EQ(val, "foo2");
391   ASSERT_EQ(cf2.read("nonesuch", "keyring", val), -ENOENT);
392 }
393
394 TEST(ConfUtils, ReadFiles2) {
395   std::deque<std::string> err;
396   std::ostringstream warn;
397   std::string conf3_f(next_tempfile(conf3));
398   ConfFile cf1;
399   std::string val;
400   ASSERT_EQ(cf1.parse_file(conf3_f.c_str(), &err, &warn), 0);
401   ASSERT_EQ(err.size(), 0U);
402   ASSERT_EQ(cf1.read("global", "log file", val), 0);
403   ASSERT_EQ(val, "/quite/a/long/path/for/a/log/file");
404   ASSERT_EQ(cf1.read("global", "pid file", val), 0);
405   ASSERT_EQ(val, "spork");
406
407   std::string unicode_config_1f(next_tempfile(unicode_config_1));
408   ConfFile cf2;
409   ASSERT_EQ(cf2.parse_file(unicode_config_1f.c_str(), &err, &warn), 0);
410   ASSERT_EQ(err.size(), 0U);
411   ASSERT_EQ(cf2.read("global", "log file", val), 0);
412   ASSERT_EQ(val, "\x66\xd1\x86\xd1\x9d\xd3\xad\xd3\xae");
413 }
414
415 TEST(ConfUtils, IllegalFiles) {
416   std::deque<std::string> err;
417   std::ostringstream warn;
418   std::string illegal_conf1_f(next_tempfile(illegal_conf1));
419   ConfFile cf1;
420   ASSERT_EQ(cf1.parse_file(illegal_conf1_f.c_str(), &err, &warn), 0);
421   ASSERT_EQ(err.size(), 1U);
422
423   bufferlist bl2;
424   bl2.append(illegal_conf2, strlen(illegal_conf2));
425   ConfFile cf2;
426   ASSERT_EQ(cf2.parse_bufferlist(&bl2, &err, &warn), 0);
427   ASSERT_EQ(err.size(), 1U);
428
429   std::string illegal_conf3_f(next_tempfile(illegal_conf3));
430   ConfFile cf3;
431   ASSERT_EQ(cf3.parse_file(illegal_conf3_f.c_str(), &err, &warn), 0);
432   ASSERT_EQ(err.size(), 1U);
433
434   std::string illegal_conf4_f(next_tempfile(illegal_conf4));
435   ConfFile cf4;
436   ASSERT_EQ(cf4.parse_file(illegal_conf4_f.c_str(), &err, &warn), 0);
437   ASSERT_EQ(err.size(), 1U);
438
439   std::string illegal_conf5_f(next_tempfile(illegal_conf5));
440   ConfFile cf5;
441   ASSERT_EQ(cf5.parse_file(illegal_conf5_f.c_str(), &err, &warn), 0);
442   ASSERT_EQ(err.size(), 1U);
443 }
444
445 TEST(ConfUtils, EscapingFiles) {
446   std::deque<std::string> err;
447   std::ostringstream warn;
448   std::string escaping_conf_1_f(next_tempfile(escaping_conf_1));
449   ConfFile cf1;
450   std::string val;
451   ASSERT_EQ(cf1.parse_file(escaping_conf_1_f.c_str(), &err, &warn), 0);
452   ASSERT_EQ(err.size(), 0U);
453
454   ASSERT_EQ(cf1.read("global", "log file", val), 0);
455   ASSERT_EQ(val, "the \"scare quotes\"");
456   ASSERT_EQ(cf1.read("global", "pid file", val), 0);
457   ASSERT_EQ(val, "a pid file");
458   ASSERT_EQ(cf1.read("mon", "keyring", val), 0);
459   ASSERT_EQ(val, "nested \"quotes\"");
460
461   std::string escaping_conf_2_f(next_tempfile(escaping_conf_2));
462   ConfFile cf2;
463   ASSERT_EQ(cf2.parse_file(escaping_conf_2_f.c_str(), &err, &warn), 0);
464   ASSERT_EQ(err.size(), 0U);
465
466   ASSERT_EQ(cf2.read("apple ][", "log file", val), 0);
467   ASSERT_EQ(val, "floppy disk");
468   ASSERT_EQ(cf2.read("mon", "keyring", val), 0);
469   ASSERT_EQ(val, "backslash\\");
470 }
471
472 TEST(ConfUtils, Overrides) {
473   md_config_t conf;
474   std::ostringstream warn;
475   std::string override_conf_1_f(next_tempfile(override_config_1));
476
477   conf.name.set(CEPH_ENTITY_TYPE_MON, "0");
478   conf.parse_config_files(override_conf_1_f.c_str(), &warn, 0);
479   ASSERT_EQ(conf.parse_errors.size(), 0U);
480   ASSERT_EQ(conf.log_file, "global_log");
481
482   conf.name.set(CEPH_ENTITY_TYPE_MDS, "a");
483   conf.parse_config_files(override_conf_1_f.c_str(), &warn, 0);
484   ASSERT_EQ(conf.parse_errors.size(), 0U);
485   ASSERT_EQ(conf.log_file, "mds_log");
486
487   conf.name.set(CEPH_ENTITY_TYPE_OSD, "0");
488   conf.parse_config_files(override_conf_1_f.c_str(), &warn, 0);
489   ASSERT_EQ(conf.parse_errors.size(), 0U);
490   ASSERT_EQ(conf.log_file, "osd0_log");
491 }
492
493 TEST(ConfUtils, DupKey) {
494   md_config_t conf;
495   std::ostringstream warn;
496   std::string dup_key_config_f(next_tempfile(dup_key_config_1));
497
498   conf.name.set(CEPH_ENTITY_TYPE_MDS, "a");
499   conf.parse_config_files(dup_key_config_f.c_str(), &warn, 0);
500   ASSERT_EQ(conf.parse_errors.size(), 0U);
501   ASSERT_EQ(conf.log_file, string("3"));
502 }
503
504