You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@accumulo.apache.org by md...@apache.org on 2014/04/08 02:37:16 UTC

[13/27] Revert "ACCUMULO-1897 Move shell into new package and module"

http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/ShellOptionsJC.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/ShellOptionsJC.java b/core/src/main/java/org/apache/accumulo/core/util/shell/ShellOptionsJC.java
new file mode 100644
index 0000000..38692a0
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/ShellOptionsJC.java
@@ -0,0 +1,280 @@
+/*
+ * 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.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.TreeMap;
+
+import org.apache.accumulo.core.client.ClientConfiguration;
+import org.apache.accumulo.core.client.ClientConfiguration.ClientProperty;
+import org.apache.accumulo.core.client.security.tokens.AuthenticationToken;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.log4j.Logger;
+
+import com.beust.jcommander.DynamicParameter;
+import com.beust.jcommander.IStringConverter;
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.ParameterException;
+import com.beust.jcommander.converters.FileConverter;
+
+public class ShellOptionsJC {
+  // Use the Shell logger because this is really just an extension.
+  public static final Logger log = Logger.getLogger(Shell.class);
+
+  @Parameter(names = {"-u", "--user"}, description = "username (defaults to your OS user)")
+  private String username = System.getProperty("user.name", "root");
+
+  public static class PasswordConverter implements IStringConverter<String> {
+    public static final String STDIN = "stdin";
+
+    private enum KeyType {
+      PASS("pass:"), ENV("env:") {
+        @Override
+        String process(String value) {
+          return System.getenv(value);
+        }
+      },
+      FILE("file:") {
+        @Override
+        String process(String value) {
+          Scanner scanner = null;
+          try {
+            scanner = new Scanner(new File(value));
+            return scanner.nextLine();
+          } catch (FileNotFoundException e) {
+            throw new ParameterException(e);
+          } finally {
+            if (scanner != null) {
+              scanner.close();
+            }
+          }
+        }
+      },
+      STDIN(PasswordConverter.STDIN) {
+        @Override
+        public boolean matches(String value) {
+          return prefix.equals(value);
+        }
+
+        @Override
+        public String convert(String value) {
+          // Will check for this later
+          return prefix;
+        }
+      };
+
+      String prefix;
+
+      private KeyType(String prefix) {
+        this.prefix = prefix;
+      }
+
+      public boolean matches(String value) {
+        return value.startsWith(prefix);
+      }
+
+      public String convert(String value) {
+        return process(value.substring(prefix.length()));
+      }
+
+      String process(String value) {
+        return value;
+      }
+    };
+
+    @Override
+    public String convert(String value) {
+      for (KeyType keyType : KeyType.values()) {
+        if (keyType.matches(value)) {
+          return keyType.convert(value);
+        }
+      }
+
+      return value;
+    }
+  }
+
+  // Note: Don't use "password = true" because then it will prompt even if we have a token
+  @Parameter(names = {"-p", "--password"}, description = "password (can be specified as 'pass:<password>', 'file:<local file containing the password>', "
+      + "'env:<variable containing the pass>', or stdin)", converter = PasswordConverter.class)
+  private String password;
+
+  public static class TokenConverter implements IStringConverter<AuthenticationToken> {
+    @Override
+    public AuthenticationToken convert(String value) {
+      try {
+        return Class.forName(value).asSubclass(AuthenticationToken.class).newInstance();
+      } catch (Exception e) {
+        // Catching ClassNotFoundException, ClassCastException, InstantiationException and IllegalAccessException
+        throw new ParameterException(e);
+      }
+    }
+  }
+
+  @Parameter(names = {"-tc", "--tokenClass"}, description = "token type to create, use the -l to pass options", converter = TokenConverter.class)
+  private AuthenticationToken authenticationToken;
+
+  @DynamicParameter(names = {"-l", "--tokenProperty"}, description = "login properties in the format key=value. Reuse -l for each property")
+  private Map<String,String> tokenProperties = new TreeMap<String,String>();
+
+  @Parameter(names = "--disable-tab-completion", description = "disables tab completion (for less overhead when scripting)")
+  private boolean tabCompletionDisabled;
+
+  @Parameter(names = "--debug", description = "enables client debugging")
+  private boolean debugEnabled;
+
+  @Parameter(names = "--fake", description = "fake a connection to accumulo")
+  private boolean fake;
+
+  @Parameter(names = {"-?", "--help"}, help = true, description = "display this help")
+  private boolean helpEnabled;
+
+  @Parameter(names = {"-e", "--execute-command"}, description = "executes a command, and then exits")
+  private String execCommand;
+
+  @Parameter(names = {"-f", "--execute-file"}, description = "executes commands from a file at startup", converter = FileConverter.class)
+  private File execFile;
+
+  @Parameter(names = {"-fv", "--execute-file-verbose"}, description = "executes commands from a file at startup, with commands shown",
+      converter = FileConverter.class)
+  private File execFileVerbose;
+
+  @Parameter(names = {"-h", "--hdfsZooInstance"}, description = "use hdfs zoo instance")
+  private boolean hdfsZooInstance;
+
+  @Parameter(names = {"-z", "--zooKeeperInstance"}, description = "use a zookeeper instance with the given instance name and list of zoo hosts", arity = 2)
+  private List<String> zooKeeperInstance = new ArrayList<String>();
+
+  @Parameter(names = {"--ssl"}, description = "use ssl to connect to accumulo")
+  private boolean useSsl = false;
+
+  @Parameter(
+      names = "--config-file",
+      description = "read the given client config file.  If omitted, the path searched can be specified with $ACCUMULO_CLIENT_CONF_PATH, which defaults to ~/.accumulo/config:$ACCUMULO_CONF_DIR/client.conf:/etc/accumulo/client.conf")
+  private String clientConfigFile = null;
+
+  @Parameter(names = {"-zi", "--zooKeeperInstanceName"}, description = "use a zookeeper instance with the given instance name")
+  private String zooKeeperInstanceName;
+
+  @Parameter(names = {"-zh", "--zooKeeperHosts"}, description = "use a zookeeper instance with the given list of zoo hosts")
+  private String zooKeeperHosts;
+
+  @Parameter(names = "--auth-timeout", description = "minutes the shell can be idle without re-entering a password")
+  private int authTimeout = 60; // TODO Add validator for positive number
+
+  @Parameter(names = "--disable-auth-timeout", description = "disables requiring the user to re-type a password after being idle")
+  private boolean authTimeoutDisabled;
+
+  @Parameter(hidden = true)
+  private List<String> unrecognizedOptions;
+
+  public String getUsername() {
+    return username;
+  }
+
+  public String getPassword() {
+    return password;
+  }
+
+  public AuthenticationToken getAuthenticationToken() {
+    return authenticationToken;
+  }
+
+  public Map<String,String> getTokenProperties() {
+    return tokenProperties;
+  }
+
+  public boolean isTabCompletionDisabled() {
+    return tabCompletionDisabled;
+  }
+
+  public boolean isDebugEnabled() {
+    return debugEnabled;
+  }
+
+  public boolean isFake() {
+    return fake;
+  }
+
+  public boolean isHelpEnabled() {
+    return helpEnabled;
+  }
+
+  public String getExecCommand() {
+    return execCommand;
+  }
+
+  public File getExecFile() {
+    return execFile;
+  }
+
+  public File getExecFileVerbose() {
+    return execFileVerbose;
+  }
+
+  public boolean isHdfsZooInstance() {
+    return hdfsZooInstance;
+  }
+
+  public List<String> getZooKeeperInstance() {
+    return zooKeeperInstance;
+  }
+
+  public String getZooKeeperInstanceName() {
+    return zooKeeperInstanceName;
+  }
+
+  public String getZooKeeperHosts() {
+    return zooKeeperHosts;
+  }
+
+  public int getAuthTimeout() {
+    return authTimeout;
+  }
+
+  public boolean isAuthTimeoutDisabled() {
+    return authTimeoutDisabled;
+  }
+
+  public List<String> getUnrecognizedOptions() {
+    return unrecognizedOptions;
+  }
+
+  public boolean useSsl() {
+    return useSsl;
+  }
+
+  public String getClientConfigFile() {
+    return clientConfigFile;
+  }
+
+  public ClientConfiguration getClientConfiguration() throws ConfigurationException, FileNotFoundException {
+    ClientConfiguration clientConfig = clientConfigFile == null ? ClientConfiguration.loadDefault() : new ClientConfiguration(new PropertiesConfiguration(
+        getClientConfigFile()));
+    if (useSsl()) {
+      clientConfig.setProperty(ClientProperty.INSTANCE_RPC_SSL_ENABLED, "true");
+    }
+    return clientConfig;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/ShellUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/ShellUtil.java b/core/src/main/java/org/apache/accumulo/core/util/shell/ShellUtil.java
new file mode 100644
index 0000000..f0dd505
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/ShellUtil.java
@@ -0,0 +1,60 @@
+/*
+ * 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.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Scanner;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.hadoop.io.Text;
+
+import com.google.common.collect.Lists;
+
+public class ShellUtil {
+
+  /**
+   * Scans the given file line-by-line (ignoring empty lines) and returns a list containing those lines. If decode is set to true, every line is decoded using
+   * {@link Base64#decodeBase64(byte[])} from the UTF-8 bytes of that line before inserting in the list.
+   * 
+   * @param filename
+   *          Path to the file that needs to be scanned
+   * @param decode
+   *          Whether to decode lines in the file
+   * @return List of {@link Text} objects containing data in the given file
+   * @throws FileNotFoundException
+   *           if the given file doesn't exist
+   */
+  public static List<Text> scanFile(String filename, boolean decode) throws FileNotFoundException {
+    String line;
+    Scanner file = new Scanner(new File(filename), StandardCharsets.UTF_8.name());
+    List<Text> result = Lists.newArrayList();
+    try {
+      while (file.hasNextLine()) {
+        line = file.nextLine();
+        if (!line.isEmpty()) {
+          result.add(decode ? new Text(Base64.decodeBase64(line.getBytes(StandardCharsets.UTF_8))) : new Text(line));
+        }
+      }
+    } finally {
+      file.close();
+    }
+    return result;
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/Token.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/Token.java b/core/src/main/java/org/apache/accumulo/core/util/shell/Token.java
new file mode 100644
index 0000000..b6c5869
--- /dev/null
+++ b/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<String> command = new HashSet<String>();
+  private Set<Token> subcommands = new HashSet<Token>();
+  private boolean caseSensitive = false;
+  
+  public Token() {}
+  
+  public Token(String commandName) {
+    this();
+    command.add(commandName);
+  }
+  
+  public Token(Collection<String> commandNames) {
+    this();
+    command.addAll(commandNames);
+  }
+  
+  public Token(Set<String> commandNames, Set<Token> subCommandNames) {
+    this();
+    command.addAll(commandNames);
+    subcommands.addAll(subCommandNames);
+  }
+  
+  public void setCaseSensitive(boolean cs) {
+    caseSensitive = cs;
+  }
+  
+  public boolean getCaseSensitive() {
+    return caseSensitive;
+  }
+  
+  public Set<String> getCommandNames() {
+    return command;
+  }
+  
+  public Set<Token> getSubcommandList() {
+    return subcommands;
+  }
+  
+  public Token getSubcommand(String name) {
+    Iterator<Token> iter = subcommands.iterator();
+    while (iter.hasNext()) {
+      Token t = iter.next();
+      if (t.containsCommand(name))
+        return t;
+    }
+    return null;
+  }
+  
+  public Set<String> getSubcommandNames() {
+    HashSet<String> set = new HashSet<String>();
+    for (Token t : subcommands)
+      set.addAll(t.getCommandNames());
+    return set;
+  }
+  
+  public Set<String> getSubcommandNames(String startsWith) {
+    Iterator<Token> iter = subcommands.iterator();
+    HashSet<String> set = new HashSet<String>();
+    while (iter.hasNext()) {
+      Token t = iter.next();
+      Set<String> 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<String> 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<String> 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/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AboutCommand.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AboutCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AboutCommand.java
new file mode 100644
index 0000000..a0a73e4
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AboutCommand.java
@@ -0,0 +1,56 @@
+/*
+ * 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(final String fullCommand, final CommandLine cl, final 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() {
+    final 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/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ActiveCompactionIterator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ActiveCompactionIterator.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ActiveCompactionIterator.java
new file mode 100644
index 0000000..bd7d9b2
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ActiveCompactionIterator.java
@@ -0,0 +1,136 @@
+/*
+ * 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.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.accumulo.core.client.IteratorSetting;
+import org.apache.accumulo.core.client.admin.ActiveCompaction;
+import org.apache.accumulo.core.client.admin.InstanceOperations;
+import org.apache.accumulo.core.util.Duration;
+
+class ActiveCompactionIterator implements Iterator<String> {
+  
+  private InstanceOperations instanceOps;
+  private Iterator<String> tsIter;
+  private Iterator<String> compactionIter;
+  
+  private static String maxDecimal(double count) {
+    if (count < 9.995)
+      return String.format("%.2f", count);
+    if (count < 99.95)
+      return String.format("%.1f", count);
+    return String.format("%.0f", count);
+  }
+
+  private static String shortenCount(long count) {
+    if (count < 1000)
+      return count + "";
+    if (count < 1000000)
+      return maxDecimal(count / 1000.0) + "K";
+    if (count < 1000000000)
+      return maxDecimal(count / 1000000.0) + "M";
+    return maxDecimal(count / 1000000000.0) + "B";
+  }
+
+  private void readNext() {
+    final List<String> compactions = new ArrayList<String>();
+    
+    while (tsIter.hasNext()) {
+      
+      final String tserver = tsIter.next();
+      try {
+        List<ActiveCompaction> acl = instanceOps.getActiveCompactions(tserver);
+        
+        acl = new ArrayList<ActiveCompaction>(acl);
+        
+        Collections.sort(acl, new Comparator<ActiveCompaction>() {
+          @Override
+          public int compare(ActiveCompaction o1, ActiveCompaction o2) {
+            return (int) (o2.getAge() - o1.getAge());
+          }
+        });
+
+        for (ActiveCompaction ac : acl) {
+          String output = ac.getOutputFile();
+          int index = output.indexOf("tables");
+          if (index > 0) {
+            output = output.substring(index + 6);
+          }
+          
+          ac.getIterators();
+          
+          List<String> iterList = new ArrayList<String>();
+          Map<String,Map<String,String>> iterOpts = new HashMap<String,Map<String,String>>();
+          for (IteratorSetting is : ac.getIterators()) {
+            iterList.add(is.getName() + "=" + is.getPriority() + "," + is.getIteratorClass());
+            iterOpts.put(is.getName(), is.getOptions());
+          }
+
+          compactions.add(String.format("%21s | %9s | %5s | %6s | %5s | %5s | %15s | %-40s | %5s | %35s | %9s | %s", tserver,
+              Duration.format(ac.getAge(), "", "-"), ac.getType(), ac.getReason(), shortenCount(ac.getEntriesRead()), shortenCount(ac.getEntriesWritten()),
+              ac.getTable(), ac.getExtent(), ac.getInputFiles().size(), output, iterList, iterOpts));
+        }
+      } catch (Exception e) {
+        compactions.add(tserver + " ERROR " + e.getMessage());
+      }
+      
+      if (compactions.size() > 0) {
+        break;
+      }
+    }
+    
+    compactionIter = compactions.iterator();
+  }
+  
+  ActiveCompactionIterator(List<String> tservers, InstanceOperations instanceOps) {
+    this.instanceOps = instanceOps;
+    this.tsIter = tservers.iterator();
+    
+    final String header = String.format(" %-21s| %-9s | %-5s | %-6s | %-5s | %-5s | %-15s | %-40s | %-5s | %-35s | %-9s | %s", "TABLET SERVER", "AGE", "TYPE",
+        "REASON", "READ", "WROTE", "TABLE", "TABLET", "INPUT", "OUTPUT", "ITERATORS", "ITERATOR OPTIONS");
+    
+    compactionIter = Collections.singletonList(header).iterator();
+  }
+  
+  @Override
+  public boolean hasNext() {
+    return compactionIter.hasNext();
+  }
+  
+  @Override
+  public String next() {
+    final String next = compactionIter.next();
+    
+    if (!compactionIter.hasNext())
+      readNext();
+    
+    return next;
+  }
+  
+  @Override
+  public void remove() {
+    throw new UnsupportedOperationException();
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ActiveScanIterator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ActiveScanIterator.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ActiveScanIterator.java
new file mode 100644
index 0000000..f1e736f
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ActiveScanIterator.java
@@ -0,0 +1,91 @@
+/*
+ * 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<String> {
+  
+  private InstanceOperations instanceOps;
+  private Iterator<String> tsIter;
+  private Iterator<String> scansIter;
+  
+  private void readNext() {
+    final List<String> scans = new ArrayList<String>();
+    
+    while (tsIter.hasNext()) {
+      
+      final String tserver = tsIter.next();
+      try {
+        final List<ActiveScan> asl = instanceOps.getActiveScans(tserver);
+        
+        for (ActiveScan as : asl) {
+          scans.add(String.format("%21s |%21s |%9s |%9s |%7s |%6s |%8s |%8s |%10s |%20s |%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.getAuthorizations(), (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<String> tservers, InstanceOperations instanceOps) {
+    this.instanceOps = instanceOps;
+    this.tsIter = tservers.iterator();
+    
+    final String header = String.format(" %-21s| %-21s| %-9s| %-9s| %-7s| %-6s| %-8s| %-8s| %-10s| %-20s| %-10s| %-10s | %s", "TABLET SERVER", "CLIENT", "AGE",
+        "LAST", "STATE", "TYPE", "USER", "TABLE", "COLUMNS", "AUTHORIZATIONS", "TABLET", "ITERATORS", "ITERATOR OPTIONS");
+    
+    scansIter = Collections.singletonList(header).iterator();
+  }
+  
+  @Override
+  public boolean hasNext() {
+    return scansIter.hasNext();
+  }
+  
+  @Override
+  public String next() {
+    final 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/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AddAuthsCommand.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AddAuthsCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AddAuthsCommand.java
new file mode 100644
index 0000000..bd478de
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AddAuthsCommand.java
@@ -0,0 +1,82 @@
+/*
+ * 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.Map;
+import java.util.Set;
+
+import org.apache.accumulo.core.client.AccumuloException;
+import org.apache.accumulo.core.client.AccumuloSecurityException;
+import org.apache.accumulo.core.security.Authorizations;
+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.OptionGroup;
+import org.apache.commons.cli.Options;
+
+public class AddAuthsCommand extends Command {
+  private Option userOpt;
+  private Option scanOptAuths;
+  
+  @Override
+  public int execute(final String fullCommand, final CommandLine cl, final Shell shellState) throws AccumuloException, AccumuloSecurityException {
+    final String user = cl.getOptionValue(userOpt.getOpt(), shellState.getConnector().whoami());
+    final String scanOpts = cl.getOptionValue(scanOptAuths.getOpt());
+    Authorizations auths = shellState.getConnector().securityOperations().getUserAuthorizations(user);
+    StringBuilder userAuths = new StringBuilder();
+    if (!auths.isEmpty()) {
+      userAuths.append(auths.toString());
+      userAuths.append(",");
+    }
+    userAuths.append(scanOpts);
+    shellState.getConnector().securityOperations().changeUserAuthorizations(user, ScanCommand.parseAuthorizations(userAuths.toString()));
+    Shell.log.debug("Changed record-level authorizations for user " + user);
+    return 0;
+  }
+  
+  @Override
+  public String description() {
+    return "adds authorizations to the maximum scan authorizations for a user";
+  }
+  
+  @Override
+  public void registerCompletion(final Token root, final Map<Command.CompletionSet,Set<String>> completionSet) {
+    registerCompletionForUsers(root, completionSet);
+  }
+  
+  @Override
+  public Options getOptions() {
+    final Options o = new Options();
+    final OptionGroup setOrClear = new OptionGroup();
+    scanOptAuths = new Option("s", "scan-authorizations", true, "scan authorizations to set");
+    scanOptAuths.setArgName("comma-separated-authorizations");
+    setOrClear.addOption(scanOptAuths);
+    setOrClear.setRequired(true);
+    o.addOptionGroup(setOrClear);
+    userOpt = new Option(Shell.userOption, "user", true, "user to operate on");
+    userOpt.setArgName("user");
+    o.addOption(userOpt);
+    return o;
+  }
+  
+  @Override
+  public int numArgs() {
+    return 0;
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AddSplitsCommand.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AddSplitsCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AddSplitsCommand.java
new file mode 100644
index 0000000..b8ba621
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AddSplitsCommand.java
@@ -0,0 +1,88 @@
+/*
+ * 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.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.accumulo.core.util.shell.ShellUtil;
+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.hadoop.io.Text;
+
+public class AddSplitsCommand extends Command {
+  private Option optSplitsFile, base64Opt;
+  
+  public int execute(final String fullCommand, final CommandLine cl, final Shell shellState) throws Exception {
+    final String tableName = OptUtil.getTableOpt(cl, shellState);
+    final boolean decode = cl.hasOption(base64Opt.getOpt());
+    
+    final TreeSet<Text> splits = new TreeSet<Text>();
+    
+    if (cl.hasOption(optSplitsFile.getOpt())) {
+      splits.addAll(ShellUtil.scanFile(cl.getOptionValue(optSplitsFile.getOpt()), decode));
+    } 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() {
+    final 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() + " [<split>{ <split>} ]";
+  }
+  
+  @Override
+  public int numArgs() {
+    return Shell.NO_FIXED_ARG_LENGTH_CHECK;
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AuthenticateCommand.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AuthenticateCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AuthenticateCommand.java
new file mode 100644
index 0000000..8ebdae8
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/AuthenticateCommand.java
@@ -0,0 +1,66 @@
+/*
+ * 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.nio.charset.StandardCharsets;
+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.security.tokens.PasswordToken;
+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(final String fullCommand, final CommandLine cl, final Shell shellState) throws AccumuloException, AccumuloSecurityException, IOException {
+    final String user = cl.getArgs()[0];
+    final String p = shellState.readMaskedLine("Enter current password for '" + user + "': ", '*');
+    if (p == null) {
+      shellState.getReader().println();
+      return 0;
+    } // user canceled
+    final byte[] password = p.getBytes(StandardCharsets.UTF_8);
+    final boolean valid = shellState.getConnector().securityOperations().authenticateUser(user, new PasswordToken(password));
+    shellState.getReader().println((valid ? "V" : "Not v") + "alid");
+    return 0;
+  }
+  
+  @Override
+  public String description() {
+    return "verifies a user's credentials";
+  }
+  
+  @Override
+  public String usage() {
+    return getName() + " <username>";
+  }
+  
+  @Override
+  public void registerCompletion(final Token root, final Map<Command.CompletionSet,Set<String>> completionSet) {
+    registerCompletionForUsers(root, completionSet);
+  }
+  
+  @Override
+  public int numArgs() {
+    return 1;
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ByeCommand.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ByeCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ByeCommand.java
new file mode 100644
index 0000000..f0c9d24
--- /dev/null
+++ b/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/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClasspathCommand.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClasspathCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClasspathCommand.java
new file mode 100644
index 0000000..b2fe300
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClasspathCommand.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 jline.console.ConsoleReader;
+
+import org.apache.accumulo.core.util.shell.Shell;
+import org.apache.accumulo.core.util.shell.Shell.Command;
+import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader;
+import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader.Printer;
+import org.apache.commons.cli.CommandLine;
+
+public class ClasspathCommand extends Command {
+  @Override
+  public int execute(final String fullCommand, final CommandLine cl, final Shell shellState) {
+    final ConsoleReader reader = shellState.getReader();
+    AccumuloVFSClassLoader.printClassPath(new Printer() {
+      @Override
+      public void print(String s) {
+        try {
+          reader.println(s);
+        } catch (IOException ex) {
+          throw new RuntimeException(ex);
+        }
+      }
+    });
+    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/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClearCommand.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClearCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClearCommand.java
new file mode 100644
index 0000000..4f15650
--- /dev/null
+++ b/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(final String fullCommand, final CommandLine cl, final 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().print(((char) 27) + "[2J");
+    shellState.getReader().flush();
+    
+    // then send the ANSI code to go to position 1,1
+    shellState.getReader().print(((char) 27) + "[1;1H");
+    shellState.getReader().flush();
+    
+    return 0;
+  }
+  
+  @Override
+  public int numArgs() {
+    return 0;
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CloneTableCommand.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CloneTableCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CloneTableCommand.java
new file mode 100644
index 0000000..207e530
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CloneTableCommand.java
@@ -0,0 +1,102 @@
+/*
+ * 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(final String fullCommand, final CommandLine cl, final Shell shellState) throws AccumuloException, AccumuloSecurityException, TableNotFoundException,
+      TableExistsException {
+    
+    final HashMap<String,String> props = new HashMap<String,String>();
+    final HashSet<String> exclude = new HashSet<String>();
+    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() + " <current table name> <new table name>";
+  }
+  
+  @Override
+  public String description() {
+    return "clones a table";
+  }
+  
+  public void registerCompletion(final Token root, final Map<Command.CompletionSet,Set<String>> completionSet) {
+    registerCompletionForTables(root, completionSet);
+  }
+  
+  @Override
+  public Options getOptions() {
+    final Options o = new Options();
+    setPropsOption = new Option("s", "set", true, "set initial properties before the table comes online. Expects <prop>=<value>{,<prop>=<value>}");
+    o.addOption(setPropsOption);
+    excludePropsOption = new Option("e", "exclude", true, "exclude properties that should not be copied from source table. Expects <prop>{,<prop>}");
+    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/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClsCommand.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClsCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ClsCommand.java
new file mode 100644
index 0000000..cef6098
--- /dev/null
+++ b/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/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CompactCommand.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CompactCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CompactCommand.java
new file mode 100644
index 0000000..55e9395
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CompactCommand.java
@@ -0,0 +1,129 @@
+/*
+ * 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.List;
+
+import org.apache.accumulo.core.client.AccumuloException;
+import org.apache.accumulo.core.client.AccumuloSecurityException;
+import org.apache.accumulo.core.client.IteratorSetting;
+import org.apache.accumulo.core.client.TableNotFoundException;
+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, profileOpt, cancelOpt;
+  private boolean flush;
+  private Text startRow;
+  private Text endRow;
+  private List<IteratorSetting> iterators;
+  
+  boolean override = false;
+  private boolean wait;
+  
+  private boolean cancel = false;
+
+  @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(final Shell shellState, final String tableName) throws AccumuloException, AccumuloSecurityException {
+    // compact the tables
+    
+    if (cancel) {
+      try {
+        shellState.getConnector().tableOperations().cancelCompaction(tableName);
+        Shell.log.info("Compaction canceled for table " + tableName);
+      } catch (TableNotFoundException e) {
+        throw new AccumuloException(e);
+      }
+    } else {
+      try {
+        if (wait) {
+          Shell.log.info("Compacting table ...");
+        }
+        
+        shellState.getConnector().tableOperations().compact(tableName, startRow, endRow, iterators, 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(final String fullCommand, final CommandLine cl, final Shell shellState) throws Exception {
+    
+    if (cl.hasOption(cancelOpt.getLongOpt())) {
+      cancel = true;
+      
+      if (cl.getOptions().length > 2) {
+        throw new IllegalArgumentException("Can not specify other options with cancel");
+      }
+    } else {
+      cancel = false;
+    }
+
+    flush = !cl.hasOption(noFlushOption.getOpt());
+    startRow = OptUtil.getStartRow(cl);
+    endRow = OptUtil.getEndRow(cl);
+    wait = cl.hasOption(waitOpt.getOpt());
+    
+    if (cl.hasOption(profileOpt.getOpt())) {
+      List<IteratorSetting> iterators = shellState.iteratorProfiles.get(cl.getOptionValue(profileOpt.getOpt()));
+      if (iterators == null) {
+        Shell.log.error("Profile " + cl.getOptionValue(profileOpt.getOpt()) + " does not exist");
+        return -1;
+      }
+      
+      this.iterators = new ArrayList<IteratorSetting>(iterators);
+    } else {
+      this.iterators = Collections.emptyList();
+    }
+
+
+    return super.execute(fullCommand, cl, shellState);
+  }
+  
+  @Override
+  public Options getOptions() {
+    final 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);
+    
+    profileOpt = new Option("pn", "profile", true, "iterator profile name");
+    profileOpt.setArgName("profile");
+    opts.addOption(profileOpt);
+
+    cancelOpt = new Option(null, "cancel", false, "cancel user initiated compactions");
+    opts.addOption(cancelOpt);
+
+    return opts;
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ConfigCommand.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ConfigCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ConfigCommand.java
new file mode 100644
index 0000000..c76a51f
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ConfigCommand.java
@@ -0,0 +1,315 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+
+import jline.console.ConsoleReader;
+
+import org.apache.accumulo.core.client.AccumuloException;
+import org.apache.accumulo.core.client.AccumuloSecurityException;
+import org.apache.accumulo.core.client.NamespaceNotFoundException;
+import org.apache.accumulo.core.client.TableNotFoundException;
+import org.apache.accumulo.core.client.impl.Namespaces;
+import org.apache.accumulo.core.client.impl.Tables;
+import org.apache.accumulo.core.conf.AccumuloConfiguration;
+import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.security.ColumnVisibility;
+import org.apache.accumulo.core.util.BadArgumentException;
+import org.apache.accumulo.core.util.shell.Shell;
+import org.apache.accumulo.core.util.shell.Shell.Command;
+import org.apache.accumulo.core.util.shell.Shell.PrintFile;
+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.OptionGroup;
+import org.apache.commons.cli.Options;
+
+public class ConfigCommand extends Command {
+  private Option tableOpt, deleteOpt, setOpt, filterOpt, disablePaginationOpt, outputFileOpt, namespaceOpt;
+
+  private int COL1 = 10, COL2 = 7;
+  private ConsoleReader reader;
+
+  @Override
+  public void registerCompletion(final Token root, final Map<Command.CompletionSet,Set<String>> completionSet) {
+    final Token cmd = new Token(getName());
+    final Token sub = new Token("-" + setOpt.getOpt());
+    for (Property p : Property.values()) {
+      if (!(p.getKey().endsWith(".")) && !p.isExperimental()) {
+        sub.addSubcommand(new Token(p.toString()));
+      }
+    }
+    cmd.addSubcommand(sub);
+    root.addSubcommand(cmd);
+  }
+
+  @Override
+  public int execute(final String fullCommand, final CommandLine cl, final Shell shellState) throws AccumuloException, AccumuloSecurityException,
+      TableNotFoundException, IOException, ClassNotFoundException, NamespaceNotFoundException {
+    reader = shellState.getReader();
+
+    final String tableName = cl.getOptionValue(tableOpt.getOpt());
+    if (tableName != null && !shellState.getConnector().tableOperations().exists(tableName)) {
+      throw new TableNotFoundException(null, tableName, null);
+    }
+    final String namespace = cl.getOptionValue(namespaceOpt.getOpt());
+    if (namespace != null && !shellState.getConnector().namespaceOperations().exists(namespace)) {
+      throw new NamespaceNotFoundException(null, namespace, null);
+    }
+    if (cl.hasOption(deleteOpt.getOpt())) {
+      // delete property from table
+      String property = cl.getOptionValue(deleteOpt.getOpt());
+      if (property.contains("=")) {
+        throw new BadArgumentException("Invalid '=' operator in delete operation.", fullCommand, fullCommand.indexOf('='));
+      }
+      if (tableName != null) {
+        if (!Property.isValidTablePropertyKey(property)) {
+          Shell.log.warn("Invalid per-table property : " + property + ", still removing from zookeeper if it's there.");
+        }
+        shellState.getConnector().tableOperations().removeProperty(tableName, property);
+        Shell.log.debug("Successfully deleted table configuration option.");
+      } else if (namespace != null) {
+        if (!Property.isValidTablePropertyKey(property)) {
+          Shell.log.warn("Invalid per-table property : " + property + ", still removing from zookeeper if it's there.");
+        }
+        shellState.getConnector().namespaceOperations().removeProperty(namespace, property);
+        Shell.log.debug("Successfully deleted namespace configuration option.");
+      } else {
+        if (!Property.isValidZooPropertyKey(property)) {
+          Shell.log.warn("Invalid per-table property : " + property + ", still removing from zookeeper if it's there.");
+        }
+        shellState.getConnector().instanceOperations().removeProperty(property);
+        Shell.log.debug("Successfully deleted system configuration option");
+      }
+    } else if (cl.hasOption(setOpt.getOpt())) {
+      // set property on table
+      String property = cl.getOptionValue(setOpt.getOpt()), value = null;
+      if (!property.contains("=")) {
+        throw new BadArgumentException("Missing '=' operator in set operation.", fullCommand, fullCommand.indexOf(property));
+      }
+      final String pair[] = property.split("=", 2);
+      property = pair[0];
+      value = pair[1];
+
+      if (tableName != null) {
+        if (!Property.isValidTablePropertyKey(property)) {
+          throw new BadArgumentException("Invalid per-table property.", fullCommand, fullCommand.indexOf(property));
+        }
+        if (property.equals(Property.TABLE_DEFAULT_SCANTIME_VISIBILITY.getKey())) {
+          new ColumnVisibility(value); // validate that it is a valid expression
+        }
+        shellState.getConnector().tableOperations().setProperty(tableName, property, value);
+        Shell.log.debug("Successfully set table configuration option.");
+      } else if (namespace != null) {
+        if (!Property.isValidTablePropertyKey(property)) {
+          throw new BadArgumentException("Invalid per-table property.", fullCommand, fullCommand.indexOf(property));
+        }
+        if (property.equals(Property.TABLE_DEFAULT_SCANTIME_VISIBILITY.getKey())) {
+          new ColumnVisibility(value); // validate that it is a valid expression
+        }
+        shellState.getConnector().namespaceOperations().setProperty(namespace, property, value);
+        Shell.log.debug("Successfully set table configuration option.");
+      } else {
+        if (!Property.isValidZooPropertyKey(property)) {
+          throw new BadArgumentException("Property cannot be modified in zookeeper", fullCommand, fullCommand.indexOf(property));
+        }
+        shellState.getConnector().instanceOperations().setProperty(property, value);
+        Shell.log.debug("Successfully set system configuration option");
+      }
+    } else {
+      // display properties
+      final TreeMap<String,String> systemConfig = new TreeMap<String,String>();
+      systemConfig.putAll(shellState.getConnector().instanceOperations().getSystemConfiguration());
+
+      final String outputFile = cl.getOptionValue(outputFileOpt.getOpt());
+      final PrintFile printFile = outputFile == null ? null : new PrintFile(outputFile);
+
+      final TreeMap<String,String> siteConfig = new TreeMap<String,String>();
+      siteConfig.putAll(shellState.getConnector().instanceOperations().getSiteConfiguration());
+
+      final TreeMap<String,String> defaults = new TreeMap<String,String>();
+      for (Entry<String,String> defaultEntry : AccumuloConfiguration.getDefaultConfiguration()) {
+        defaults.put(defaultEntry.getKey(), defaultEntry.getValue());
+      }
+
+      final TreeMap<String,String> namespaceConfig = new TreeMap<String,String>();
+      if (tableName != null) {
+        String n = Namespaces.getNamespaceName(shellState.getInstance(),
+            Tables.getNamespaceId(shellState.getInstance(), Tables.getTableId(shellState.getInstance(), tableName)));
+        for (Entry<String,String> e : shellState.getConnector().namespaceOperations().getProperties(n)) {
+          namespaceConfig.put(e.getKey(), e.getValue());
+        }
+      }
+
+      Iterable<Entry<String,String>> acuconf = shellState.getConnector().instanceOperations().getSystemConfiguration().entrySet();
+      if (tableName != null) {
+        acuconf = shellState.getConnector().tableOperations().getProperties(tableName);
+      } else if (namespace != null) {
+        acuconf = shellState.getConnector().namespaceOperations().getProperties(namespace);
+      }
+      final TreeMap<String,String> sortedConf = new TreeMap<String,String>();
+      for (Entry<String,String> propEntry : acuconf) {
+        sortedConf.put(propEntry.getKey(), propEntry.getValue());
+      }
+
+      for (Entry<String,String> propEntry : acuconf) {
+        final String key = propEntry.getKey();
+        // only show properties with similar names to that
+        // specified, or all of them if none specified
+        if (cl.hasOption(filterOpt.getOpt()) && !key.contains(cl.getOptionValue(filterOpt.getOpt()))) {
+          continue;
+        }
+        if ((tableName != null || namespace != null) && !Property.isValidTablePropertyKey(key)) {
+          continue;
+        }
+        COL2 = Math.max(COL2, propEntry.getKey().length() + 3);
+      }
+
+      final ArrayList<String> output = new ArrayList<String>();
+      printConfHeader(output);
+
+      for (Entry<String,String> propEntry : sortedConf.entrySet()) {
+        final String key = propEntry.getKey();
+
+        // only show properties with similar names to that
+        // specified, or all of them if none specified
+        if (cl.hasOption(filterOpt.getOpt()) && !key.contains(cl.getOptionValue(filterOpt.getOpt()))) {
+          continue;
+        }
+        if ((tableName != null || namespace != null) && !Property.isValidTablePropertyKey(key)) {
+          continue;
+        }
+        String siteVal = siteConfig.get(key);
+        String sysVal = systemConfig.get(key);
+        String curVal = propEntry.getValue();
+        String dfault = defaults.get(key);
+        String nspVal = namespaceConfig.get(key);
+        boolean printed = false;
+
+        if (dfault != null && key.toLowerCase().contains("password")) {
+          siteVal = sysVal = dfault = curVal = curVal.replaceAll(".", "*");
+        }
+        if (sysVal != null) {
+          if (defaults.containsKey(key) && !Property.getPropertyByKey(key).isExperimental()) {
+            printConfLine(output, "default", key, dfault);
+            printed = true;
+          }
+          if (!defaults.containsKey(key) || !defaults.get(key).equals(siteVal)) {
+            printConfLine(output, "site", printed ? "   @override" : key, siteVal == null ? "" : siteVal);
+            printed = true;
+          }
+          if (!siteConfig.containsKey(key) || !siteVal.equals(sysVal)) {
+            printConfLine(output, "system", printed ? "   @override" : key, sysVal == null ? "" : sysVal);
+            printed = true;
+          }
+
+        }
+        if (nspVal != null) {
+          if (!systemConfig.containsKey(key) || !sysVal.equals(nspVal)) {
+            printConfLine(output, "namespace", printed ? "   @override" : key, nspVal == null ? "" : nspVal);
+            printed = true;
+          }
+        }
+
+        // show per-table value only if it is different (overridden)
+        if (tableName != null && !curVal.equals(nspVal)) {
+          printConfLine(output, "table", printed ? "   @override" : key, curVal);
+        } else if (namespace != null && !curVal.equals(sysVal)) {
+          printConfLine(output, "namespace", printed ? "   @override" : key, curVal);
+        }
+      }
+      printConfFooter(output);
+      shellState.printLines(output.iterator(), !cl.hasOption(disablePaginationOpt.getOpt()), printFile);
+      if (printFile != null) {
+        printFile.close();
+      }
+    }
+    return 0;
+  }
+
+  private void printConfHeader(List<String> output) {
+    printConfFooter(output);
+    output.add(String.format("%-" + COL1 + "s | %-" + COL2 + "s | %s", "SCOPE", "NAME", "VALUE"));
+    printConfFooter(output);
+  }
+
+  private void printConfLine(List<String> output, String s1, String s2, String s3) {
+    if (s2.length() < COL2) {
+      s2 += " " + Shell.repeat(".", COL2 - s2.length() - 1);
+    }
+    output.add(String.format("%-" + COL1 + "s | %-" + COL2 + "s | %s", s1, s2,
+        s3.replace("\n", "\n" + Shell.repeat(" ", COL1 + 1) + "|" + Shell.repeat(" ", COL2 + 2) + "|" + " ")));
+  }
+
+  private void printConfFooter(List<String> output) {
+    int col3 = Math.max(1, Math.min(Integer.MAX_VALUE, reader.getTerminal().getWidth() - COL1 - COL2 - 6));
+    output.add(String.format("%" + COL1 + "s-+-%" + COL2 + "s-+-%-" + col3 + "s", Shell.repeat("-", COL1), Shell.repeat("-", COL2), Shell.repeat("-", col3)));
+  }
+
+  @Override
+  public String description() {
+    return "prints system properties and table specific properties";
+  }
+
+  @Override
+  public Options getOptions() {
+    final Options o = new Options();
+    final OptionGroup og = new OptionGroup();
+    final OptionGroup tgroup = new OptionGroup();
+
+    tableOpt = new Option(Shell.tableOption, "table", true, "table to display/set/delete properties for");
+    deleteOpt = new Option("d", "delete", true, "delete a per-table property");
+    setOpt = new Option("s", "set", true, "set a per-table property");
+    filterOpt = new Option("f", "filter", true, "show only properties that contain this string");
+    disablePaginationOpt = new Option("np", "no-pagination", false, "disables pagination of output");
+    outputFileOpt = new Option("o", "output", true, "local file to write the scan output to");
+    namespaceOpt = new Option(Shell.namespaceOption, "namespace", true, "namespace to display/set/delete properties for");
+
+    tableOpt.setArgName("table");
+    deleteOpt.setArgName("property");
+    setOpt.setArgName("property=value");
+    filterOpt.setArgName("string");
+    outputFileOpt.setArgName("file");
+    namespaceOpt.setArgName("namespace");
+
+    og.addOption(deleteOpt);
+    og.addOption(setOpt);
+    og.addOption(filterOpt);
+
+    tgroup.addOption(tableOpt);
+    tgroup.addOption(namespaceOpt);
+
+    o.addOptionGroup(tgroup);
+    o.addOptionGroup(og);
+    o.addOption(disablePaginationOpt);
+    o.addOption(outputFileOpt);
+
+    return o;
+  }
+
+  @Override
+  public int numArgs() {
+    return 0;
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ConstraintCommand.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ConstraintCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ConstraintCommand.java
new file mode 100644
index 0000000..208ac4a
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ConstraintCommand.java
@@ -0,0 +1,134 @@
+/*
+ * 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.Map.Entry;
+
+import org.apache.accumulo.core.constraints.Constraint;
+import org.apache.accumulo.core.util.shell.Shell;
+import org.apache.accumulo.core.util.shell.Shell.Command;
+import org.apache.accumulo.core.util.shell.ShellCommandException;
+import org.apache.accumulo.core.util.shell.ShellCommandException.ErrorCode;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionGroup;
+import org.apache.commons.cli.Options;
+
+public class ConstraintCommand extends Command {
+  protected Option namespaceOpt;
+
+  @Override
+  public int execute(final String fullCommand, final CommandLine cl, final Shell shellState) throws Exception {
+    final String tableName;
+    final String namespace;
+
+    if (cl.hasOption(namespaceOpt.getOpt())) {
+      namespace = cl.getOptionValue(namespaceOpt.getOpt());
+    } else {
+      namespace = null;
+    }
+
+    if (cl.hasOption(OptUtil.tableOpt().getOpt()) || !shellState.getTableName().isEmpty()) {
+      tableName = OptUtil.getTableOpt(cl, shellState);
+    } else {
+      tableName = null;
+    }
+
+    int i;
+    switch (OptUtil.getAldOpt(cl)) {
+      case ADD:
+        for (String constraint : cl.getArgs()) {
+          if (namespace != null) {
+            if (!shellState.getConnector().namespaceOperations().testClassLoad(namespace, constraint, Constraint.class.getName())) {
+              throw new ShellCommandException(ErrorCode.INITIALIZATION_FAILURE, "Servers are unable to load " + constraint + " as type "
+                  + Constraint.class.getName());
+            }
+            i = shellState.getConnector().namespaceOperations().addConstraint(namespace, constraint);
+            shellState.getReader().println("Added constraint " + constraint + " to namespace " + namespace + " with number " + i);
+          } else if (tableName != null && !tableName.isEmpty()) {
+            if (!shellState.getConnector().tableOperations().testClassLoad(tableName, constraint, Constraint.class.getName())) {
+              throw new ShellCommandException(ErrorCode.INITIALIZATION_FAILURE, "Servers are unable to load " + constraint + " as type "
+                  + Constraint.class.getName());
+            }
+            i = shellState.getConnector().tableOperations().addConstraint(tableName, constraint);
+            shellState.getReader().println("Added constraint " + constraint + " to table " + tableName + " with number " + i);
+          } else {
+            throw new IllegalArgumentException("Please specify either a table or a namespace");
+          }
+        }
+        break;
+      case DELETE:
+        for (String constraint : cl.getArgs()) {
+          i = Integer.parseInt(constraint);
+          if (namespace != null) {
+            shellState.getConnector().namespaceOperations().removeConstraint(namespace, i);
+            shellState.getReader().println("Removed constraint " + i + " from namespace " + namespace);
+          } else if (tableName != null) {
+            shellState.getConnector().tableOperations().removeConstraint(tableName, i);
+            shellState.getReader().println("Removed constraint " + i + " from table " + tableName);
+          } else {
+            throw new IllegalArgumentException("Please specify either a table or a namespace");
+          }
+        }
+        break;
+      case LIST:
+        if (namespace != null) {
+          for (Entry<String,Integer> property : shellState.getConnector().namespaceOperations().listConstraints(namespace).entrySet()) {
+            shellState.getReader().println(property.toString());
+          }
+        } else if (tableName != null) {
+          for (Entry<String,Integer> property : shellState.getConnector().tableOperations().listConstraints(tableName).entrySet()) {
+            shellState.getReader().println(property.toString());
+          }
+        } else {
+          throw new IllegalArgumentException("Please specify either a table or a namespace");
+        }
+    }
+
+    return 0;
+  }
+
+  @Override
+  public String description() {
+    return "adds, deletes, or lists constraints for a table";
+  }
+
+  @Override
+  public int numArgs() {
+    return Shell.NO_FIXED_ARG_LENGTH_CHECK;
+  }
+
+  @Override
+  public String usage() {
+    return getName() + " <constraint>{ <constraint>}";
+  }
+
+  @Override
+  public Options getOptions() {
+    final Options o = new Options();
+    o.addOptionGroup(OptUtil.addListDeleteGroup("constraint"));
+
+    OptionGroup grp = new OptionGroup();
+    grp.addOption(OptUtil.tableOpt("table to add, delete, or list constraints for"));
+    namespaceOpt = new Option(Shell.namespaceOption, "namespace", true, "name of a namespace to operate on");
+    namespaceOpt.setArgName("namespace");
+    grp.addOption(namespaceOpt);
+
+    o.addOptionGroup(grp);
+    return o;
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/b2b985e2/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CreateNamespaceCommand.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CreateNamespaceCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CreateNamespaceCommand.java
new file mode 100644
index 0000000..a9f3deb
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CreateNamespaceCommand.java
@@ -0,0 +1,99 @@
+/*
+ * 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.Entry;
+
+import org.apache.accumulo.core.client.AccumuloException;
+import org.apache.accumulo.core.client.AccumuloSecurityException;
+import org.apache.accumulo.core.client.NamespaceExistsException;
+import org.apache.accumulo.core.client.NamespaceNotFoundException;
+import org.apache.accumulo.core.client.TableExistsException;
+import org.apache.accumulo.core.client.TableNotFoundException;
+import org.apache.accumulo.core.conf.Property;
+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.OptionGroup;
+import org.apache.commons.cli.Options;
+
+public class CreateNamespaceCommand extends Command {
+  private Option createNamespaceOptCopyConfig;
+
+  @Override
+  public int execute(final String fullCommand, final CommandLine cl, final Shell shellState) throws AccumuloException, AccumuloSecurityException,
+      TableExistsException, TableNotFoundException, IOException, ClassNotFoundException, NamespaceExistsException, NamespaceNotFoundException {
+
+    if (createNamespaceOptCopyConfig == null) {
+      getOptions();
+    }
+
+    String namespace = cl.getArgs()[0];
+
+    shellState.getConnector().namespaceOperations().create(namespace);
+
+    // Copy options if flag was set
+    Iterable<Entry<String,String>> configuration = null;
+    if (cl.hasOption(createNamespaceOptCopyConfig.getOpt())) {
+      String copy = cl.getOptionValue(createNamespaceOptCopyConfig.getOpt());
+      if (shellState.getConnector().namespaceOperations().exists(namespace)) {
+        configuration = shellState.getConnector().namespaceOperations().getProperties(copy);
+      }
+    } 
+    if (configuration != null) {
+      for (Entry<String,String> entry : configuration) {
+        if (Property.isValidTablePropertyKey(entry.getKey())) {
+          shellState.getConnector().namespaceOperations().setProperty(namespace, entry.getKey(), entry.getValue());
+        }
+      }
+    }
+
+    return 0;
+  }
+
+  @Override
+  public String description() {
+    return "creates a new namespace";
+  }
+
+  @Override
+  public String usage() {
+    return getName() + " <namespaceName>";
+  }
+
+  @Override
+  public Options getOptions() {
+    final Options o = new Options();
+
+    createNamespaceOptCopyConfig = new Option("cc", "copy-config", true, "namespace to copy configuration from");
+    createNamespaceOptCopyConfig.setArgName("namespace");
+
+    OptionGroup ogp = new OptionGroup();
+    ogp.addOption(createNamespaceOptCopyConfig);
+
+    o.addOptionGroup(ogp);
+
+    return o;
+  }
+
+  @Override
+  public int numArgs() {
+    return 1;
+  }
+}