Return-Path: X-Original-To: apmail-accumulo-commits-archive@www.apache.org Delivered-To: apmail-accumulo-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 0D2C118A7B for ; Thu, 3 Mar 2016 21:59:27 +0000 (UTC) Received: (qmail 98807 invoked by uid 500); 3 Mar 2016 21:59:26 -0000 Delivered-To: apmail-accumulo-commits-archive@accumulo.apache.org Received: (qmail 98691 invoked by uid 500); 3 Mar 2016 21:59:26 -0000 Mailing-List: contact commits-help@accumulo.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@accumulo.apache.org Delivered-To: mailing list commits@accumulo.apache.org Received: (qmail 98544 invoked by uid 99); 3 Mar 2016 21:59:26 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 03 Mar 2016 21:59:26 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 43267E7906; Thu, 3 Mar 2016 21:59:26 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: ctubbsii@apache.org To: commits@accumulo.apache.org Date: Thu, 03 Mar 2016 21:59:30 -0000 Message-Id: <801b2bdd6de54309a5497eafa9bbf5af@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [05/61] [abbrv] [partial] accumulo git commit: ACCUMULO-722 put trunk in my sandbox http://git-wip-us.apache.org/repos/asf/accumulo/blob/7bdbfccb/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java ---------------------------------------------------------------------- diff --git a/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java new file mode 100644 index 0000000..babd61e --- /dev/null +++ b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java @@ -0,0 +1,978 @@ +/* + * 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.accumulo.core.util.shell; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.UUID; + +import jline.ConsoleReader; +import jline.History; + +import org.apache.accumulo.core.Constants; +import org.apache.accumulo.core.client.AccumuloException; +import org.apache.accumulo.core.client.AccumuloSecurityException; +import org.apache.accumulo.core.client.Connector; +import org.apache.accumulo.core.client.Instance; +import org.apache.accumulo.core.client.IteratorSetting; +import org.apache.accumulo.core.client.TableNotFoundException; +import org.apache.accumulo.core.client.ZooKeeperInstance; +import org.apache.accumulo.core.client.mock.MockInstance; +import org.apache.accumulo.core.conf.AccumuloConfiguration; +import org.apache.accumulo.core.conf.Property; +import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Value; +import org.apache.accumulo.core.data.thrift.TConstraintViolationSummary; +import org.apache.accumulo.core.security.AuditLevel; +import org.apache.accumulo.core.security.thrift.AuthInfo; +import org.apache.accumulo.core.tabletserver.thrift.ConstraintViolationException; +import org.apache.accumulo.core.trace.DistributedTrace; +import org.apache.accumulo.core.util.BadArgumentException; +import org.apache.accumulo.core.util.format.BinaryFormatter; +import org.apache.accumulo.core.util.format.DefaultFormatter; +import org.apache.accumulo.core.util.format.Formatter; +import org.apache.accumulo.core.util.format.FormatterFactory; +import org.apache.accumulo.core.util.shell.commands.AboutCommand; +import org.apache.accumulo.core.util.shell.commands.AddSplitsCommand; +import org.apache.accumulo.core.util.shell.commands.AuthenticateCommand; +import org.apache.accumulo.core.util.shell.commands.ByeCommand; +import org.apache.accumulo.core.util.shell.commands.ClasspathCommand; +import org.apache.accumulo.core.util.shell.commands.ClearCommand; +import org.apache.accumulo.core.util.shell.commands.CloneTableCommand; +import org.apache.accumulo.core.util.shell.commands.ClsCommand; +import org.apache.accumulo.core.util.shell.commands.CompactCommand; +import org.apache.accumulo.core.util.shell.commands.ConfigCommand; +import org.apache.accumulo.core.util.shell.commands.ConstraintCommand; +import org.apache.accumulo.core.util.shell.commands.CreateTableCommand; +import org.apache.accumulo.core.util.shell.commands.CreateUserCommand; +import org.apache.accumulo.core.util.shell.commands.DUCommand; +import org.apache.accumulo.core.util.shell.commands.DebugCommand; +import org.apache.accumulo.core.util.shell.commands.DeleteCommand; +import org.apache.accumulo.core.util.shell.commands.DeleteIterCommand; +import org.apache.accumulo.core.util.shell.commands.DeleteManyCommand; +import org.apache.accumulo.core.util.shell.commands.DeleteRowsCommand; +import org.apache.accumulo.core.util.shell.commands.DeleteScanIterCommand; +import org.apache.accumulo.core.util.shell.commands.DeleteTableCommand; +import org.apache.accumulo.core.util.shell.commands.DeleteUserCommand; +import org.apache.accumulo.core.util.shell.commands.DropTableCommand; +import org.apache.accumulo.core.util.shell.commands.DropUserCommand; +import org.apache.accumulo.core.util.shell.commands.EGrepCommand; +import org.apache.accumulo.core.util.shell.commands.ExecfileCommand; +import org.apache.accumulo.core.util.shell.commands.ExitCommand; +import org.apache.accumulo.core.util.shell.commands.ExportTableCommand; +import org.apache.accumulo.core.util.shell.commands.FlushCommand; +import org.apache.accumulo.core.util.shell.commands.FormatterCommand; +import org.apache.accumulo.core.util.shell.commands.GetAuthsCommand; +import org.apache.accumulo.core.util.shell.commands.GetGroupsCommand; +import org.apache.accumulo.core.util.shell.commands.GetSplitsCommand; +import org.apache.accumulo.core.util.shell.commands.GrantCommand; +import org.apache.accumulo.core.util.shell.commands.GrepCommand; +import org.apache.accumulo.core.util.shell.commands.HelpCommand; +import org.apache.accumulo.core.util.shell.commands.HiddenCommand; +import org.apache.accumulo.core.util.shell.commands.HistoryCommand; +import org.apache.accumulo.core.util.shell.commands.ImportDirectoryCommand; +import org.apache.accumulo.core.util.shell.commands.ImportTableCommand; +import org.apache.accumulo.core.util.shell.commands.InfoCommand; +import org.apache.accumulo.core.util.shell.commands.InsertCommand; +import org.apache.accumulo.core.util.shell.commands.ListIterCommand; +import org.apache.accumulo.core.util.shell.commands.ListScansCommand; +import org.apache.accumulo.core.util.shell.commands.MaxRowCommand; +import org.apache.accumulo.core.util.shell.commands.MergeCommand; +import org.apache.accumulo.core.util.shell.commands.NoTableCommand; +import org.apache.accumulo.core.util.shell.commands.OfflineCommand; +import org.apache.accumulo.core.util.shell.commands.OnlineCommand; +import org.apache.accumulo.core.util.shell.commands.PasswdCommand; +import org.apache.accumulo.core.util.shell.commands.QuestionCommand; +import org.apache.accumulo.core.util.shell.commands.QuitCommand; +import org.apache.accumulo.core.util.shell.commands.QuotedStringTokenizer; +import org.apache.accumulo.core.util.shell.commands.RenameTableCommand; +import org.apache.accumulo.core.util.shell.commands.RevokeCommand; +import org.apache.accumulo.core.util.shell.commands.ScanCommand; +import org.apache.accumulo.core.util.shell.commands.SetAuthsCommand; +import org.apache.accumulo.core.util.shell.commands.SetGroupsCommand; +import org.apache.accumulo.core.util.shell.commands.SetIterCommand; +import org.apache.accumulo.core.util.shell.commands.SetScanIterCommand; +import org.apache.accumulo.core.util.shell.commands.SleepCommand; +import org.apache.accumulo.core.util.shell.commands.SystemPermissionsCommand; +import org.apache.accumulo.core.util.shell.commands.TableCommand; +import org.apache.accumulo.core.util.shell.commands.TablePermissionsCommand; +import org.apache.accumulo.core.util.shell.commands.TablesCommand; +import org.apache.accumulo.core.util.shell.commands.TraceCommand; +import org.apache.accumulo.core.util.shell.commands.UserCommand; +import org.apache.accumulo.core.util.shell.commands.UserPermissionsCommand; +import org.apache.accumulo.core.util.shell.commands.UsersCommand; +import org.apache.accumulo.core.util.shell.commands.WhoAmICommand; +import org.apache.accumulo.fate.zookeeper.ZooReader; +import org.apache.commons.cli.BasicParser; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.MissingArgumentException; +import org.apache.commons.cli.MissingOptionException; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.hadoop.fs.Path; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; + +/** + * A convenient console interface to perform basic accumulo functions Includes auto-complete, help, and quoted strings with escape sequences + */ +public class Shell extends ShellOptions { + public static final Logger log = Logger.getLogger(Shell.class); + private static final Logger audit = Logger.getLogger(Shell.class.getName() + ".audit"); + + public static final String CHARSET = "ISO-8859-1"; + public static final int NO_FIXED_ARG_LENGTH_CHECK = -1; + private static final String SHELL_DESCRIPTION = "Shell - Apache Accumulo Interactive Shell"; + private static final String DEFAULT_AUTH_TIMEOUT = "60"; // in minutes + + protected int exitCode = 0; + private String tableName; + protected Instance instance; + private Connector connector; + protected ConsoleReader reader; + private AuthInfo credentials; + private Class defaultFormatterClass = DefaultFormatter.class; + private Class binaryFormatterClass = BinaryFormatter.class; + public Map> scanIteratorOptions = new HashMap>(); + + private Token rootToken; + public final Map commandFactory = new TreeMap(); + public final Map commandGrouping = new TreeMap(); + protected boolean configError = false; + + // exit if true + private boolean exit = false; + + // file to execute commands from + protected String execFile = null; + // single command to execute from the command line + protected String execCommand = null; + protected boolean verbose = true; + + private boolean tabCompletion; + private boolean disableAuthTimeout; + private long authTimeout; + private long lastUserActivity = System.currentTimeMillis(); + private boolean logErrorsToConsole = false; + private PrintWriter writer = null; + private boolean masking = false; + + public Shell() throws IOException { + this(new ConsoleReader()); + } + + public Shell(ConsoleReader reader) { + super(); + this.reader = reader; + } + + public Shell(ConsoleReader reader, PrintWriter writer) { + this(reader); + this.writer = writer; + } + + // Not for client use + public boolean config(String... args) { + + CommandLine cl; + try { + cl = new BasicParser().parse(opts, args); + if (cl.getArgs().length > 0) + throw new ParseException("Unrecognized arguments: " + cl.getArgList()); + + if (cl.hasOption(helpOpt.getOpt())) { + configError = true; + printHelp("shell", SHELL_DESCRIPTION, opts); + return true; + } + + setDebugging(cl.hasOption(debugOption.getLongOpt())); + authTimeout = Integer.parseInt(cl.getOptionValue(authTimeoutOpt.getLongOpt(), DEFAULT_AUTH_TIMEOUT)) * 60 * 1000; + disableAuthTimeout = cl.hasOption(disableAuthTimeoutOpt.getLongOpt()); + + if (cl.hasOption(zooKeeperInstance.getOpt()) && cl.getOptionValues(zooKeeperInstance.getOpt()).length != 2) + throw new MissingArgumentException(zooKeeperInstance); + + } catch (Exception e) { + configError = true; + printException(e); + printHelp("shell", SHELL_DESCRIPTION, opts); + return true; + } + + // get the options that were parsed + String sysUser = System.getProperty("user.name"); + if (sysUser == null) + sysUser = "root"; + String user = cl.getOptionValue(usernameOption.getOpt(), sysUser); + + String passw = cl.getOptionValue(passwOption.getOpt(), null); + tabCompletion = !cl.hasOption(tabCompleteOption.getLongOpt()); + + // Use a fake (Mock), ZK, or HdfsZK Accumulo instance + setInstance(cl); + + // process default parameters if unspecified + byte[] pass; + try { + if (!cl.hasOption(fakeOption.getLongOpt())) { + DistributedTrace.enable(instance, new ZooReader(instance.getZooKeepers(), instance.getZooKeepersSessionTimeOut()), "shell", InetAddress.getLocalHost() + .getHostName()); + } + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void start() { + reader.getTerminal().enableEcho(); + } + }); + + if (passw == null) + passw = readMaskedLine("Enter current password for '" + user + "'@'" + instance.getInstanceName() + "': ", '*'); + if (passw == null) { + reader.printNewline(); + configError = true; + return true; + } // user canceled + + pass = passw.getBytes(); + this.setTableName(""); + connector = instance.getConnector(user, pass); + this.credentials = new AuthInfo(user, ByteBuffer.wrap(pass), connector.getInstance().getInstanceID()); + + } catch (Exception e) { + printException(e); + configError = true; + } + + // decide whether to execute commands from a file and quit + if (cl.hasOption(execfileOption.getOpt())) { + execFile = cl.getOptionValue(execfileOption.getOpt()); + verbose = false; + } else if (cl.hasOption(execfileVerboseOption.getOpt())) { + execFile = cl.getOptionValue(execfileVerboseOption.getOpt()); + } + if (cl.hasOption(execCommandOpt.getOpt())) { + execCommand = cl.getOptionValue(execCommandOpt.getOpt()); + verbose = false; + } + + rootToken = new Token(); + + Command[] dataCommands = {new DeleteCommand(), new DeleteManyCommand(), new DeleteRowsCommand(), new EGrepCommand(), new FormatterCommand(), + new GrepCommand(), new ImportDirectoryCommand(), new InsertCommand(), new MaxRowCommand(), new ScanCommand()}; + Command[] debuggingCommands = {new ClasspathCommand(), new DebugCommand(), new ListScansCommand(), new TraceCommand()}; + Command[] execCommands = {new ExecfileCommand(), new HistoryCommand()}; + Command[] exitCommands = {new ByeCommand(), new ExitCommand(), new QuitCommand()}; + Command[] helpCommands = {new AboutCommand(), new HelpCommand(), new InfoCommand(), new QuestionCommand()}; + Command[] iteratorCommands = {new DeleteIterCommand(), new DeleteScanIterCommand(), new ListIterCommand(), new SetIterCommand(), new SetScanIterCommand()}; + Command[] otherCommands = {new HiddenCommand()}; + Command[] permissionsCommands = {new GrantCommand(), new RevokeCommand(), new SystemPermissionsCommand(), new TablePermissionsCommand(), + new UserPermissionsCommand()}; + Command[] stateCommands = {new AuthenticateCommand(), new ClsCommand(), new ClearCommand(), new NoTableCommand(), new SleepCommand(), new TableCommand(), + new UserCommand(), new WhoAmICommand()}; + Command[] tableCommands = {new CloneTableCommand(), new ConfigCommand(), new CreateTableCommand(), new DeleteTableCommand(), new DropTableCommand(), + new DUCommand(), new ExportTableCommand(), new ImportTableCommand(), new OfflineCommand(), new OnlineCommand(), new RenameTableCommand(), + new TablesCommand()}; + Command[] tableControlCommands = {new AddSplitsCommand(), new CompactCommand(), new ConstraintCommand(), new FlushCommand(), new GetGroupsCommand(), + new GetSplitsCommand(), new MergeCommand(), new SetGroupsCommand()}; + Command[] userCommands = {new CreateUserCommand(), new DeleteUserCommand(), new DropUserCommand(), new GetAuthsCommand(), new PasswdCommand(), + new SetAuthsCommand(), new UsersCommand()}; + commandGrouping.put("-- Writing, Reading, and Removing Data --", dataCommands); + commandGrouping.put("-- Debugging Commands -------------------", debuggingCommands); + commandGrouping.put("-- Shell Execution Commands -------------", execCommands); + commandGrouping.put("-- Exiting Commands ---------------------", exitCommands); + commandGrouping.put("-- Help Commands ------------------------", helpCommands); + commandGrouping.put("-- Iterator Configuration ---------------", iteratorCommands); + commandGrouping.put("-- Permissions Administration Commands --", permissionsCommands); + commandGrouping.put("-- Shell State Commands -----------------", stateCommands); + commandGrouping.put("-- Table Administration Commands --------", tableCommands); + commandGrouping.put("-- Table Control Commands ---------------", tableControlCommands); + commandGrouping.put("-- User Administration Commands ---------", userCommands); + + for (Command[] cmds : commandGrouping.values()) { + for (Command cmd : cmds) + commandFactory.put(cmd.getName(), cmd); + } + for (Command cmd : otherCommands) { + commandFactory.put(cmd.getName(), cmd); + } + return configError; + } + + @SuppressWarnings("deprecation") + protected void setInstance(CommandLine cl) { + // should only be one instance option set + instance = null; + if (cl.hasOption(fakeOption.getLongOpt())) { + instance = new MockInstance("fake"); + } else if (cl.hasOption(hdfsZooInstance.getOpt())) { + instance = getDefaultInstance(AccumuloConfiguration.getSiteConfiguration()); + } else if (cl.hasOption(zooKeeperInstance.getOpt())) { + String[] zkOpts = cl.getOptionValues(zooKeeperInstance.getOpt()); + instance = new ZooKeeperInstance(zkOpts[0], zkOpts[1]); + } else { + instance = getDefaultInstance(AccumuloConfiguration.getSiteConfiguration()); + } + } + + /** + * @deprecated Not for client use + */ + private static Instance getDefaultInstance(AccumuloConfiguration conf) { + String keepers = conf.get(Property.INSTANCE_ZK_HOST); + Path instanceDir = new Path(conf.get(Property.INSTANCE_DFS_DIR), "instance_id"); + return new ZooKeeperInstance(UUID.fromString(ZooKeeperInstance.getInstanceIDFromHdfs(instanceDir)), keepers); + } + + public Connector getConnector() { + return connector; + } + + public static void main(String args[]) throws IOException { + Shell shell = new Shell(); + shell.config(args); + + System.exit(shell.start()); + } + + public int start() throws IOException { + if (configError) + return 1; + + String input; + if (isVerbose()) + printInfo(); + + String configDir = System.getenv("HOME") + "/.accumulo"; + String historyPath = configDir + "/shell_history.txt"; + File accumuloDir = new File(configDir); + if (!accumuloDir.exists() && !accumuloDir.mkdirs()) + log.warn("Unable to make directory for history at " + accumuloDir); + try { + History history = new History(); + history.setHistoryFile(new File(historyPath)); + reader.setHistory(history); + } catch (IOException e) { + log.warn("Unable to load history file at " + historyPath); + } + + ShellCompletor userCompletor = null; + + if (execFile != null) { + java.util.Scanner scanner = new java.util.Scanner(new File(execFile)); + while (scanner.hasNextLine()) + execCommand(scanner.nextLine(), true, isVerbose()); + } else if (execCommand != null) { + for (String command : execCommand.split("\n")) { + execCommand(command, true, isVerbose()); + } + return exitCode; + } + + while (true) { + if (hasExited()) + return exitCode; + + // If tab completion is true we need to reset + if (tabCompletion) { + if (userCompletor != null) + reader.removeCompletor(userCompletor); + + userCompletor = setupCompletion(); + reader.addCompletor(userCompletor); + } + + reader.setDefaultPrompt(getDefaultPrompt()); + input = reader.readLine(); + if (input == null) { + reader.printNewline(); + return exitCode; + } // user canceled + + execCommand(input, disableAuthTimeout, false); + } + } + + public void printInfo() throws IOException { + reader.printString("\n" + SHELL_DESCRIPTION + "\n" + "- \n" + "- version: " + Constants.VERSION + "\n" + "- instance name: " + + connector.getInstance().getInstanceName() + "\n" + "- instance id: " + connector.getInstance().getInstanceID() + "\n" + "- \n" + + "- type 'help' for a list of available commands\n" + "- \n"); + reader.flushConsole(); + } + + public void printVerboseInfo() throws IOException { + StringBuilder sb = new StringBuilder("-\n"); + sb.append("- Current user: ").append(connector.whoami()).append("\n"); + if (execFile != null) + sb.append("- Executing commands from: ").append(execFile).append("\n"); + if (disableAuthTimeout) + sb.append("- Authorization timeout: disabled\n"); + else + sb.append("- Authorization timeout: ").append(String.format("%.2fs\n", authTimeout / 1000.0)); + sb.append("- Debug: ").append(isDebuggingEnabled() ? "on" : "off").append("\n"); + if (!scanIteratorOptions.isEmpty()) { + for (Entry> entry : scanIteratorOptions.entrySet()) { + sb.append("- Session scan iterators for table ").append(entry.getKey()).append(":\n"); + for (IteratorSetting setting : entry.getValue()) { + sb.append("- Iterator ").append(setting.getName()).append(" options:\n"); + sb.append("- ").append("iteratorPriority").append(" = ").append(setting.getPriority()).append("\n"); + sb.append("- ").append("iteratorClassName").append(" = ").append(setting.getIteratorClass()).append("\n"); + for (Entry optEntry : setting.getOptions().entrySet()) { + sb.append("- ").append(optEntry.getKey()).append(" = ").append(optEntry.getValue()).append("\n"); + } + } + } + } + sb.append("-\n"); + reader.printString(sb.toString()); + } + + public String getDefaultPrompt() { + return connector.whoami() + "@" + connector.getInstance().getInstanceName() + (getTableName().isEmpty() ? "" : " ") + getTableName() + "> "; + } + + public void execCommand(String input, boolean ignoreAuthTimeout, boolean echoPrompt) throws IOException { + audit.log(AuditLevel.AUDIT, getDefaultPrompt() + input); + if (echoPrompt) { + reader.printString(getDefaultPrompt()); + reader.printString(input); + reader.printNewline(); + } + + String fields[]; + try { + fields = new QuotedStringTokenizer(input).getTokens(); + } catch (BadArgumentException e) { + printException(e); + ++exitCode; + return; + } + if (fields.length == 0) + return; + + String command = fields[0]; + fields = fields.length > 1 ? Arrays.copyOfRange(fields, 1, fields.length) : new String[] {}; + + Command sc = null; + if (command.length() > 0) { + try { + // Obtain the command from the command table + sc = commandFactory.get(command); + if (sc == null) { + reader.printString(String.format("Unknown command \"%s\". Enter \"help\" for a list possible commands.\n", command)); + reader.flushConsole(); + return; + } + + if (!(sc instanceof ExitCommand) && !ignoreAuthTimeout && System.currentTimeMillis() - lastUserActivity > authTimeout) { + reader.printString("Shell has been idle for too long. Please re-authenticate.\n"); + boolean authFailed = true; + do { + String pwd = readMaskedLine("Enter current password for '" + connector.whoami() + "': ", '*'); + if (pwd == null) { + reader.printNewline(); + return; + } // user canceled + + try { + authFailed = !connector.securityOperations().authenticateUser(connector.whoami(), pwd.getBytes()); + } catch (Exception e) { + ++exitCode; + printException(e); + } + + if (authFailed) + reader.printString("Invalid password. "); + } while (authFailed); + lastUserActivity = System.currentTimeMillis(); + } + + // Get the options from the command on how to parse the string + Options parseOpts = sc.getOptionsWithHelp(); + + // Parse the string using the given options + CommandLine cl = new BasicParser().parse(parseOpts, fields); + + int actualArgLen = cl.getArgs().length; + int expectedArgLen = sc.numArgs(); + if (cl.hasOption(helpOption)) { + // Display help if asked to; otherwise execute the command + sc.printHelp(this); + } else if (expectedArgLen != NO_FIXED_ARG_LENGTH_CHECK && actualArgLen != expectedArgLen) { + ++exitCode; + // Check for valid number of fixed arguments (if not + // negative; negative means it is not checked, for + // vararg-like commands) + printException(new IllegalArgumentException(String.format("Expected %d argument%s. There %s %d.", expectedArgLen, expectedArgLen == 1 ? "" : "s", + actualArgLen == 1 ? "was" : "were", actualArgLen))); + sc.printHelp(this); + } else { + int tmpCode = sc.execute(input, cl, this); + exitCode += tmpCode; + reader.flushConsole(); + } + + } catch (ConstraintViolationException e) { + ++exitCode; + printConstraintViolationException(e); + } catch (TableNotFoundException e) { + ++exitCode; + if (getTableName().equals(e.getTableName())) + setTableName(""); + printException(e); + } catch (ParseException e) { + // not really an error if the exception is a missing required + // option when the user is asking for help + if (!(e instanceof MissingOptionException && (Arrays.asList(fields).contains("-" + helpOption) || Arrays.asList(fields).contains("--" + helpLongOption)))) { + ++exitCode; + printException(e); + } + if (sc != null) + sc.printHelp(this); + } catch (Exception e) { + ++exitCode; + printException(e); + } + } else { + ++exitCode; + printException(new BadArgumentException("Unrecognized empty command", command, -1)); + } + reader.flushConsole(); + } + + /** + * The command tree is built in reverse so that the references are more easily linked up. There is some code in token to allow forward building of the command + * tree. + */ + private ShellCompletor setupCompletion() { + rootToken = new Token(); + + Set tableNames = null; + try { + tableNames = connector.tableOperations().list(); + } catch (Exception e) { + log.debug("Unable to obtain list of tables", e); + tableNames = Collections.emptySet(); + } + + Set userlist = null; + try { + userlist = connector.securityOperations().listUsers(); + } catch (Exception e) { + log.debug("Unable to obtain list of users", e); + userlist = Collections.emptySet(); + } + + Map> options = new HashMap>(); + + Set commands = new HashSet(); + for (String a : commandFactory.keySet()) + commands.add(a); + + Set modifiedUserlist = new HashSet(); + Set modifiedTablenames = new HashSet(); + + for (String a : tableNames) + modifiedTablenames.add(a.replaceAll("([\\s'\"])", "\\\\$1")); + for (String a : userlist) + modifiedUserlist.add(a.replaceAll("([\\s'\"])", "\\\\$1")); + + options.put(Command.CompletionSet.USERNAMES, modifiedUserlist); + options.put(Command.CompletionSet.TABLENAMES, modifiedTablenames); + options.put(Command.CompletionSet.COMMANDS, commands); + + for (Command[] cmdGroup : commandGrouping.values()) { + for (Command c : cmdGroup) { + c.getOptionsWithHelp(); // prep the options for the command + // so that the completion can + // include them + c.registerCompletion(rootToken, options); + } + } + return new ShellCompletor(rootToken, options); + } + + /** + * The Command class represents a command to be run in the shell. It contains the methods to execute along with some methods to help tab completion, and + * return the command name, help, and usage. + */ + public static abstract class Command { + // Helper methods for completion + public enum CompletionSet { + TABLENAMES, USERNAMES, COMMANDS + } + + static Set getCommandNames(Map> objects) { + return objects.get(CompletionSet.COMMANDS); + } + + static Set getTableNames(Map> objects) { + return objects.get(CompletionSet.TABLENAMES); + } + + static Set getUserNames(Map> objects) { + return objects.get(CompletionSet.USERNAMES); + } + + public void registerCompletionGeneral(Token root, Set args, boolean caseSens) { + Token t = new Token(args); + t.setCaseSensitive(caseSens); + + Token command = new Token(getName()); + command.addSubcommand(t); + + root.addSubcommand(command); + } + + public void registerCompletionForTables(Token root, Map> completionSet) { + registerCompletionGeneral(root, completionSet.get(CompletionSet.TABLENAMES), true); + } + + public void registerCompletionForUsers(Token root, Map> completionSet) { + registerCompletionGeneral(root, completionSet.get(CompletionSet.USERNAMES), true); + } + + public void registerCompletionForCommands(Token root, Map> completionSet) { + registerCompletionGeneral(root, completionSet.get(CompletionSet.COMMANDS), false); + } + + // abstract methods to override + public abstract int execute(String fullCommand, CommandLine cl, Shell shellState) throws Exception; + + public abstract String description(); + + /** + * If the number of arguments is not always zero (not including those arguments handled through Options), make sure to override the {@link #usage()} method. + * Otherwise, {@link #usage()} does need to be overridden. + */ + public abstract int numArgs(); + + // OPTIONAL methods to override: + + // the general version of getname uses reflection to get the class name + // and then cuts off the suffix -Command to get the name of the command + public String getName() { + String s = this.getClass().getName(); + int st = Math.max(s.lastIndexOf('$'), s.lastIndexOf('.')); + int i = s.indexOf("Command"); + return i > 0 ? s.substring(st + 1, i).toLowerCase(Locale.ENGLISH) : null; + } + + // The general version of this method adds the name + // of the command to the completion tree + public void registerCompletion(Token root, Map> completion_set) { + root.addSubcommand(new Token(getName())); + } + + // The general version of this method uses the HelpFormatter + // that comes with the apache Options package to print out the help + public final void printHelp(Shell shellState) { + shellState.printHelp(usage(), "description: " + this.description(), getOptionsWithHelp()); + } + + public final void printHelp(Shell shellState, int width) { + shellState.printHelp(usage(), "description: " + this.description(), getOptionsWithHelp(), width); + } + + // Get options with help + public final Options getOptionsWithHelp() { + Options opts = getOptions(); + opts.addOption(new Option(helpOption, helpLongOption, false, "display this help")); + return opts; + } + + // General usage is just the command + public String usage() { + return getName(); + } + + // General Options are empty + public Options getOptions() { + return new Options(); + } + } + + public interface PrintLine { + public void print(String s); + + public void close(); + } + + public static class PrintShell implements PrintLine { + ConsoleReader reader; + + public PrintShell(ConsoleReader reader) { + this.reader = reader; + } + + public void print(String s) { + try { + reader.printString(s + "\n"); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public void close() {} + }; + + public static class PrintFile implements PrintLine { + PrintWriter writer; + + public PrintFile(String filename) throws FileNotFoundException { + writer = new PrintWriter(filename); + } + + public void print(String s) { + writer.println(s); + } + + public void close() { + writer.close(); + } + }; + + public final void printLines(Iterator lines, boolean paginate) throws IOException { + int linesPrinted = 0; + String prompt = "-- hit any key to continue or 'q' to quit --"; + int lastPromptLength = prompt.length(); + int termWidth = reader.getTermwidth(); + int maxLines = reader.getTermheight(); + + String peek = null; + while (lines.hasNext()) { + String nextLine = lines.next(); + if (nextLine == null) + continue; + for (String line : nextLine.split("\\n")) { + if (peek != null) { + reader.printString(peek); + reader.printNewline(); + if (paginate) { + linesPrinted += peek.length() == 0 ? 0 : Math.ceil(peek.length() * 1.0 / termWidth); + + // check if displaying the next line would result in + // scrolling off the screen + if (linesPrinted + Math.ceil(lastPromptLength * 1.0 / termWidth) + Math.ceil(prompt.length() * 1.0 / termWidth) + + Math.ceil(line.length() * 1.0 / termWidth) > maxLines) { + linesPrinted = 0; + int numdashes = (termWidth - prompt.length()) / 2; + String nextPrompt = repeat("-", numdashes) + prompt + repeat("-", numdashes); + lastPromptLength = nextPrompt.length(); + reader.printString(nextPrompt); + reader.flushConsole(); + if (Character.toUpperCase((char) reader.readVirtualKey()) == 'Q') { + reader.printNewline(); + return; + } + reader.printNewline(); + termWidth = reader.getTermwidth(); + maxLines = reader.getTermheight(); + } + } + } + peek = line; + } + } + if (peek != null) { + reader.printString(peek); + reader.printNewline(); + } + } + + public final void printRecords(Iterable> scanner, boolean printTimestamps, boolean paginate) throws IOException { + Class formatterClass = getFormatter(); + + printRecords(scanner, printTimestamps, paginate, formatterClass); + } + + public final void printRecords(Iterable> scanner, boolean printTimestamps, boolean paginate, Class formatterClass) + throws IOException { + printLines(FormatterFactory.getFormatter(formatterClass, scanner, printTimestamps), paginate); + } + + public final void printBinaryRecords(Iterable> scanner, boolean printTimestamps, boolean paginate) throws IOException { + printLines(FormatterFactory.getFormatter(binaryFormatterClass, scanner, printTimestamps), paginate); + } + + public static String repeat(String s, int c) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < c; i++) + sb.append(s); + return sb.toString(); + } + + public void checkTableState() { + if (getTableName().isEmpty()) + throw new IllegalStateException( + "Not in a table context. Please use 'table ' to switch to a table, or use '-t' to specify a table if option is available."); + } + + private final void printConstraintViolationException(ConstraintViolationException cve) { + printException(cve, ""); + int COL1 = 50, COL2 = 14; + int col3 = Math.max(1, Math.min(Integer.MAX_VALUE, reader.getTermwidth() - COL1 - COL2 - 6)); + logError(String.format("%" + COL1 + "s-+-%" + COL2 + "s-+-%" + col3 + "s\n", repeat("-", COL1), repeat("-", COL2), repeat("-", col3))); + logError(String.format("%-" + COL1 + "s | %" + COL2 + "s | %-" + col3 + "s\n", "Constraint class", "Violation code", "Violation Description")); + logError(String.format("%" + COL1 + "s-+-%" + COL2 + "s-+-%" + col3 + "s\n", repeat("-", COL1), repeat("-", COL2), repeat("-", col3))); + for (TConstraintViolationSummary cvs : cve.violationSummaries) + logError(String.format("%-" + COL1 + "s | %" + COL2 + "d | %-" + col3 + "s\n", cvs.constrainClass, cvs.violationCode, cvs.violationDescription)); + logError(String.format("%" + COL1 + "s-+-%" + COL2 + "s-+-%" + col3 + "s\n", repeat("-", COL1), repeat("-", COL2), repeat("-", col3))); + } + + public final void printException(Exception e) { + printException(e, e.getMessage()); + } + + private final void printException(Exception e, String msg) { + logError(e.getClass().getName() + (msg != null ? ": " + msg : "")); + log.debug(e.getClass().getName() + (msg != null ? ": " + msg : ""), e); + } + + public static final void setDebugging(boolean debuggingEnabled) { + Logger.getLogger(Constants.CORE_PACKAGE_NAME).setLevel(debuggingEnabled ? Level.TRACE : Level.INFO); + } + + public static final boolean isDebuggingEnabled() { + return Logger.getLogger(Constants.CORE_PACKAGE_NAME).isTraceEnabled(); + } + + private final void printHelp(String usage, String description, Options opts) { + printHelp(usage, description, opts, Integer.MAX_VALUE); + } + + private final void printHelp(String usage, String description, Options opts, int width) { + PrintWriter pw = new PrintWriter(System.err); + new HelpFormatter().printHelp(pw, width, usage, description, opts, 2, 5, null, true); + pw.flush(); + if (logErrorsToConsole && writer != null) { + new HelpFormatter().printHelp(writer, width, usage, description, opts, 2, 5, null, true); + writer.flush(); + } + } + + public int getExitCode() { + return exitCode; + } + + public void resetExitCode() { + exitCode = 0; + } + + public void setExit(boolean exit) { + this.exit = exit; + } + + public boolean isVerbose() { + return verbose; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public String getTableName() { + return tableName; + } + + public ConsoleReader getReader() { + return reader; + } + + public void updateUser(AuthInfo authInfo) throws AccumuloException, AccumuloSecurityException { + connector = instance.getConnector(authInfo); + credentials = authInfo; + } + + public AuthInfo getCredentials() { + return credentials; + } + + /** + * Return the formatter for the current table. + * + * @return the formatter class for the current table + */ + public Class getFormatter() { + return getFormatter(this.tableName); + } + + /** + * Return the formatter for the given table. + * + * @param tableName + * the table name + * @return the formatter class for the given table + */ + public Class getFormatter(String tableName) { + Class formatter = FormatterCommand.getCurrentFormatter(tableName, this); + + if (null == formatter) { + logError("Could not load the specified formatter. Using the DefaultFormatter"); + return this.defaultFormatterClass; + } else { + return formatter; + } + } + + public void setLogErrorsToConsole() { + this.logErrorsToConsole = true; + } + + private void logError(String s) { + log.error(s); + if (logErrorsToConsole) { + try { + reader.printString("ERROR: " + s + "\n"); + reader.flushConsole(); + } catch (IOException e) {} + } + } + + public String readMaskedLine(String prompt, Character mask) throws IOException { + this.masking = true; + String s = reader.readLine(prompt, mask); + this.masking = false; + return s; + } + + public boolean isMasking() { + return masking; + } + + public boolean hasExited() { + return exit; + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/7bdbfccb/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/ShellCommandException.java ---------------------------------------------------------------------- diff --git a/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/ShellCommandException.java b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/ShellCommandException.java new file mode 100644 index 0000000..6bef379 --- /dev/null +++ b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/ShellCommandException.java @@ -0,0 +1,59 @@ +/* + * 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.accumulo.core.util.shell; + +public class ShellCommandException extends Exception { + private static final long serialVersionUID = 1L; + + public enum ErrorCode { + UNKNOWN_ERROR("Unknown error"), + UNSUPPORTED_LANGUAGE("Programming language used is not supported"), + UNRECOGNIZED_COMMAND("Command is not supported"), + INITIALIZATION_FAILURE("Command could not be initialized"), + XML_PARSING_ERROR("Failed to parse the XML file"); + + private String description; + + private ErrorCode(String description) { + this.description = description; + } + + public String getDescription() { + return this.description; + } + + public String toString() { + return getDescription(); + } + } + + private ErrorCode code; + private String command; + + public ShellCommandException(ErrorCode code) { + this(code, null); + } + + public ShellCommandException(ErrorCode code, String command) { + this.code = code; + this.command = command; + } + + public String getMessage() { + return code + (command != null ? " (" + command + ")" : ""); + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/7bdbfccb/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/ShellCompletor.java ---------------------------------------------------------------------- diff --git a/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/ShellCompletor.java b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/ShellCompletor.java new file mode 100644 index 0000000..17ce228 --- /dev/null +++ b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/ShellCompletor.java @@ -0,0 +1,152 @@ +/* + * 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.accumulo.core.util.shell; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import jline.Completor; + +import org.apache.accumulo.core.util.shell.Shell.Command.CompletionSet; +import org.apache.accumulo.core.util.shell.commands.QuotedStringTokenizer; + +public class ShellCompletor implements Completor { + + // private static final Logger log = Logger.getLogger(ShellCompletor.class); + + Map> options; + Token root = null; + + public ShellCompletor() {} + + public ShellCompletor(Token root) { + this.root = root; + } + + public ShellCompletor(Token rootToken, Map> options) { + this.root = rootToken; + this.options = options; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public int complete(String buffer, int cursor, List candidates) { + try { + return _complete(buffer, cursor, candidates); + } catch (Exception e) { + candidates.add(""); + candidates.add(e.getMessage()); + return cursor; + } + } + + private int _complete(String fullBuffer, int cursor, List candidates) { + boolean inTableFlag = false, inUserFlag = false; + // Only want to grab the buffer up to the cursor because + // the user could be trying to tab complete in the middle + // of the line + String buffer = fullBuffer.substring(0, cursor); + + Token current_command_token = root; + String current_string_token = null; + boolean end_space = buffer.endsWith(" "); + + // tabbing with no text + if (buffer.length() == 0) { + candidates.addAll(root.getSubcommandNames()); + return 0; + } + + String prefix = ""; + + QuotedStringTokenizer qst = new QuotedStringTokenizer(buffer); + + Iterator iter = qst.iterator(); + while (iter.hasNext()) { + current_string_token = iter.next(); + current_string_token = current_string_token.replaceAll("([\\s'\"])", "\\\\$1"); + + if (!iter.hasNext()) { + // if we end in a space and that space isn't part of the last token + // (which would be the case at the start of a quote) OR the buffer + // ends with a " indicating that we need to start on the next command + // and not complete the current command. + if (end_space && !current_string_token.endsWith(" ") || buffer.endsWith("\"")) { + // match subcommands + + // we're in a subcommand so try to match the universal + // option flags if we're there + if (current_string_token.trim().equals("-" + Shell.tableOption)) { + candidates.addAll(options.get(Shell.Command.CompletionSet.TABLENAMES)); + prefix += "-" + Shell.tableOption + " "; + } else if (current_string_token.trim().equals("-" + Shell.userOption)) { + candidates.addAll(options.get(Shell.Command.CompletionSet.USERNAMES)); + prefix += "-" + Shell.userOption + " "; + } else if (current_command_token != null) { + Token next = current_command_token.getSubcommand(current_string_token); + if (next != null) { + current_command_token = next; + + if (current_command_token.getCaseSensitive()) + prefix += current_string_token + " "; + else + prefix += current_string_token.toUpperCase() + " "; + + candidates.addAll(current_command_token.getSubcommandNames()); + } + } + Collections.sort(candidates); + return (prefix.length()); + } + // need to match current command + // if we're in -t or -u complete those + if (inTableFlag) { + for (String a : options.get(Shell.Command.CompletionSet.TABLENAMES)) + if (a.startsWith(current_string_token)) + candidates.add(a); + } else if (inUserFlag) { + for (String a : options.get(Shell.Command.CompletionSet.USERNAMES)) + if (a.startsWith(current_string_token)) + candidates.add(a); + } else if (current_command_token != null) + candidates.addAll(current_command_token.getSubcommandNames(current_string_token)); + + Collections.sort(candidates); + return (prefix.length()); + } + + if (current_string_token.trim().equals("-" + Shell.tableOption)) + inTableFlag = true; + else if (current_string_token.trim().equals("-" + Shell.userOption)) + inUserFlag = true; + else + inUserFlag = inTableFlag = false; + + if (current_command_token != null && current_command_token.getCaseSensitive()) + prefix += current_string_token + " "; + else + prefix += current_string_token.toUpperCase() + " "; + + if (current_command_token != null && current_command_token.getSubcommandNames().contains(current_string_token)) + current_command_token = current_command_token.getSubcommand(current_string_token); + + } + return 0; + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/7bdbfccb/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/ShellOptions.java ---------------------------------------------------------------------- diff --git a/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/ShellOptions.java b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/ShellOptions.java new file mode 100644 index 0000000..20c26ed --- /dev/null +++ b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/ShellOptions.java @@ -0,0 +1,96 @@ +/** + * 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.accumulo.core.util.shell; + +import org.apache.commons.cli.Option; +import org.apache.commons.cli.OptionGroup; +import org.apache.commons.cli.Options; + +/** + * Abstract class to encompass the Options available on the Accumulo Shell + */ +public abstract class ShellOptions { + protected static final String DEFAULT_AUTH_TIMEOUT = "60"; // in minutes + + // Global options flags + public static final String userOption = "u"; + public static final String tableOption = "t"; + public static final String helpOption = "?"; + public static final String helpLongOption = "help"; + + final Options opts = new Options(); + final Option usernameOption = new Option("u", "user", true, "username (defaults to your OS user)"); + final Option passwOption = new Option("p", "password", true, "password (prompt for password if this option is missing)"); + final Option tabCompleteOption = new Option(null, "disable-tab-completion", false, "disables tab completion (for less overhead when scripting)"); + final Option debugOption = new Option(null, "debug", false, "enables client debugging"); + final Option fakeOption = new Option(null, "fake", false, "fake a connection to accumulo"); + final Option helpOpt = new Option(helpOption, helpLongOption, false, "display this help"); + final Option execCommandOpt = new Option("e", "execute-command", true, "executes a command, and then exits"); + final OptionGroup execFileGroup = new OptionGroup(); + final Option execfileOption = new Option("f", "execute-file", true, "executes commands from a file at startup"); + final Option execfileVerboseOption = new Option("fv", "execute-file-verbose", true, "executes commands from a file at startup, with commands shown"); + final OptionGroup instanceOptions = new OptionGroup(); + final Option hdfsZooInstance = new Option("h", "hdfsZooInstance", false, "use hdfs zoo instance"); + final Option zooKeeperInstance = new Option("z", "zooKeeperInstance", true, "use a zookeeper instance with the given instance name and list of zoo hosts"); + final OptionGroup authTimeoutOptions = new OptionGroup(); + final Option authTimeoutOpt = new Option(null, "auth-timeout", true, "minutes the shell can be idle without re-entering a password (default " + + DEFAULT_AUTH_TIMEOUT + " min)"); + final Option disableAuthTimeoutOpt = new Option(null, "disable-auth-timeout", false, "disables requiring the user to re-type a password after being idle"); + + public ShellOptions() { + usernameOption.setArgName("user"); + opts.addOption(usernameOption); + + passwOption.setArgName("pass"); + opts.addOption(passwOption); + + opts.addOption(tabCompleteOption); + + opts.addOption(debugOption); + + opts.addOption(fakeOption); + + opts.addOption(helpOpt); + + opts.addOption(execCommandOpt); + + + execfileOption.setArgName("file"); + execFileGroup.addOption(execfileOption); + + execfileVerboseOption.setArgName("file"); + execFileGroup.addOption(execfileVerboseOption); + + opts.addOptionGroup(execFileGroup); + + + instanceOptions.addOption(hdfsZooInstance); + + zooKeeperInstance.setArgName("name hosts"); + zooKeeperInstance.setArgs(2); + instanceOptions.addOption(zooKeeperInstance); + + opts.addOptionGroup(instanceOptions); + + authTimeoutOpt.setArgName("minutes"); + authTimeoutOptions.addOption(authTimeoutOpt); + + authTimeoutOptions.addOption(disableAuthTimeoutOpt); + + opts.addOptionGroup(authTimeoutOptions); + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/7bdbfccb/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/Token.java ---------------------------------------------------------------------- diff --git a/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/Token.java b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/Token.java new file mode 100644 index 0000000..b6c5869 --- /dev/null +++ b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/Token.java @@ -0,0 +1,137 @@ +/* + * 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.accumulo.core.util.shell; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +/* + * A token is a word in a command in the shell. The tree that this builds is used for + * tab-completion of tables, users, commands and certain other parts of the shell that + * can be realistically and quickly gathered. Tokens can have multiple commands grouped + * together and many possible subcommands, although they are stored in a set so duplicates + * aren't allowed. + */ + +public class Token { + private Set command = new HashSet(); + private Set subcommands = new HashSet(); + private boolean caseSensitive = false; + + public Token() {} + + public Token(String commandName) { + this(); + command.add(commandName); + } + + public Token(Collection commandNames) { + this(); + command.addAll(commandNames); + } + + public Token(Set commandNames, Set subCommandNames) { + this(); + command.addAll(commandNames); + subcommands.addAll(subCommandNames); + } + + public void setCaseSensitive(boolean cs) { + caseSensitive = cs; + } + + public boolean getCaseSensitive() { + return caseSensitive; + } + + public Set getCommandNames() { + return command; + } + + public Set getSubcommandList() { + return subcommands; + } + + public Token getSubcommand(String name) { + Iterator iter = subcommands.iterator(); + while (iter.hasNext()) { + Token t = iter.next(); + if (t.containsCommand(name)) + return t; + } + return null; + } + + public Set getSubcommandNames() { + HashSet set = new HashSet(); + for (Token t : subcommands) + set.addAll(t.getCommandNames()); + return set; + } + + public Set getSubcommandNames(String startsWith) { + Iterator iter = subcommands.iterator(); + HashSet set = new HashSet(); + while (iter.hasNext()) { + Token t = iter.next(); + Set subset = t.getCommandNames(); + for (String s : subset) { + if (!t.getCaseSensitive()) { + if (s.toLowerCase().startsWith(startsWith.toLowerCase())) { + set.add(s); + } + } else { + if (s.startsWith(startsWith)) { + set.add(s); + } + } + } + } + return set; + } + + public boolean containsCommand(String match) { + Iterator iter = command.iterator(); + while (iter.hasNext()) { + String t = iter.next(); + if (caseSensitive) { + if (t.equals(match)) + return true; + } else { + if (t.equalsIgnoreCase(match)) + return true; + } + } + return false; + } + + public void addSubcommand(Token t) { + subcommands.add(t); + } + + public void addSubcommand(Collection t) { + for (String a : t) { + addSubcommand(new Token(a)); + } + } + + public String toString() { + return this.command.toString(); + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/7bdbfccb/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AboutCommand.java ---------------------------------------------------------------------- diff --git a/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AboutCommand.java b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AboutCommand.java new file mode 100644 index 0000000..16971f4 --- /dev/null +++ b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AboutCommand.java @@ -0,0 +1,55 @@ +/* + * 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.accumulo.core.util.shell.commands; + +import java.io.IOException; + +import org.apache.accumulo.core.util.shell.Shell; +import org.apache.accumulo.core.util.shell.Shell.Command; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; + +public class AboutCommand extends Command { + private Option verboseOption; + + @Override + public String description() { + return "displays information about this program"; + } + + @Override + public int execute(String fullCommand, CommandLine cl, Shell shellState) throws IOException { + shellState.printInfo(); + if (cl.hasOption(verboseOption.getOpt())) + shellState.printVerboseInfo(); + return 0; + } + + @Override + public int numArgs() { + return 0; + } + + @Override + public Options getOptions() { + Options opts = new Options(); + verboseOption = new Option("v", "verbose", false, "display detailed session information"); + opts.addOption(verboseOption); + return opts; + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/7bdbfccb/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ActiveScanIterator.java ---------------------------------------------------------------------- diff --git a/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ActiveScanIterator.java b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ActiveScanIterator.java new file mode 100644 index 0000000..baed8d1 --- /dev/null +++ b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ActiveScanIterator.java @@ -0,0 +1,90 @@ +/* + * 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.accumulo.core.util.shell.commands; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.apache.accumulo.core.client.admin.ActiveScan; +import org.apache.accumulo.core.client.admin.InstanceOperations; +import org.apache.accumulo.core.client.admin.ScanType; +import org.apache.accumulo.core.util.Duration; + +class ActiveScanIterator implements Iterator { + + private InstanceOperations instanceOps; + private Iterator tsIter; + private Iterator scansIter; + + private void readNext() { + List scans = new ArrayList(); + + while (tsIter.hasNext()) { + + String tserver = tsIter.next(); + try { + List asl = instanceOps.getActiveScans(tserver); + + for (ActiveScan as : asl) + scans.add(String.format("%21s |%21s |%9s |%9s |%7s |%6s |%8s |%8s |%10s |%10s |%10s | %s", tserver, as.getClient(), + Duration.format(as.getAge(), "", "-"), Duration.format(as.getLastContactTime(), "", "-"), as.getState(), as.getType(), as.getUser(), + as.getTable(), as.getColumns(), (as.getType() == ScanType.SINGLE ? as.getExtent() : "N/A"), as.getSsiList(), as.getSsio())); + + } catch (Exception e) { + scans.add(tserver + " ERROR " + e.getMessage()); + } + + if (scans.size() > 0) + break; + } + + scansIter = scans.iterator(); + } + + ActiveScanIterator(List tservers, InstanceOperations instanceOps) { + this.instanceOps = instanceOps; + this.tsIter = tservers.iterator(); + + String header = String.format(" %-21s| %-21s| %-9s| %-9s| %-7s| %-6s| %-8s| %-8s| %-10s| %-10s| %-10s | %s", "TABLET SERVER", "CLIENT", "AGE", "LAST", + "STATE", "TYPE", "USER", "TABLE", "COLUMNS", "TABLET", "ITERATORS", "ITERATOR OPTIONS"); + + scansIter = Collections.singletonList(header).iterator(); + } + + @Override + public boolean hasNext() { + return scansIter.hasNext(); + } + + @Override + public String next() { + String next = scansIter.next(); + + if (!scansIter.hasNext()) + readNext(); + + return next; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/7bdbfccb/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AddSplitsCommand.java ---------------------------------------------------------------------- diff --git a/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AddSplitsCommand.java b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AddSplitsCommand.java new file mode 100644 index 0000000..b4864e2 --- /dev/null +++ b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AddSplitsCommand.java @@ -0,0 +1,96 @@ +/* + * 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.accumulo.core.util.shell.commands; + +import java.io.File; +import java.util.TreeSet; + +import org.apache.accumulo.core.client.TableNotFoundException; +import org.apache.accumulo.core.util.shell.Shell; +import org.apache.accumulo.core.util.shell.Shell.Command; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.MissingArgumentException; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.codec.binary.Base64; +import org.apache.hadoop.io.Text; + +public class AddSplitsCommand extends Command { + private Option optSplitsFile, base64Opt; + + public int execute(String fullCommand, CommandLine cl, Shell shellState) throws Exception { + String tableName = OptUtil.getTableOpt(cl, shellState); + boolean decode = cl.hasOption(base64Opt.getOpt()); + + TreeSet splits = new TreeSet(); + + if (cl.hasOption(optSplitsFile.getOpt())) { + String f = cl.getOptionValue(optSplitsFile.getOpt()); + + String line; + java.util.Scanner file = new java.util.Scanner(new File(f)); + while (file.hasNextLine()) { + line = file.nextLine(); + if (!line.isEmpty()) + splits.add(decode ? new Text(Base64.decodeBase64(line.getBytes())) : new Text(line)); + } + } else { + if (cl.getArgList().isEmpty()) + throw new MissingArgumentException("No split points specified"); + + for (String s : cl.getArgs()) { + splits.add(new Text(s.getBytes(Shell.CHARSET))); + } + } + + if (!shellState.getConnector().tableOperations().exists(tableName)) + throw new TableNotFoundException(null, tableName, null); + shellState.getConnector().tableOperations().addSplits(tableName, splits); + + return 0; + } + + @Override + public String description() { + return "adds split points to an existing table"; + } + + @Override + public Options getOptions() { + Options o = new Options(); + + optSplitsFile = new Option("sf", "splits-file", true, "file with a newline-separated list of rows to split the table with"); + optSplitsFile.setArgName("filename"); + + base64Opt = new Option("b64", "base64encoded", false, "decode encoded split points (splits file only)"); + + o.addOption(OptUtil.tableOpt("name of the table to add split points to")); + o.addOption(optSplitsFile); + o.addOption(base64Opt); + return o; + } + + @Override + public String usage() { + return getName() + " [{ } ]"; + } + + @Override + public int numArgs() { + return Shell.NO_FIXED_ARG_LENGTH_CHECK; + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/7bdbfccb/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AuthenticateCommand.java ---------------------------------------------------------------------- diff --git a/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AuthenticateCommand.java b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AuthenticateCommand.java new file mode 100644 index 0000000..4c93b52 --- /dev/null +++ b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AuthenticateCommand.java @@ -0,0 +1,64 @@ +/* + * 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.accumulo.core.util.shell.commands; + +import java.io.IOException; +import java.util.Map; +import java.util.Set; + +import org.apache.accumulo.core.client.AccumuloException; +import org.apache.accumulo.core.client.AccumuloSecurityException; +import org.apache.accumulo.core.util.shell.Shell; +import org.apache.accumulo.core.util.shell.Shell.Command; +import org.apache.accumulo.core.util.shell.Token; +import org.apache.commons.cli.CommandLine; + +public class AuthenticateCommand extends Command { + @Override + public int execute(String fullCommand, CommandLine cl, Shell shellState) throws AccumuloException, AccumuloSecurityException, IOException { + String user = cl.getArgs()[0]; + String p = shellState.readMaskedLine("Enter current password for '" + user + "': ", '*'); + if (p == null) { + shellState.getReader().printNewline(); + return 0; + } // user canceled + byte[] password = p.getBytes(); + boolean valid = shellState.getConnector().securityOperations().authenticateUser(user, password); + shellState.getReader().printString((valid ? "V" : "Not v") + "alid\n"); + return 0; + } + + @Override + public String description() { + return "verifies a user's credentials"; + } + + @Override + public String usage() { + return getName() + " "; + } + + @Override + public void registerCompletion(Token root, Map> completionSet) { + registerCompletionForUsers(root, completionSet); + } + + @Override + public int numArgs() { + return 1; + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/7bdbfccb/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ByeCommand.java ---------------------------------------------------------------------- diff --git a/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ByeCommand.java b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ByeCommand.java new file mode 100644 index 0000000..f0c9d24 --- /dev/null +++ b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ByeCommand.java @@ -0,0 +1,19 @@ +/* + * 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.accumulo.core.util.shell.commands; + +public class ByeCommand extends ExitCommand {} http://git-wip-us.apache.org/repos/asf/accumulo/blob/7bdbfccb/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClasspathCommand.java ---------------------------------------------------------------------- diff --git a/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClasspathCommand.java b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClasspathCommand.java new file mode 100644 index 0000000..a39e8a4 --- /dev/null +++ b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClasspathCommand.java @@ -0,0 +1,40 @@ +/* + * 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.accumulo.core.util.shell.commands; + +import org.apache.accumulo.core.util.shell.Shell; +import org.apache.accumulo.core.util.shell.Shell.Command; +import org.apache.accumulo.start.classloader.AccumuloClassLoader; +import org.apache.commons.cli.CommandLine; + +public class ClasspathCommand extends Command { + @Override + public int execute(String fullCommand, CommandLine cl, Shell shellState) { + AccumuloClassLoader.printClassPath(); + return 0; + } + + @Override + public String description() { + return "lists the current files on the classpath"; + } + + @Override + public int numArgs() { + return 0; + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/7bdbfccb/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClearCommand.java ---------------------------------------------------------------------- diff --git a/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClearCommand.java b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClearCommand.java new file mode 100644 index 0000000..efdb0a6 --- /dev/null +++ b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClearCommand.java @@ -0,0 +1,52 @@ +/* + * 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.accumulo.core.util.shell.commands; + +import java.io.IOException; + +import org.apache.accumulo.core.util.shell.Shell; +import org.apache.accumulo.core.util.shell.Shell.Command; +import org.apache.commons.cli.CommandLine; + +public class ClearCommand extends Command { + @Override + public String description() { + return "clears the screen"; + } + + @Override + public int execute(String fullCommand, CommandLine cl, Shell shellState) throws IOException { + // custom clear screen, so I don't have to redraw the prompt twice + if (!shellState.getReader().getTerminal().isANSISupported()) + throw new IOException("Terminal does not support ANSI commands"); + + // send the ANSI code to clear the screen + shellState.getReader().printString(((char) 27) + "[2J"); + shellState.getReader().flushConsole(); + + // then send the ANSI code to go to position 1,1 + shellState.getReader().printString(((char) 27) + "[1;1H"); + shellState.getReader().flushConsole(); + + return 0; + } + + @Override + public int numArgs() { + return 0; + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/7bdbfccb/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CloneTableCommand.java ---------------------------------------------------------------------- diff --git a/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CloneTableCommand.java b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CloneTableCommand.java new file mode 100644 index 0000000..4593b48 --- /dev/null +++ b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CloneTableCommand.java @@ -0,0 +1,101 @@ +/* + * 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.accumulo.core.util.shell.commands; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.accumulo.core.client.AccumuloException; +import org.apache.accumulo.core.client.AccumuloSecurityException; +import org.apache.accumulo.core.client.TableExistsException; +import org.apache.accumulo.core.client.TableNotFoundException; +import org.apache.accumulo.core.util.shell.Shell; +import org.apache.accumulo.core.util.shell.Shell.Command; +import org.apache.accumulo.core.util.shell.Token; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; + +public class CloneTableCommand extends Command { + + private Option setPropsOption; + private Option excludePropsOption; + private Option noFlushOption; + + @Override + public int execute(String fullCommand, CommandLine cl, Shell shellState) throws AccumuloException, AccumuloSecurityException, TableNotFoundException, + TableExistsException { + + HashMap props = new HashMap(); + HashSet exclude = new HashSet(); + boolean flush = true; + + if (cl.hasOption(setPropsOption.getOpt())) { + String[] keyVals = cl.getOptionValue(setPropsOption.getOpt()).split(","); + for (String keyVal : keyVals) { + String[] sa = keyVal.split("="); + props.put(sa[0], sa[1]); + } + } + + if (cl.hasOption(excludePropsOption.getOpt())) { + String[] keys = cl.getOptionValue(excludePropsOption.getOpt()).split(","); + for (String key : keys) + exclude.add(key); + } + + if (cl.hasOption(noFlushOption.getOpt())) { + flush = false; + } + + shellState.getConnector().tableOperations().clone(cl.getArgs()[0], cl.getArgs()[1], flush, props, exclude); + return 0; + } + + @Override + public String usage() { + return getName() + " "; + } + + @Override + public String description() { + return "clones a table"; + } + + public void registerCompletion(Token root, Map> completionSet) { + registerCompletionForTables(root, completionSet); + } + + @Override + public Options getOptions() { + Options o = new Options(); + setPropsOption = new Option("s", "set", true, "set initial properties before the table comes online. Expects ={,=}"); + o.addOption(setPropsOption); + excludePropsOption = new Option("e", "exclude", true, "exclude properties that should not be copied from source table. Expects {,}"); + o.addOption(excludePropsOption); + noFlushOption = new Option("nf", "noFlush", false, "do not flush table data in memory before cloning."); + o.addOption(noFlushOption); + return o; + } + + @Override + public int numArgs() { + return 2; + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/7bdbfccb/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClsCommand.java ---------------------------------------------------------------------- diff --git a/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClsCommand.java b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClsCommand.java new file mode 100644 index 0000000..cef6098 --- /dev/null +++ b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClsCommand.java @@ -0,0 +1,19 @@ +/* + * 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.accumulo.core.util.shell.commands; + +public class ClsCommand extends ClearCommand {} http://git-wip-us.apache.org/repos/asf/accumulo/blob/7bdbfccb/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CompactCommand.java ---------------------------------------------------------------------- diff --git a/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CompactCommand.java b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CompactCommand.java new file mode 100644 index 0000000..c08a13a --- /dev/null +++ b/1.5/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CompactCommand.java @@ -0,0 +1,78 @@ +/* + * 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.accumulo.core.util.shell.commands; + +import org.apache.accumulo.core.client.AccumuloException; +import org.apache.accumulo.core.client.AccumuloSecurityException; +import org.apache.accumulo.core.util.shell.Shell; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.hadoop.io.Text; + +public class CompactCommand extends TableOperation { + private Option noFlushOption, waitOpt; + private boolean flush; + private Text startRow; + private Text endRow; + + boolean override = false; + private boolean wait; + + @Override + public String description() { + return "sets all tablets for a table to major compact as soon as possible (based on current time)"; + } + + protected void doTableOp(Shell shellState, String tableName) throws AccumuloException, AccumuloSecurityException { + // compact the tables + try { + if (wait) + Shell.log.info("Compacting table ..."); + + shellState.getConnector().tableOperations().compact(tableName, startRow, endRow, flush, wait); + + Shell.log.info("Compaction of table " + tableName + " " + (wait ? "completed" : "started") + " for given range"); + } catch (Exception ex) { + throw new AccumuloException(ex); + } + } + + @Override + public int execute(String fullCommand, CommandLine cl, Shell shellState) throws Exception { + flush = !cl.hasOption(noFlushOption.getOpt()); + startRow = OptUtil.getStartRow(cl); + endRow = OptUtil.getEndRow(cl); + wait = cl.hasOption(waitOpt.getOpt()); + + return super.execute(fullCommand, cl, shellState); + } + + @Override + public Options getOptions() { + Options opts = super.getOptions(); + + opts.addOption(OptUtil.startRowOpt()); + opts.addOption(OptUtil.endRowOpt()); + noFlushOption = new Option("nf", "noFlush", false, "do not flush table data in memory before compacting."); + opts.addOption(noFlushOption); + waitOpt = new Option("w", "wait", false, "wait for compact to finish"); + opts.addOption(waitOpt); + + return opts; + } +}