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 A079410801 for ; Fri, 1 Nov 2013 02:00:03 +0000 (UTC) Received: (qmail 28147 invoked by uid 500); 1 Nov 2013 02:00:03 -0000 Delivered-To: apmail-accumulo-commits-archive@accumulo.apache.org Received: (qmail 27952 invoked by uid 500); 1 Nov 2013 02:00:03 -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 27745 invoked by uid 99); 1 Nov 2013 02:00:02 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 01 Nov 2013 02:00:02 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 7A729D0A1; Fri, 1 Nov 2013 02:00:02 +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: Fri, 01 Nov 2013 02:00:03 -0000 Message-Id: <1839729e81304bc2bacd2b4d5897af87@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [02/54] ACCUMULO-802 Apply Sean Hickey's patch (v5) http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java b/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java index 52e1d04..cdb86f1 100644 --- a/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java +++ b/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java @@ -73,6 +73,7 @@ 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.CreateNamespaceCommand; 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; @@ -80,6 +81,7 @@ 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.DeleteNamespaceCommand; 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.DeleteShellIterCommand; @@ -114,6 +116,7 @@ import org.apache.accumulo.core.util.shell.commands.ListScansCommand; import org.apache.accumulo.core.util.shell.commands.ListShellIterCommand; 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.NamespacesCommand; 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; @@ -122,6 +125,7 @@ import org.apache.accumulo.core.util.shell.commands.PingCommand; 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.RenameNamespaceCommand; 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; @@ -349,7 +353,7 @@ public class Shell extends ShellOptions { 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()}; + new TablesCommand(), new NamespacesCommand(), new CreateNamespaceCommand(), new DeleteNamespaceCommand(), new RenameNamespaceCommand()}; Command[] tableControlCommands = {new AddSplitsCommand(), new CompactCommand(), new ConstraintCommand(), new FlushCommand(), new GetGroupsCommand(), new GetSplitsCommand(), new MergeCommand(), new SetGroupsCommand()}; Command[] userCommands = {new AddAuthsCommand(), new CreateUserCommand(), new DeleteUserCommand(), new DropUserCommand(), new GetAuthsCommand(), @@ -705,6 +709,14 @@ public class Shell extends ShellOptions { userlist = Collections.emptySet(); } + Set tableNamespaces = null; + try { + tableNamespaces = connector.tableNamespaceOperations().list(); + } catch (Exception e) { + log.debug("Unable to obtain list of table namespaces", e); + tableNamespaces = Collections.emptySet(); + } + Map> options = new HashMap>(); Set commands = new HashSet(); @@ -713,14 +725,18 @@ public class Shell extends ShellOptions { Set modifiedUserlist = new HashSet(); Set modifiedTablenames = new HashSet(); + Set modifiedTableNamespaces = new HashSet(); for (String a : tableNames) modifiedTablenames.add(a.replaceAll("([\\s'\"])", "\\\\$1")); for (String a : userlist) modifiedUserlist.add(a.replaceAll("([\\s'\"])", "\\\\$1")); + for (String a : tableNamespaces) + modifiedTableNamespaces.add(a.replaceAll("([\\s'\"])", "\\\\$1")); options.put(Command.CompletionSet.USERNAMES, modifiedUserlist); options.put(Command.CompletionSet.TABLENAMES, modifiedTablenames); + options.put(Command.CompletionSet.TABLENAMESPACES, modifiedTableNamespaces); options.put(Command.CompletionSet.COMMANDS, commands); for (Command[] cmdGroup : commandGrouping.values()) { @@ -741,7 +757,7 @@ public class Shell extends ShellOptions { public static abstract class Command { // Helper methods for completion public enum CompletionSet { - TABLENAMES, USERNAMES, COMMANDS + TABLENAMES, USERNAMES, COMMANDS, TABLENAMESPACES } static Set getCommandNames(Map> objects) { @@ -756,6 +772,10 @@ public class Shell extends ShellOptions { return objects.get(CompletionSet.USERNAMES); } + static Set getTableNamespaces(Map> objects) { + return objects.get(CompletionSet.TABLENAMESPACES); + } + public void registerCompletionGeneral(Token root, Set args, boolean caseSens) { Token t = new Token(args); t.setCaseSensitive(caseSens); @@ -778,6 +798,10 @@ public class Shell extends ShellOptions { registerCompletionGeneral(root, completionSet.get(CompletionSet.COMMANDS), false); } + public void registerCompletionForTableNamespaces(Token root, Map> completionSet) { + registerCompletionGeneral(root, completionSet.get(CompletionSet.TABLENAMESPACES), true); + } + // abstract methods to override public abstract int execute(String fullCommand, CommandLine cl, Shell shellState) throws Exception; http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/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..4e35fcb --- /dev/null +++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/CreateNamespaceCommand.java @@ -0,0 +1,95 @@ +/* + * 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.TableExistsException; +import org.apache.accumulo.core.client.TableNamespaceExistsException; +import org.apache.accumulo.core.client.TableNamespaceNotFoundException; +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.Options; + +public class CreateNamespaceCommand extends Command { + private Option createTableOptCopyConfig; + private Option base64Opt; + + public int execute(final String fullCommand, final CommandLine cl, final Shell shellState) throws AccumuloException, AccumuloSecurityException, + TableExistsException, TableNotFoundException, IOException, ClassNotFoundException, TableNamespaceExistsException, TableNamespaceNotFoundException { + + if (createTableOptCopyConfig == null) { + getOptions(); + } + + String namespace = cl.getArgs()[0]; + + shellState.getConnector().tableNamespaceOperations().create(namespace); + + // Copy options if flag was set + if (cl.hasOption(createTableOptCopyConfig.getOpt())) { + String copy = cl.getOptionValue(createTableOptCopyConfig.getOpt()); + if (shellState.getConnector().tableNamespaceOperations().exists(namespace)) { + Iterable> configuration = shellState.getConnector().tableNamespaceOperations().getProperties(copy); + for (Entry entry : configuration) { + if (Property.isValidTablePropertyKey(entry.getKey())) { + shellState.getConnector().tableNamespaceOperations().setProperty(namespace, entry.getKey(), entry.getValue()); + } + } + } + } + + return 0; + } + + @Override + public String description() { + return "creates a new table namespace"; + } + + @Override + public String usage() { + return getName() + " "; + } + + @Override + public Options getOptions() { + final Options o = new Options(); + + createTableOptCopyConfig = new Option("cc", "copy-config", true, "table namespace to copy configuration from"); + + createTableOptCopyConfig.setArgName("tableNamespace"); + + base64Opt = new Option("b64", "base64encoded", false, "decode encoded split points"); + o.addOption(base64Opt); + o.addOption(createTableOptCopyConfig); + + return o; + } + + @Override + public int numArgs() { + return 1; + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/core/src/main/java/org/apache/accumulo/core/util/shell/commands/DUCommand.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/DUCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/DUCommand.java index 189458a..356cfed 100644 --- a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/DUCommand.java +++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/DUCommand.java @@ -21,8 +21,11 @@ import java.util.Arrays; import java.util.SortedSet; import java.util.TreeSet; +import org.apache.accumulo.core.client.Instance; +import org.apache.accumulo.core.client.TableNamespaceNotFoundException; import org.apache.accumulo.core.client.TableNotFoundException; import org.apache.accumulo.core.client.admin.DiskUsage; +import org.apache.accumulo.core.client.impl.TableNamespaces; import org.apache.accumulo.core.util.NumUtil; import org.apache.accumulo.core.util.shell.Shell; import org.apache.accumulo.core.util.shell.Shell.Command; @@ -32,10 +35,10 @@ import org.apache.commons.cli.Options; public class DUCommand extends Command { - private Option optTablePattern, optHumanReadble; - - public int execute(final String fullCommand, final CommandLine cl, final Shell shellState) throws IOException, TableNotFoundException { - + private Option optTablePattern, optHumanReadble, optTableNamespace; + + public int execute(final String fullCommand, final CommandLine cl, final Shell shellState) throws IOException, TableNotFoundException, TableNamespaceNotFoundException { + final SortedSet tables = new TreeSet(Arrays.asList(cl.getArgs())); if (cl.hasOption(Shell.tableOption)) { @@ -45,9 +48,15 @@ public class DUCommand extends Command { } tables.add(tableName); } - + + if (cl.hasOption(optTableNamespace.getOpt())) { + Instance instance = shellState.getInstance(); + String namespaceId = TableNamespaces.getNamespaceId(instance, cl.getOptionValue(optTableNamespace.getOpt())); + tables.addAll(TableNamespaces.getTableNames(instance, namespaceId)); + } + boolean prettyPrint = cl.hasOption(optHumanReadble.getOpt()) ? true : false; - + if (cl.hasOption(optTablePattern.getOpt())) { for (String table : shellState.getConnector().tableOperations().list()) { if (table.matches(cl.getOptionValue(optTablePattern.getOpt()))) { @@ -83,14 +92,18 @@ public class DUCommand extends Command { optTablePattern = new Option("p", "pattern", true, "regex pattern of table names"); optTablePattern.setArgName("pattern"); - + optHumanReadble = new Option("h", "human-readable", false, "format large sizes to human readable units"); optHumanReadble.setArgName("human readable output"); + optTableNamespace = new Option("tn", "table-namespace", true, "name of a table namespace"); + optTableNamespace.setArgName("table-namespace"); + o.addOption(OptUtil.tableOpt("table to examine")); - + o.addOption(optTablePattern); o.addOption(optHumanReadble); + o.addOption(optTableNamespace); return o; } http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/core/src/main/java/org/apache/accumulo/core/util/shell/commands/DeleteNamespaceCommand.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/DeleteNamespaceCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/DeleteNamespaceCommand.java new file mode 100644 index 0000000..27d669b --- /dev/null +++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/DeleteNamespaceCommand.java @@ -0,0 +1,104 @@ +/* + * 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.List; +import java.util.Map; +import java.util.Set; + +import org.apache.accumulo.core.Constants; +import org.apache.accumulo.core.client.TableNamespaceNotFoundException; +import org.apache.accumulo.core.client.impl.TableNamespaces; +import org.apache.accumulo.core.util.shell.Shell; +import org.apache.accumulo.core.util.shell.Token; +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 DeleteNamespaceCommand extends Command { + private Option forceOpt; + + public int execute(final String fullCommand, final CommandLine cl, final Shell shellState) throws Exception { + boolean force = false; + boolean operate = true; + if (cl.hasOption(forceOpt.getOpt())) { + force = true; + } + String namespace = cl.getArgs()[0]; + + if (!force) { + shellState.getReader().flush(); + String line = shellState.getReader().readLine(getName() + " { " + namespace + " } (yes|no)? "); + operate = line != null && (line.equalsIgnoreCase("y") || line.equalsIgnoreCase("yes")); + } + if (operate) { + doTableOp(shellState, namespace, force); + } + return 0; + } + + @Override + public String description() { + return "deletes a table namespace"; + } + + protected void doTableOp(final Shell shellState, final String namespace, boolean force) throws Exception { + boolean resetContext = false; + String currentTable = shellState.getTableName(); + if (!TableNamespaces.getNameToIdMap(shellState.getInstance()).containsKey(namespace)) { + throw new TableNamespaceNotFoundException(null, namespace, null); + } + + String namespaceId = TableNamespaces.getNamespaceId(shellState.getInstance(), namespace); + List tables = TableNamespaces.getTableNames(shellState.getInstance(), namespaceId); + resetContext = tables.contains(currentTable); + + if (force) { + shellState.getConnector().tableNamespaceOperations().delete(namespace, true); + } else { + shellState.getConnector().tableNamespaceOperations().delete(namespace); + } + if (namespace.equals(Constants.SYSTEM_TABLE_NAMESPACE)) { + shellState.getReader().println("Table namespace: [" + namespace + "], can't delete system or default namespace."); + } else { + shellState.getReader().println("Table namespace: [" + namespace + "] has been deleted."); + } + if (resetContext) { + shellState.setTableName(""); + } + } + + @Override + public Options getOptions() { + forceOpt = new Option("f", "force", false, "force deletion without prompting"); + final Options opts = super.getOptions(); + + opts.addOption(forceOpt); + return opts; + } + + @Override + public int numArgs() { + return 1; + } + + @Override + public void registerCompletion(final Token root, final Map> special) { + registerCompletionForTableNamespaces(root, special); + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/core/src/main/java/org/apache/accumulo/core/util/shell/commands/NamespacesCommand.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/NamespacesCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/NamespacesCommand.java new file mode 100644 index 0000000..dcb4bd7 --- /dev/null +++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/NamespacesCommand.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.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.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; + +public class NamespacesCommand extends Command { + private Option disablePaginationOpt; + + @Override + public int execute(final String fullCommand, final CommandLine cl, final Shell shellState) throws AccumuloException, AccumuloSecurityException, IOException { + shellState.printLines(shellState.getConnector().tableNamespaceOperations().list().iterator(), !cl.hasOption(disablePaginationOpt.getOpt())); + return 0; + } + + @Override + public String description() { + return "displays a list of all existing table namespaces"; + } + + @Override + public Options getOptions() { + final Options o = new Options(); + disablePaginationOpt = new Option("np", "no-pagination", false, "disable pagination of output"); + o.addOption(disablePaginationOpt); + return o; + } + + @Override + public int numArgs() { + return 0; + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/core/src/main/java/org/apache/accumulo/core/util/shell/commands/RenameNamespaceCommand.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/RenameNamespaceCommand.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/RenameNamespaceCommand.java new file mode 100644 index 0000000..9f4596a --- /dev/null +++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/RenameNamespaceCommand.java @@ -0,0 +1,79 @@ +/* + * 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.List; +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.TableNamespaceExistsException; +import org.apache.accumulo.core.client.TableNamespaceNotFoundException; +import org.apache.accumulo.core.client.TableNotFoundException; +import org.apache.accumulo.core.client.impl.TableNamespaces; +import org.apache.accumulo.core.client.impl.Tables; +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 RenameNamespaceCommand extends Command { + @Override + public int execute(final String fullCommand, final CommandLine cl, final Shell shellState) throws AccumuloException, AccumuloSecurityException, + TableNotFoundException, TableExistsException, TableNamespaceNotFoundException, TableNamespaceExistsException { + String old = cl.getArgs()[0]; + String newer = cl.getArgs()[1]; + boolean resetContext = false; + String currentTableId = ""; + if (!(shellState.getTableName() == null) && !shellState.getTableName().isEmpty()) { + String namespaceId = TableNamespaces.getNamespaceId(shellState.getInstance(), old); + List tableIds = TableNamespaces.getTableIds(shellState.getInstance(), namespaceId); + currentTableId = Tables.getTableId(shellState.getInstance(), shellState.getTableName()); + resetContext = tableIds.contains(currentTableId); + } + + shellState.getConnector().tableNamespaceOperations().rename(old, newer); + + if (resetContext) { + shellState.setTableName(Tables.getTableName(shellState.getInstance(), currentTableId)); + } + + return 0; + } + + @Override + public String usage() { + return getName() + " "; + } + + @Override + public String description() { + return "renames a table namespace"; + } + + @Override + public void registerCompletion(final Token root, final Map> special) { + registerCompletionForTableNamespaces(root, special); + } + + @Override + public int numArgs() { + return 2; + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/core/src/main/java/org/apache/accumulo/core/util/shell/commands/TableOperation.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/TableOperation.java b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/TableOperation.java index 2bec526..8c73674 100644 --- a/core/src/main/java/org/apache/accumulo/core/util/shell/commands/TableOperation.java +++ b/core/src/main/java/org/apache/accumulo/core/util/shell/commands/TableOperation.java @@ -21,7 +21,10 @@ import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import org.apache.accumulo.core.client.Instance; import org.apache.accumulo.core.client.TableNotFoundException; +import org.apache.accumulo.core.client.impl.TableNamespaces; +import org.apache.accumulo.core.client.impl.Tables; 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; @@ -32,7 +35,7 @@ import org.apache.commons.cli.Options; public abstract class TableOperation extends Command { - protected Option optTablePattern, optTableName; + protected Option optTablePattern, optTableName, optTableNamespace; private boolean force = true; private boolean useCommandLine = true; @@ -46,6 +49,12 @@ public abstract class TableOperation extends Command { } } else if (cl.hasOption(optTableName.getOpt())) { tableSet.add(cl.getOptionValue(optTableName.getOpt())); + } else if (cl.hasOption(optTableNamespace.getOpt())) { + Instance instance = shellState.getInstance(); + String namespaceId = TableNamespaces.getNamespaceId(instance, optTableNamespace.getValue()); + for (String tableId : TableNamespaces.getTableIds(instance, namespaceId)) { + tableSet.add(Tables.getTableName(instance, tableId)); + } } else if (useCommandLine && cl.getArgs().length > 0) { for (String tableName : cl.getArgs()) { tableSet.add(tableName); @@ -99,10 +108,14 @@ public abstract class TableOperation extends Command { optTableName = new Option(Shell.tableOption, "table", true, "name of a table to operate on"); optTableName.setArgName("tableName"); + optTableNamespace = new Option("tn", "table-namespace", true, "name of a table namespace to operate on"); + optTableName.setArgName("table-namespace"); + final OptionGroup opg = new OptionGroup(); opg.addOption(optTablePattern); opg.addOption(optTableName); + opg.addOption(optTableNamespace); o.addOptionGroup(opg); http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/core/src/main/thrift/client.thrift ---------------------------------------------------------------------- diff --git a/core/src/main/thrift/client.thrift b/core/src/main/thrift/client.thrift index 2f5e9d1..1e6a145 100644 --- a/core/src/main/thrift/client.thrift +++ b/core/src/main/thrift/client.thrift @@ -135,6 +135,7 @@ service ClientService { // configuration methods map getConfiguration(2:trace.TInfo tinfo, 3:security.TCredentials credentials, 1:ConfigurationType type); map getTableConfiguration(1:trace.TInfo tinfo, 3:security.TCredentials credentials, 2:string tableName) throws (1:ThriftTableOperationException tope); + map getTableNamespaceConfiguration(1:trace.TInfo tinfo, 3:security.TCredentials credentials, 2:string ns) throws (1:ThriftTableOperationException tope); bool checkClass(1:trace.TInfo tinfo, 4:security.TCredentials credentials, 2:string className, 3:string interfaceMatch); bool checkTableClass(1:trace.TInfo tinfo, 5:security.TCredentials credentials, 2:string tableId, 3:string className, 4:string interfaceMatch) throws (1:ThriftSecurityException sec, 2:ThriftTableOperationException tope); } http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/core/src/main/thrift/master.thrift ---------------------------------------------------------------------- diff --git a/core/src/main/thrift/master.thrift b/core/src/main/thrift/master.thrift index 19960c8..6270f33 100644 --- a/core/src/main/thrift/master.thrift +++ b/core/src/main/thrift/master.thrift @@ -137,6 +137,9 @@ service MasterClientService { void setTableProperty(5:trace.TInfo tinfo, 1:security.TCredentials credentials, 2:string tableName, 3:string property, 4:string value) throws (1:client.ThriftSecurityException sec, 2:client.ThriftTableOperationException tope) void removeTableProperty(4:trace.TInfo tinfo, 1:security.TCredentials credentials, 2:string tableName, 3:string property) throws (1:client.ThriftSecurityException sec, 2:client.ThriftTableOperationException tope) + + void setTableNamespaceProperty(5:trace.TInfo tinfo, 1:security.TCredentials credentials, 2:string ns, 3:string property, 4:string value) throws (1:client.ThriftSecurityException sec, 2:client.ThriftTableOperationException tope) + void removeTableNamespaceProperty(4:trace.TInfo tinfo, 1:security.TCredentials credentials, 2:string ns, 3:string property) throws (1:client.ThriftSecurityException sec, 2:client.ThriftTableOperationException tope) // system management methods void setMasterGoalState(3:trace.TInfo tinfo, 1:security.TCredentials credentials, 2:MasterGoalState state) throws (1:client.ThriftSecurityException sec); @@ -154,7 +157,14 @@ service MasterClientService { //table operations i64 beginTableOperation(2:trace.TInfo tinfo, 1:security.TCredentials credentials) throws (1:client.ThriftSecurityException sec) - void executeTableOperation(7:trace.TInfo tinfo, 1:security.TCredentials credentials, 2:i64 opid, 3:TableOperation op, 4:list arguments, 5:map options, 6:bool autoClean)throws (1:client.ThriftSecurityException sec, 2:client.ThriftTableOperationException tope) + void executeTableOperation(7:trace.TInfo tinfo, 1:security.TCredentials credentials, 2:i64 opid, 3:TableOperation op, 4:list arguments, 5:map options, 6:bool autoClean) throws (1:client.ThriftSecurityException sec, 2:client.ThriftTableOperationException tope) string waitForTableOperation(3:trace.TInfo tinfo, 1:security.TCredentials credentials, 2:i64 opid) throws (1:client.ThriftSecurityException sec, 2:client.ThriftTableOperationException tope) void finishTableOperation(3:trace.TInfo tinfo, 1:security.TCredentials credentials, 2:i64 opid) throws (1:client.ThriftSecurityException sec) + + //table namespace operations + i64 beginTableNamespaceOperation(1:trace.TInfo tinfo, 2:security.TCredentials credentials) throws (1:client.ThriftSecurityException sec) + void executeTableNamespaceOperation(1:trace.TInfo tinfo, 2:security.TCredentials credentials, 3:i64 opid, 4:TableOperation op, 5:list arguments, 6:map options, 7:bool autoClean) throws (1:client.ThriftSecurityException sec, 2:client.ThriftTableOperationException tope) + string waitForTableNamespaceOperation(1:trace.TInfo tinfo, 2:security.TCredentials credentials, 3:i64 opid) throws (1:client.ThriftSecurityException sec, 2:client.ThriftTableOperationException tope) + void finishTableNamespaceOperation(1:trace.TInfo tinfo, 2:security.TCredentials credentials, 3:i64 opid) throws (1:client.ThriftSecurityException sec) + } http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/core/src/test/java/org/apache/accumulo/core/client/mock/MockTableNamespacesTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/accumulo/core/client/mock/MockTableNamespacesTest.java b/core/src/test/java/org/apache/accumulo/core/client/mock/MockTableNamespacesTest.java new file mode 100644 index 0000000..d15b3e5 --- /dev/null +++ b/core/src/test/java/org/apache/accumulo/core/client/mock/MockTableNamespacesTest.java @@ -0,0 +1,265 @@ +/* + * 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.client.mock; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Map.Entry; +import java.util.Random; + +import org.apache.accumulo.core.Constants; +import org.apache.accumulo.core.client.Connector; +import org.apache.accumulo.core.client.Instance; +import org.apache.accumulo.core.client.TableNamespaceNotEmptyException; +import org.apache.accumulo.core.client.security.tokens.PasswordToken; +import org.apache.accumulo.core.conf.Property; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class MockTableNamespacesTest { + + Random random = new Random(); + public static TemporaryFolder folder = new TemporaryFolder(); + + /** + * This test creates a table without specifying a namespace. In this case, it puts the table into the default namespace. + * + * @throws Exception + */ + @Test + public void testDefaultNamespace() throws Exception { + String tableName = "test"; + Instance instance = new MockInstance("default"); + Connector c = instance.getConnector("user", new PasswordToken("pass")); + + assertTrue(c.tableNamespaceOperations().exists(Constants.DEFAULT_TABLE_NAMESPACE)); + c.tableOperations().create(tableName); + assertTrue(c.tableOperations().exists(tableName)); + } + + /** + * This test creates a new namespace "testing" and a table "testing.table1" which puts "table1" into the "testing" namespace. + * Then we create "testing.table2" which creates "table2" and puts it into "testing" as well. + * Then we make sure that you can't delete a namespace with tables in it, and then we delete the tables and delete the namespace. + * + * @throws Exception + */ + @Test + public void testCreateAndDeleteNamespace() throws Exception { + String namespace = "testing"; + String tableName1 = namespace + ".table1"; + String tableName2 = namespace + ".table2"; + + Instance instance = new MockInstance("createdelete"); + Connector c = instance.getConnector("user", new PasswordToken("pass")); + + c.tableNamespaceOperations().create(namespace); + assertTrue(c.tableNamespaceOperations().exists(namespace)); + + c.tableOperations().create(tableName1); + assertTrue(c.tableOperations().exists(tableName1)); + + c.tableOperations().create(tableName2); + assertTrue(c.tableOperations().exists(tableName2)); + + // deleting + try { + // can't delete a namespace with tables in it + c.tableNamespaceOperations().delete(namespace); + fail(); + } catch (TableNamespaceNotEmptyException e) { + // ignore, supposed to happen + } + assertTrue(c.tableNamespaceOperations().exists(namespace)); + assertTrue(c.tableOperations().exists(tableName1)); + assertTrue(c.tableOperations().exists(tableName2)); + + c.tableOperations().delete(tableName2); + assertTrue(!c.tableOperations().exists(tableName2)); + assertTrue(c.tableNamespaceOperations().exists(namespace)); + + c.tableOperations().delete(tableName1); + assertTrue(!c.tableOperations().exists(tableName1)); + c.tableNamespaceOperations().delete(namespace); + assertTrue(!c.tableNamespaceOperations().exists(namespace)); + } + + /** + * This test creates a namespace, modifies it's properties, and checks to make sure that those properties are applied to its tables. To do something on a + * namespace-wide level, use TableNamespaceOperations. + * + * Checks to make sure namespace-level properties are overridden by table-level properties. + * + * Checks to see if the default namespace's properties work as well. + * + * @throws Exception + */ + + @Test + public void testNamespaceProperties() throws Exception { + String namespace = "propchange"; + String tableName1 = namespace + ".table1"; + String tableName2 = namespace + ".table2"; + + String propKey = Property.TABLE_SCAN_MAXMEM.getKey(); + String propVal = "42K"; + + Instance instance = new MockInstance("props"); + Connector c = instance.getConnector("user", new PasswordToken("pass")); + + c.tableNamespaceOperations().create(namespace); + c.tableOperations().create(tableName1); + c.tableNamespaceOperations().setProperty(namespace, propKey, propVal); + + // check the namespace has the property + boolean itWorked = false; + for (Entry prop : c.tableNamespaceOperations().getProperties(namespace)) { + if (prop.getKey().equals(propKey) && prop.getValue().equals(propVal)) { + itWorked = true; + break; + } + } + + assertTrue(itWorked); + + // check that the table gets it from the namespace + itWorked = false; + for (Entry prop : c.tableOperations().getProperties(tableName1)) { + if (prop.getKey().equals(propKey) && prop.getValue().equals(propVal)) { + itWorked = true; + break; + } + } + assertTrue(itWorked); + + // test a second table to be sure the first wasn't magical + // (also, changed the order, the namespace already exists with the property) + itWorked = false; + c.tableOperations().create(tableName2); + for (Entry prop : c.tableOperations().getProperties(tableName2)) { + if (prop.getKey().equals(propKey) && prop.getValue().equals(propVal)) { + itWorked = true; + break; + } + } + assertTrue(itWorked); + + // test that table properties override namespace properties + String propKey2 = Property.TABLE_FILE_MAX.getKey(); + String propVal2 = "42"; + String tablePropVal = "13"; + + c.tableOperations().setProperty(tableName2, propKey2, tablePropVal); + c.tableNamespaceOperations().setProperty("propchange", propKey2, propVal2); + + itWorked = false; + for (Entry prop : c.tableOperations().getProperties(tableName2)) { + if (prop.getKey().equals(propKey2) && prop.getValue().equals(tablePropVal)) { + itWorked = true; + break; + } + } + assertTrue(itWorked); + + // now check that you can change the default namespace's properties + propVal = "13K"; + propVal2 = "44"; + String tableName = "some_table"; + c.tableOperations().create(tableName); + c.tableNamespaceOperations().setProperty(Constants.DEFAULT_TABLE_NAMESPACE, propKey, propVal); + + itWorked = false; + for (Entry prop : c.tableOperations().getProperties(tableName)) { + if (prop.getKey().equals(propKey) && prop.getValue().equals(propVal)) { + itWorked = true; + break; + } + } + assertTrue(itWorked); + } + + /** + * This test creates a new user and a namespace. It checks to make sure the user can't modify anything in the namespace at first, then it grants the user + * permissions and makes sure that they can modify the namespace. Then it also checks if the user has the correct permissions on tables both already existing + * in the namespace and ones they create. + * + * @throws Exception + */ + @Test + public void testNamespacePermissions() throws Exception { + // TODO make the test once namespace-level permissions are implemented. (ACCUMULO-1479) + } + + /** + * This test renames and clones two separate table into different namespaces. + * different namespace. + * + * @throws Exception + */ + @Test + public void testRenameAndCloneTableToNewNamespace() throws Exception { + String namespace1 = "renamed"; + String namespace2 = "cloned"; + String tableName = "table"; + String tableName1 = "renamed.table1"; + //String tableName2 = "cloned.table2"; + + Instance instance = new MockInstance("renameclone"); + Connector c = instance.getConnector("user", new PasswordToken("pass")); + + c.tableOperations().create(tableName); + c.tableNamespaceOperations().create(namespace1); + c.tableNamespaceOperations().create(namespace2); + + c.tableOperations().rename(tableName, tableName1); + + assertTrue(c.tableOperations().exists(tableName1)); + assertTrue(!c.tableOperations().exists(tableName)); + + // TODO implement clone in mock + /*c.tableOperations().clone(tableName1, tableName2, false, null, null); + + assertTrue(c.tableOperations().exists(tableName1)); + assertTrue(c.tableOperations().exists(tableName2));*/ + return; + } + + /** + * This test renames a table namespace and ensures that its tables are still correct + */ + @Test + public void testNamespaceRename() throws Exception { + String namespace1 = "n1"; + String namespace2 = "n2"; + String table = "t"; + + Instance instance = new MockInstance("rename"); + Connector c = instance.getConnector("user", new PasswordToken("pass")); + + c.tableNamespaceOperations().create(namespace1); + c.tableOperations().create(namespace1 + "." + table); + + c.tableNamespaceOperations().rename(namespace1, namespace2); + + assertTrue(!c.tableNamespaceOperations().exists(namespace1)); + assertTrue(c.tableNamespaceOperations().exists(namespace2)); + assertTrue(!c.tableOperations().exists(namespace1 + "." + table)); + assertTrue(c.tableOperations().exists(namespace2 + "." + table)); + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/base/src/main/java/org/apache/accumulo/server/ServerConstants.java ---------------------------------------------------------------------- diff --git a/server/base/src/main/java/org/apache/accumulo/server/ServerConstants.java b/server/base/src/main/java/org/apache/accumulo/server/ServerConstants.java index 9e0ac39..04b51b8 100644 --- a/server/base/src/main/java/org/apache/accumulo/server/ServerConstants.java +++ b/server/base/src/main/java/org/apache/accumulo/server/ServerConstants.java @@ -29,11 +29,14 @@ import org.apache.hadoop.fs.Path; public class ServerConstants { - // versions should never be negative - public static final Integer WIRE_VERSION = 2; + /** + * current version (3) reflects additional namespace operations (ACCUMULO-802) in version 1.6.0
+ * (versions should never be negative) + */ + public static final Integer WIRE_VERSION = 3; /** - * current version reflects the addition of a separate root table (ACCUMULO-1481) + * current version (6) reflects the addition of a separate root table (ACCUMULO-1481) in version 1.6.0 */ public static final int DATA_VERSION = 6; public static final int PREV_DATA_VERSION = 5; http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/base/src/main/java/org/apache/accumulo/server/client/ClientServiceHandler.java ---------------------------------------------------------------------- diff --git a/server/base/src/main/java/org/apache/accumulo/server/client/ClientServiceHandler.java b/server/base/src/main/java/org/apache/accumulo/server/client/ClientServiceHandler.java index 3f1aaa2..e061f99 100644 --- a/server/base/src/main/java/org/apache/accumulo/server/client/ClientServiceHandler.java +++ b/server/base/src/main/java/org/apache/accumulo/server/client/ClientServiceHandler.java @@ -33,6 +33,8 @@ 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.TableNamespaceNotFoundException; +import org.apache.accumulo.core.client.impl.TableNamespaces; import org.apache.accumulo.core.client.impl.Tables; import org.apache.accumulo.core.client.impl.thrift.ClientService; import org.apache.accumulo.core.client.impl.thrift.ConfigurationType; @@ -228,7 +230,8 @@ public class ClientServiceHandler implements ClientService.Iface { @Override public Map getTableConfiguration(TInfo tinfo, TCredentials credentials, String tableName) throws TException, ThriftTableOperationException { String tableId = checkTableId(tableName, null); - return conf(credentials, new ServerConfiguration(instance).getTableConfiguration(tableId)); + AccumuloConfiguration config = new ServerConfiguration(instance).getTableConfiguration(tableId); + return conf(credentials, config); } @Override @@ -349,4 +352,17 @@ public class ClientServiceHandler implements ClientService.Iface { throw new TException(e); } } + + @Override + public Map getTableNamespaceConfiguration(TInfo tinfo, TCredentials credentials, String ns) throws ThriftTableOperationException, TException { + String namespaceId; + try { + namespaceId = TableNamespaces.getNamespaceId(instance, ns); + } catch (TableNamespaceNotFoundException e) { + String why = "Could not find table namespace while getting configuration."; + throw new ThriftTableOperationException(null, ns, null, null, why); + } + AccumuloConfiguration config = new ServerConfiguration(instance).getTableNamespaceConfiguration(namespaceId); + return conf(credentials, config); + } } http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/base/src/main/java/org/apache/accumulo/server/conf/ServerConfiguration.java ---------------------------------------------------------------------- diff --git a/server/base/src/main/java/org/apache/accumulo/server/conf/ServerConfiguration.java b/server/base/src/main/java/org/apache/accumulo/server/conf/ServerConfiguration.java index c9fd5a1..7897093 100644 --- a/server/base/src/main/java/org/apache/accumulo/server/conf/ServerConfiguration.java +++ b/server/base/src/main/java/org/apache/accumulo/server/conf/ServerConfiguration.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.Map.Entry; import org.apache.accumulo.core.client.Instance; +import org.apache.accumulo.core.client.impl.Tables; import org.apache.accumulo.core.conf.AccumuloConfiguration; import org.apache.accumulo.core.conf.ConfigSanityCheck; import org.apache.accumulo.core.conf.DefaultConfiguration; @@ -29,55 +30,69 @@ import org.apache.accumulo.core.conf.SiteConfiguration; import org.apache.accumulo.core.data.KeyExtent; public class ServerConfiguration { - + private static final Map tableInstances = new HashMap(1); + private static final Map tableNamespaceInstances = new HashMap(1); private static SecurityPermission CONFIGURATION_PERMISSION = new SecurityPermission("configurationPermission"); - + public static synchronized SiteConfiguration getSiteConfiguration() { checkPermissions(); return SiteConfiguration.getInstance(getDefaultConfiguration()); } - + private static void checkPermissions() { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(CONFIGURATION_PERMISSION); } } - + private static synchronized ZooConfiguration getZooConfiguration(Instance instance) { checkPermissions(); return ZooConfiguration.getInstance(instance, getSiteConfiguration()); } - + public static synchronized DefaultConfiguration getDefaultConfiguration() { checkPermissions(); return DefaultConfiguration.getInstance(); } - + public static synchronized AccumuloConfiguration getSystemConfiguration(Instance instance) { return getZooConfiguration(instance); } - + + public static TableNamespaceConfiguration getTableNamespaceConfiguration(Instance instance, String namespaceId) { + checkPermissions(); + synchronized (tableNamespaceInstances) { + TableNamespaceConfiguration conf = tableNamespaceInstances.get(namespaceId); + if (conf == null) { + conf = new TableNamespaceConfiguration(instance, namespaceId, getSystemConfiguration(instance)); + ConfigSanityCheck.validate(conf); + tableNamespaceInstances.put(namespaceId, conf); + } + return conf; + } + } + public static TableConfiguration getTableConfiguration(Instance instance, String tableId) { checkPermissions(); synchronized (tableInstances) { TableConfiguration conf = tableInstances.get(tableId); - if (conf == null) { - conf = new TableConfiguration(instance.getInstanceID(), tableId, getSystemConfiguration(instance)); + if (conf == null && Tables.exists(instance, tableId)) { + conf = new TableConfiguration(instance.getInstanceID(), tableId, getTableNamespaceConfiguration(instance, Tables.getNamespace(instance, tableId))); ConfigSanityCheck.validate(conf); tableInstances.put(tableId, conf); } return conf; } } - + static void removeTableIdInstance(String tableId) { synchronized (tableInstances) { tableInstances.remove(tableId); } } - + static void expireAllTableObservers() { synchronized (tableInstances) { for (Entry entry : tableInstances.entrySet()) { @@ -85,27 +100,31 @@ public class ServerConfiguration { } } } - + private final Instance instance; - + public ServerConfiguration(Instance instance) { this.instance = instance; } - + public TableConfiguration getTableConfiguration(String tableId) { return getTableConfiguration(instance, tableId); } - + public TableConfiguration getTableConfiguration(KeyExtent extent) { return getTableConfiguration(extent.getTableId().toString()); } - + + public TableNamespaceConfiguration getTableNamespaceConfiguration(String namespaceId) { + return getTableNamespaceConfiguration(instance, namespaceId); + } + public synchronized AccumuloConfiguration getConfiguration() { return getZooConfiguration(instance); } - + public Instance getInstance() { return instance; } - + } http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/base/src/main/java/org/apache/accumulo/server/conf/TableNamespaceConfiguration.java ---------------------------------------------------------------------- diff --git a/server/base/src/main/java/org/apache/accumulo/server/conf/TableNamespaceConfiguration.java b/server/base/src/main/java/org/apache/accumulo/server/conf/TableNamespaceConfiguration.java new file mode 100644 index 0000000..a5c3437 --- /dev/null +++ b/server/base/src/main/java/org/apache/accumulo/server/conf/TableNamespaceConfiguration.java @@ -0,0 +1,103 @@ +/* + * 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.server.conf; + +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; +import java.util.TreeMap; + +import org.apache.accumulo.core.Constants; +import org.apache.accumulo.core.client.Instance; +import org.apache.accumulo.core.conf.AccumuloConfiguration; +import org.apache.accumulo.core.conf.Property; +import org.apache.accumulo.core.zookeeper.ZooUtil; +import org.apache.accumulo.fate.zookeeper.ZooCache; +import org.apache.accumulo.server.client.HdfsZooInstance; +import org.apache.log4j.Logger; + +public class TableNamespaceConfiguration extends AccumuloConfiguration { + private static final Logger log = Logger.getLogger(TableNamespaceConfiguration.class); + + private final AccumuloConfiguration parent; + private final String instanceId; + private static ZooCache propCache = null; + private String namespaceId = null; + + public TableNamespaceConfiguration (Instance inst, String namespaceId, AccumuloConfiguration parent) { + propCache = new ZooCache(inst.getZooKeepers(), inst.getZooKeepersSessionTimeOut()); + this.instanceId = inst.getInstanceID(); + this.parent = parent; + this.namespaceId = namespaceId; + } + + public void invalidateCache() { + if (propCache != null) + propCache.clear(); + } + + @Override + public String get(Property property) { + String key = property.getKey(); + String value = get(key); + + if (value == null || !property.getType().isValidFormat(value)) { + if (value != null) + log.error("Using default value for " + key + " due to improperly formatted " + property.getType() + ": " + value); + value = parent.get(property); + } + return value; + } + + private String get(String key) { + String zPath = ZooUtil.getRoot(instanceId) + Constants.ZNAMESPACES + "/" + namespaceId + Constants.ZNAMESPACE_CONF + "/" + key; + byte[] v = getPropCache().get(zPath); + String value = null; + if (v != null) + value = new String(v); + return value; + } + + private static ZooCache getPropCache() { + Instance inst = HdfsZooInstance.getInstance(); + if (propCache == null) + synchronized (TableNamespaceConfiguration.class) { + if (propCache == null) + propCache = new ZooCache(inst.getZooKeepers(), inst.getZooKeepersSessionTimeOut(), new TableConfWatcher(inst)); + } + return propCache; + } + + @Override + public Iterator> iterator() { + TreeMap entries = new TreeMap(); + + for (Entry parentEntry : parent) + entries.put(parentEntry.getKey(), parentEntry.getValue()); + + List children = getPropCache().getChildren(ZooUtil.getRoot(instanceId) + Constants.ZNAMESPACES + "/" + namespaceId + Constants.ZNAMESPACE_CONF); + if (children != null) { + for (String child : children) { + String value = get(child); + if (child != null && value != null) + entries.put(child, value); + } + } + + return entries.entrySet().iterator(); + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/base/src/main/java/org/apache/accumulo/server/init/Initialize.java ---------------------------------------------------------------------- diff --git a/server/base/src/main/java/org/apache/accumulo/server/init/Initialize.java b/server/base/src/main/java/org/apache/accumulo/server/init/Initialize.java index 0477a44..47179fc 100644 --- a/server/base/src/main/java/org/apache/accumulo/server/init/Initialize.java +++ b/server/base/src/main/java/org/apache/accumulo/server/init/Initialize.java @@ -376,6 +376,22 @@ public class Initialize { zoo.putPersistentData(zkInstanceRoot + Constants.ZHDFS_RESERVATIONS, new byte[0], NodeExistsPolicy.FAIL); zoo.putPersistentData(zkInstanceRoot + Constants.ZNEXT_FILE, new byte[] {'0'}, NodeExistsPolicy.FAIL); zoo.putPersistentData(zkInstanceRoot + Constants.ZRECOVERY, new byte[] {'0'}, NodeExistsPolicy.FAIL); + zoo.putPersistentData(zkInstanceRoot + Constants.ZNAMESPACES, new byte[0], NodeExistsPolicy.FAIL); + + createInitialTableNamespace(zoo, zkInstanceRoot, Constants.DEFAULT_TABLE_NAMESPACE_ID, Constants.DEFAULT_TABLE_NAMESPACE); + createInitialTableNamespace(zoo, zkInstanceRoot, Constants.SYSTEM_TABLE_NAMESPACE_ID, Constants.SYSTEM_TABLE_NAMESPACE); + + zoo.putPersistentData(zkInstanceRoot + Constants.ZTABLES + "/" + MetadataTable.ID + Constants.ZTABLE_NAMESPACE, + Constants.SYSTEM_TABLE_NAMESPACE_ID.getBytes(Constants.UTF8), NodeExistsPolicy.FAIL); + zoo.putPersistentData(zkInstanceRoot + Constants.ZTABLES + "/" + RootTable.ID + Constants.ZTABLE_NAMESPACE, + Constants.SYSTEM_TABLE_NAMESPACE_ID.getBytes(Constants.UTF8), NodeExistsPolicy.FAIL); + } + + private static void createInitialTableNamespace(IZooReaderWriter zoo, String root, String id, String namespace) throws KeeperException, InterruptedException { + String zPath = root + Constants.ZNAMESPACES + "/" + id; + zoo.putPersistentData(zPath, new byte[0], NodeExistsPolicy.FAIL); + zoo.putPersistentData(zPath + Constants.ZNAMESPACE_NAME, namespace.getBytes(Constants.UTF8), NodeExistsPolicy.FAIL); + zoo.putPersistentData(zPath + Constants.ZNAMESPACE_CONF, new byte[0], NodeExistsPolicy.FAIL); } private static String getInstanceNamePath(Opts opts) throws IOException, KeeperException, InterruptedException { @@ -494,7 +510,6 @@ public class Initialize { public static void main(String[] args) { Opts opts = new Opts(); opts.parseArgs(Initialize.class.getName(), args); - try { SecurityUtil.serverLogin(); Configuration conf = CachedConfiguration.getInstance(); http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/base/src/main/java/org/apache/accumulo/server/tables/TableManager.java ---------------------------------------------------------------------- diff --git a/server/base/src/main/java/org/apache/accumulo/server/tables/TableManager.java b/server/base/src/main/java/org/apache/accumulo/server/tables/TableManager.java index 8a74d0b..aed6321 100644 --- a/server/base/src/main/java/org/apache/accumulo/server/tables/TableManager.java +++ b/server/base/src/main/java/org/apache/accumulo/server/tables/TableManager.java @@ -26,6 +26,7 @@ import java.util.Set; import org.apache.accumulo.core.Constants; import org.apache.accumulo.core.client.Instance; +import org.apache.accumulo.core.client.impl.Tables; import org.apache.accumulo.core.master.state.tables.TableState; import org.apache.accumulo.core.zookeeper.ZooUtil; import org.apache.accumulo.fate.zookeeper.IZooReaderWriter; @@ -57,15 +58,16 @@ public class TableManager { public static void prepareNewTableState(String instanceId, String tableId, String tableName, TableState state, NodeExistsPolicy existsPolicy) throws KeeperException, InterruptedException { // state gets created last + tableName = Tables.extractTableName(tableName); String zTablePath = Constants.ZROOT + "/" + instanceId + Constants.ZTABLES + "/" + tableId; IZooReaderWriter zoo = ZooReaderWriter.getRetryingInstance(); zoo.putPersistentData(zTablePath, new byte[0], existsPolicy); zoo.putPersistentData(zTablePath + Constants.ZTABLE_CONF, new byte[0], existsPolicy); - zoo.putPersistentData(zTablePath + Constants.ZTABLE_NAME, tableName.getBytes(), existsPolicy); - zoo.putPersistentData(zTablePath + Constants.ZTABLE_STATE, state.name().getBytes(), existsPolicy); - zoo.putPersistentData(zTablePath + Constants.ZTABLE_FLUSH_ID, "0".getBytes(), existsPolicy); - zoo.putPersistentData(zTablePath + Constants.ZTABLE_COMPACT_ID, "0".getBytes(), existsPolicy); - zoo.putPersistentData(zTablePath + Constants.ZTABLE_COMPACT_CANCEL_ID, "0".getBytes(), existsPolicy); + zoo.putPersistentData(zTablePath + Constants.ZTABLE_NAME, tableName.getBytes(Constants.UTF8), existsPolicy); + zoo.putPersistentData(zTablePath + Constants.ZTABLE_STATE, state.name().getBytes(Constants.UTF8), existsPolicy); + zoo.putPersistentData(zTablePath + Constants.ZTABLE_FLUSH_ID, "0".getBytes(Constants.UTF8), existsPolicy); + zoo.putPersistentData(zTablePath + Constants.ZTABLE_COMPACT_ID, "0".getBytes(Constants.UTF8), existsPolicy); + zoo.putPersistentData(zTablePath + Constants.ZTABLE_COMPACT_CANCEL_ID, "0".getBytes(Constants.UTF8), existsPolicy); } public synchronized static TableManager getInstance() { @@ -290,6 +292,33 @@ public class TableManager { } } + public void addNamespace(String namespaceId, String namespace, NodeExistsPolicy existsPolicy) throws KeeperException, InterruptedException { + // state gets created last + String zPath = Constants.ZROOT + "/" + instance.getInstanceID() + Constants.ZNAMESPACES + "/" + namespaceId; + + IZooReaderWriter zoo = ZooReaderWriter.getRetryingInstance(); + + zoo.putPersistentData(zPath, new byte[0], existsPolicy); + zoo.putPersistentData(zPath + Constants.ZNAMESPACE_NAME, namespace.getBytes(Constants.UTF8), existsPolicy); + zoo.putPersistentData(zPath + Constants.ZNAMESPACE_CONF, new byte[0], existsPolicy); + } + + public void removeNamespace(String namespaceId) throws KeeperException, InterruptedException { + ZooReaderWriter.getRetryingInstance().recursiveDelete(ZooUtil.getRoot(instance) + Constants.ZNAMESPACES + "/" + namespaceId, NodeMissingPolicy.SKIP); + } + + public void addNamespaceToTable(String tableId, String namespaceId) throws KeeperException, InterruptedException { + String zPath = Constants.ZROOT + "/" + instance.getInstanceID() + Constants.ZTABLES + "/" + tableId + Constants.ZTABLE_NAMESPACE; + ZooReaderWriter.getRetryingInstance().putPersistentData(zPath, namespaceId.getBytes(Constants.UTF8), NodeExistsPolicy.OVERWRITE); + } + + public void removeNamespaceFromTable(String tableId, String namespaceId) throws KeeperException, InterruptedException { + // actually, revert it to the default namespace. + String zPath = Constants.ZROOT + "/" + instance.getInstanceID() + Constants.ZTABLES + "/" + tableId + Constants.ZTABLE_NAMESPACE; + ZooReaderWriter.getRetryingInstance().putPersistentData(zPath, Constants.DEFAULT_TABLE_NAMESPACE.getBytes(Constants.UTF8), NodeExistsPolicy.OVERWRITE); + } + + /* * private static boolean verifyTabletAssignments(String tableId) { log.info( "Sending message to load balancer to verify assignment of tablets with tableId=" * + tableId); // Return true only if transitions to other states did not interrupt // this process. (like deleting the table) return true; } http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/base/src/main/java/org/apache/accumulo/server/util/NamespacePropUtil.java ---------------------------------------------------------------------- diff --git a/server/base/src/main/java/org/apache/accumulo/server/util/NamespacePropUtil.java b/server/base/src/main/java/org/apache/accumulo/server/util/NamespacePropUtil.java new file mode 100644 index 0000000..a3ff33a --- /dev/null +++ b/server/base/src/main/java/org/apache/accumulo/server/util/NamespacePropUtil.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.server.util; + +import org.apache.accumulo.core.Constants; +import org.apache.accumulo.core.conf.Property; +import org.apache.accumulo.core.zookeeper.ZooUtil; +import org.apache.accumulo.fate.zookeeper.ZooUtil.NodeExistsPolicy; +import org.apache.accumulo.fate.zookeeper.ZooUtil.NodeMissingPolicy; +import org.apache.accumulo.server.client.HdfsZooInstance; +import org.apache.accumulo.server.zookeeper.ZooReaderWriter; +import org.apache.zookeeper.KeeperException; + +public class NamespacePropUtil { + public static boolean setNamespaceProperty(String namespaceId, String property, String value) throws KeeperException, InterruptedException { + if (!isPropertyValid(property, value)) + return false; + + // create the zk node for per-namespace properties for this namespace if it doesn't already exist + String zkNamespacePath = getPath(namespaceId); + ZooReaderWriter.getInstance().putPersistentData(zkNamespacePath, new byte[0], NodeExistsPolicy.SKIP); + + // create the zk node for this property and set it's data to the specified value + String zPath = zkNamespacePath + "/" + property; + ZooReaderWriter.getInstance().putPersistentData(zPath, value.getBytes(), NodeExistsPolicy.OVERWRITE); + + return true; + } + + public static boolean isPropertyValid(String property, String value) { + Property p = Property.getPropertyByKey(property); + if ((p != null && !p.getType().isValidFormat(value)) || !Property.isValidTablePropertyKey(property)) + return false; + + return true; + } + + public static void removeNamespaceProperty(String namespaceId, String property) throws InterruptedException, KeeperException { + String zPath = getPath(namespaceId) + "/" + property; + ZooReaderWriter.getInstance().recursiveDelete(zPath, NodeMissingPolicy.SKIP); + } + + private static String getPath(String namespaceId) { + return ZooUtil.getRoot(HdfsZooInstance.getInstance()) + Constants.ZNAMESPACES + "/" + namespaceId + Constants.ZNAMESPACE_CONF; + } +} http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/master/src/main/java/org/apache/accumulo/master/Master.java ---------------------------------------------------------------------- diff --git a/server/master/src/main/java/org/apache/accumulo/master/Master.java b/server/master/src/main/java/org/apache/accumulo/master/Master.java index 8a1a9b2..e9910d5 100644 --- a/server/master/src/main/java/org/apache/accumulo/master/Master.java +++ b/server/master/src/main/java/org/apache/accumulo/master/Master.java @@ -44,6 +44,8 @@ import org.apache.accumulo.core.client.RowIterator; import org.apache.accumulo.core.client.Scanner; import org.apache.accumulo.core.client.TableNotFoundException; import org.apache.accumulo.core.client.admin.TableOperationsImpl; +import org.apache.accumulo.core.client.admin.TimeType; +import org.apache.accumulo.core.client.impl.TableNamespaces; import org.apache.accumulo.core.client.impl.Tables; import org.apache.accumulo.core.client.impl.ThriftTransportPool; import org.apache.accumulo.core.client.impl.thrift.SecurityErrorCode; @@ -97,10 +99,13 @@ import org.apache.accumulo.master.tableOps.ChangeTableState; import org.apache.accumulo.master.tableOps.CloneTable; import org.apache.accumulo.master.tableOps.CompactRange; import org.apache.accumulo.master.tableOps.CreateTable; +import org.apache.accumulo.master.tableOps.CreateTableNamespace; import org.apache.accumulo.master.tableOps.DeleteTable; +import org.apache.accumulo.master.tableOps.DeleteTableNamespace; import org.apache.accumulo.master.tableOps.ExportTable; import org.apache.accumulo.master.tableOps.ImportTable; import org.apache.accumulo.master.tableOps.RenameTable; +import org.apache.accumulo.master.tableOps.RenameTableNamespace; import org.apache.accumulo.master.tableOps.TableRangeOp; import org.apache.accumulo.master.tableOps.TraceRepo; import org.apache.accumulo.master.tserverOps.ShutdownTServer; @@ -138,6 +143,7 @@ import org.apache.accumulo.server.tables.TableObserver; import org.apache.accumulo.server.util.DefaultMap; import org.apache.accumulo.server.util.Halt; import org.apache.accumulo.server.util.MetadataTableUtil; +import org.apache.accumulo.server.util.NamespacePropUtil; import org.apache.accumulo.server.util.SystemPropUtil; import org.apache.accumulo.server.util.TServerUtils; import org.apache.accumulo.server.util.TServerUtils.ServerAddress; @@ -321,6 +327,33 @@ public class Master implements LiveTServerSet.Listener, TableObserver, CurrentSt zoo.putPersistentData(ZooUtil.getRoot(instance) + Constants.ZTABLES + "/" + id + Constants.ZTABLE_COMPACT_CANCEL_ID, "0".getBytes(), NodeExistsPolicy.SKIP); } + + // setup default and system table namespaces if not already there + String tableNamespaces = ZooUtil.getRoot(instance) + Constants.ZNAMESPACES; + String defaultTableNamespace = ZooUtil.getRoot(instance) + Constants.ZNAMESPACES + "/" + Constants.DEFAULT_TABLE_NAMESPACE_ID; + String systemTableNamespace = ZooUtil.getRoot(instance) + Constants.ZNAMESPACES + "/" + Constants.SYSTEM_TABLE_NAMESPACE_ID; + String tables = ZooUtil.getRoot(instance) + Constants.ZTABLES; + zoo.putPersistentData(tableNamespaces, new byte[0], NodeExistsPolicy.SKIP); + + zoo.putPersistentData(defaultTableNamespace, new byte[0], NodeExistsPolicy.SKIP); + zoo.putPersistentData(defaultTableNamespace + Constants.ZNAMESPACE_CONF, new byte[0], NodeExistsPolicy.SKIP); + zoo.putPersistentData(defaultTableNamespace + Constants.ZNAMESPACE_NAME, Constants.DEFAULT_TABLE_NAMESPACE.getBytes(Constants.UTF8), + NodeExistsPolicy.SKIP); + + zoo.putPersistentData(systemTableNamespace, new byte[0], NodeExistsPolicy.SKIP); + zoo.putPersistentData(systemTableNamespace + Constants.ZNAMESPACE_CONF, new byte[0], NodeExistsPolicy.SKIP); + zoo.putPersistentData(systemTableNamespace + Constants.ZNAMESPACE_NAME, Constants.SYSTEM_TABLE_NAMESPACE.getBytes(Constants.UTF8), + NodeExistsPolicy.SKIP); + + for (Entry table : Tables.getIdToNameMap(instance).entrySet()) { + if (table.getValue().equals(MetadataTable.NAME) || table.getValue().equals(RootTable.NAME)) { + zoo.putPersistentData(tables + "/" + table.getKey() + Constants.ZTABLE_NAMESPACE, Constants.SYSTEM_TABLE_NAMESPACE_ID.getBytes(Constants.UTF8), + NodeExistsPolicy.SKIP); + } else { + zoo.putPersistentData(tables + "/" + table.getKey() + Constants.ZTABLE_NAMESPACE, Constants.DEFAULT_TABLE_NAMESPACE_ID.getBytes(Constants.UTF8), + NodeExistsPolicy.SKIP); + } + } } catch (Exception ex) { log.fatal("Error performing upgrade", ex); System.exit(1); @@ -697,6 +730,24 @@ public class Master implements LiveTServerSet.Listener, TableObserver, CurrentSt } } + private void alterTableNamespaceProperty(TCredentials c, String namespace, String property, String value, TableOperation op) + throws ThriftSecurityException, ThriftTableOperationException { + + String namespaceId = null; + try { + namespaceId = TableNamespaces.getNamespaceId(instance, namespace); + // TODO insert a permission check here once namespace-level permissions exist. (ACCUMULO-1479) + if (value == null) { + NamespacePropUtil.removeNamespaceProperty(namespaceId, property); + } else { + NamespacePropUtil.setNamespaceProperty(namespaceId, property, value); + } + } catch (Exception e) { + log.error("Problem altering table namespace property", e); + throw new ThriftTableOperationException(namespaceId, namespace, op, TableOperationExceptionType.OTHER, "Problem altering table namespace property"); + } + } + @Override public void removeTableProperty(TInfo info, TCredentials credentials, String tableName, String property) throws ThriftSecurityException, ThriftTableOperationException, TException { @@ -845,10 +896,9 @@ public class Master implements LiveTServerSet.Listener, TableObserver, CurrentSt checkNotMetadataTable(tableName, TableOperation.CREATE); checkTableName(tableName, TableOperation.CREATE); - org.apache.accumulo.core.client.admin.TimeType timeType = org.apache.accumulo.core.client.admin.TimeType.valueOf(ByteBufferUtil.toString(arguments - .get(1))); - fate.seedTransaction(opid, new TraceRepo(new CreateTable(c.getPrincipal(), tableName, timeType, options)), autoCleanup); + TimeType timeType = TimeType.valueOf(ByteBufferUtil.toString(arguments.get(1))); + fate.seedTransaction(opid, new TraceRepo(new CreateTable(c.getPrincipal(), tableName, timeType, options)), autoCleanup); break; } case RENAME: { @@ -902,7 +952,6 @@ public class Master implements LiveTServerSet.Listener, TableObserver, CurrentSt checkNotMetadataTable(tableName, TableOperation.DELETE); if (!security.canDeleteTable(c, tableId)) throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); - fate.seedTransaction(opid, new TraceRepo(new DeleteTable(tableId)), autoCleanup); break; } @@ -1052,6 +1101,85 @@ public class Master implements LiveTServerSet.Listener, TableObserver, CurrentSt authenticate(credentials); fate.delete(opid); } + + @Override + public long beginTableNamespaceOperation(TInfo tinfo, TCredentials credentials) throws ThriftSecurityException, TException { + return beginTableOperation(tinfo, credentials); + } + + @Override + public void executeTableNamespaceOperation(TInfo tinfo, TCredentials c, long opid, org.apache.accumulo.core.master.thrift.TableOperation op, + List arguments, Map options, boolean autoCleanup) throws ThriftSecurityException, ThriftTableOperationException, TException { + authenticate(c); + + switch (op) { + case CREATE: { + String namespace = ByteBufferUtil.toString(arguments.get(0)); + // TODO security check once namespace permissions exist (ACCUMULO-1479) + + checkTableNamespaceName(namespace); + fate.seedTransaction(opid, new TraceRepo(new CreateTableNamespace(c.getPrincipal(), namespace, options)), autoCleanup); + break; + } + case RENAME: { + + String oldName = ByteBufferUtil.toString(arguments.get(0)); + String newName = ByteBufferUtil.toString(arguments.get(1)); + // TODO security check (ACCUMULO-1479) + String namespaceId = checkNamespaceId(oldName, TableOperation.RENAME); + checkTableNamespaceName(newName); + fate.seedTransaction(opid, new TraceRepo(new RenameTableNamespace(namespaceId, oldName, newName)), autoCleanup); + break; + } + case DELETE: { + String namespace = ByteBufferUtil.toString(arguments.get(0)); + String namespaceId = checkNamespaceId(namespace, TableOperation.DELETE); + // TODO security check (ACCUMULO-1479) + fate.seedTransaction(opid, new TraceRepo(new DeleteTableNamespace(namespaceId)), autoCleanup); + break; + } + default: + throw new UnsupportedOperationException(); + } + + } + + protected void checkTableNamespaceName(String namespace) throws ThriftTableOperationException { + if (TableNamespaces.getNameToIdMap(instance).containsKey(namespace)) { + String why = "Table namespace already exists: " + namespace; + throw new ThriftTableOperationException(null, namespace, TableOperation.CREATE, TableOperationExceptionType.EXISTS, why); + } + } + + protected String checkNamespaceId(String namespace, TableOperation operation) throws ThriftTableOperationException { + final String namespaceId = TableNamespaces.getNameToIdMap(getConfiguration().getInstance()).get(namespace); + if (namespaceId == null) + throw new ThriftTableOperationException(null, namespace, operation, TableOperationExceptionType.NOTFOUND, null); + return namespaceId; + } + + @Override + public String waitForTableNamespaceOperation(TInfo tinfo, TCredentials credentials, long opid) throws ThriftSecurityException, + ThriftTableOperationException, TException { + return waitForTableOperation(tinfo, credentials, opid); + } + + @Override + public void finishTableNamespaceOperation(TInfo tinfo, TCredentials credentials, long opid) throws ThriftSecurityException, TException { + finishTableOperation(tinfo, credentials, opid); + } + + @Override + public void setTableNamespaceProperty(TInfo tinfo, TCredentials credentials, String ns, String property, String value) throws ThriftSecurityException, + ThriftTableOperationException, TException { + alterTableNamespaceProperty(credentials, ns, property, value, TableOperation.SET_PROPERTY); + } + + @Override + public void removeTableNamespaceProperty(TInfo tinfo, TCredentials credentials, String ns, String property) throws ThriftSecurityException, + ThriftTableOperationException, TException { + alterTableNamespaceProperty(credentials, ns, property, null, TableOperation.REMOVE_PROPERTY); + } } public MergeInfo getMergeInfo(KeyExtent tablet) { http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/master/src/main/java/org/apache/accumulo/master/tableOps/CloneTable.java ---------------------------------------------------------------------- diff --git a/server/master/src/main/java/org/apache/accumulo/master/tableOps/CloneTable.java b/server/master/src/main/java/org/apache/accumulo/master/tableOps/CloneTable.java index 8d906d6..b4c8aa2 100644 --- a/server/master/src/main/java/org/apache/accumulo/master/tableOps/CloneTable.java +++ b/server/master/src/main/java/org/apache/accumulo/master/tableOps/CloneTable.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.Set; import org.apache.accumulo.core.client.Instance; +import org.apache.accumulo.core.client.impl.TableNamespaces; import org.apache.accumulo.core.client.impl.Tables; import org.apache.accumulo.core.client.impl.thrift.TableOperation; import org.apache.accumulo.core.client.impl.thrift.ThriftSecurityException; @@ -147,6 +148,15 @@ class CloneZookeeper extends MasterRepo { TableManager.getInstance().cloneTable(cloneInfo.srcTableId, cloneInfo.tableId, cloneInfo.tableName, cloneInfo.propertiesToSet, cloneInfo.propertiesToExclude, NodeExistsPolicy.OVERWRITE); Tables.clearCache(instance); + + String namespace = Tables.extractNamespace(cloneInfo.tableName); + String namespaceId = TableNamespaces.getNamespaceId(instance, namespace); + TableManager tm = TableManager.getInstance(); + if (!TableNamespaces.getNameToIdMap(instance).containsKey(namespace)) { + tm.addNamespace(namespaceId, namespace, NodeExistsPolicy.SKIP); + } + tm.addNamespaceToTable(cloneInfo.tableId, namespaceId); + return new CloneMetadata(cloneInfo); } finally { Utils.tableNameLock.unlock(); http://git-wip-us.apache.org/repos/asf/accumulo/blob/d4c3e6a9/server/master/src/main/java/org/apache/accumulo/master/tableOps/CreateTable.java ---------------------------------------------------------------------- diff --git a/server/master/src/main/java/org/apache/accumulo/master/tableOps/CreateTable.java b/server/master/src/main/java/org/apache/accumulo/master/tableOps/CreateTable.java index df2e028..d425fe8 100644 --- a/server/master/src/main/java/org/apache/accumulo/master/tableOps/CreateTable.java +++ b/server/master/src/main/java/org/apache/accumulo/master/tableOps/CreateTable.java @@ -23,6 +23,7 @@ import java.util.Map.Entry; import org.apache.accumulo.core.Constants; import org.apache.accumulo.core.client.Instance; import org.apache.accumulo.core.client.admin.TimeType; +import org.apache.accumulo.core.client.impl.TableNamespaces; import org.apache.accumulo.core.client.impl.Tables; import org.apache.accumulo.core.client.impl.thrift.TableOperation; import org.apache.accumulo.core.client.impl.thrift.ThriftSecurityException; @@ -212,6 +213,11 @@ class PopulateZookeeper extends MasterRepo { TableManager.getInstance().addTable(tableInfo.tableId, tableInfo.tableName, NodeExistsPolicy.OVERWRITE); + String namespace = Tables.extractNamespace(tableInfo.tableName); + String namespaceId = TableNamespaces.getNamespaceId(instance, namespace); + + TableManager.getInstance().addNamespaceToTable(tableInfo.tableId, namespaceId); + for (Entry entry : tableInfo.props.entrySet()) TablePropUtil.setTableProperty(tableInfo.tableId, entry.getKey(), entry.getValue()); @@ -288,7 +294,7 @@ public class CreateTable extends MasterRepo { } @Override - public Repo call(long tid, Master master) throws Exception { + public Repo call(long tid, Master master) throws Exception { // first step is to reserve a table id.. if the machine fails during this step // it is ok to retry... the only side effect is that a table id may not be used // or skipped