LogCabin
|
00001 /* Copyright (c) 2012 Stanford University 00002 * 00003 * Permission to use, copy, modify, and distribute this software for any 00004 * purpose with or without fee is hereby granted, provided that the above 00005 * copyright notice and this permission notice appear in all copies. 00006 * 00007 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES 00008 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 00009 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR 00010 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 00011 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 00012 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 00013 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 00014 */ 00015 00016 /** 00017 * \file 00018 * Changes the membership of a LogCabin cluster. 00019 */ 00020 00021 #include <getopt.h> 00022 #include <iostream> 00023 #include <string> 00024 00025 #include <LogCabin/Client.h> 00026 #include <LogCabin/Debug.h> 00027 00028 namespace { 00029 00030 using LogCabin::Client::Cluster; 00031 using LogCabin::Client::Configuration; 00032 using LogCabin::Client::ConfigurationResult; 00033 using LogCabin::Client::Result; 00034 using LogCabin::Client::Server; 00035 using LogCabin::Client::Status; 00036 00037 /** 00038 * Parses argv for the main function. 00039 */ 00040 class OptionParser { 00041 public: 00042 OptionParser(int& argc, char**& argv) 00043 : argc(argc) 00044 , argv(argv) 00045 , cluster("logcabin:5254") 00046 , logPolicy("") 00047 , servers() 00048 { 00049 while (true) { 00050 static struct option longOptions[] = { 00051 {"cluster", required_argument, NULL, 'c'}, 00052 {"help", no_argument, NULL, 'h'}, 00053 {"verbose", no_argument, NULL, 'v'}, 00054 {"verbosity", required_argument, NULL, 256}, 00055 {0, 0, 0, 0} 00056 }; 00057 int c = getopt_long(argc, argv, "c:hv", longOptions, NULL); 00058 00059 // Detect the end of the options. 00060 if (c == -1) 00061 break; 00062 00063 switch (c) { 00064 case 'c': 00065 cluster = optarg; 00066 break; 00067 case 'h': 00068 usage(); 00069 exit(0); 00070 case 'v': 00071 logPolicy = "VERBOSE"; 00072 break; 00073 case 256: 00074 logPolicy = optarg; 00075 break; 00076 case '?': 00077 default: 00078 // getopt_long already printed an error message. 00079 usage(); 00080 exit(1); 00081 } 00082 } 00083 00084 // Additional command line arguments are required. 00085 if (optind == argc) { 00086 usage(); 00087 exit(1); 00088 } 00089 // For now, first argument must be the command "set". 00090 if (std::string("set") == argv[optind]) { 00091 ++optind; 00092 while (optind < argc) { 00093 servers.push_back(argv[optind]); 00094 ++optind; 00095 } 00096 } else { 00097 std::cerr << "Invalid command: " << argv[optind] << std::endl; 00098 usage(); 00099 exit(1); 00100 } 00101 } 00102 00103 void usage() { 00104 std::cout 00105 << "Changes the membership of a LogCabin cluster." 00106 << std::endl 00107 << std::endl 00108 << "This program was released in LogCabin v1.0.0." 00109 << std::endl 00110 << std::endl 00111 00112 << "Usage: " << argv[0] << " [options] set <server>..." 00113 << std::endl 00114 << std::endl 00115 00116 << "Options:" 00117 << std::endl 00118 00119 << " -c <addresses>, --cluster=<addresses> " 00120 << "Network addresses of the LogCabin" 00121 << std::endl 00122 << " " 00123 << "servers, including both the old and" 00124 << std::endl 00125 << " " 00126 << "the new servers, comma-separated" 00127 << std::endl 00128 << " " 00129 << "[default: logcabin:5254]" 00130 << std::endl 00131 00132 << " -h, --help " 00133 << "Print this usage information" 00134 << std::endl 00135 00136 << " -v, --verbose " 00137 << "Same as --verbosity=VERBOSE (added in v1.1.0)" 00138 << std::endl 00139 00140 << " --verbosity=<policy> " 00141 << "Set which log messages are shown." 00142 << std::endl 00143 << " " 00144 << "Comma-separated LEVEL or PATTERN@LEVEL rules." 00145 << std::endl 00146 << " " 00147 << "Levels: SILENT ERROR WARNING NOTICE VERBOSE." 00148 << std::endl 00149 << " " 00150 << "Patterns match filename prefixes or suffixes." 00151 << std::endl 00152 << " " 00153 << "Example: Client@NOTICE,Test.cc@SILENT,VERBOSE." 00154 << std::endl 00155 << " " 00156 << "(added in v1.1.0)" 00157 << std::endl; 00158 } 00159 00160 int& argc; 00161 char**& argv; 00162 std::string cluster; 00163 std::string logPolicy; 00164 std::vector<std::string> servers; 00165 }; 00166 00167 void 00168 printConfiguration(const std::pair<uint64_t, Configuration>& configuration) 00169 { 00170 std::cout << "Configuration " << configuration.first << ":" << std::endl; 00171 for (auto it = configuration.second.begin(); 00172 it != configuration.second.end(); 00173 ++it) { 00174 std::cout << "- " << it->serverId << ": " << it->addresses 00175 << std::endl; 00176 } 00177 std::cout << std::endl; 00178 } 00179 00180 00181 } // anonymous namespace 00182 00183 int 00184 main(int argc, char** argv) 00185 { 00186 OptionParser options(argc, argv); 00187 LogCabin::Client::Debug::setLogPolicy( 00188 LogCabin::Client::Debug::logPolicyFromString( 00189 options.logPolicy)); 00190 Cluster cluster(options.cluster); 00191 00192 std::pair<uint64_t, Configuration> configuration = 00193 cluster.getConfiguration(); 00194 uint64_t id = configuration.first; 00195 std::cout << "Current configuration:" << std::endl; 00196 printConfiguration(configuration); 00197 00198 std::cout << "Attempting to change cluster membership to the following:" 00199 << std::endl; 00200 Configuration servers; 00201 for (auto it = options.servers.begin(); 00202 it != options.servers.end(); 00203 ++it) { 00204 Server info; 00205 Result result = cluster.getServerInfo(*it, 00206 /* timeout = 2s */ 2000000000UL, 00207 info); 00208 switch (result.status) { 00209 case Status::OK: 00210 std::cout << info.serverId << ": " 00211 << info.addresses 00212 << " (given as " << *it << ")" 00213 << std::endl; 00214 servers.emplace_back(info.serverId, info.addresses); 00215 break; 00216 case Status::TIMEOUT: 00217 std::cout << "Could not fetch server info from " 00218 << *it << " (" << result.error << "). Aborting." 00219 << std::endl; 00220 return 1; 00221 default: 00222 std::cout << "Unknown error from " 00223 << *it << " (" << result.error << "). Aborting." 00224 << std::endl; 00225 return 1; 00226 } 00227 } 00228 std::cout << std::endl; 00229 00230 ConfigurationResult result = cluster.setConfiguration(id, servers); 00231 std::cout << "Membership change result: "; 00232 if (result.status == ConfigurationResult::OK) { 00233 std::cout << "OK" << std::endl; 00234 } else if (result.status == ConfigurationResult::CHANGED) { 00235 std::cout << "CHANGED (" << result.error << ")" << std::endl; 00236 } else if (result.status == ConfigurationResult::BAD) { 00237 std::cout << "BAD SERVERS (" << result.error << "):" << std::endl; 00238 for (auto it = result.badServers.begin(); 00239 it != result.badServers.end(); 00240 ++it) { 00241 std::cout << "- " << it->serverId << ": " << it->addresses 00242 << std::endl; 00243 } 00244 } 00245 std::cout << std::endl; 00246 00247 std::cout << "Current configuration:" << std::endl; 00248 printConfiguration(cluster.getConfiguration()); 00249 00250 if (result.status == ConfigurationResult::OK) 00251 return 0; 00252 else 00253 return 1; 00254 }