blob: 480ce0ed5fb209feedcfc934282d282e6f043aaf [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.registry.cli;
import static org.apache.hadoop.registry.client.binding.RegistryTypeUtils.*;
import java.io.Closeable;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import com.google.common.base.Preconditions;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.PathNotFoundException;
import org.apache.hadoop.security.AccessControlException;
import org.apache.hadoop.service.ServiceOperations;
import org.apache.hadoop.util.ExitUtil;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import org.apache.hadoop.registry.client.api.BindFlags;
import org.apache.hadoop.registry.client.api.RegistryOperations;
import org.apache.hadoop.registry.client.api.RegistryOperationsFactory;
import org.apache.hadoop.registry.client.exceptions.AuthenticationFailedException;
import org.apache.hadoop.registry.client.exceptions.InvalidPathnameException;
import org.apache.hadoop.registry.client.exceptions.InvalidRecordException;
import org.apache.hadoop.registry.client.exceptions.NoPathPermissionsException;
import org.apache.hadoop.registry.client.exceptions.NoRecordException;
import org.apache.hadoop.registry.client.types.Endpoint;
import org.apache.hadoop.registry.client.types.ProtocolTypes;
import org.apache.hadoop.registry.client.types.ServiceRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Command line for registry operations.
*/
public class RegistryCli extends Configured implements Tool, Closeable {
private static final Logger LOG =
LoggerFactory.getLogger(RegistryCli.class);
protected final PrintStream sysout;
protected final PrintStream syserr;
private RegistryOperations registry;
private static final String LS_USAGE = "ls pathName";
private static final String RESOLVE_USAGE = "resolve pathName";
private static final String BIND_USAGE =
"bind -inet -api apiName -p portNumber -h hostName pathName" + "\n"
+ "bind -webui uriString -api apiName pathName" + "\n"
+ "bind -rest uriString -api apiName pathName";
private static final String MKNODE_USAGE = "mknode directoryName";
private static final String RM_USAGE = "rm pathName";
private static final String USAGE =
"\n" + LS_USAGE + "\n" + RESOLVE_USAGE + "\n" + BIND_USAGE + "\n" +
MKNODE_USAGE + "\n" + RM_USAGE;
public RegistryCli(PrintStream sysout, PrintStream syserr) {
Configuration conf = new Configuration();
super.setConf(conf);
registry = RegistryOperationsFactory.createInstance(conf);
registry.start();
this.sysout = sysout;
this.syserr = syserr;
}
public RegistryCli(RegistryOperations reg,
Configuration conf,
PrintStream sysout,
PrintStream syserr) {
super(conf);
Preconditions.checkArgument(reg != null, "Null registry");
registry = reg;
this.sysout = sysout;
this.syserr = syserr;
}
@SuppressWarnings("UseOfSystemOutOrSystemErr")
public static void main(String[] args) throws Exception {
int res = -1;
try (RegistryCli cli = new RegistryCli(System.out, System.err)) {
res = ToolRunner.run(cli, args);
} catch (Exception e) {
ExitUtil.terminate(res, e);
}
ExitUtil.terminate(res);
}
/**
* Close the object by stopping the registry.
* <p>
* <i>Important:</i>
* <p>
* After this call is made, no operations may be made of this
* object, <i>or of a YARN registry instance used when constructing
* this object. </i>
* @throws IOException
*/
@Override
public void close() throws IOException {
ServiceOperations.stopQuietly(registry);
registry = null;
}
private int usageError(String err, String usage) {
syserr.println("Error: " + err);
syserr.println("Usage: " + usage);
return -1;
}
private boolean validatePath(String path) {
if (!path.startsWith("/")) {
syserr.println("Path must start with /; given path was: " + path);
return false;
}
return true;
}
@Override
public int run(String[] args) throws Exception {
Preconditions.checkArgument(getConf() != null, "null configuration");
if (args.length > 0) {
switch (args[0]) {
case "ls":
return ls(args);
case "resolve":
return resolve(args);
case "bind":
return bind(args);
case "mknode":
return mknode(args);
case "rm":
return rm(args);
default:
return usageError("Invalid command: " + args[0], USAGE);
}
}
return usageError("No command arg passed.", USAGE);
}
@SuppressWarnings("unchecked")
public int ls(String[] args) {
Options lsOption = new Options();
CommandLineParser parser = new GnuParser();
try {
CommandLine line = parser.parse(lsOption, args);
List<String> argsList = line.getArgList();
if (argsList.size() != 2) {
return usageError("ls requires exactly one path argument", LS_USAGE);
}
if (!validatePath(argsList.get(1))) {
return -1;
}
try {
List<String> children = registry.list(argsList.get(1));
for (String child : children) {
sysout.println(child);
}
return 0;
} catch (Exception e) {
syserr.println(analyzeException("ls", e, argsList));
}
return -1;
} catch (ParseException exp) {
return usageError("Invalid syntax " + exp, LS_USAGE);
}
}
@SuppressWarnings("unchecked")
public int resolve(String[] args) {
Options resolveOption = new Options();
CommandLineParser parser = new GnuParser();
try {
CommandLine line = parser.parse(resolveOption, args);
List<String> argsList = line.getArgList();
if (argsList.size() != 2) {
return usageError("resolve requires exactly one path argument",
RESOLVE_USAGE);
}
if (!validatePath(argsList.get(1))) {
return -1;
}
try {
ServiceRecord record = registry.resolve(argsList.get(1));
for (Endpoint endpoint : record.external) {
sysout.println(" Endpoint(ProtocolType="
+ endpoint.protocolType + ", Api="
+ endpoint.api + ");"
+ " Addresses(AddressType="
+ endpoint.addressType + ") are: ");
for (Map<String, String> address : endpoint.addresses) {
sysout.println("[ ");
for (Map.Entry<String, String> entry : address.entrySet()) {
sysout.print("\t" + entry.getKey()
+ ":" + entry.getValue());
}
sysout.println("\n]");
}
sysout.println();
}
return 0;
} catch (Exception e) {
syserr.println(analyzeException("resolve", e, argsList));
}
return -1;
} catch (ParseException exp) {
return usageError("Invalid syntax " + exp, RESOLVE_USAGE);
}
}
public int bind(String[] args) {
Option rest = OptionBuilder.withArgName("rest")
.hasArg()
.withDescription("rest Option")
.create("rest");
Option webui = OptionBuilder.withArgName("webui")
.hasArg()
.withDescription("webui Option")
.create("webui");
Option inet = OptionBuilder.withArgName("inet")
.withDescription("inet Option")
.create("inet");
Option port = OptionBuilder.withArgName("port")
.hasArg()
.withDescription("port to listen on [9999]")
.create("p");
Option host = OptionBuilder.withArgName("host")
.hasArg()
.withDescription("host name")
.create("h");
Option apiOpt = OptionBuilder.withArgName("api")
.hasArg()
.withDescription("api")
.create("api");
Options inetOption = new Options();
inetOption.addOption(inet);
inetOption.addOption(port);
inetOption.addOption(host);
inetOption.addOption(apiOpt);
Options webuiOpt = new Options();
webuiOpt.addOption(webui);
webuiOpt.addOption(apiOpt);
Options restOpt = new Options();
restOpt.addOption(rest);
restOpt.addOption(apiOpt);
CommandLineParser parser = new GnuParser();
ServiceRecord sr = new ServiceRecord();
CommandLine line;
if (args.length <= 1) {
return usageError("Invalid syntax ", BIND_USAGE);
}
if (args[1].equals("-inet")) {
int portNum;
String hostName;
String api;
try {
line = parser.parse(inetOption, args);
} catch (ParseException exp) {
return usageError("Invalid syntax " + exp.getMessage(), BIND_USAGE);
}
if (line.hasOption("inet") && line.hasOption("p") &&
line.hasOption("h") && line.hasOption("api")) {
try {
portNum = Integer.parseInt(line.getOptionValue("p"));
} catch (NumberFormatException exp) {
return usageError("Invalid Port - int required" + exp.getMessage(),
BIND_USAGE);
}
hostName = line.getOptionValue("h");
api = line.getOptionValue("api");
sr.addExternalEndpoint(
inetAddrEndpoint(api, ProtocolTypes.PROTOCOL_HADOOP_IPC, hostName,
portNum));
} else {
return usageError("Missing options: must have host, port and api",
BIND_USAGE);
}
} else if (args[1].equals("-webui")) {
try {
line = parser.parse(webuiOpt, args);
} catch (ParseException exp) {
return usageError("Invalid syntax " + exp.getMessage(), BIND_USAGE);
}
if (line.hasOption("webui") && line.hasOption("api")) {
URI theUri;
try {
theUri = new URI(line.getOptionValue("webui"));
} catch (URISyntaxException e) {
return usageError("Invalid URI: " + e.getMessage(), BIND_USAGE);
}
sr.addExternalEndpoint(webEndpoint(line.getOptionValue("api"), theUri));
} else {
return usageError("Missing options: must have value for uri and api",
BIND_USAGE);
}
} else if (args[1].equals("-rest")) {
try {
line = parser.parse(restOpt, args);
} catch (ParseException exp) {
return usageError("Invalid syntax " + exp.getMessage(), BIND_USAGE);
}
if (line.hasOption("rest") && line.hasOption("api")) {
URI theUri = null;
try {
theUri = new URI(line.getOptionValue("rest"));
} catch (URISyntaxException e) {
return usageError("Invalid URI: " + e.getMessage(), BIND_USAGE);
}
sr.addExternalEndpoint(
restEndpoint(line.getOptionValue("api"), theUri));
} else {
return usageError("Missing options: must have value for uri and api",
BIND_USAGE);
}
} else {
return usageError("Invalid syntax", BIND_USAGE);
}
@SuppressWarnings("unchecked")
List<String> argsList = line.getArgList();
if (argsList.size() != 2) {
return usageError("bind requires exactly one path argument", BIND_USAGE);
}
if (!validatePath(argsList.get(1))) {
return -1;
}
try {
registry.bind(argsList.get(1), sr, BindFlags.OVERWRITE);
return 0;
} catch (Exception e) {
syserr.println(analyzeException("bind", e, argsList));
}
return -1;
}
@SuppressWarnings("unchecked")
public int mknode(String[] args) {
Options mknodeOption = new Options();
CommandLineParser parser = new GnuParser();
try {
CommandLine line = parser.parse(mknodeOption, args);
List<String> argsList = line.getArgList();
if (argsList.size() != 2) {
return usageError("mknode requires exactly one path argument",
MKNODE_USAGE);
}
if (!validatePath(argsList.get(1))) {
return -1;
}
try {
registry.mknode(args[1], false);
return 0;
} catch (Exception e) {
syserr.println(analyzeException("mknode", e, argsList));
}
return -1;
} catch (ParseException exp) {
return usageError("Invalid syntax " + exp.toString(), MKNODE_USAGE);
}
}
@SuppressWarnings("unchecked")
public int rm(String[] args) {
Option recursive = OptionBuilder.withArgName("recursive")
.withDescription("delete recursively")
.create("r");
Options rmOption = new Options();
rmOption.addOption(recursive);
boolean recursiveOpt = false;
CommandLineParser parser = new GnuParser();
try {
CommandLine line = parser.parse(rmOption, args);
List<String> argsList = line.getArgList();
if (argsList.size() != 2) {
return usageError("RM requires exactly one path argument", RM_USAGE);
}
if (!validatePath(argsList.get(1))) {
return -1;
}
try {
if (line.hasOption("r")) {
recursiveOpt = true;
}
registry.delete(argsList.get(1), recursiveOpt);
return 0;
} catch (Exception e) {
syserr.println(analyzeException("rm", e, argsList));
}
return -1;
} catch (ParseException exp) {
return usageError("Invalid syntax " + exp.toString(), RM_USAGE);
}
}
/**
* Given an exception and a possibly empty argument list, generate
* a diagnostics string for use in error messages
* @param operation the operation that failed
* @param e exception
* @param argsList arguments list
* @return a string intended for the user
*/
String analyzeException(String operation,
Exception e,
List<String> argsList) {
String pathArg = !argsList.isEmpty() ? argsList.get(1) : "(none)";
if (LOG.isDebugEnabled()) {
LOG.debug("Operation {} on path {} failed with exception {}",
operation, pathArg, e, e);
}
if (e instanceof InvalidPathnameException) {
return "InvalidPath :" + pathArg + ": " + e;
}
if (e instanceof PathNotFoundException) {
return "Path not found: " + pathArg;
}
if (e instanceof NoRecordException) {
return "No service record at path " + pathArg;
}
if (e instanceof AuthenticationFailedException) {
return "Failed to authenticate to registry : " + e;
}
if (e instanceof NoPathPermissionsException) {
return "No Permission to path: " + pathArg + ": " + e;
}
if (e instanceof AccessControlException) {
return "No Permission to path: " + pathArg + ": " + e;
}
if (e instanceof InvalidRecordException) {
return "Unable to read record at: " + pathArg + ": " + e;
}
if (e instanceof IOException) {
return "IO Exception when accessing path :" + pathArg + ": " + e;
}
// something else went very wrong here
return "Exception " + e;
}
}