Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / test_rgw_admin_log.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) 2013 eNovance SAS <licensing@enovance.com>
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 <iostream>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <time.h>
20 #include <sys/wait.h>
21 #include <unistd.h>
22 #include <fstream>
23 #include <map>
24 #include <list>
25 extern "C"{
26 #include <curl/curl.h>
27 }
28 #include "common/ceph_crypto.h"
29 #include "include/str_list.h"
30 #include "common/ceph_json.h"
31 #include "common/code_environment.h"
32 #include "common/ceph_argparse.h"
33 #include "common/Finisher.h"
34 #include "global/global_init.h"
35 #include "rgw/rgw_common.h"
36 #include "rgw/rgw_bucket.h"
37 #include "rgw/rgw_rados.h"
38 #include "include/utime.h"
39 #include "include/object.h"
40 #define GTEST
41 #ifdef GTEST
42 #include <gtest/gtest.h>
43 #else
44 #define TEST(x, y) void y()
45 #define ASSERT_EQ(v, s) if(v != s)cout << "Error at " << __LINE__ << "(" << #v << "!= " << #s << "\n"; \
46                                 else cout << "(" << #v << "==" << #s << ") PASSED\n";
47 #define EXPECT_EQ(v, s) ASSERT_EQ(v, s)
48 #define ASSERT_TRUE(c) if(c)cout << "Error at " << __LINE__ << "(" << #c << ")" << "\n"; \
49                           else cout << "(" << #c << ") PASSED\n";
50 #define EXPECT_TRUE(c) ASSERT_TRUE(c) 
51 #endif
52 using namespace std;
53
54 #define CURL_VERBOSE 0
55 #define HTTP_RESPONSE_STR "RespCode"
56 #define CEPH_CRYPTO_HMACSHA1_DIGESTSIZE 20
57 #define RGW_ADMIN_RESP_PATH "/tmp/.test_rgw_admin_resp"
58 #define TEST_BUCKET_NAME "test_bucket"
59 #define TEST_BUCKET_OBJECT "test_object"
60 #define TEST_BUCKET_OBJECT_1 "test_object1"
61 #define TEST_BUCKET_OBJECT_SIZE 1024
62
63 static string uid = "ceph";
64 static string display_name = "CEPH";
65
66 extern "C" int ceph_armor(char *dst, const char *dst_end, 
67                           const char *src, const char *end);
68 static void print_usage(char *exec){
69   cout << "Usage: " << exec << " <Options>\n";
70   cout << "Options:\n"
71           "-g <gw-ip> - The ip address of the gateway\n"
72           "-p <gw-port> - The port number of the gateway\n"
73           "-c <ceph.conf> - Absolute path of ceph config file\n"
74           "-rgw-admin <path/to/radosgw-admin> - radosgw-admin absolute path\n";
75 }
76
77 namespace admin_log {
78 class test_helper {
79   private:
80     string host;
81     string port;
82     string creds;
83     string rgw_admin_path;
84     string conf_path;
85     CURL *curl_inst;
86     map<string, string> response;
87     list<string> extra_hdrs;
88     string *resp_data;
89     unsigned resp_code;
90   public:
91     test_helper() : resp_data(NULL){
92       curl_global_init(CURL_GLOBAL_ALL);
93     }
94     ~test_helper(){
95       curl_global_cleanup();
96     }
97     int send_request(string method, string uri, 
98                      size_t (*function)(void *,size_t,size_t,void *) = 0,
99                      void *ud = 0, size_t length = 0);
100     int extract_input(int argc, char *argv[]);
101     string& get_response(string hdr){
102       return response[hdr];
103     }
104     void set_extra_header(string hdr){
105       extra_hdrs.push_back(hdr);
106     }
107     void set_response(char *val);
108     void set_response_data(char *data, size_t len){
109       if(resp_data) delete resp_data;
110       resp_data = new string(data, len);
111     }
112     string& get_rgw_admin_path() {
113       return rgw_admin_path;
114     }
115     string& get_ceph_conf_path() {
116       return conf_path;
117     }
118     void set_creds(string& c) {
119       creds = c;
120     }
121     const string *get_response_data(){return resp_data;}
122     unsigned get_resp_code(){return resp_code;}
123 };
124
125 int test_helper::extract_input(int argc, char *argv[]){
126 #define ERR_CHECK_NEXT_PARAM(o) \
127   if(((int)loop + 1) >= argc)return -1;         \
128   else o = argv[loop+1];
129
130   for(unsigned loop = 1;loop < (unsigned)argc; loop += 2){
131     if(strcmp(argv[loop], "-g") == 0){
132       ERR_CHECK_NEXT_PARAM(host);
133     }else if(strcmp(argv[loop],"-p") == 0){
134       ERR_CHECK_NEXT_PARAM(port);
135     }else if(strcmp(argv[loop], "-c") == 0){
136       ERR_CHECK_NEXT_PARAM(conf_path);
137     }else if(strcmp(argv[loop], "-rgw-admin") == 0){
138       ERR_CHECK_NEXT_PARAM(rgw_admin_path);
139     }else return -1;
140   }
141   if(host.length() <= 0 ||
142      rgw_admin_path.length() <= 0)
143     return -1;
144   return 0;
145 }
146
147 void test_helper::set_response(char *r){
148   string sr(r), h, v;
149   size_t off = sr.find(": ");
150   if(off != string::npos){
151     h.assign(sr, 0, off);
152     v.assign(sr, off + 2, sr.find("\r\n") - (off+2));
153   }else{
154     /*Could be the status code*/
155     if(sr.find("HTTP/") != string::npos){
156       h.assign(HTTP_RESPONSE_STR);
157       off = sr.find(" ");
158       v.assign(sr, off + 1, sr.find("\r\n") - (off + 1));
159       resp_code = atoi((v.substr(0, 3)).c_str());
160     }
161   }
162   response[h] = v;
163 }
164
165 size_t write_header(void *ptr, size_t size, size_t nmemb, void *ud){
166   test_helper *h = static_cast<test_helper *>(ud);
167   h->set_response((char *)ptr);
168   return size*nmemb;
169 }
170
171 size_t write_data(void *ptr, size_t size, size_t nmemb, void *ud){
172   test_helper *h = static_cast<test_helper *>(ud);
173   h->set_response_data((char *)ptr, size*nmemb);
174   return size*nmemb;
175 }
176
177 static inline void buf_to_hex(const unsigned char *buf, int len, char *str)
178 {
179   int i;
180   str[0] = '\0';
181   for (i = 0; i < len; i++) {
182     sprintf(&str[i*2], "%02x", (int)buf[i]);
183   }
184 }
185
186 static void calc_hmac_sha1(const char *key, int key_len,
187                     const char *msg, int msg_len, char *dest)
188 /* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
189 {
190   ceph::crypto::HMACSHA1 hmac((const unsigned char *)key, key_len);
191   hmac.Update((const unsigned char *)msg, msg_len);
192   hmac.Final((unsigned char *)dest);
193   
194   char hex_str[(CEPH_CRYPTO_HMACSHA1_DIGESTSIZE * 2) + 1];
195   admin_log::buf_to_hex((unsigned char *)dest, CEPH_CRYPTO_HMACSHA1_DIGESTSIZE, hex_str);
196 }
197
198 static int get_s3_auth(string method, string creds, string date, string res, string& out){
199   string aid, secret, auth_hdr;
200   string tmp_res;
201   size_t off = creds.find(":");
202   out = "";
203   if(off != string::npos){
204     aid.assign(creds, 0, off);
205     secret.assign(creds, off + 1, string::npos);
206
207     /*sprintf(auth_hdr, "%s\n\n\n%s\n%s", req_type, date, res);*/
208     char hmac_sha1[CEPH_CRYPTO_HMACSHA1_DIGESTSIZE];
209     char b64[65]; /* 64 is really enough */
210     size_t off = res.find("?");
211     if(off == string::npos)
212       tmp_res = res;
213     else
214       tmp_res.assign(res, 0, off);
215     auth_hdr.append(method + string("\n\n\n") + date + string("\n") + tmp_res);
216     admin_log::calc_hmac_sha1(secret.c_str(), secret.length(), 
217                                auth_hdr.c_str(), auth_hdr.length(), hmac_sha1);
218     int ret = ceph_armor(b64, b64 + 64, hmac_sha1,
219                          hmac_sha1 + CEPH_CRYPTO_HMACSHA1_DIGESTSIZE);
220     if (ret < 0) {
221       cout << "ceph_armor failed\n";
222       return -1;
223     }
224     b64[ret] = 0;
225     out.append(aid + string(":") + b64);
226   }else return -1;
227   return 0;
228 }
229
230 void get_date(string& d){
231   struct timeval tv;
232   char date[64];
233   struct tm tm;
234   char *days[] = {(char *)"Sun", (char *)"Mon", (char *)"Tue",
235                   (char *)"Wed", (char *)"Thu", (char *)"Fri", 
236                   (char *)"Sat"};
237   char *months[] = {(char *)"Jan", (char *)"Feb", (char *)"Mar", 
238                     (char *)"Apr", (char *)"May", (char *)"Jun",
239                     (char *)"Jul",(char *) "Aug", (char *)"Sep", 
240                     (char *)"Oct", (char *)"Nov", (char *)"Dec"};
241   gettimeofday(&tv, NULL);
242   gmtime_r(&tv.tv_sec, &tm);
243   sprintf(date, "%s, %d %s %d %d:%d:%d GMT", 
244           days[tm.tm_wday], 
245           tm.tm_mday, months[tm.tm_mon], 
246           tm.tm_year + 1900,
247           tm.tm_hour, tm.tm_min, tm.tm_sec);
248   d = date;
249 }
250
251 int test_helper::send_request(string method, string res, 
252                                    size_t (*read_function)( void *,size_t,size_t,void *),
253                                    void *ud,
254                                    size_t length){
255   string url;
256   string auth, date;
257   url.append(string("http://") + host);
258   if(port.length() > 0)url.append(string(":") + port);
259   url.append(res);
260   curl_inst = curl_easy_init();
261   if(curl_inst){
262     curl_easy_setopt(curl_inst, CURLOPT_URL, url.c_str());
263     curl_easy_setopt(curl_inst, CURLOPT_CUSTOMREQUEST, method.c_str());
264     curl_easy_setopt(curl_inst, CURLOPT_VERBOSE, CURL_VERBOSE);
265     curl_easy_setopt(curl_inst, CURLOPT_HEADERFUNCTION, admin_log::write_header);
266     curl_easy_setopt(curl_inst, CURLOPT_WRITEHEADER, (void *)this);
267     curl_easy_setopt(curl_inst, CURLOPT_WRITEFUNCTION, admin_log::write_data);
268     curl_easy_setopt(curl_inst, CURLOPT_WRITEDATA, (void *)this);
269     if(read_function){
270       curl_easy_setopt(curl_inst, CURLOPT_READFUNCTION, read_function);
271       curl_easy_setopt(curl_inst, CURLOPT_READDATA, (void *)ud);
272       curl_easy_setopt(curl_inst, CURLOPT_UPLOAD, 1L);
273       curl_easy_setopt(curl_inst, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length);
274     }
275
276     get_date(date);
277     string http_date;
278     http_date.append(string("Date: ") + date);
279
280     string s3auth;
281     if (admin_log::get_s3_auth(method, creds, date, res, s3auth) < 0)
282       return -1;
283     auth.append(string("Authorization: AWS ") + s3auth);
284
285     struct curl_slist *slist = NULL;
286     slist = curl_slist_append(slist, auth.c_str());
287     slist = curl_slist_append(slist, http_date.c_str());
288     for(list<string>::iterator it = extra_hdrs.begin();
289         it != extra_hdrs.end(); ++it){
290       slist = curl_slist_append(slist, (*it).c_str());
291     }
292     if(read_function)
293       curl_slist_append(slist, "Expect:");
294     curl_easy_setopt(curl_inst, CURLOPT_HTTPHEADER, slist); 
295
296     response.erase(response.begin(), response.end());
297     extra_hdrs.erase(extra_hdrs.begin(), extra_hdrs.end());
298     CURLcode res = curl_easy_perform(curl_inst);
299     if(res != CURLE_OK){
300       cout << "Curl perform failed for " << url << ", res: " << 
301         curl_easy_strerror(res) << "\n";
302       return -1;
303     }
304     curl_slist_free_all(slist);
305   }
306   curl_easy_cleanup(curl_inst);
307   return 0;
308 }
309 };
310
311 admin_log::test_helper *g_test;
312 Finisher *finisher;
313
314 int run_rgw_admin(string& cmd, string& resp) {
315   pid_t pid;
316   pid = fork();
317   if (pid == 0) {
318     /* child */
319     list<string> l;
320     get_str_list(cmd, " \t", l);
321     char *argv[l.size()];
322     unsigned loop = 1;
323
324     argv[0] = (char *)"radosgw-admin";
325     for (list<string>::iterator it = l.begin(); 
326          it != l.end(); ++it) {
327       argv[loop++] = (char *)(*it).c_str();
328     }
329     argv[loop] = NULL;
330     if (!freopen(RGW_ADMIN_RESP_PATH, "w+", stdout)) {
331       cout << "Unable to open stdout file" << std::endl;
332     }
333     execv((g_test->get_rgw_admin_path()).c_str(), argv); 
334   } else if (pid > 0) {
335     int status;
336     waitpid(pid, &status, 0);
337     if (WIFEXITED(status)) {
338       if(WEXITSTATUS(status) != 0) {
339         cout << "Child exited with status " << WEXITSTATUS(status) << std::endl;
340         return -1;
341       }
342     }
343     ifstream in;
344     struct stat st;
345
346     if (stat(RGW_ADMIN_RESP_PATH, &st) < 0) {
347       cout << "Error stating the admin response file, errno " << errno << std::endl;
348       return -1;
349     } else {
350       char *data = (char *)malloc(st.st_size + 1);
351       in.open(RGW_ADMIN_RESP_PATH);
352       in.read(data, st.st_size);
353       in.close();
354       data[st.st_size] = 0;
355       resp = data;
356       free(data);
357       unlink(RGW_ADMIN_RESP_PATH);
358       /* cout << "radosgw-admin " << cmd << ": " << resp << std::endl; */
359     }
360   } else 
361     return -1;
362   return 0;
363 }
364
365 int get_creds(string& json, string& creds) {
366   JSONParser parser;
367   if(!parser.parse(json.c_str(), json.length())) {
368     cout << "Error parsing create user response" << std::endl;
369     return -1;
370   }
371
372   RGWUserInfo info;
373   decode_json_obj(info, &parser);
374   creds = "";
375   for(map<string, RGWAccessKey>::iterator it = info.access_keys.begin();
376       it != info.access_keys.end(); ++it) {
377     RGWAccessKey _k = it->second;
378     /*cout << "accesskeys [ " << it->first << " ] = " << 
379       "{ " << _k.id << ", " << _k.key << ", " << _k.subuser << "}" << std::endl;*/
380     creds.append(it->first + string(":") + _k.key);
381     break;
382   }
383   return 0;
384 }
385
386 int user_create(string& uid, string& display_name, bool set_creds = true) {
387   stringstream ss;
388   string creds;
389   ss << "-c " << g_test->get_ceph_conf_path() << " user create --uid=" << uid
390     << " --display-name=" << display_name;
391
392   string out;
393   string cmd = ss.str();
394   if(run_rgw_admin(cmd, out) != 0) {
395     cout << "Error creating user" << std::endl;
396     return -1;
397   }
398   get_creds(out, creds);
399   if(set_creds)
400     g_test->set_creds(creds);
401   return 0;
402 }
403
404 int user_info(string& uid, string& display_name, RGWUserInfo& uinfo) {
405   stringstream ss;
406   ss << "-c " << g_test->get_ceph_conf_path() << " user info --uid=" << uid
407     << " --display-name=" << display_name;
408
409   string out;
410   string cmd = ss.str();
411   if(run_rgw_admin(cmd, out) != 0) {
412     cout << "Error reading user information" << std::endl;
413     return -1;
414   }
415   JSONParser parser;
416   if(!parser.parse(out.c_str(), out.length())) {
417     cout << "Error parsing create user response" << std::endl;
418     return -1;
419   }
420   decode_json_obj(uinfo, &parser);
421   return 0;
422 }
423
424 int user_rm(string& uid, string& display_name) {
425   stringstream ss;
426   ss << "-c " << g_test->get_ceph_conf_path() << 
427     " metadata rm --metadata-key=user:" << uid;
428
429   string out;
430   string cmd = ss.str();
431   if(run_rgw_admin(cmd, out) != 0) {
432     cout << "Error removing user" << std::endl;
433     return -1;
434   }
435   return 0;
436 }
437
438 int caps_add(const char * name, const char *perm) {
439   stringstream ss;
440
441   ss << "-c " << g_test->get_ceph_conf_path() << " caps add --caps=" <<
442      name << "=" << perm << " --uid=" << uid;
443   string out;
444   string cmd = ss.str();
445   if(run_rgw_admin(cmd, out) != 0) {
446     cout << "Error creating user" << std::endl;
447     return -1;
448   }
449   return 0;
450 }
451
452 int caps_rm(const char * name, const char *perm) {
453   stringstream ss;
454
455   ss << "-c " << g_test->get_ceph_conf_path() << " caps rm --caps=" <<
456      name << "=" << perm << " --uid=" << uid;
457   string out;
458   string cmd = ss.str();
459   if(run_rgw_admin(cmd, out) != 0) {
460     cout << "Error creating user" << std::endl;
461     return -1;
462   }
463   return 0;
464 }
465
466 static int create_bucket(void){
467   g_test->send_request(string("PUT"), string("/" TEST_BUCKET_NAME));
468   if(g_test->get_resp_code() != 200U){
469     cout << "Error creating bucket, http code " << g_test->get_resp_code();
470     return -1;
471   }
472   return 0;
473 }
474
475 static int delete_bucket(void){
476   g_test->send_request(string("DELETE"), string("/" TEST_BUCKET_NAME));
477   if(g_test->get_resp_code() != 204U){
478     cout << "Error deleting bucket, http code " << g_test->get_resp_code();
479     return -1;
480   }
481   return 0;
482 }
483
484 size_t read_dummy_post(void *ptr, size_t s, size_t n, void *ud) {
485   int dummy = 0;
486   memcpy(ptr, &dummy, sizeof(dummy));
487   return sizeof(dummy);
488 }
489
490 size_t read_bucket_object(void *ptr, size_t s, size_t n, void *ud) {
491   memcpy(ptr, ud, TEST_BUCKET_OBJECT_SIZE);
492   return TEST_BUCKET_OBJECT_SIZE;
493 }
494
495 static int put_bucket_obj(const char *obj_name, char *data, unsigned len) {
496   string req = "/" TEST_BUCKET_NAME"/";
497   req.append(obj_name);
498   g_test->send_request(string("PUT"), req,
499                        read_bucket_object, (void *)data, (size_t)len);
500   if (g_test->get_resp_code() != 200U) {
501     cout << "Errror sending object to the bucket, http_code " << g_test->get_resp_code();
502     return -1;
503   }
504   return 0;
505 }
506
507 static int read_bucket_obj(const char *obj_name) {
508   string req = "/" TEST_BUCKET_NAME"/";
509   req.append(obj_name);
510   g_test->send_request(string("GET"), req);
511   if (g_test->get_resp_code() != 200U) {
512     cout << "Errror sending object to the bucket, http_code " << g_test->get_resp_code();
513     return -1;
514   }
515   return 0;
516 }
517
518 static int delete_obj(const char *obj_name) {
519   string req = "/" TEST_BUCKET_NAME"/";
520   req.append(obj_name);
521   g_test->send_request(string("DELETE"), req);
522   if (g_test->get_resp_code() != 204U) {
523     cout << "Errror deleting object from bucket, http_code " << g_test->get_resp_code();
524     return -1;
525   }
526   return 0;
527 }
528
529 int get_formatted_time(string& ret) {
530   struct tm *tm = NULL;
531   char str_time[200];
532   const char *format = "%Y-%m-%d%%20%H:%M:%S";
533   time_t t;
534
535   t = time(NULL);
536   tm = gmtime(&t);
537   if(!tm) {
538     cerr << "Error returned by gmtime\n";
539     return -1;
540   }
541   if (strftime(str_time, sizeof(str_time), format, tm) == 0) {
542     cerr << "Error returned by strftime\n";
543     return -1;
544   }
545   ret = str_time;
546   return 0;
547 }
548
549 int parse_json_resp(JSONParser &parser) {
550   string *resp;
551   resp = (string *)g_test->get_response_data();
552   if(!resp)
553     return -1;
554   if(!parser.parse(resp->c_str(), resp->length())) {
555     cout << "Error parsing create user response" << std::endl;
556     return -1;
557   }
558   return 0;
559 }
560
561 struct cls_log_entry_json {
562   string section;
563   string name;
564   utime_t timestamp;
565   RGWMetadataLogData log_data;
566 };
567
568 static int decode_json(JSONObj *obj, RGWMetadataLogData &data) {
569   JSONObj *jo;
570
571   jo = obj->find_obj("read_version");
572   if (!jo)
573     return -1;
574   data.read_version.decode_json(obj);
575   data.write_version.decode_json(obj);
576
577   jo = obj->find_obj("status");
578   if (!jo)
579     return -1;
580   JSONDecoder::decode_json("status", data, jo);
581   return 0;
582 }
583
584 static int decode_json(JSONObj *obj, cls_log_entry_json& ret) {
585   JSONDecoder::decode_json("section", ret.section, obj);
586   JSONDecoder::decode_json("name", ret.name, obj);
587   JSONObj *jo = obj->find_obj("data");
588   if(!jo) 
589     return 0;
590   return decode_json(jo, ret.log_data);
591 }
592
593 static int get_log_list(list<cls_log_entry_json> &entries) {
594   JSONParser parser;
595   if (parse_json_resp(parser) != 0)
596     return -1;
597   if (!parser.is_array()) 
598     return -1;
599
600   vector<string> l;
601   l = parser.get_array_elements();
602   int loop = 0;
603   for(vector<string>::iterator it = l.begin();
604       it != l.end(); ++it, loop++) {
605     JSONParser jp;
606     cls_log_entry_json entry;
607
608     if(!jp.parse((*it).c_str(), (*it).length())) {
609       cerr << "Error parsing log json object" << std::endl;
610       return -1;
611     }
612     EXPECT_EQ(decode_json((JSONObj *)&jp, entry), 0);
613     entries.push_back(entry);
614   }
615   return 0;
616 }
617
618 struct cls_bilog_entry {
619   string op_id;
620   string op_tag;
621   string op;
622   string object;
623   string status;
624   unsigned index_ver;
625 };
626
627 static int decode_json(JSONObj *obj, cls_bilog_entry& ret) {
628   JSONDecoder::decode_json("op_id", ret.op_id, obj);
629   JSONDecoder::decode_json("op_tag", ret.op_tag, obj);
630   JSONDecoder::decode_json("op", ret.op, obj);
631   JSONDecoder::decode_json("object", ret.object, obj);
632   JSONDecoder::decode_json("state", ret.status, obj);
633   JSONDecoder::decode_json("index_ver", ret.index_ver, obj);
634   return 0;
635 }
636
637 static int get_bilog_list(list<cls_bilog_entry> &entries) {
638   JSONParser parser;
639   if (parse_json_resp(parser) != 0)
640     return -1;
641   if (!parser.is_array()) 
642     return -1;
643
644   vector<string> l;
645   l = parser.get_array_elements();
646   int loop = 0;
647   for(vector<string>::iterator it = l.begin();
648       it != l.end(); ++it, loop++) {
649     JSONParser jp;
650     cls_bilog_entry entry;
651
652     if(!jp.parse((*it).c_str(), (*it).length())) {
653       cerr << "Error parsing log json object" << std::endl;
654       return -1;
655     }
656     EXPECT_EQ(decode_json((JSONObj *)&jp, entry), 0);
657     entries.push_back(entry);
658   }
659   return 0;
660 }
661
662 static int decode_json(JSONObj *obj, rgw_data_change& ret) {
663   string entity;
664
665   JSONDecoder::decode_json("entity_type", entity, obj);
666   if (entity.compare("bucket") == 0)
667     ret.entity_type = ENTITY_TYPE_BUCKET;
668   JSONDecoder::decode_json("key", ret.key, obj);
669   return 0;
670 }
671
672 static int get_datalog_list(list<rgw_data_change> &entries) {
673   JSONParser parser;
674
675   if (parse_json_resp(parser) != 0)
676     return -1;
677   if (!parser.is_array()) 
678     return -1;
679
680   vector<string> l;
681   l = parser.get_array_elements();
682   int loop = 0;
683   for(vector<string>::iterator it = l.begin();
684       it != l.end(); ++it, loop++) {
685     JSONParser jp;
686     rgw_data_change entry;
687
688     if(!jp.parse((*it).c_str(), (*it).length())) {
689       cerr << "Error parsing log json object" << std::endl;
690       return -1;
691     }
692     EXPECT_EQ(decode_json((JSONObj *)&jp, entry), 0);
693     entries.push_back(entry);
694   }
695   return 0;
696 }
697
698 unsigned get_mdlog_shard_id(string& key, int max_shards) {
699   string section = "user";
700   uint32_t val = ceph_str_hash_linux(key.c_str(), key.size());
701   val ^= ceph_str_hash_linux(section.c_str(), section.size());
702   return (unsigned)(val % max_shards);
703 }
704
705 unsigned get_datalog_shard_id(const char *bucket_name, int max_shards) {
706   uint32_t r = ceph_str_hash_linux(bucket_name, strlen(bucket_name)) % max_shards;
707   return (int)r;
708 }
709
710 TEST(TestRGWAdmin, datalog_list) {
711   string start_time, 
712          end_time;
713   const char *cname = "datalog",
714              *perm = "*";
715   string rest_req;
716   unsigned shard_id = get_datalog_shard_id(TEST_BUCKET_NAME, g_ceph_context->_conf->rgw_data_log_num_shards);
717   stringstream ss;
718   list<rgw_data_change> entries;
719
720   ASSERT_EQ(get_formatted_time(start_time), 0);
721   ASSERT_EQ(0, user_create(uid, display_name));
722   ASSERT_EQ(0, caps_add(cname, perm));
723
724   rest_req = "/admin/log?type=data";
725   g_test->send_request(string("GET"), rest_req);
726   EXPECT_EQ(200U, g_test->get_resp_code());
727   JSONParser parser;
728   int num_objects;
729   EXPECT_EQ (parse_json_resp(parser), 0);
730   JSONDecoder::decode_json("num_objects", num_objects, (JSONObj *)&parser);
731   ASSERT_EQ(num_objects,g_ceph_context->_conf->rgw_data_log_num_shards);
732  
733   sleep(1);
734   ASSERT_EQ(0, create_bucket());
735   
736   char *bucket_obj = (char *)calloc(1, TEST_BUCKET_OBJECT_SIZE);
737   ASSERT_TRUE(bucket_obj != NULL);
738   EXPECT_EQ(put_bucket_obj(TEST_BUCKET_OBJECT, bucket_obj, TEST_BUCKET_OBJECT_SIZE), 0);
739   sleep(1); 
740   ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time;
741   rest_req = ss.str();
742   g_test->send_request(string("GET"), rest_req);
743   EXPECT_EQ(200U, g_test->get_resp_code());
744   entries.clear();
745   get_datalog_list(entries);
746   EXPECT_EQ(1U, entries.size());
747   if (entries.size() == 1) {
748     rgw_data_change entry = *(entries.begin());
749     EXPECT_EQ(entry.entity_type, ENTITY_TYPE_BUCKET);
750     EXPECT_EQ(entry.key.compare(TEST_BUCKET_NAME), 0);
751   }
752   ASSERT_EQ(0, delete_obj(TEST_BUCKET_OBJECT));
753   sleep(1);
754   ASSERT_EQ(get_formatted_time(end_time), 0);
755   ss.str("");
756   ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time;
757   rest_req = ss.str();
758   g_test->send_request(string("GET"), rest_req);
759   EXPECT_EQ(200U, g_test->get_resp_code());
760   entries.clear();
761   get_datalog_list(entries);
762   EXPECT_EQ(1U, entries.size());
763   if (entries.size() == 1) {
764     list<rgw_data_change>::iterator it = (entries.begin());
765     EXPECT_EQ((*it).entity_type, ENTITY_TYPE_BUCKET);
766     EXPECT_EQ((*it).key.compare(TEST_BUCKET_NAME), 0);
767   }
768
769   sleep(1);
770   EXPECT_EQ(put_bucket_obj(TEST_BUCKET_OBJECT, bucket_obj, TEST_BUCKET_OBJECT_SIZE), 0);
771   free(bucket_obj);
772   sleep(20);
773   ss.str("");
774   ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time;
775   rest_req = ss.str();
776   g_test->send_request(string("GET"), rest_req);
777   EXPECT_EQ(200U, g_test->get_resp_code());
778   entries.clear();
779   get_datalog_list(entries);
780   EXPECT_EQ(2U, entries.size());
781   if (entries.size() == 2) {
782     list<rgw_data_change>::iterator it = (entries.begin());
783     EXPECT_EQ((*it).entity_type, ENTITY_TYPE_BUCKET);
784     EXPECT_EQ((*it).key.compare(TEST_BUCKET_NAME), 0);
785     ++it; 
786     EXPECT_EQ((*it).entity_type, ENTITY_TYPE_BUCKET);
787     EXPECT_EQ((*it).key.compare(TEST_BUCKET_NAME), 0);
788   }
789
790   ss.str("");
791   ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time
792     << "&max-entries=1";
793   rest_req = ss.str();
794   g_test->send_request(string("GET"), rest_req);
795   EXPECT_EQ(200U, g_test->get_resp_code());
796   entries.clear();
797   get_datalog_list(entries);
798   EXPECT_EQ(1U, entries.size());
799   
800   ss.str("");
801   ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time 
802     << "&end-time=" << end_time;
803   rest_req = ss.str();
804   g_test->send_request(string("GET"), rest_req);
805   EXPECT_EQ(200U, g_test->get_resp_code());
806   entries.clear();
807   get_datalog_list(entries);
808   EXPECT_EQ(1U, entries.size());
809
810   ASSERT_EQ(0, caps_rm(cname, perm));
811   perm = "read";
812   ASSERT_EQ(0, caps_add(cname, perm));
813   ss.str("");
814   ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time;
815   rest_req = ss.str();
816   g_test->send_request(string("GET"), rest_req);
817   EXPECT_EQ(200U, g_test->get_resp_code());
818   ASSERT_EQ(0, caps_rm(cname, perm));
819   ss.str("");
820   ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time;
821   rest_req = ss.str();
822
823   g_test->send_request(string("GET"), rest_req);
824   EXPECT_EQ(403U, g_test->get_resp_code());
825
826   ASSERT_EQ(0, delete_obj(TEST_BUCKET_OBJECT));
827   ASSERT_EQ(0, delete_bucket());
828   ASSERT_EQ(0, user_rm(uid, display_name));
829 }
830
831 TEST(TestRGWAdmin, datalog_lock_unlock) {
832   const char *cname = "datalog",
833              *perm = "*";
834   string rest_req;
835
836   ASSERT_EQ(0, user_create(uid, display_name));
837   ASSERT_EQ(0, caps_add(cname, perm));
838
839   rest_req = "/admin/log?type=data&lock&length=3&locker-id=ceph&zone-id=1";
840   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
841   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
842   
843   rest_req = "/admin/log?type=data&lock&id=3&locker-id=ceph&zone-id=1";
844   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
845   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
846   
847   rest_req = "/admin/log?type=data&lock&length=3&id=1&zone-id=1";
848   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
849   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
850   
851   rest_req = "/admin/log?type=data&lock&length=3&id=1&locker-id=1";
852   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
853   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
854
855   rest_req = "/admin/log?type=data&unlock&id=1&zone-id=1";
856   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
857   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
858
859   rest_req = "/admin/log?type=data&unlock&locker-id=ceph&zone-id=1";
860   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
861   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
862   
863   rest_req = "/admin/log?type=data&unlock&locker-id=ceph&id=1";
864   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
865   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
866   
867   rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph&zone-id=1";
868   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
869   EXPECT_EQ(200U, g_test->get_resp_code()); 
870   
871   rest_req = "/admin/log?type=data&unlock&id=1&locker-id=ceph&zone-id=1";
872   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
873   EXPECT_EQ(200U, g_test->get_resp_code()); 
874   
875   rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph1&zone-id=1";
876   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
877   EXPECT_EQ(200U, g_test->get_resp_code()); 
878   
879   rest_req = "/admin/log?type=data&unlock&id=1&locker-id=ceph1&zone-id=1";
880   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
881   EXPECT_EQ(200U, g_test->get_resp_code()); 
882   
883   rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph&zone-id=1";
884   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
885   EXPECT_EQ(200U, g_test->get_resp_code()); 
886   utime_t sleep_time(3, 0);
887
888   rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph1&zone-id=1";
889   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
890   EXPECT_EQ(500U, g_test->get_resp_code()); 
891
892   rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph1&zone-id=2";
893   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
894   EXPECT_EQ(500U, g_test->get_resp_code()); 
895
896   rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph&zone-id=1";
897   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
898   EXPECT_EQ(200U, g_test->get_resp_code()); 
899   sleep_time.sleep();
900
901   rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph1&zone-id=1";
902   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
903   EXPECT_EQ(200U, g_test->get_resp_code()); 
904   
905   rest_req = "/admin/log?type=data&unlock&id=1&locker-id=ceph1&zone-id=1";
906   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
907   EXPECT_EQ(200U, g_test->get_resp_code()); 
908
909   ASSERT_EQ(0, caps_rm(cname, perm));
910   perm = "read";
911   ASSERT_EQ(0, caps_add(cname, perm));
912   rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph&zone-id=1";
913   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
914   EXPECT_EQ(403U, g_test->get_resp_code()); 
915   
916   rest_req = "/admin/log?type=data&unlock&id=1&locker-id=ceph&zone-id=1";
917   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
918   EXPECT_EQ(403U, g_test->get_resp_code()); 
919   
920   ASSERT_EQ(0, caps_rm(cname, perm));
921   perm = "write";
922   ASSERT_EQ(0, caps_add(cname, perm));
923   rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph&zone-id=1";
924   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
925   EXPECT_EQ(200U, g_test->get_resp_code()); 
926   
927   rest_req = "/admin/log?type=data&unlock&id=1&locker-id=ceph&zone-id=1";
928   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
929   EXPECT_EQ(200U, g_test->get_resp_code()); 
930   
931   ASSERT_EQ(0, caps_rm(cname, perm));
932   rest_req = "/admin/log?type=data&lock&id=1&length=3&locker-id=ceph&zone-id=1";
933   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
934   EXPECT_EQ(403U, g_test->get_resp_code()); 
935   
936   rest_req = "/admin/log?type=data&unlock&id=1&locker-id=ceph&zone-id=1";
937   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
938   EXPECT_EQ(403U, g_test->get_resp_code()); 
939   
940   ASSERT_EQ(0, user_rm(uid, display_name));
941 }
942
943 TEST(TestRGWAdmin, datalog_trim) {
944   string start_time, 
945          end_time;
946   const char *cname = "datalog",
947              *perm = "*";
948   string rest_req;
949   unsigned shard_id = get_datalog_shard_id(TEST_BUCKET_NAME, g_ceph_context->_conf->rgw_data_log_num_shards);
950   stringstream ss;
951   list<rgw_data_change> entries;
952
953   ASSERT_EQ(get_formatted_time(start_time), 0);
954   ASSERT_EQ(0, user_create(uid, display_name));
955   ASSERT_EQ(0, caps_add(cname, perm));
956
957   rest_req = "/admin/log?type=data";
958   g_test->send_request(string("DELETE"), rest_req);
959   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
960   
961   ss.str("");
962   ss << "/admin/log?type=data&start-time=" << start_time;
963   rest_req = ss.str();
964   g_test->send_request(string("DELETE"), rest_req);
965   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
966  
967   ss.str("");
968   ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time;
969   rest_req = ss.str();
970   g_test->send_request(string("DELETE"), rest_req);
971   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
972   
973   ASSERT_EQ(0, create_bucket());
974
975   char *bucket_obj = (char *)calloc(1, TEST_BUCKET_OBJECT_SIZE);
976   ASSERT_TRUE(bucket_obj != NULL);
977   EXPECT_EQ(put_bucket_obj(TEST_BUCKET_OBJECT, bucket_obj, TEST_BUCKET_OBJECT_SIZE), 0);
978   ASSERT_EQ(0, delete_obj(TEST_BUCKET_OBJECT));
979   sleep(1);
980   EXPECT_EQ(put_bucket_obj(TEST_BUCKET_OBJECT, bucket_obj, TEST_BUCKET_OBJECT_SIZE), 0);
981   ASSERT_EQ(0, delete_obj(TEST_BUCKET_OBJECT));
982   sleep(20);
983   free(bucket_obj);
984
985   ASSERT_EQ(get_formatted_time(end_time), 0);
986   ss.str("");
987   ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time 
988     << "&end-time=" << end_time;
989   rest_req = ss.str();
990   g_test->send_request(string("GET"), rest_req);
991   EXPECT_EQ(200U, g_test->get_resp_code());
992   entries.clear();
993   get_datalog_list(entries);
994   EXPECT_TRUE(!entries.empty());
995
996   ss.str("");
997   ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time 
998     << "&end-time=" << end_time;
999   rest_req = ss.str();
1000   g_test->send_request(string("DELETE"), rest_req);
1001   EXPECT_EQ(200U, g_test->get_resp_code());
1002
1003   ss.str("");
1004   ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time 
1005     << "&end-time=" << end_time;
1006   rest_req = ss.str();
1007   g_test->send_request(string("GET"), rest_req);
1008   EXPECT_EQ(200U, g_test->get_resp_code());
1009   entries.clear();
1010   get_datalog_list(entries);
1011   EXPECT_TRUE(entries.empty());
1012
1013   ASSERT_EQ(0, caps_rm(cname, perm));
1014   perm = "write";
1015   ASSERT_EQ(0, caps_add(cname, perm));
1016   ss.str("");
1017   ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time 
1018     << "&end-time=" << end_time;
1019   rest_req = ss.str();
1020   g_test->send_request(string("DELETE"), rest_req);
1021   EXPECT_EQ(200U, g_test->get_resp_code());
1022
1023   ASSERT_EQ(0, caps_rm(cname, perm));
1024   perm = "";
1025   ASSERT_EQ(0, caps_add(cname, perm));
1026   ss.str("");
1027   ss << "/admin/log?type=data&id=" << shard_id << "&start-time=" << start_time 
1028     << "&end-time=" << end_time;
1029   rest_req = ss.str();
1030   g_test->send_request(string("DELETE"), rest_req);
1031   EXPECT_EQ(403U, g_test->get_resp_code());
1032
1033   ASSERT_EQ(0, delete_bucket());
1034   ASSERT_EQ(0, user_rm(uid, display_name));
1035 }
1036
1037 TEST(TestRGWAdmin, mdlog_list) {
1038   string start_time, 
1039          end_time,
1040          start_time_2;
1041   const char *cname = "mdlog",
1042              *perm = "*";
1043   string rest_req;
1044   unsigned shard_id = get_mdlog_shard_id(uid, g_ceph_context->_conf->rgw_md_log_max_shards);
1045   stringstream ss;
1046
1047   sleep(2);
1048   ASSERT_EQ(get_formatted_time(start_time), 0);
1049   ASSERT_EQ(0, user_create(uid, display_name));
1050   ASSERT_EQ(0, caps_add(cname, perm));
1051
1052   rest_req = "/admin/log?type=metadata";
1053   g_test->send_request(string("GET"), rest_req);
1054   EXPECT_EQ(200U, g_test->get_resp_code());
1055   JSONParser parser;
1056   int num_objects;
1057   EXPECT_EQ (parse_json_resp(parser), 0);
1058   JSONDecoder::decode_json("num_objects", num_objects, (JSONObj *)&parser);
1059   ASSERT_EQ(num_objects,g_ceph_context->_conf->rgw_md_log_max_shards);
1060
1061   ss.str("");
1062   ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time;
1063   rest_req = ss.str();
1064   g_test->send_request(string("GET"), rest_req);
1065   EXPECT_EQ(200U, g_test->get_resp_code());
1066   
1067   list<cls_log_entry_json> entries;
1068   EXPECT_EQ(get_log_list(entries), 0);
1069   EXPECT_EQ(entries.size(), 4U);
1070
1071   if(entries.size() == 4) {
1072     list<cls_log_entry_json>::iterator it = entries.begin();
1073     EXPECT_TRUE(it->section.compare("user") == 0);
1074     EXPECT_TRUE(it->name.compare(uid) == 0);
1075     EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_WRITE);
1076     ++it;
1077     EXPECT_TRUE(it->section.compare("user") == 0);
1078     EXPECT_TRUE(it->name.compare(uid) == 0);
1079     EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_COMPLETE);
1080     ++it;
1081     EXPECT_TRUE(it->section.compare("user") == 0);
1082     EXPECT_TRUE(it->name.compare(uid) == 0);
1083     EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_WRITE);
1084     ++it;
1085     EXPECT_TRUE(it->section.compare("user") == 0);
1086     EXPECT_TRUE(it->name.compare(uid) == 0);
1087     EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_COMPLETE);
1088   }
1089
1090   sleep(1); /*To get a modified time*/
1091   ASSERT_EQ(get_formatted_time(start_time_2), 0);
1092   ASSERT_EQ(0, caps_rm(cname, perm));
1093   perm="read";
1094   ASSERT_EQ(0, caps_add(cname, perm));
1095   ss.str("");
1096   ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time_2;
1097   rest_req = ss.str();
1098   g_test->send_request(string("GET"), rest_req);
1099   EXPECT_EQ(200U, g_test->get_resp_code());
1100  
1101   entries.clear();
1102   EXPECT_EQ(get_log_list(entries), 0);
1103   EXPECT_EQ(entries.size(), 4U);
1104
1105   if(entries.size() == 4) {
1106     list<cls_log_entry_json>::iterator it = entries.begin();
1107     EXPECT_TRUE(it->section.compare("user") == 0);
1108     EXPECT_TRUE(it->name.compare(uid) == 0);
1109     EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_WRITE);
1110     ++it;
1111     EXPECT_TRUE(it->section.compare("user") == 0);
1112     EXPECT_TRUE(it->name.compare(uid) == 0);
1113     EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_COMPLETE);
1114     ++it;
1115     EXPECT_TRUE(it->section.compare("user") == 0);
1116     EXPECT_TRUE(it->name.compare(uid) == 0);
1117     EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_WRITE);
1118     ++it;
1119     EXPECT_TRUE(it->section.compare("user") == 0);
1120     EXPECT_TRUE(it->name.compare(uid) == 0);
1121     EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_COMPLETE);
1122   }
1123
1124   sleep(1);
1125   ASSERT_EQ(get_formatted_time(start_time_2), 0);
1126   ASSERT_EQ(0, user_rm(uid, display_name));
1127   
1128   ASSERT_EQ(0, user_create(uid, display_name));
1129   perm = "*";
1130   ASSERT_EQ(0, caps_add(cname, perm));
1131
1132   ss.str("");
1133   ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time_2;
1134   rest_req = ss.str();
1135   g_test->send_request(string("GET"), rest_req);
1136   EXPECT_EQ(200U, g_test->get_resp_code());
1137   
1138   entries.clear();
1139   EXPECT_EQ(get_log_list(entries), 0);
1140   EXPECT_EQ(entries.size(), 6U);
1141   if(entries.size() == 6) {
1142     list<cls_log_entry_json>::iterator it = entries.begin();
1143     EXPECT_TRUE(it->section.compare("user") == 0);
1144     EXPECT_TRUE(it->name.compare(uid) == 0);
1145     EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_REMOVE);
1146     ++it;
1147     EXPECT_TRUE(it->section.compare("user") == 0);
1148     EXPECT_TRUE(it->name.compare(uid) == 0);
1149     ++it;
1150     EXPECT_TRUE(it->section.compare("user") == 0);
1151     EXPECT_TRUE(it->name.compare(uid) == 0);
1152     EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_WRITE);
1153     ++it;
1154     EXPECT_TRUE(it->section.compare("user") == 0);
1155     EXPECT_TRUE(it->name.compare(uid) == 0);
1156     EXPECT_TRUE(it->log_data.status == MDLOG_STATUS_COMPLETE);
1157   }
1158
1159   sleep(1);
1160   ASSERT_EQ(get_formatted_time(end_time), 0);
1161   ss.str("");
1162   ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time 
1163     << "&end-time=" << end_time;
1164   rest_req = ss.str();
1165   g_test->send_request(string("GET"), rest_req);
1166   EXPECT_EQ(200U, g_test->get_resp_code());
1167   entries.clear();
1168   EXPECT_EQ(get_log_list(entries), 0);
1169   EXPECT_EQ(entries.size(), 14U);
1170
1171   ss.str("");
1172   ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time 
1173     << "&max-entries=" << 1;
1174   rest_req = ss.str();
1175   g_test->send_request(string("GET"), rest_req);
1176   EXPECT_EQ(200U, g_test->get_resp_code());
1177   entries.clear();
1178   EXPECT_EQ(get_log_list(entries), 0);
1179   EXPECT_EQ(entries.size(), 1U);
1180
1181   ss.str("");
1182   ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time 
1183     << "&max-entries=" << 6;
1184   rest_req = ss.str();
1185   g_test->send_request(string("GET"), rest_req);
1186   EXPECT_EQ(200U, g_test->get_resp_code());
1187   entries.clear();
1188   EXPECT_EQ(get_log_list(entries), 0);
1189   EXPECT_EQ(entries.size(), 6U);
1190
1191   ASSERT_EQ(0, caps_rm(cname, perm));
1192   ss.str("");
1193   ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time;
1194   rest_req = ss.str();
1195   g_test->send_request(string("GET"), rest_req);
1196   EXPECT_EQ(403U, g_test->get_resp_code());
1197
1198   /*cleanup*/
1199   ASSERT_EQ(0, caps_add(cname, perm));
1200   sleep(1);
1201   ASSERT_EQ(get_formatted_time(end_time), 0);
1202   ss.str("");
1203   ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time 
1204     << "&end-time=" << end_time;
1205   rest_req = ss.str();
1206   g_test->send_request(string("DELETE"), rest_req);
1207   EXPECT_EQ(200U, g_test->get_resp_code());
1208
1209   ASSERT_EQ(0, user_rm(uid, display_name));
1210 }
1211
1212 TEST(TestRGWAdmin, mdlog_trim) {
1213   string start_time, 
1214          end_time;
1215   const char *cname = "mdlog",
1216              *perm = "*";
1217   string rest_req;
1218   list<cls_log_entry_json> entries;
1219   unsigned shard_id = get_mdlog_shard_id(uid, g_ceph_context->_conf->rgw_md_log_max_shards);
1220   ostringstream ss;
1221
1222   sleep(1);
1223   ASSERT_EQ(get_formatted_time(start_time), 0);
1224   ASSERT_EQ(0, user_create(uid, display_name));
1225   ASSERT_EQ(0, caps_add(cname, perm));
1226
1227   ss.str("");
1228   ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time;
1229   rest_req = ss.str();
1230   g_test->send_request(string("DELETE"), rest_req);
1231   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
1232
1233   g_test->send_request(string("GET"), rest_req);
1234   EXPECT_EQ(200U, g_test->get_resp_code());
1235   EXPECT_EQ(get_log_list(entries), 0);
1236   EXPECT_EQ(entries.size(), 4U);
1237
1238   sleep(1);
1239   ASSERT_EQ(get_formatted_time(end_time), 0);
1240   ss.str("");
1241   ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time << "&end-time=" << end_time;
1242   rest_req = ss.str();
1243   g_test->send_request(string("DELETE"), rest_req);
1244   EXPECT_EQ(200U, g_test->get_resp_code());
1245
1246   ss.str("");
1247   ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time;
1248   rest_req = ss.str();
1249   g_test->send_request(string("GET"), rest_req);
1250   EXPECT_EQ(200U, g_test->get_resp_code());
1251   entries.clear();
1252   EXPECT_EQ(get_log_list(entries), 0);
1253   EXPECT_EQ(entries.size(), 0U);
1254
1255   ASSERT_EQ(0, caps_rm(cname, perm));
1256   perm="write";
1257   ASSERT_EQ(0, caps_add(cname, perm));
1258   ASSERT_EQ(get_formatted_time(end_time), 0);
1259   ss.str("");
1260   ss << "/admin/log?type=metadata&id=" << shard_id << "&start-time=" << start_time << "&end-time=" << end_time;
1261   rest_req = ss.str();
1262   g_test->send_request(string("DELETE"), rest_req);
1263   EXPECT_EQ(200U, g_test->get_resp_code());
1264
1265   ASSERT_EQ(0, caps_rm(cname, perm));
1266   g_test->send_request(string("DELETE"), rest_req);
1267   EXPECT_EQ(403U, g_test->get_resp_code());
1268   ASSERT_EQ(0, user_rm(uid, display_name));
1269 }
1270
1271 TEST(TestRGWAdmin, mdlog_lock_unlock) {
1272   const char *cname = "mdlog",
1273              *perm = "*";
1274   string rest_req;
1275
1276   ASSERT_EQ(0, user_create(uid, display_name));
1277   ASSERT_EQ(0, caps_add(cname, perm));
1278
1279   rest_req = "/admin/log?type=metadata&lock&length=3&locker-id=ceph&zone-id=1";
1280   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1281   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
1282   
1283   rest_req = "/admin/log?type=metadata&lock&id=3&locker-id=ceph&zone-id=1";
1284   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1285   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
1286   
1287   rest_req = "/admin/log?type=metadata&lock&length=3&id=1&zone-id=1";
1288   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1289   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
1290
1291   rest_req = "/admin/log?type=metadata&lock&id=3&locker-id=ceph&length=1";
1292   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1293   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
1294   
1295   rest_req = "/admin/log?type=metadata&unlock&id=1&zone-id=1";
1296   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1297   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
1298
1299   rest_req = "/admin/log?type=metadata&unlock&locker-id=ceph&zone-id=1";
1300   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1301   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
1302   
1303   rest_req = "/admin/log?type=metadata&unlock&locker-id=ceph&id=1";
1304   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1305   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
1306   
1307   rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph&zone-id=1";
1308   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1309   EXPECT_EQ(200U, g_test->get_resp_code()); 
1310   
1311   rest_req = "/admin/log?type=metadata&unlock&id=1&locker-id=ceph&zone-id=1";
1312   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1313   EXPECT_EQ(200U, g_test->get_resp_code()); 
1314   
1315   rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph1&zone-id=1";
1316   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1317   EXPECT_EQ(200U, g_test->get_resp_code()); 
1318   
1319   rest_req = "/admin/log?type=metadata&unlock&id=1&locker-id=ceph1&zone-id=1";
1320   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1321   EXPECT_EQ(200U, g_test->get_resp_code()); 
1322   
1323   rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph&zone-id=1";
1324   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1325   EXPECT_EQ(200U, g_test->get_resp_code()); 
1326   utime_t sleep_time(3, 0);
1327
1328   rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph1&zone-id=1";
1329   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1330   EXPECT_EQ(500U, g_test->get_resp_code()); 
1331
1332   rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph&zone-id=2";
1333   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1334   EXPECT_EQ(500U, g_test->get_resp_code()); 
1335
1336   rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph&zone-id=1";
1337   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1338   EXPECT_EQ(200U, g_test->get_resp_code()); 
1339   sleep_time.sleep();
1340
1341   rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph1&zone-id=1";
1342   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1343   EXPECT_EQ(200U, g_test->get_resp_code()); 
1344   
1345   rest_req = "/admin/log?type=metadata&unlock&id=1&locker-id=ceph1&zone-id=1";
1346   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1347   EXPECT_EQ(200U, g_test->get_resp_code()); 
1348
1349   ASSERT_EQ(0, caps_rm(cname, perm));
1350   perm = "read";
1351   ASSERT_EQ(0, caps_add(cname, perm));
1352   rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph&zone-id=1";
1353   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1354   EXPECT_EQ(403U, g_test->get_resp_code()); 
1355   
1356   rest_req = "/admin/log?type=metadata&unlock&id=1&locker-id=ceph&zone-id=1";
1357   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1358   EXPECT_EQ(403U, g_test->get_resp_code()); 
1359   
1360   ASSERT_EQ(0, caps_rm(cname, perm));
1361   perm = "write";
1362   ASSERT_EQ(0, caps_add(cname, perm));
1363   rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph&zone-id=1";
1364   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1365   EXPECT_EQ(200U, g_test->get_resp_code()); 
1366   
1367   rest_req = "/admin/log?type=metadata&unlock&id=1&locker-id=ceph&zone-id=1";
1368   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1369   EXPECT_EQ(200U, g_test->get_resp_code()); 
1370   
1371   ASSERT_EQ(0, caps_rm(cname, perm));
1372   rest_req = "/admin/log?type=metadata&lock&id=1&length=3&locker-id=ceph&zone-id=1";
1373   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1374   EXPECT_EQ(403U, g_test->get_resp_code()); 
1375   
1376   rest_req = "/admin/log?type=metadata&unlock&id=1&locker-id=ceph&zone-id=1";
1377   g_test->send_request(string("POST"), rest_req, read_dummy_post, NULL, sizeof(int));
1378   EXPECT_EQ(403U, g_test->get_resp_code()); 
1379   
1380   ASSERT_EQ(0, user_rm(uid, display_name));
1381 }
1382
1383 TEST(TestRGWAdmin, bilog_list) {
1384   const char *cname = "bilog",
1385              *perm = "*";
1386   string rest_req;
1387
1388   ASSERT_EQ(0, user_create(uid, display_name));
1389   ASSERT_EQ(0, caps_add(cname, perm));
1390
1391   ASSERT_EQ(0, create_bucket());
1392
1393   char *bucket_obj = (char *)calloc(1, TEST_BUCKET_OBJECT_SIZE);
1394   ASSERT_TRUE(bucket_obj != NULL);
1395   EXPECT_EQ(put_bucket_obj(TEST_BUCKET_OBJECT, bucket_obj, TEST_BUCKET_OBJECT_SIZE), 0);
1396   free(bucket_obj);
1397   
1398   rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
1399   g_test->send_request(string("GET"), rest_req);
1400   EXPECT_EQ(200U, g_test->get_resp_code());
1401   list<cls_bilog_entry> entries;
1402   get_bilog_list(entries);
1403   EXPECT_EQ(2U, entries.size());
1404   if (entries.size() == 2) {
1405     list<cls_bilog_entry>::iterator it = entries.begin();
1406     EXPECT_EQ(it->op.compare("write"), 0);
1407     EXPECT_EQ(it->object.compare(TEST_BUCKET_OBJECT), 0);
1408     EXPECT_EQ(it->status.compare("pending"), 0);
1409     EXPECT_EQ(it->index_ver, 1U);
1410     ++it;
1411     EXPECT_EQ(it->op.compare("write"), 0);
1412     EXPECT_EQ(it->object.compare(TEST_BUCKET_OBJECT), 0);
1413     EXPECT_EQ(it->status.compare("complete"), 0);
1414     EXPECT_EQ(it->index_ver, 2U);
1415   }
1416   EXPECT_EQ(read_bucket_obj(TEST_BUCKET_OBJECT), 0);
1417   g_test->send_request(string("GET"), rest_req);
1418   EXPECT_EQ(200U, g_test->get_resp_code());
1419   entries.clear();
1420   get_bilog_list(entries);
1421   EXPECT_EQ(2U, entries.size());
1422
1423   bucket_obj = (char *)calloc(1, TEST_BUCKET_OBJECT_SIZE);
1424   ASSERT_TRUE(bucket_obj != NULL);
1425   EXPECT_EQ(put_bucket_obj(TEST_BUCKET_OBJECT_1, bucket_obj, TEST_BUCKET_OBJECT_SIZE), 0);
1426   free(bucket_obj);
1427   
1428   rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
1429   g_test->send_request(string("GET"), rest_req);
1430   EXPECT_EQ(200U, g_test->get_resp_code());
1431   entries.clear();
1432   get_bilog_list(entries);
1433   EXPECT_EQ(4U, entries.size());
1434   if (entries.size() == 4) {
1435     list<cls_bilog_entry>::iterator it = entries.begin();
1436
1437     ++it; ++it;
1438     EXPECT_EQ(it->op.compare("write"), 0);
1439     EXPECT_EQ(it->object.compare(TEST_BUCKET_OBJECT_1), 0);
1440     EXPECT_EQ(it->status.compare("pending"), 0);
1441     EXPECT_EQ(it->index_ver, 3U);
1442     ++it;
1443     EXPECT_EQ(it->op.compare("write"), 0);
1444     EXPECT_EQ(it->object.compare(TEST_BUCKET_OBJECT_1), 0);
1445     EXPECT_EQ(it->status.compare("complete"), 0);
1446     EXPECT_EQ(it->index_ver, 4U);
1447   }
1448
1449   ASSERT_EQ(0, delete_obj(TEST_BUCKET_OBJECT));
1450   rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
1451   g_test->send_request(string("GET"), rest_req);
1452   EXPECT_EQ(200U, g_test->get_resp_code());
1453   entries.clear();
1454   get_bilog_list(entries);
1455
1456   EXPECT_EQ(6U, entries.size());
1457   string marker;
1458   if (entries.size() == 6) {
1459     list<cls_bilog_entry>::iterator it = entries.begin();
1460     
1461     ++it; ++it; ++it; ++it;
1462     marker = it->op_id;
1463     EXPECT_EQ(it->op.compare("del"), 0);
1464     EXPECT_EQ(it->object.compare(TEST_BUCKET_OBJECT), 0);
1465     EXPECT_EQ(it->status.compare("pending"), 0);
1466     EXPECT_EQ(it->index_ver, 5U);
1467     ++it;
1468     EXPECT_EQ(it->op.compare("del"), 0);
1469     EXPECT_EQ(it->object.compare(TEST_BUCKET_OBJECT), 0);
1470     EXPECT_EQ(it->status.compare("complete"), 0);
1471     EXPECT_EQ(it->index_ver, 6U);
1472   }
1473
1474   rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
1475   rest_req.append("&marker=");
1476   rest_req.append(marker);
1477   g_test->send_request(string("GET"), rest_req);
1478   EXPECT_EQ(200U, g_test->get_resp_code());
1479   entries.clear();
1480   get_bilog_list(entries);
1481   EXPECT_EQ(2U, entries.size());
1482   if (entries.size() == 2U) {
1483     list<cls_bilog_entry>::iterator it = entries.begin();
1484     EXPECT_EQ(it->index_ver, 5U);
1485     ++it;
1486     EXPECT_EQ(it->index_ver, 6U);
1487     EXPECT_EQ(it->op.compare("del"), 0);
1488   }
1489
1490   rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
1491   rest_req.append("&marker=");
1492   rest_req.append(marker);
1493   rest_req.append("&max-entries=1");
1494   g_test->send_request(string("GET"), rest_req);
1495   EXPECT_EQ(200U, g_test->get_resp_code());
1496   entries.clear();
1497   get_bilog_list(entries);
1498   EXPECT_EQ(1U, entries.size());
1499   EXPECT_EQ((entries.begin())->index_ver, 5U);
1500
1501   ASSERT_EQ(0, caps_rm(cname, perm));
1502   perm = "read";
1503   ASSERT_EQ(0, caps_add(cname, perm));
1504   rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
1505   g_test->send_request(string("GET"), rest_req);
1506   EXPECT_EQ(200U, g_test->get_resp_code());
1507
1508   ASSERT_EQ(0, caps_rm(cname, perm));
1509   perm = "write";
1510   ASSERT_EQ(0, caps_add(cname, perm));
1511   rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
1512   g_test->send_request(string("GET"), rest_req);
1513   EXPECT_EQ(403U, g_test->get_resp_code());
1514
1515   ASSERT_EQ(0, delete_obj(TEST_BUCKET_OBJECT_1));
1516   ASSERT_EQ(0, delete_bucket());
1517   ASSERT_EQ(0, user_rm(uid, display_name));
1518 }
1519
1520 TEST(TestRGWAdmin, bilog_trim) {
1521   const char *cname = "bilog",
1522              *perm = "*";
1523   string rest_req, start_marker, end_marker;
1524
1525   ASSERT_EQ(0, user_create(uid, display_name));
1526   ASSERT_EQ(0, caps_add(cname, perm));
1527
1528   ASSERT_EQ(0, create_bucket());
1529
1530   rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
1531   g_test->send_request(string("DELETE"), rest_req);
1532   EXPECT_EQ(400U, g_test->get_resp_code()); /*Bad request*/
1533
1534   char *bucket_obj = (char *)calloc(1, TEST_BUCKET_OBJECT_SIZE);
1535   ASSERT_TRUE(bucket_obj != NULL);
1536   EXPECT_EQ(put_bucket_obj(TEST_BUCKET_OBJECT, bucket_obj, TEST_BUCKET_OBJECT_SIZE), 0);
1537   free(bucket_obj);
1538   
1539   rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
1540   g_test->send_request(string("GET"), rest_req);
1541   EXPECT_EQ(200U, g_test->get_resp_code());
1542   list<cls_bilog_entry> entries;
1543   get_bilog_list(entries);
1544   EXPECT_EQ(2U, entries.size());
1545
1546   list<cls_bilog_entry>::iterator it = entries.begin();
1547   start_marker = it->op_id;
1548   ++it;
1549   end_marker = it->op_id;
1550
1551   rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
1552   rest_req.append("&start-marker=");
1553   rest_req.append(start_marker);
1554   rest_req.append("&end-marker=");
1555   rest_req.append(end_marker);
1556   g_test->send_request(string("DELETE"), rest_req);
1557   EXPECT_EQ(200U, g_test->get_resp_code());
1558
1559   rest_req = "/admin/log?type=bucket-index&bucket=" TEST_BUCKET_NAME;
1560   g_test->send_request(string("GET"), rest_req);
1561   EXPECT_EQ(200U, g_test->get_resp_code());
1562   entries.clear();
1563   get_bilog_list(entries);
1564   EXPECT_EQ(0U, entries.size());
1565   
1566   ASSERT_EQ(0, delete_obj(TEST_BUCKET_OBJECT));
1567   ASSERT_EQ(0, delete_bucket());
1568   ASSERT_EQ(0, user_rm(uid, display_name));
1569 }
1570
1571 int main(int argc, char *argv[]){
1572   vector<const char*> args;
1573   argv_to_vec(argc, (const char **)argv, args);
1574
1575   auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
1576                          CODE_ENVIRONMENT_UTILITY, 0);
1577   common_init_finish(g_ceph_context);
1578   g_test = new admin_log::test_helper();
1579   finisher = new Finisher(g_ceph_context);
1580 #ifdef GTEST
1581   ::testing::InitGoogleTest(&argc, argv);
1582 #endif
1583   finisher->start();
1584
1585   if(g_test->extract_input(argc, argv) < 0){
1586     print_usage(argv[0]);
1587     return -1;
1588   }
1589 #ifdef GTEST
1590   int r = RUN_ALL_TESTS();
1591   if (r >= 0) {
1592     cout << "There are no failures in the test case\n";
1593   } else {
1594     cout << "There are some failures\n";
1595   }
1596 #endif
1597   finisher->stop();
1598   return 0;
1599 }