LogCabin
Examples/TreeOps.cc
Go to the documentation of this file.
00001 /* Copyright (c) 2012 Stanford University
00002  * Copyright (c) 2015 Diego Ongaro
00003  *
00004  * Permission to use, copy, modify, and distribute this software for any
00005  * purpose with or without fee is hereby granted, provided that the above
00006  * copyright notice and this permission notice appear in all copies.
00007  *
00008  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
00009  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00010  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
00011  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00012  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00013  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00014  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00015  */
00016 
00017 #include <cassert>
00018 #include <getopt.h>
00019 #include <iostream>
00020 #include <iterator>
00021 #include <sstream>
00022 
00023 #include <LogCabin/Client.h>
00024 #include <LogCabin/Debug.h>
00025 #include <LogCabin/Util.h>
00026 
00027 namespace {
00028 
00029 using LogCabin::Client::Cluster;
00030 using LogCabin::Client::Tree;
00031 using LogCabin::Client::Util::parseNonNegativeDuration;
00032 
00033 enum class Command {
00034     MKDIR,
00035     LIST,
00036     DUMP,
00037     RMDIR,
00038     WRITE,
00039     READ,
00040     REMOVE,
00041 };
00042 
00043 /**
00044  * Parses argv for the main function.
00045  */
00046 class OptionParser {
00047   public:
00048     OptionParser(int& argc, char**& argv)
00049         : argc(argc)
00050         , argv(argv)
00051         , cluster("logcabin:5254")
00052         , command()
00053         , condition()
00054         , dir()
00055         , logPolicy("")
00056         , path()
00057         , timeout(parseNonNegativeDuration("0s"))
00058     {
00059         while (true) {
00060             static struct option longOptions[] = {
00061                {"cluster",  required_argument, NULL, 'c'},
00062                {"dir",  required_argument, NULL, 'd'},
00063                {"help",  no_argument, NULL, 'h'},
00064                {"condition",  required_argument, NULL, 'p'},
00065                {"quiet",  no_argument, NULL, 'q'},
00066                {"timeout",  required_argument, NULL, 't'},
00067                {"verbose",  no_argument, NULL, 'v'},
00068                {"verbosity",  required_argument, NULL, 256},
00069                {0, 0, 0, 0}
00070             };
00071             int c = getopt_long(argc, argv, "c:d:p:t:hqv", longOptions, NULL);
00072 
00073             // Detect the end of the options.
00074             if (c == -1)
00075                 break;
00076 
00077             switch (c) {
00078                 case 'c':
00079                     cluster = optarg;
00080                     break;
00081                 case 'd':
00082                     dir = optarg;
00083                     break;
00084                 case 'h':
00085                     usage();
00086                     exit(0);
00087                 case 'p': {
00088                     std::istringstream stream(optarg);
00089                     std::string path;
00090                     std::string value;
00091                     std::getline(stream, path, ':');
00092                     std::getline(stream, value);
00093                     condition = {path, value};
00094                     break;
00095                 }
00096                 case 'q':
00097                     logPolicy = "WARNING";
00098                     break;
00099                 case 't':
00100                     timeout = parseNonNegativeDuration(optarg);
00101                     break;
00102                 case 'v':
00103                     logPolicy = "VERBOSE";
00104                     break;
00105                 case 256:
00106                     logPolicy = optarg;
00107                     break;
00108                 case '?':
00109                 default:
00110                     // getopt_long already printed an error message.
00111                     usage();
00112                     exit(1);
00113             }
00114         }
00115 
00116         // Additional command line arguments are required.
00117         if (optind == argc) {
00118             usage();
00119             exit(1);
00120         }
00121 
00122         std::string cmdStr = argv[optind];
00123         ++optind;
00124         if (cmdStr == "mkdir") {
00125             command = Command::MKDIR;
00126         } else if (cmdStr == "list" || cmdStr == "ls") {
00127             command = Command::LIST;
00128         } else if (cmdStr == "dump") {
00129             command = Command::DUMP;
00130             path = "/";
00131         } else if (cmdStr == "rmdir" || cmdStr == "removeDir") {
00132             command = Command::RMDIR;
00133         } else if (cmdStr == "write" || cmdStr == "create" ||
00134                    cmdStr == "set") {
00135             command = Command::WRITE;
00136         } else if (cmdStr == "read" || cmdStr == "get") {
00137             command = Command::READ;
00138         } else if (cmdStr == "remove" || cmdStr == "rm" ||
00139                    cmdStr == "removeFile") {
00140             command = Command::REMOVE;
00141         } else {
00142             std::cout << "Unknown command: " << cmdStr << std::endl;
00143             usage();
00144             exit(1);
00145         }
00146 
00147         if (optind < argc) {
00148             path = argv[optind];
00149             ++optind;
00150         }
00151 
00152         if (path.empty()) {
00153             std::cout << "No path given" << std::endl;
00154             usage();
00155             exit(1);
00156         }
00157         if (optind < argc) {
00158             std::cout << "Unexpected positional argument: " << argv[optind]
00159                       << std::endl;
00160             usage();
00161             exit(1);
00162         }
00163     }
00164 
00165     void usage() {
00166         std::cout << "Run various operations on a LogCabin replicated state "
00167                   << "machine."
00168                   << std::endl
00169                   << std::endl
00170                   << "This program was released in LogCabin v1.0.0."
00171                   << std::endl;
00172         std::cout << std::endl;
00173 
00174         std::cout << "Usage: " << argv[0] << " [options] <command> [<args>]"
00175                   << std::endl;
00176         std::cout << std::endl;
00177 
00178         std::cout << "Commands:" << std::endl;
00179         std::cout
00180             << "  mkdir <path>    If no directory exists at <path>, create it."
00181             << std::endl
00182             << "  list <path>     List keys within directory at <path>. "
00183             << "Alias: ls."
00184             << std::endl
00185             << "  dump [<path>]   Recursively print keys and values within "
00186             << "directory at <path>."
00187             << std::endl
00188             << "                  Defaults to printing all keys and values "
00189             << "from root of tree."
00190             << std::endl
00191             << "  rmdir <path>    Recursively remove directory at <path>, if "
00192             << "any."
00193             << std::endl
00194             << "                  Alias: removedir."
00195             << std::endl
00196             << "  write <path>    Set/create value of file at <path> to "
00197             << "stdin."
00198             << std::endl
00199             << "                  Alias: create, set."
00200             << std::endl
00201             << "  read <path>     Print value of file at <path>. Alias: get."
00202             << std::endl
00203             << "  remove <path>   Remove file at <path>, if any. Alias: rm, "
00204             << "removefile."
00205             << std::endl
00206             << std::endl;
00207 
00208         std::cout << "Options:" << std::endl;
00209         std::cout
00210             << "  -c <addresses>, --cluster=<addresses>  "
00211             << "Network addresses of the LogCabin"
00212             << std::endl
00213             << "                                         "
00214             << "servers, comma-separated"
00215             << std::endl
00216             << "                                         "
00217             << "[default: logcabin:5254]"
00218             << std::endl
00219 
00220             << "  -d <path>, --dir=<path>        "
00221             << "Set working directory [default: /]"
00222             << std::endl
00223 
00224             << "  -h, --help                     "
00225             << "Print this usage information"
00226             << std::endl
00227 
00228             << "  -p <pred>, --condition=<pred>  "
00229             << "Set predicate on the operation of the"
00230             << std::endl
00231             << "                                 "
00232             << "form <path>:<value>, indicating that the key"
00233             << std::endl
00234             << "                                 "
00235             << "at <path> must have the given value."
00236             << std::endl
00237 
00238             << "  -q, --quiet                    "
00239             << "Same as --verbosity=WARNING"
00240             << std::endl
00241 
00242             << "  -t <time>, --timeout=<time>    "
00243             << "Set timeout for the operation"
00244             << std::endl
00245             << "                                 "
00246             << "(0 means wait forever) [default: 0s]"
00247             << std::endl
00248 
00249             << "  -v, --verbose                  "
00250             << "Same as --verbosity=VERBOSE (added in v1.1.0)"
00251             << std::endl
00252 
00253             << "  --verbosity=<policy>           "
00254             << "Set which log messages are shown."
00255             << std::endl
00256             << "                                 "
00257             << "Comma-separated LEVEL or PATTERN@LEVEL rules."
00258             << std::endl
00259             << "                                 "
00260             << "Levels: SILENT ERROR WARNING NOTICE VERBOSE."
00261             << std::endl
00262             << "                                 "
00263             << "Patterns match filename prefixes or suffixes."
00264             << std::endl
00265             << "                                 "
00266             << "Example: Client@NOTICE,Test.cc@SILENT,VERBOSE."
00267             << std::endl
00268             << "                                 "
00269             << "(added in v1.1.0)"
00270             << std::endl;
00271     }
00272 
00273     int& argc;
00274     char**& argv;
00275     std::string cluster;
00276     Command command;
00277     std::pair<std::string, std::string> condition;
00278     std::string dir;
00279     std::string logPolicy;
00280     std::string path;
00281     uint64_t timeout;
00282 };
00283 
00284 /**
00285  * Depth-first search tree traversal, dumping out contents of all files
00286  */
00287 void
00288 dumpTree(const Tree& tree, std::string path)
00289 {
00290     std::cout << path << std::endl;
00291     std::vector<std::string> children = tree.listDirectoryEx(path);
00292     for (auto it = children.begin(); it != children.end(); ++it) {
00293         std::string child = path + *it;
00294         if (*child.rbegin() == '/') { // directory
00295             dumpTree(tree, child);
00296         } else { // file
00297             std::cout << child << ": " << std::endl;
00298             std::cout << "    " << tree.readEx(child) << std::endl;
00299         }
00300     }
00301 }
00302 
00303 std::string
00304 readStdin()
00305 {
00306     std::cin >> std::noskipws;
00307     std::istream_iterator<char> it(std::cin);
00308     std::istream_iterator<char> end;
00309     std::string results(it, end);
00310     return results;
00311 }
00312 
00313 } // anonymous namespace
00314 
00315 int
00316 main(int argc, char** argv)
00317 {
00318     try {
00319         OptionParser options(argc, argv);
00320 
00321         LogCabin::Client::Debug::setLogPolicy(
00322             LogCabin::Client::Debug::logPolicyFromString(
00323                 options.logPolicy));
00324 
00325         Cluster cluster(options.cluster);
00326         Tree tree = cluster.getTree();
00327 
00328         if (options.timeout > 0) {
00329             tree.setTimeout(options.timeout);
00330         }
00331 
00332         if (!options.dir.empty()) {
00333             tree.setWorkingDirectoryEx(options.dir);
00334         }
00335 
00336         if (!options.condition.first.empty()) {
00337             tree.setConditionEx(options.condition.first,
00338                                 options.condition.second);
00339         }
00340 
00341         std::string& path = options.path;
00342         switch (options.command) {
00343             case Command::MKDIR:
00344                 tree.makeDirectoryEx(path);
00345                 break;
00346             case Command::LIST: {
00347                 std::vector<std::string> keys = tree.listDirectoryEx(path);
00348                 for (auto it = keys.begin(); it != keys.end(); ++it)
00349                     std::cout << *it << std::endl;
00350                 break;
00351             }
00352             case Command::DUMP: {
00353                 if (path.empty() || path.at(path.size() - 1) != '/')
00354                     path.append("/");
00355                 dumpTree(tree, path);
00356                 break;
00357             }
00358             case Command::RMDIR:
00359                 tree.removeDirectoryEx(path);
00360                 break;
00361             case Command::WRITE:
00362                 tree.writeEx(path, readStdin());
00363                 break;
00364             case Command::READ: {
00365                 std::string contents = tree.readEx(path);
00366                 std::cout << contents;
00367                 if (contents.empty() ||
00368                     contents.at(contents.size() - 1) != '\n') {
00369                     std::cout << std::endl;
00370                 } else {
00371                     std::cout.flush();
00372                 }
00373                 break;
00374             }
00375             case Command::REMOVE:
00376                 tree.removeFileEx(path);
00377                 break;
00378         }
00379         return 0;
00380 
00381     } catch (const LogCabin::Client::Exception& e) {
00382         std::cerr << "Exiting due to LogCabin::Client::Exception: "
00383                   << e.what()
00384                   << std::endl;
00385         exit(1);
00386     }
00387 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines