hbase-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From syuanji...@apache.org
Subject [1/4] hbase git commit: HBASE-13212: Procedure V2 - master Create/Modify/Delete namespace (Stephen Yuan Jiang)
Date Wed, 26 Aug 2015 15:18:27 GMT
Repository: hbase
Updated Branches:
  refs/heads/master 6661f2d02 -> dc79b3c5c


http://git-wip-us.apache.org/repos/asf/hbase/blob/dc79b3c5/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/CreateNamespaceProcedure.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/CreateNamespaceProcedure.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/CreateNamespaceProcedure.java
new file mode 100644
index 0000000..c91092a
--- /dev/null
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/CreateNamespaceProcedure.java
@@ -0,0 +1,364 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.master.procedure;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.NamespaceExistException;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.master.MasterFileSystem;
+import org.apache.hadoop.hbase.master.TableNamespaceManager;
+import org.apache.hadoop.hbase.procedure2.StateMachineProcedure;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.CreateNamespaceState;
+import org.apache.hadoop.hbase.util.FSUtils;
+
+/**
+ * The procedure to create a new namespace.
+ */
+@InterfaceAudience.Private
+public class CreateNamespaceProcedure
+    extends StateMachineProcedure<MasterProcedureEnv, CreateNamespaceState>
+    implements TableProcedureInterface {
+  private static final Log LOG = LogFactory.getLog(CreateNamespaceProcedure.class);
+
+  private final AtomicBoolean aborted = new AtomicBoolean(false);
+
+  private NamespaceDescriptor nsDescriptor;
+  private Boolean traceEnabled;
+
+  public CreateNamespaceProcedure() {
+    this.traceEnabled = null;
+  }
+
+  public CreateNamespaceProcedure(
+      final MasterProcedureEnv env,
+      final NamespaceDescriptor nsDescriptor) throws IOException {
+    this.nsDescriptor = nsDescriptor;
+    this.traceEnabled = null;
+  }
+
+  @Override
+  protected Flow executeFromState(final MasterProcedureEnv env, final CreateNamespaceState state)
+      throws InterruptedException {
+    if (isTraceEnabled()) {
+      LOG.trace(this + " execute state=" + state);
+    }
+
+    try {
+      switch (state) {
+      case CREATE_NAMESPACE_PREPARE:
+        prepareCreate(env);
+        setNextState(CreateNamespaceState.CREATE_NAMESPACE_CREATE_DIRECTORY);
+        break;
+      case CREATE_NAMESPACE_CREATE_DIRECTORY:
+        createDirectory(env, nsDescriptor);
+        setNextState(CreateNamespaceState.CREATE_NAMESPACE_INSERT_INTO_NS_TABLE);
+        break;
+      case CREATE_NAMESPACE_INSERT_INTO_NS_TABLE:
+        insertIntoNSTable(env, nsDescriptor);
+        setNextState(CreateNamespaceState.CREATE_NAMESPACE_UPDATE_ZK);
+        break;
+      case CREATE_NAMESPACE_UPDATE_ZK:
+        updateZKNamespaceManager(env, nsDescriptor);
+        setNextState(CreateNamespaceState.CREATE_NAMESPACE_SET_NAMESPACE_QUOTA);
+        break;
+      case CREATE_NAMESPACE_SET_NAMESPACE_QUOTA:
+        setNamespaceQuota(env, nsDescriptor);
+        return Flow.NO_MORE_STATE;
+      default:
+        throw new UnsupportedOperationException(this + " unhandled state=" + state);
+      }
+    } catch (IOException e) {
+      LOG.warn("Error trying to create the namespace" + nsDescriptor.getName()
+        + " (in state=" + state + ")", e);
+
+      setFailure("master-create-namespace", e);
+    }
+    return Flow.HAS_MORE_STATE;
+  }
+
+  @Override
+  protected void rollbackState(final MasterProcedureEnv env, final CreateNamespaceState state)
+      throws IOException {
+    if (isTraceEnabled()) {
+      LOG.trace(this + " rollback state=" + state);
+    }
+    try {
+      switch (state) {
+      case CREATE_NAMESPACE_SET_NAMESPACE_QUOTA:
+        rollbackSetNamespaceQuota(env);
+        break;
+      case CREATE_NAMESPACE_UPDATE_ZK:
+        rollbackZKNamespaceManagerChange(env);
+        break;
+      case CREATE_NAMESPACE_INSERT_INTO_NS_TABLE:
+        rollbackInsertIntoNSTable(env);
+        break;
+      case CREATE_NAMESPACE_CREATE_DIRECTORY:
+        rollbackCreateDirectory(env);
+        break;
+      case CREATE_NAMESPACE_PREPARE:
+        break; // nothing to do
+      default:
+        throw new UnsupportedOperationException(this + " unhandled state=" + state);
+      }
+    } catch (IOException e) {
+      // This will be retried. Unless there is a bug in the code,
+      // this should be just a "temporary error" (e.g. network down)
+      LOG.warn("Failed rollback attempt step " + state + " for creating the namespace "
+          + nsDescriptor.getName(), e);
+      throw e;
+    }
+  }
+
+  @Override
+  protected CreateNamespaceState getState(final int stateId) {
+    return CreateNamespaceState.valueOf(stateId);
+  }
+
+  @Override
+  protected int getStateId(final CreateNamespaceState state) {
+    return state.getNumber();
+  }
+
+  @Override
+  protected CreateNamespaceState getInitialState() {
+    return CreateNamespaceState.CREATE_NAMESPACE_PREPARE;
+  }
+
+  @Override
+  protected void setNextState(CreateNamespaceState state) {
+    if (aborted.get()) {
+      setAbortFailure("create-namespace", "abort requested");
+    } else {
+      super.setNextState(state);
+    }
+  }
+
+  @Override
+  public boolean abort(final MasterProcedureEnv env) {
+    aborted.set(true);
+    return true;
+  }
+
+  @Override
+  public void serializeStateData(final OutputStream stream) throws IOException {
+    super.serializeStateData(stream);
+
+    MasterProcedureProtos.CreateNamespaceStateData.Builder createNamespaceMsg =
+        MasterProcedureProtos.CreateNamespaceStateData.newBuilder().setNamespaceDescriptor(
+          ProtobufUtil.toProtoNamespaceDescriptor(this.nsDescriptor));
+    createNamespaceMsg.build().writeDelimitedTo(stream);
+  }
+
+  @Override
+  public void deserializeStateData(final InputStream stream) throws IOException {
+    super.deserializeStateData(stream);
+
+    MasterProcedureProtos.CreateNamespaceStateData createNamespaceMsg =
+        MasterProcedureProtos.CreateNamespaceStateData.parseDelimitedFrom(stream);
+    nsDescriptor = ProtobufUtil.toNamespaceDescriptor(createNamespaceMsg.getNamespaceDescriptor());
+  }
+
+  @Override
+  public void toStringClassDetails(StringBuilder sb) {
+    sb.append(getClass().getSimpleName());
+    sb.append(" (Namespace=");
+    sb.append(nsDescriptor.getName());
+    sb.append(")");
+  }
+
+  @Override
+  protected boolean acquireLock(final MasterProcedureEnv env) {
+    if (!env.getMasterServices().isInitialized()) {
+      // Namespace manager might not be ready if master is not fully initialized,
+      // return false to reject user namespace creation; return true for default
+      // and system namespace creation (this is part of master initialization).
+      if (nsDescriptor.equals(NamespaceDescriptor.DEFAULT_NAMESPACE) ||
+        nsDescriptor.equals(NamespaceDescriptor.SYSTEM_NAMESPACE)) {
+        return true;
+      }
+
+      return false;
+    }
+    return getTableNamespaceManager(env).acquireExclusiveLock();
+  }
+
+  @Override
+  protected void releaseLock(final MasterProcedureEnv env) {
+    if (env.getMasterServices().isInitialized()) {
+      getTableNamespaceManager(env).releaseExclusiveLock();
+    }
+  }
+
+  @Override
+  public TableName getTableName() {
+    return TableName.NAMESPACE_TABLE_NAME;
+  }
+
+  @Override
+  public TableOperationType getTableOperationType() {
+    return TableOperationType.EDIT;
+  }
+
+  /**
+   * Action before any real action of creating namespace.
+   * @param env MasterProcedureEnv
+   * @throws IOException
+   */
+  private void prepareCreate(final MasterProcedureEnv env) throws IOException {
+    if (getTableNamespaceManager(env).doesNamespaceExist(nsDescriptor.getName())) {
+      throw new NamespaceExistException(nsDescriptor.getName());
+    }
+    getTableNamespaceManager(env).validateTableAndRegionCount(nsDescriptor);
+  }
+
+  /**
+   * Create the namespace directory
+   * @param env MasterProcedureEnv
+   * @param nsDescriptor NamespaceDescriptor
+   * @throws IOException
+   */
+  protected static void createDirectory(
+      final MasterProcedureEnv env,
+      final NamespaceDescriptor nsDescriptor) throws IOException {
+    MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem();
+    mfs.getFileSystem().mkdirs(
+      FSUtils.getNamespaceDir(mfs.getRootDir(), nsDescriptor.getName()));
+  }
+
+  /**
+   * undo create directory
+   * @param env MasterProcedureEnv
+   * @throws IOException
+   */
+  private void rollbackCreateDirectory(final MasterProcedureEnv env) throws IOException {
+    try {
+      DeleteNamespaceProcedure.deleteDirectory(env, nsDescriptor.getName());
+    } catch (Exception e) {
+      // Ignore exception
+      LOG.debug("Rollback of createDirectory throws exception: " + e);
+    }
+  }
+
+  /**
+   * Insert the row into ns table
+   * @param env MasterProcedureEnv
+   * @param nsDescriptor NamespaceDescriptor
+   * @throws IOException
+   */
+  protected static void insertIntoNSTable(
+      final MasterProcedureEnv env,
+      final NamespaceDescriptor nsDescriptor) throws IOException {
+    getTableNamespaceManager(env).insertIntoNSTable(nsDescriptor);
+  }
+
+  /**
+   * Undo the insert.
+   * @param env MasterProcedureEnv
+   * @throws IOException
+   */
+  private void rollbackInsertIntoNSTable(final MasterProcedureEnv env) throws IOException {
+    try {
+      DeleteNamespaceProcedure.deleteFromNSTable(env, nsDescriptor.getName());
+    } catch (Exception e) {
+      // Ignore exception
+      LOG.debug("Rollback of insertIntoNSTable throws exception: " + e);
+    }
+  }
+
+  /**
+   * Update Zookeeper.
+   * @param env MasterProcedureEnv
+   * @param nsDescriptor NamespaceDescriptor
+   * @throws IOException
+   */
+  protected static void updateZKNamespaceManager(
+      final MasterProcedureEnv env,
+      final NamespaceDescriptor nsDescriptor) throws IOException {
+    getTableNamespaceManager(env).updateZKNamespaceManager(nsDescriptor);
+  }
+
+  /**
+   * rollback Zookeeper update.
+   * @param env MasterProcedureEnv
+   * @throws IOException
+   */
+  private void rollbackZKNamespaceManagerChange(final MasterProcedureEnv env) throws IOException {
+    try {
+      DeleteNamespaceProcedure.removeFromZKNamespaceManager(env, nsDescriptor.getName());
+    } catch (Exception e) {
+      // Ignore exception
+      LOG.debug("Rollback of updateZKNamespaceManager throws exception: " + e);
+    }
+  }
+
+  /**
+   * Set quota for the namespace
+   * @param env MasterProcedureEnv
+   * @param nsDescriptor NamespaceDescriptor
+   * @throws IOException
+   **/
+  protected static void setNamespaceQuota(
+      final MasterProcedureEnv env,
+      final NamespaceDescriptor nsDescriptor) throws IOException {
+    if (env.getMasterServices().isInitialized()) {
+      env.getMasterServices().getMasterQuotaManager().setNamespaceQuota(nsDescriptor);
+    }
+  }
+
+  /**
+   * remove quota for the namespace if exists
+   * @param env MasterProcedureEnv
+   * @throws IOException
+   **/
+  private void rollbackSetNamespaceQuota(final MasterProcedureEnv env) throws IOException {
+    try {
+      DeleteNamespaceProcedure.removeNamespaceQuota(env, nsDescriptor.getName());
+    } catch (Exception e) {
+      // Ignore exception
+      LOG.debug("Rollback of setNamespaceQuota throws exception: " + e);
+    }
+  }
+
+  private static TableNamespaceManager getTableNamespaceManager(final MasterProcedureEnv env) {
+    return env.getMasterServices().getTableNamespaceManager();
+  }
+
+  /**
+   * The procedure could be restarted from a different machine. If the variable is null, we need to
+   * retrieve it.
+   * @return traceEnabled
+   */
+  private Boolean isTraceEnabled() {
+    if (traceEnabled == null) {
+      traceEnabled = LOG.isTraceEnabled();
+    }
+    return traceEnabled;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hbase/blob/dc79b3c5/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/DeleteNamespaceProcedure.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/DeleteNamespaceProcedure.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/DeleteNamespaceProcedure.java
new file mode 100644
index 0000000..8715b0b
--- /dev/null
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/DeleteNamespaceProcedure.java
@@ -0,0 +1,398 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.master.procedure;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.fs.FileStatus;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.NamespaceNotFoundException;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.constraint.ConstraintException;
+import org.apache.hadoop.hbase.master.MasterFileSystem;
+import org.apache.hadoop.hbase.master.TableNamespaceManager;
+import org.apache.hadoop.hbase.procedure2.StateMachineProcedure;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.DeleteNamespaceState;
+import org.apache.hadoop.hbase.util.FSUtils;
+
+/**
+ * The procedure to remove a namespace.
+ */
+@InterfaceAudience.Private
+public class DeleteNamespaceProcedure
+    extends StateMachineProcedure<MasterProcedureEnv, DeleteNamespaceState>
+    implements TableProcedureInterface {
+  private static final Log LOG = LogFactory.getLog(DeleteNamespaceProcedure.class);
+
+  private final AtomicBoolean aborted = new AtomicBoolean(false);
+
+  private NamespaceDescriptor nsDescriptor;
+  private String namespaceName;
+  private Boolean traceEnabled;
+
+  public DeleteNamespaceProcedure() {
+    this.nsDescriptor = null;
+    this.traceEnabled = null;
+  }
+
+  public DeleteNamespaceProcedure(
+      final MasterProcedureEnv env,
+      final String namespaceName) throws IOException {
+    this.namespaceName = namespaceName;
+    this.nsDescriptor = null;
+    this.traceEnabled = null;
+  }
+
+  @Override
+  protected Flow executeFromState(final MasterProcedureEnv env, final DeleteNamespaceState state)
+      throws InterruptedException {
+    if (isTraceEnabled()) {
+      LOG.trace(this + " execute state=" + state);
+    }
+
+    try {
+      switch (state) {
+      case DELETE_NAMESPACE_PREPARE:
+        prepareDelete(env);
+        setNextState(DeleteNamespaceState.DELETE_NAMESPACE_DELETE_FROM_NS_TABLE);
+        break;
+      case DELETE_NAMESPACE_DELETE_FROM_NS_TABLE:
+        deleteFromNSTable(env, namespaceName);
+        setNextState(DeleteNamespaceState.DELETE_NAMESPACE_REMOVE_FROM_ZK);
+        break;
+      case DELETE_NAMESPACE_REMOVE_FROM_ZK:
+        removeFromZKNamespaceManager(env, namespaceName);
+        setNextState(DeleteNamespaceState.DELETE_NAMESPACE_DELETE_DIRECTORIES);
+        break;
+      case DELETE_NAMESPACE_DELETE_DIRECTORIES:
+        deleteDirectory(env, namespaceName);
+        setNextState(DeleteNamespaceState.DELETE_NAMESPACE_REMOVE_NAMESPACE_QUOTA);
+        break;
+      case DELETE_NAMESPACE_REMOVE_NAMESPACE_QUOTA:
+        removeNamespaceQuota(env, namespaceName);
+        return Flow.NO_MORE_STATE;
+      default:
+        throw new UnsupportedOperationException(this + " unhandled state=" + state);
+      }
+    } catch (IOException e) {
+      LOG.warn("Error trying to delete the namespace" + namespaceName
+        + " (in state=" + state + ")", e);
+
+      setFailure("master-delete-namespace", e);
+    }
+    return Flow.HAS_MORE_STATE;
+  }
+
+  @Override
+  protected void rollbackState(final MasterProcedureEnv env, final DeleteNamespaceState state)
+      throws IOException {
+    if (isTraceEnabled()) {
+      LOG.trace(this + " rollback state=" + state);
+    }
+    try {
+      switch (state) {
+      case DELETE_NAMESPACE_REMOVE_NAMESPACE_QUOTA:
+        rollbacRemoveNamespaceQuota(env);
+        break;
+      case DELETE_NAMESPACE_DELETE_DIRECTORIES:
+        rollbackDeleteDirectory(env);
+        break;
+      case DELETE_NAMESPACE_REMOVE_FROM_ZK:
+        undoRemoveFromZKNamespaceManager(env);
+        break;
+      case DELETE_NAMESPACE_DELETE_FROM_NS_TABLE:
+        undoDeleteFromNSTable(env);
+        break;
+      case DELETE_NAMESPACE_PREPARE:
+        break; // nothing to do
+      default:
+        throw new UnsupportedOperationException(this + " unhandled state=" + state);
+      }
+    } catch (IOException e) {
+      // This will be retried. Unless there is a bug in the code,
+      // this should be just a "temporary error" (e.g. network down)
+      LOG.warn("Failed rollback attempt step " + state + " for deleting the namespace "
+        + namespaceName, e);
+      throw e;
+    }
+  }
+
+  @Override
+  protected DeleteNamespaceState getState(final int stateId) {
+    return DeleteNamespaceState.valueOf(stateId);
+  }
+
+  @Override
+  protected int getStateId(final DeleteNamespaceState state) {
+    return state.getNumber();
+  }
+
+  @Override
+  protected DeleteNamespaceState getInitialState() {
+    return DeleteNamespaceState.DELETE_NAMESPACE_PREPARE;
+  }
+
+  @Override
+  protected void setNextState(DeleteNamespaceState state) {
+    if (aborted.get()) {
+      setAbortFailure("delete-namespace", "abort requested");
+    } else {
+      super.setNextState(state);
+    }
+  }
+
+  @Override
+  public boolean abort(final MasterProcedureEnv env) {
+    aborted.set(true);
+    return true;
+  }
+
+  @Override
+  public void serializeStateData(final OutputStream stream) throws IOException {
+    super.serializeStateData(stream);
+
+    MasterProcedureProtos.DeleteNamespaceStateData.Builder deleteNamespaceMsg =
+        MasterProcedureProtos.DeleteNamespaceStateData.newBuilder().setNamespaceName(namespaceName);
+    if (this.nsDescriptor != null) {
+      deleteNamespaceMsg.setNamespaceDescriptor(
+        ProtobufUtil.toProtoNamespaceDescriptor(this.nsDescriptor));
+    }
+    deleteNamespaceMsg.build().writeDelimitedTo(stream);
+  }
+
+  @Override
+  public void deserializeStateData(final InputStream stream) throws IOException {
+    super.deserializeStateData(stream);
+
+    MasterProcedureProtos.DeleteNamespaceStateData deleteNamespaceMsg =
+        MasterProcedureProtos.DeleteNamespaceStateData.parseDelimitedFrom(stream);
+    namespaceName = deleteNamespaceMsg.getNamespaceName();
+    if (deleteNamespaceMsg.hasNamespaceDescriptor()) {
+      nsDescriptor =
+          ProtobufUtil.toNamespaceDescriptor(deleteNamespaceMsg.getNamespaceDescriptor());
+    }
+  }
+
+  @Override
+  public void toStringClassDetails(StringBuilder sb) {
+    sb.append(getClass().getSimpleName());
+    sb.append(" (Namespace=");
+    sb.append(namespaceName);
+    sb.append(")");
+  }
+
+  @Override
+  protected boolean acquireLock(final MasterProcedureEnv env) {
+    return getTableNamespaceManager(env).acquireExclusiveLock();
+  }
+
+  @Override
+  protected void releaseLock(final MasterProcedureEnv env) {
+    getTableNamespaceManager(env).releaseExclusiveLock();
+  }
+
+  @Override
+  public TableName getTableName() {
+    return TableName.NAMESPACE_TABLE_NAME;
+  }
+
+  @Override
+  public TableOperationType getTableOperationType() {
+    return TableOperationType.EDIT;
+  }
+
+  /**
+   * Action before any real action of deleting namespace.
+   * @param env MasterProcedureEnv
+   * @throws IOException
+   */
+  private void prepareDelete(final MasterProcedureEnv env) throws IOException {
+    if (getTableNamespaceManager(env).doesNamespaceExist(namespaceName) == false) {
+      throw new NamespaceNotFoundException(namespaceName);
+    }
+    if (NamespaceDescriptor.RESERVED_NAMESPACES.contains(namespaceName)) {
+      throw new ConstraintException("Reserved namespace "+ namespaceName +" cannot be removed.");
+    }
+
+    int tableCount = 0;
+    try {
+      tableCount = env.getMasterServices().listTableDescriptorsByNamespace(namespaceName).size();
+    } catch (FileNotFoundException fnfe) {
+      throw new NamespaceNotFoundException(namespaceName);
+    }
+    if (tableCount > 0) {
+      throw new ConstraintException("Only empty namespaces can be removed. " +
+          "Namespace "+ namespaceName + " has "+ tableCount +" tables");
+    }
+
+    // This is used for rollback
+    nsDescriptor = getTableNamespaceManager(env).get(namespaceName);
+  }
+
+  /**
+   * delete the row from namespace table
+   * @param env MasterProcedureEnv
+   * @param namespaceName name of the namespace in string format
+   * @throws IOException
+   */
+  protected static void deleteFromNSTable(
+      final MasterProcedureEnv env,
+      final String namespaceName) throws IOException {
+    getTableNamespaceManager(env).removeFromNSTable(namespaceName);
+  }
+
+  /**
+   * undo the delete
+   * @param env MasterProcedureEnv
+   * @throws IOException
+   */
+  private void undoDeleteFromNSTable(final MasterProcedureEnv env) {
+    try {
+      if (nsDescriptor != null) {
+        CreateNamespaceProcedure.insertIntoNSTable(env, nsDescriptor);
+      }
+    } catch (Exception e) {
+      // Ignore
+      LOG.debug("Rollback of deleteFromNSTable throws exception: " + e);
+    }
+  }
+
+  /**
+   * remove from Zookeeper.
+   * @param env MasterProcedureEnv
+   * @param namespaceName name of the namespace in string format
+   * @throws IOException
+   */
+  protected static void removeFromZKNamespaceManager(
+      final MasterProcedureEnv env,
+      final String namespaceName) throws IOException {
+    getTableNamespaceManager(env).removeFromZKNamespaceManager(namespaceName);
+  }
+
+  /**
+   * undo the remove from Zookeeper
+   * @param env MasterProcedureEnv
+   * @throws IOException
+   */
+  private void undoRemoveFromZKNamespaceManager(final MasterProcedureEnv env) {
+    try {
+      if (nsDescriptor != null) {
+        CreateNamespaceProcedure.updateZKNamespaceManager(env, nsDescriptor);
+      }
+    } catch (Exception e) {
+      // Ignore
+      LOG.debug("Rollback of removeFromZKNamespaceManager throws exception: " + e);
+    }
+  }
+
+  /**
+   * Delete the namespace directories from the file system
+   * @param env MasterProcedureEnv
+   * @param namespaceName name of the namespace in string format
+   * @throws IOException
+   */
+  protected static void deleteDirectory(
+      final MasterProcedureEnv env,
+      final String namespaceName) throws IOException {
+    MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem();
+    FileSystem fs = mfs.getFileSystem();
+    Path p = FSUtils.getNamespaceDir(mfs.getRootDir(), namespaceName);
+
+    try {
+      for(FileStatus status : fs.listStatus(p)) {
+        if (!HConstants.HBASE_NON_TABLE_DIRS.contains(status.getPath().getName())) {
+          throw new IOException("Namespace directory contains table dir: " + status.getPath());
+        }
+      }
+      if (!fs.delete(FSUtils.getNamespaceDir(mfs.getRootDir(), namespaceName), true)) {
+        throw new IOException("Failed to remove namespace: " + namespaceName);
+      }
+    } catch (FileNotFoundException e) {
+      // File already deleted, continue
+      LOG.debug("deleteDirectory throws exception: " + e);
+    }
+  }
+
+  /**
+   * undo delete directory
+   * @param env MasterProcedureEnv
+   * @throws IOException
+   */
+  private void rollbackDeleteDirectory(final MasterProcedureEnv env) throws IOException {
+    try {
+      CreateNamespaceProcedure.createDirectory(env, nsDescriptor);
+    } catch (Exception e) {
+      // Ignore exception
+      LOG.debug("Rollback of deleteDirectory throws exception: " + e);
+    }
+  }
+
+  /**
+   * remove quota for the namespace
+   * @param env MasterProcedureEnv
+   * @param namespaceName name of the namespace in string format
+   * @throws IOException
+   **/
+  protected static void removeNamespaceQuota(
+      final MasterProcedureEnv env,
+      final String namespaceName) throws IOException {
+    env.getMasterServices().getMasterQuotaManager().removeNamespaceQuota(namespaceName);
+  }
+
+  /**
+   * undo remove quota for the namespace
+   * @param env MasterProcedureEnv
+   * @throws IOException
+   **/
+  private void rollbacRemoveNamespaceQuota(final MasterProcedureEnv env) throws IOException {
+    try {
+      CreateNamespaceProcedure.setNamespaceQuota(env, nsDescriptor);
+    } catch (Exception e) {
+      // Ignore exception
+      LOG.debug("Rollback of removeNamespaceQuota throws exception: " + e);
+    }
+  }
+
+  private static TableNamespaceManager getTableNamespaceManager(final MasterProcedureEnv env) {
+    return env.getMasterServices().getTableNamespaceManager();
+  }
+  /**
+   * The procedure could be restarted from a different machine. If the variable is null, we need to
+   * retrieve it.
+   * @return traceEnabled
+   */
+  private Boolean isTraceEnabled() {
+    if (traceEnabled == null) {
+      traceEnabled = LOG.isTraceEnabled();
+    }
+    return traceEnabled;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hbase/blob/dc79b3c5/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyNamespaceProcedure.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyNamespaceProcedure.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyNamespaceProcedure.java
new file mode 100644
index 0000000..ba5c0ad
--- /dev/null
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyNamespaceProcedure.java
@@ -0,0 +1,281 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.master.procedure;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.NamespaceNotFoundException;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.master.TableNamespaceManager;
+import org.apache.hadoop.hbase.procedure2.StateMachineProcedure;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.ModifyNamespaceState;
+
+/**
+ * The procedure to add a namespace to an existing table.
+ */
+@InterfaceAudience.Private
+public class ModifyNamespaceProcedure
+    extends StateMachineProcedure<MasterProcedureEnv, ModifyNamespaceState>
+    implements TableProcedureInterface {
+  private static final Log LOG = LogFactory.getLog(ModifyNamespaceProcedure.class);
+
+  private final AtomicBoolean aborted = new AtomicBoolean(false);
+
+  private NamespaceDescriptor oldNsDescriptor;
+  private NamespaceDescriptor newNsDescriptor;
+  private Boolean traceEnabled;
+
+  public ModifyNamespaceProcedure() {
+    this.oldNsDescriptor = null;
+    this.traceEnabled = null;
+  }
+
+  public ModifyNamespaceProcedure(
+      final MasterProcedureEnv env,
+      final NamespaceDescriptor newNsDescriptor) throws IOException {
+    this.oldNsDescriptor = null;
+    this.newNsDescriptor = newNsDescriptor;
+    this.traceEnabled = null;
+  }
+
+  @Override
+  protected Flow executeFromState(final MasterProcedureEnv env, final ModifyNamespaceState state)
+      throws InterruptedException {
+    if (isTraceEnabled()) {
+      LOG.trace(this + " execute state=" + state);
+    }
+
+    try {
+      switch (state) {
+      case MODIFY_NAMESPACE_PREPARE:
+        prepareModify(env);
+        setNextState(ModifyNamespaceState.MODIFY_NAMESPACE_UPDATE_NS_TABLE);
+        break;
+      case MODIFY_NAMESPACE_UPDATE_NS_TABLE:
+        insertIntoNSTable(env);
+        setNextState(ModifyNamespaceState.MODIFY_NAMESPACE_UPDATE_ZK);
+        break;
+      case MODIFY_NAMESPACE_UPDATE_ZK:
+        updateZKNamespaceManager(env);
+        return Flow.NO_MORE_STATE;
+      default:
+        throw new UnsupportedOperationException(this + " unhandled state=" + state);
+      }
+    } catch (IOException e) {
+      LOG.warn("Error trying to modify the namespace" + newNsDescriptor.getName()
+        + " (in state=" + state + ")", e);
+
+      setFailure("master-modify-namespace", e);
+    }
+    return Flow.HAS_MORE_STATE;
+  }
+
+  @Override
+  protected void rollbackState(final MasterProcedureEnv env, final ModifyNamespaceState state)
+      throws IOException {
+    if (isTraceEnabled()) {
+      LOG.trace(this + " rollback state=" + state);
+    }
+    try {
+      switch (state) {
+      case MODIFY_NAMESPACE_UPDATE_ZK:
+        rollbackZKNamespaceManagerChange(env);
+        break;
+      case MODIFY_NAMESPACE_UPDATE_NS_TABLE:
+        rollbackUpdateInNSTable(env);
+        break;
+      case MODIFY_NAMESPACE_PREPARE:
+        break; // nothing to do
+      default:
+        throw new UnsupportedOperationException(this + " unhandled state=" + state);
+      }
+    } catch (IOException e) {
+      // This will be retried. Unless there is a bug in the code,
+      // this should be just a "temporary error" (e.g. network down)
+      LOG.warn("Failed rollback attempt step " + state + " for creating the namespace "
+          + newNsDescriptor.getName(), e);
+      throw e;
+    }
+  }
+
+  @Override
+  protected ModifyNamespaceState getState(final int stateId) {
+    return ModifyNamespaceState.valueOf(stateId);
+  }
+
+  @Override
+  protected int getStateId(final ModifyNamespaceState state) {
+    return state.getNumber();
+  }
+
+  @Override
+  protected ModifyNamespaceState getInitialState() {
+    return ModifyNamespaceState.MODIFY_NAMESPACE_PREPARE;
+  }
+
+  @Override
+  protected void setNextState(ModifyNamespaceState state) {
+    if (aborted.get()) {
+      setAbortFailure("modify-namespace", "abort requested");
+    } else {
+      super.setNextState(state);
+    }
+  }
+
+  @Override
+  public boolean abort(final MasterProcedureEnv env) {
+    aborted.set(true);
+    return true;
+  }
+
+  @Override
+  public void serializeStateData(final OutputStream stream) throws IOException {
+    super.serializeStateData(stream);
+
+    MasterProcedureProtos.ModifyNamespaceStateData.Builder modifyNamespaceMsg =
+        MasterProcedureProtos.ModifyNamespaceStateData.newBuilder().setNamespaceDescriptor(
+          ProtobufUtil.toProtoNamespaceDescriptor(this.newNsDescriptor));
+    if (this.oldNsDescriptor != null) {
+      modifyNamespaceMsg.setUnmodifiedNamespaceDescriptor(
+        ProtobufUtil.toProtoNamespaceDescriptor(this.oldNsDescriptor));
+    }
+    modifyNamespaceMsg.build().writeDelimitedTo(stream);
+  }
+
+  @Override
+  public void deserializeStateData(final InputStream stream) throws IOException {
+    super.deserializeStateData(stream);
+
+    MasterProcedureProtos.ModifyNamespaceStateData modifyNamespaceMsg =
+        MasterProcedureProtos.ModifyNamespaceStateData.parseDelimitedFrom(stream);
+    newNsDescriptor =
+        ProtobufUtil.toNamespaceDescriptor(modifyNamespaceMsg.getNamespaceDescriptor());
+    if (modifyNamespaceMsg.hasUnmodifiedNamespaceDescriptor()) {
+      oldNsDescriptor =
+          ProtobufUtil.toNamespaceDescriptor(modifyNamespaceMsg.getUnmodifiedNamespaceDescriptor());
+    }
+  }
+
+  @Override
+  public void toStringClassDetails(StringBuilder sb) {
+    sb.append(getClass().getSimpleName());
+    sb.append(" (Namespace=");
+    sb.append(newNsDescriptor.getName());
+    sb.append(")");
+  }
+
+  @Override
+  protected boolean acquireLock(final MasterProcedureEnv env) {
+    return getTableNamespaceManager(env).acquireExclusiveLock();
+  }
+
+  @Override
+  protected void releaseLock(final MasterProcedureEnv env) {
+    getTableNamespaceManager(env).releaseExclusiveLock();
+  }
+
+  @Override
+  public TableName getTableName() {
+    return TableName.NAMESPACE_TABLE_NAME;
+  }
+
+  @Override
+  public TableOperationType getTableOperationType() {
+    return TableOperationType.EDIT;
+  }
+
+  /**
+   * Action before any real action of adding namespace.
+   * @param env MasterProcedureEnv
+   * @throws IOException
+   */
+  private void prepareModify(final MasterProcedureEnv env) throws IOException {
+    if (getTableNamespaceManager(env).doesNamespaceExist(newNsDescriptor.getName()) == false) {
+      throw new NamespaceNotFoundException(newNsDescriptor.getName());
+    }
+    getTableNamespaceManager(env).validateTableAndRegionCount(newNsDescriptor);
+
+    // This is used for rollback
+    oldNsDescriptor = getTableNamespaceManager(env).get(newNsDescriptor.getName());
+  }
+
+  /**
+   * Insert/update the row into namespace table
+   * @param env MasterProcedureEnv
+   * @throws IOException
+   */
+  private void insertIntoNSTable(final MasterProcedureEnv env) throws IOException {
+    getTableNamespaceManager(env).insertIntoNSTable(newNsDescriptor);
+  }
+
+  /**
+   * rollback the row into namespace table
+   * @param env MasterProcedureEnv
+   * @throws IOException
+   */
+  private void rollbackUpdateInNSTable(final MasterProcedureEnv env) throws IOException {
+    if (oldNsDescriptor != null) {
+      getTableNamespaceManager(env).insertIntoNSTable(oldNsDescriptor);
+    }
+  }
+
+  /**
+   * Update Zookeeper.
+   * @param env MasterProcedureEnv
+   * @throws IOException
+   */
+  private void updateZKNamespaceManager(final MasterProcedureEnv env) throws IOException {
+    getTableNamespaceManager(env).updateZKNamespaceManager(newNsDescriptor);
+  }
+
+  /**
+   * Update Zookeeper during undo.
+   * @param env MasterProcedureEnv
+   * @throws IOException
+   */
+  private void rollbackZKNamespaceManagerChange(final MasterProcedureEnv env) throws IOException {
+    if (oldNsDescriptor != null) {
+      getTableNamespaceManager(env).updateZKNamespaceManager(oldNsDescriptor);
+    }
+  }
+
+  private TableNamespaceManager getTableNamespaceManager(final MasterProcedureEnv env) {
+    return env.getMasterServices().getTableNamespaceManager();
+  }
+  /**
+   * The procedure could be restarted from a different machine. If the variable is null, we need to
+   * retrieve it.
+   * @return traceEnabled
+   */
+  private Boolean isTraceEnabled() {
+    if (traceEnabled == null) {
+      traceEnabled = LOG.isTraceEnabled();
+    }
+    return traceEnabled;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hbase/blob/dc79b3c5/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java
index bc9af45..33a64a5 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java
@@ -395,17 +395,34 @@ public class TestCatalogJanitor {
     }
 
     @Override
-    public void createNamespace(NamespaceDescriptor descriptor) throws IOException {
+    public void createNamespace(
+        final NamespaceDescriptor descriptor,
+        final long nonceGroup,
+        final long nonce) throws IOException {
+      //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    @Override
+    public void createNamespaceSync(
+        final NamespaceDescriptor descriptor,
+        final long nonceGroup,
+        final long nonce) throws IOException {
       //To change body of implemented methods use File | Settings | File Templates.
     }
 
     @Override
-    public void modifyNamespace(NamespaceDescriptor descriptor) throws IOException {
+    public void modifyNamespace(
+        final NamespaceDescriptor descriptor,
+        final long nonceGroup,
+        final long nonce) throws IOException {
       //To change body of implemented methods use File | Settings | File Templates.
     }
 
     @Override
-    public void deleteNamespace(String name) throws IOException {
+    public void deleteNamespace(
+        final String name,
+        final long nonceGroup,
+        final long nonce) throws IOException {
       //To change body of implemented methods use File | Settings | File Templates.
     }
 
@@ -504,6 +521,11 @@ public class TestCatalogJanitor {
     }
 
     @Override
+    public TableNamespaceManager getTableNamespaceManager() {
+      return null;
+    }
+
+    @Override
     public void dispatchMergingRegions(HRegionInfo region_a, HRegionInfo region_b,
         boolean forcible) throws IOException {
     }

http://git-wip-us.apache.org/repos/asf/hbase/blob/dc79b3c5/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/MasterProcedureTestingUtility.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/MasterProcedureTestingUtility.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/MasterProcedureTestingUtility.java
index 394e339..b3f29db 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/MasterProcedureTestingUtility.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/MasterProcedureTestingUtility.java
@@ -38,7 +38,6 @@ import org.apache.hadoop.hbase.TableDescriptor;
 import org.apache.hadoop.hbase.client.BufferedMutator;
 import org.apache.hadoop.hbase.client.Connection;
 import org.apache.hadoop.hbase.client.Durability;
-import org.apache.hadoop.hbase.client.NonceGenerator;
 import org.apache.hadoop.hbase.client.Put;
 import org.apache.hadoop.hbase.client.Result;
 import org.apache.hadoop.hbase.client.TableState;
@@ -54,7 +53,6 @@ import org.apache.hadoop.hbase.util.MD5Hash;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 public class MasterProcedureTestingUtility {
   private static final Log LOG = LogFactory.getLog(MasterProcedureTestingUtility.class);

http://git-wip-us.apache.org/repos/asf/hbase/blob/dc79b3c5/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestCreateNamespaceProcedure.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestCreateNamespaceProcedure.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestCreateNamespaceProcedure.java
new file mode 100644
index 0000000..d25e61f
--- /dev/null
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestCreateNamespaceProcedure.java
@@ -0,0 +1,292 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.master.procedure;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.NamespaceExistException;
+import org.apache.hadoop.hbase.NamespaceNotFoundException;
+import org.apache.hadoop.hbase.constraint.ConstraintException;
+import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
+import org.apache.hadoop.hbase.procedure2.ProcedureResult;
+import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.CreateNamespaceState;
+import org.apache.hadoop.hbase.testclassification.MasterTests;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category({MasterTests.class, MediumTests.class})
+public class TestCreateNamespaceProcedure {
+  private static final Log LOG = LogFactory.getLog(TestCreateNamespaceProcedure.class);
+
+  protected static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
+
+  private static long nonceGroup = HConstants.NO_NONCE;
+  private static long nonce = HConstants.NO_NONCE;
+
+  private static void setupConf(Configuration conf) {
+    conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1);
+  }
+
+  @BeforeClass
+  public static void setupCluster() throws Exception {
+    setupConf(UTIL.getConfiguration());
+    UTIL.startMiniCluster(1);
+  }
+
+  @AfterClass
+  public static void cleanupTest() throws Exception {
+    try {
+      UTIL.shutdownMiniCluster();
+    } catch (Exception e) {
+      LOG.warn("failure shutting down cluster", e);
+    }
+  }
+
+  @Before
+  public void setup() throws Exception {
+    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false);
+    nonceGroup =
+        MasterProcedureTestingUtility.generateNonceGroup(UTIL.getHBaseCluster().getMaster());
+    nonce = MasterProcedureTestingUtility.generateNonce(UTIL.getHBaseCluster().getMaster());
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false);
+  }
+
+  @Test(timeout = 60000)
+  public void testCreateNamespace() throws Exception {
+    final NamespaceDescriptor nsd = NamespaceDescriptor.create("testCreateNamespace").build();
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    long procId = procExec.submitProcedure(
+      new CreateNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup,
+      nonce);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
+
+    validateNamespaceCreated(nsd);
+  }
+
+  @Test(timeout=60000)
+  public void testCreateSameNamespaceTwice() throws Exception {
+    final NamespaceDescriptor nsd =
+        NamespaceDescriptor.create("testCreateSameNamespaceTwice").build();
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    long procId1 = procExec.submitProcedure(
+      new CreateNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup,
+      nonce);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId1);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId1);
+
+    // Create the namespace that exists
+    long procId2 = procExec.submitProcedure(
+      new CreateNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup + 1,
+      nonce + 1);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId2);
+
+    // Second create should fail with NamespaceExistException
+    ProcedureResult result = procExec.getResult(procId2);
+    assertTrue(result.isFailed());
+    LOG.debug("Create namespace failed with exception: " + result.getException());
+    assertTrue(result.getException().getCause() instanceof NamespaceExistException);
+  }
+
+  @Test(timeout=60000)
+  public void testCreateSystemNamespace() throws Exception {
+    final NamespaceDescriptor nsd =
+        UTIL.getHBaseAdmin().getNamespaceDescriptor(NamespaceDescriptor.SYSTEM_NAMESPACE.getName());
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    long procId = procExec.submitProcedure(
+      new CreateNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup,
+      nonce);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId);
+    ProcedureResult result = procExec.getResult(procId);
+    assertTrue(result.isFailed());
+    LOG.debug("Create namespace failed with exception: " + result.getException());
+    assertTrue(result.getException().getCause() instanceof NamespaceExistException);
+  }
+
+  @Test(timeout=60000)
+  public void testCreateNamespaceWithInvalidRegionCount() throws Exception {
+    final NamespaceDescriptor nsd =
+        NamespaceDescriptor.create("testCreateNamespaceWithInvalidRegionCount").build();
+    final String nsKey = "hbase.namespace.quota.maxregions";
+    final String nsValue = "-1";
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    nsd.setConfiguration(nsKey, nsValue);
+
+    long procId = procExec.submitProcedure(
+      new CreateNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup,
+      nonce);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId);
+    ProcedureResult result = procExec.getResult(procId);
+    assertTrue(result.isFailed());
+    LOG.debug("Create namespace failed with exception: " + result.getException());
+    assertTrue(result.getException().getCause() instanceof ConstraintException);
+  }
+
+  @Test(timeout=60000)
+  public void testCreateNamespaceWithInvalidTableCount() throws Exception {
+    final NamespaceDescriptor nsd =
+        NamespaceDescriptor.create("testCreateNamespaceWithInvalidTableCount").build();
+    final String nsKey = "hbase.namespace.quota.maxtables";
+    final String nsValue = "-1";
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    nsd.setConfiguration(nsKey, nsValue);
+
+    long procId = procExec.submitProcedure(
+      new CreateNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup,
+      nonce);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId);
+    ProcedureResult result = procExec.getResult(procId);
+    assertTrue(result.isFailed());
+    LOG.debug("Create namespace failed with exception: " + result.getException());
+    assertTrue(result.getException().getCause() instanceof ConstraintException);
+  }
+
+  @Test(timeout=60000)
+  public void testCreateSameNamespaceTwiceWithSameNonce() throws Exception {
+    final NamespaceDescriptor nsd =
+        NamespaceDescriptor.create("testCreateSameNamespaceTwiceWithSameNonce").build();
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    long procId1 = procExec.submitProcedure(
+      new CreateNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup,
+      nonce);
+    long procId2 = procExec.submitProcedure(
+      new CreateNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup,
+      nonce);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId1);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId1);
+
+    validateNamespaceCreated(nsd);
+
+    // Wait the completion and expect not fail - because it is the same proc
+    ProcedureTestingUtility.waitProcedure(procExec, procId2);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId2);
+    assertTrue(procId1 == procId2);
+  }
+
+  @Test(timeout = 60000)
+  public void testRecoveryAndDoubleExecution() throws Exception {
+    final NamespaceDescriptor nsd =
+        NamespaceDescriptor.create("testRecoveryAndDoubleExecution").build();
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    ProcedureTestingUtility.waitNoProcedureRunning(procExec);
+    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
+
+    // Start the CreateNamespace procedure && kill the executor
+    long procId = procExec.submitProcedure(
+      new CreateNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup,
+      nonce);
+
+    // Restart the executor and execute the step twice
+    int numberOfSteps = CreateNamespaceState.values().length;
+    MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(
+      procExec,
+      procId,
+      numberOfSteps,
+      CreateNamespaceState.values());
+
+    // Validate the creation of namespace
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
+    validateNamespaceCreated(nsd);
+  }
+
+  @Test(timeout = 60000)
+  public void testRollbackAndDoubleExecution() throws Exception {
+    final NamespaceDescriptor nsd =
+        NamespaceDescriptor.create("testRollbackAndDoubleExecution").build();
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    ProcedureTestingUtility.waitNoProcedureRunning(procExec);
+    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
+
+    // Start the CreateNamespace procedure && kill the executor
+    long procId = procExec.submitProcedure(
+      new CreateNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup,
+      nonce);
+
+    int numberOfSteps = CreateNamespaceState.values().length - 2; // failing in the middle of proc
+    MasterProcedureTestingUtility.testRollbackAndDoubleExecution(
+      procExec,
+      procId,
+      numberOfSteps,
+      CreateNamespaceState.values());
+
+    // Validate the non-existence of namespace
+    try {
+      NamespaceDescriptor nsDescriptor = UTIL.getHBaseAdmin().getNamespaceDescriptor(nsd.getName());
+      assertNull(nsDescriptor);
+    } catch (NamespaceNotFoundException nsnfe) {
+      // Expected
+      LOG.info("The namespace " + nsd.getName() + " is not created.");
+    }
+  }
+
+  private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
+    return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
+  }
+
+  private void validateNamespaceCreated(NamespaceDescriptor nsd) throws IOException {
+    NamespaceDescriptor createdNsDescriptor =
+        UTIL.getHBaseAdmin().getNamespaceDescriptor(nsd.getName());
+    assertNotNull(createdNsDescriptor);
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/dc79b3c5/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestDeleteNamespaceProcedure.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestDeleteNamespaceProcedure.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestDeleteNamespaceProcedure.java
new file mode 100644
index 0000000..dd22de7
--- /dev/null
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestDeleteNamespaceProcedure.java
@@ -0,0 +1,282 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.master.procedure;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.NamespaceNotFoundException;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.constraint.ConstraintException;
+import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
+import org.apache.hadoop.hbase.procedure2.ProcedureResult;
+import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.DeleteNamespaceState;
+import org.apache.hadoop.hbase.testclassification.MasterTests;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category({MasterTests.class, MediumTests.class})
+public class TestDeleteNamespaceProcedure {
+  private static final Log LOG = LogFactory.getLog(TestDeleteNamespaceProcedure.class);
+
+  protected static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
+
+  private static long nonceGroup = HConstants.NO_NONCE;
+  private static long nonce = HConstants.NO_NONCE;
+
+  private static void setupConf(Configuration conf) {
+    conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1);
+  }
+
+  @BeforeClass
+  public static void setupCluster() throws Exception {
+    setupConf(UTIL.getConfiguration());
+    UTIL.startMiniCluster(1);
+  }
+
+  @AfterClass
+  public static void cleanupTest() throws Exception {
+    try {
+      UTIL.shutdownMiniCluster();
+    } catch (Exception e) {
+      LOG.warn("failure shutting down cluster", e);
+    }
+  }
+
+  @Before
+  public void setup() throws Exception {
+    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false);
+    nonceGroup =
+        MasterProcedureTestingUtility.generateNonceGroup(UTIL.getHBaseCluster().getMaster());
+    nonce = MasterProcedureTestingUtility.generateNonce(UTIL.getHBaseCluster().getMaster());
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false);
+    for (HTableDescriptor htd: UTIL.getHBaseAdmin().listTables()) {
+      LOG.info("Tear down, remove table=" + htd.getTableName());
+      UTIL.deleteTable(htd.getTableName());
+    }
+  }
+
+  @Test(timeout = 60000)
+  public void testDeleteNamespace() throws Exception {
+    final String namespaceName = "testDeleteNamespace";
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    createNamespaceForTesting(namespaceName);
+
+    long procId = procExec.submitProcedure(
+      new DeleteNamespaceProcedure(procExec.getEnvironment(), namespaceName),
+      nonceGroup,
+      nonce);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
+
+    validateNamespaceNotExist(namespaceName);
+  }
+
+  @Test(timeout=60000)
+  public void testDeleteNonExistNamespace() throws Exception {
+    final String namespaceName = "testDeleteNonExistNamespace";
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    validateNamespaceNotExist(namespaceName);
+
+    long procId = procExec.submitProcedure(
+      new DeleteNamespaceProcedure(procExec.getEnvironment(), namespaceName),
+      nonceGroup,
+      nonce);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId);
+    // Expect fail with NamespaceNotFoundException
+    ProcedureResult result = procExec.getResult(procId);
+    assertTrue(result.isFailed());
+    LOG.debug("Delete namespace failed with exception: " + result.getException());
+    assertTrue(result.getException().getCause() instanceof NamespaceNotFoundException);
+  }
+
+  @Test(timeout=60000)
+  public void testDeleteSystemNamespace() throws Exception {
+    final String namespaceName = NamespaceDescriptor.SYSTEM_NAMESPACE.getName();
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    long procId = procExec.submitProcedure(
+      new DeleteNamespaceProcedure(procExec.getEnvironment(), namespaceName),
+      nonceGroup,
+      nonce);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId);
+    ProcedureResult result = procExec.getResult(procId);
+    assertTrue(result.isFailed());
+    LOG.debug("Delete namespace failed with exception: " + result.getException());
+    assertTrue(result.getException().getCause() instanceof ConstraintException);
+  }
+
+  @Test(timeout=60000)
+  public void testDeleteNonEmptyNamespace() throws Exception {
+    final String namespaceName = "testDeleteNonExistNamespace";
+    final TableName tableName = TableName.valueOf("testDeleteNonExistNamespace:t1");
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+    // create namespace
+    createNamespaceForTesting(namespaceName);
+    // create the table under the new namespace
+    MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f1");
+
+    long procId = procExec.submitProcedure(
+      new DeleteNamespaceProcedure(procExec.getEnvironment(), namespaceName),
+      nonceGroup,
+      nonce);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId);
+    ProcedureResult result = procExec.getResult(procId);
+    assertTrue(result.isFailed());
+    LOG.debug("Delete namespace failed with exception: " + result.getException());
+    assertTrue(result.getException().getCause() instanceof ConstraintException);
+  }
+
+  @Test(timeout=60000)
+  public void testDeleteSameNamespaceTwiceWithSameNonce() throws Exception {
+    final String namespaceName = "testDeleteSameNamespaceTwiceWithSameNonce";
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    createNamespaceForTesting(namespaceName);
+
+    long procId1 = procExec.submitProcedure(
+      new DeleteNamespaceProcedure(procExec.getEnvironment(), namespaceName),
+      nonceGroup,
+      nonce);
+    long procId2 = procExec.submitProcedure(
+      new DeleteNamespaceProcedure(procExec.getEnvironment(), namespaceName),
+      nonceGroup,
+      nonce);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId1);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId1);
+
+    validateNamespaceNotExist(namespaceName);
+
+    // Wait the completion and expect not fail - because it is the same proc
+    ProcedureTestingUtility.waitProcedure(procExec, procId2);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId2);
+    assertTrue(procId1 == procId2);
+  }
+
+  @Test(timeout = 60000)
+  public void testRecoveryAndDoubleExecution() throws Exception {
+    final String namespaceName = "testRecoveryAndDoubleExecution";
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    createNamespaceForTesting(namespaceName);
+
+    ProcedureTestingUtility.waitNoProcedureRunning(procExec);
+    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
+
+    // Start the DeleteNamespace procedure && kill the executor
+    long procId = procExec.submitProcedure(
+      new DeleteNamespaceProcedure(procExec.getEnvironment(), namespaceName),
+      nonceGroup,
+      nonce);
+
+    // Restart the executor and execute the step twice
+    int numberOfSteps = DeleteNamespaceState.values().length;
+    MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(
+      procExec,
+      procId,
+      numberOfSteps,
+      DeleteNamespaceState.values());
+
+    // Validate the deletion of namespace
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
+    validateNamespaceNotExist(namespaceName);
+  }
+
+  @Test(timeout = 60000)
+  public void testRollbackAndDoubleExecution() throws Exception {
+    final String namespaceName = "testRollbackAndDoubleExecution";
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    createNamespaceForTesting(namespaceName);
+
+    ProcedureTestingUtility.waitNoProcedureRunning(procExec);
+    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
+
+    // Start the DeleteNamespace procedure && kill the executor
+    long procId = procExec.submitProcedure(
+      new DeleteNamespaceProcedure(procExec.getEnvironment(), namespaceName),
+      nonceGroup,
+      nonce);
+
+    int numberOfSteps = DeleteNamespaceState.values().length - 2; // failing in the middle of proc
+    MasterProcedureTestingUtility.testRollbackAndDoubleExecution(
+      procExec,
+      procId,
+      numberOfSteps,
+      DeleteNamespaceState.values());
+
+    // Validate the namespace still exists
+    NamespaceDescriptor createdNsDescriptor=
+        UTIL.getHBaseAdmin().getNamespaceDescriptor(namespaceName);
+    assertNotNull(createdNsDescriptor);
+  }
+
+  private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
+    return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
+  }
+
+  private void createNamespaceForTesting(final String namespaceName) throws Exception {
+    final NamespaceDescriptor nsd = NamespaceDescriptor.create(namespaceName).build();
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    long procId = procExec.submitProcedure(
+      new CreateNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup + 1,
+      nonce + 1);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
+  }
+
+  public static void validateNamespaceNotExist(final String nsName) throws IOException {
+    try {
+      NamespaceDescriptor nsDescriptor = UTIL.getHBaseAdmin().getNamespaceDescriptor(nsName);
+      assertNull(nsDescriptor);
+    } catch (NamespaceNotFoundException nsnfe) {
+      // Expected
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/dc79b3c5/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestModifyNamespaceProcedure.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestModifyNamespaceProcedure.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestModifyNamespaceProcedure.java
new file mode 100644
index 0000000..e946043
--- /dev/null
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestModifyNamespaceProcedure.java
@@ -0,0 +1,295 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.master.procedure;
+
+
+import static org.junit.Assert.*;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.NamespaceNotFoundException;
+import org.apache.hadoop.hbase.constraint.ConstraintException;
+import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
+import org.apache.hadoop.hbase.procedure2.ProcedureResult;
+import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
+import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.ModifyNamespaceState;
+import org.apache.hadoop.hbase.testclassification.MasterTests;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category({MasterTests.class, MediumTests.class})
+public class TestModifyNamespaceProcedure {
+  private static final Log LOG = LogFactory.getLog(TestModifyNamespaceProcedure.class);
+
+  protected static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
+
+  private static long nonceGroup = HConstants.NO_NONCE;
+  private static long nonce = HConstants.NO_NONCE;
+
+  private static void setupConf(Configuration conf) {
+    conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1);
+  }
+
+  @BeforeClass
+  public static void setupCluster() throws Exception {
+    setupConf(UTIL.getConfiguration());
+    UTIL.startMiniCluster(1);
+  }
+
+  @AfterClass
+  public static void cleanupTest() throws Exception {
+    try {
+      UTIL.shutdownMiniCluster();
+    } catch (Exception e) {
+      LOG.warn("failure shutting down cluster", e);
+    }
+  }
+
+  @Before
+  public void setup() throws Exception {
+    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false);
+    nonceGroup =
+        MasterProcedureTestingUtility.generateNonceGroup(UTIL.getHBaseCluster().getMaster());
+    nonce = MasterProcedureTestingUtility.generateNonce(UTIL.getHBaseCluster().getMaster());
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false);
+    for (HTableDescriptor htd: UTIL.getHBaseAdmin().listTables()) {
+      LOG.info("Tear down, remove table=" + htd.getTableName());
+      UTIL.deleteTable(htd.getTableName());
+    }
+  }
+
+
+  @Test(timeout = 60000)
+  public void testModifyNamespace() throws Exception {
+    final NamespaceDescriptor nsd = NamespaceDescriptor.create("testModifyNamespace").build();
+    final String nsKey1 = "hbase.namespace.quota.maxregions";
+    final String nsValue1before = "1111";
+    final String nsValue1after = "9999";
+    final String nsKey2 = "hbase.namespace.quota.maxtables";
+    final String nsValue2 = "10";
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    nsd.setConfiguration(nsKey1, nsValue1before);
+    createNamespaceForTesting(nsd);
+
+    // Before modify
+    NamespaceDescriptor currentNsDescriptor =
+        UTIL.getHBaseAdmin().getNamespaceDescriptor(nsd.getName());
+    assertEquals(currentNsDescriptor.getConfigurationValue(nsKey1), nsValue1before);
+    assertNull(currentNsDescriptor.getConfigurationValue(nsKey2));
+
+    // Update
+    nsd.setConfiguration(nsKey1, nsValue1after);
+    nsd.setConfiguration(nsKey2, nsValue2);
+
+    long procId1 = procExec.submitProcedure(
+      new ModifyNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup,
+      nonce);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId1);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId1);
+
+    // Verify the namespace is updated.
+    currentNsDescriptor =
+        UTIL.getHBaseAdmin().getNamespaceDescriptor(nsd.getName());
+    assertEquals(nsd.getConfigurationValue(nsKey1), nsValue1after);
+    assertEquals(currentNsDescriptor.getConfigurationValue(nsKey2), nsValue2);
+  }
+
+  @Test(timeout=60000)
+  public void testModifyNonExistNamespace() throws Exception {
+    final String namespaceName = "testModifyNonExistNamespace";
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    try {
+      NamespaceDescriptor nsDescriptor = UTIL.getHBaseAdmin().getNamespaceDescriptor(namespaceName);
+      assertNull(nsDescriptor);
+    } catch (NamespaceNotFoundException nsnfe) {
+      // Expected
+      LOG.debug("The namespace " + namespaceName + " does not exist.  This is expected.");
+    }
+
+    final NamespaceDescriptor nsd = NamespaceDescriptor.create(namespaceName).build();
+
+    long procId = procExec.submitProcedure(
+      new ModifyNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup,
+      nonce);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId);
+
+    // Expect fail with NamespaceNotFoundException
+    ProcedureResult result = procExec.getResult(procId);
+    assertTrue(result.isFailed());
+    LOG.debug("modify namespace failed with exception: " + result.getException());
+    assertTrue(result.getException().getCause() instanceof NamespaceNotFoundException);
+  }
+
+  @Test(timeout=60000)
+  public void testModifyNamespaceWithInvalidRegionCount() throws Exception {
+    final NamespaceDescriptor nsd =
+        NamespaceDescriptor.create("testModifyNamespaceWithInvalidRegionCount").build();
+    final String nsKey = "hbase.namespace.quota.maxregions";
+    final String nsValue = "-1";
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    createNamespaceForTesting(nsd);
+
+    // Modify
+    nsd.setConfiguration(nsKey, nsValue);
+
+    long procId = procExec.submitProcedure(
+      new ModifyNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup,
+      nonce);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId);
+    ProcedureResult result = procExec.getResult(procId);
+    assertTrue(result.isFailed());
+    LOG.debug("Modify namespace failed with exception: " + result.getException());
+    assertTrue(result.getException().getCause() instanceof ConstraintException);
+  }
+
+  @Test(timeout=60000)
+  public void testModifyNamespaceWithInvalidTableCount() throws Exception {
+    final NamespaceDescriptor nsd =
+        NamespaceDescriptor.create("testModifyNamespaceWithInvalidTableCount").build();
+    final String nsKey = "hbase.namespace.quota.maxtables";
+    final String nsValue = "-1";
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    createNamespaceForTesting(nsd);
+
+    // Modify
+    nsd.setConfiguration(nsKey, nsValue);
+
+    long procId = procExec.submitProcedure(
+      new ModifyNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup,
+      nonce);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId);
+    ProcedureResult result = procExec.getResult(procId);
+    assertTrue(result.isFailed());
+    LOG.debug("Modify namespace failed with exception: " + result.getException());
+    assertTrue(result.getException().getCause() instanceof ConstraintException);
+  }
+
+  @Test(timeout = 60000)
+  public void testRecoveryAndDoubleExecution() throws Exception {
+    final NamespaceDescriptor nsd =
+        NamespaceDescriptor.create("testRecoveryAndDoubleExecution").build();
+    final String nsKey = "foo";
+    final String nsValue = "bar";
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    createNamespaceForTesting(nsd);
+    ProcedureTestingUtility.waitNoProcedureRunning(procExec);
+    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
+
+    // Modify
+    nsd.setConfiguration(nsKey, nsValue);
+
+    // Start the Modify procedure && kill the executor
+    long procId = procExec.submitProcedure(
+      new ModifyNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup,
+      nonce);
+
+    // Restart the executor and execute the step twice
+    int numberOfSteps = ModifyNamespaceState.values().length;
+    MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(
+      procExec,
+      procId,
+      numberOfSteps,
+      ModifyNamespaceState.values());
+
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
+    // Validate
+    NamespaceDescriptor currentNsDescriptor =
+        UTIL.getHBaseAdmin().getNamespaceDescriptor(nsd.getName());
+    assertEquals(currentNsDescriptor.getConfigurationValue(nsKey), nsValue);
+  }
+
+  @Test(timeout = 60000)
+  public void testRollbackAndDoubleExecution() throws Exception {
+    final NamespaceDescriptor nsd =
+        NamespaceDescriptor.create("testRollbackAndDoubleExecution").build();
+    final String nsKey = "foo";
+    final String nsValue = "bar";
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    createNamespaceForTesting(nsd);
+    ProcedureTestingUtility.waitNoProcedureRunning(procExec);
+    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
+
+    // Modify
+    nsd.setConfiguration(nsKey, nsValue);
+
+    // Start the Modify procedure && kill the executor
+    long procId = procExec.submitProcedure(
+      new ModifyNamespaceProcedure(procExec.getEnvironment(), nsd),
+      nonceGroup,
+      nonce);
+
+    // Failing in the middle of proc
+    int numberOfSteps = ModifyNamespaceState.values().length - 2;
+    MasterProcedureTestingUtility.testRollbackAndDoubleExecution(
+      procExec,
+      procId,
+      numberOfSteps,
+      ModifyNamespaceState.values());
+
+    // Validate
+    NamespaceDescriptor currentNsDescriptor =
+        UTIL.getHBaseAdmin().getNamespaceDescriptor(nsd.getName());
+    assertNull(currentNsDescriptor.getConfigurationValue(nsKey));
+  }
+
+  private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
+    return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
+  }
+
+  private void createNamespaceForTesting(NamespaceDescriptor nsDescriptor) throws Exception {
+    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
+
+    long procId = procExec.submitProcedure(
+      new CreateNamespaceProcedure(procExec.getEnvironment(), nsDescriptor),
+      nonceGroup + 1,
+      nonce + 1);
+    // Wait the completion
+    ProcedureTestingUtility.waitProcedure(procExec, procId);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
+  }
+}


Mime
View raw message