001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.master.procedure; 019 020import java.io.FileNotFoundException; 021import java.io.IOException; 022import org.apache.hadoop.fs.FileStatus; 023import org.apache.hadoop.fs.FileSystem; 024import org.apache.hadoop.fs.Path; 025import org.apache.hadoop.hbase.HConstants; 026import org.apache.hadoop.hbase.NamespaceDescriptor; 027import org.apache.hadoop.hbase.NamespaceNotFoundException; 028import org.apache.hadoop.hbase.constraint.ConstraintException; 029import org.apache.hadoop.hbase.master.MasterFileSystem; 030import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer; 031import org.apache.hadoop.hbase.util.CommonFSUtils; 032import org.apache.yetus.audience.InterfaceAudience; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 037import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos; 038import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.DeleteNamespaceState; 039 040/** 041 * The procedure to remove a namespace. 042 */ 043@InterfaceAudience.Private 044public class DeleteNamespaceProcedure 045 extends AbstractStateMachineNamespaceProcedure<DeleteNamespaceState> { 046 private static final Logger LOG = LoggerFactory.getLogger(DeleteNamespaceProcedure.class); 047 048 private NamespaceDescriptor nsDescriptor; 049 private String namespaceName; 050 051 public DeleteNamespaceProcedure() { 052 } 053 054 public DeleteNamespaceProcedure(MasterProcedureEnv env, String namespaceName) { 055 this(env, namespaceName, null); 056 } 057 058 public DeleteNamespaceProcedure(MasterProcedureEnv env, String namespaceName, 059 final ProcedurePrepareLatch latch) { 060 super(env, latch); 061 this.namespaceName = namespaceName; 062 } 063 064 @Override 065 protected Flow executeFromState(MasterProcedureEnv env, DeleteNamespaceState state) 066 throws InterruptedException { 067 LOG.info(this.toString()); 068 try { 069 switch (state) { 070 case DELETE_NAMESPACE_PREPARE: 071 boolean present = prepareDelete(env); 072 releaseSyncLatch(); 073 if (!present) { 074 assert isFailed() : "Delete namespace should have an exception here"; 075 return Flow.NO_MORE_STATE; 076 } 077 setNextState(DeleteNamespaceState.DELETE_NAMESPACE_DELETE_FROM_NS_TABLE); 078 break; 079 case DELETE_NAMESPACE_DELETE_FROM_NS_TABLE: 080 deleteNamespace(env, namespaceName); 081 setNextState(DeleteNamespaceState.DELETE_NAMESPACE_DELETE_DIRECTORIES); 082 break; 083 case DELETE_NAMESPACE_REMOVE_FROM_ZK: 084 // not used any more 085 setNextState(DeleteNamespaceState.DELETE_NAMESPACE_DELETE_DIRECTORIES); 086 break; 087 case DELETE_NAMESPACE_DELETE_DIRECTORIES: 088 deleteDirectory(env, namespaceName); 089 setNextState(DeleteNamespaceState.DELETE_NAMESPACE_REMOVE_NAMESPACE_QUOTA); 090 break; 091 case DELETE_NAMESPACE_REMOVE_NAMESPACE_QUOTA: 092 removeNamespaceQuota(env, namespaceName); 093 return Flow.NO_MORE_STATE; 094 default: 095 throw new UnsupportedOperationException(this + " unhandled state=" + state); 096 } 097 } catch (IOException e) { 098 if (isRollbackSupported(state)) { 099 setFailure("master-delete-namespace", e); 100 } else { 101 LOG.warn("Retriable error trying to delete namespace " + namespaceName + " (in state=" 102 + state + ")", e); 103 } 104 } 105 return Flow.HAS_MORE_STATE; 106 } 107 108 @Override 109 protected void rollbackState(final MasterProcedureEnv env, final DeleteNamespaceState state) 110 throws IOException { 111 if (state == DeleteNamespaceState.DELETE_NAMESPACE_PREPARE) { 112 // nothing to rollback, pre is just table-state checks. 113 // We can fail if the table does not exist or is not disabled. 114 // TODO: coprocessor rollback semantic is still undefined. 115 releaseSyncLatch(); 116 return; 117 } 118 119 // The procedure doesn't have a rollback. The execution will succeed, at some point. 120 throw new UnsupportedOperationException("unhandled state=" + state); 121 } 122 123 @Override 124 protected boolean isRollbackSupported(final DeleteNamespaceState state) { 125 switch (state) { 126 case DELETE_NAMESPACE_PREPARE: 127 return true; 128 default: 129 return false; 130 } 131 } 132 133 @Override 134 protected DeleteNamespaceState getState(final int stateId) { 135 return DeleteNamespaceState.forNumber(stateId); 136 } 137 138 @Override 139 protected int getStateId(final DeleteNamespaceState state) { 140 return state.getNumber(); 141 } 142 143 @Override 144 protected DeleteNamespaceState getInitialState() { 145 return DeleteNamespaceState.DELETE_NAMESPACE_PREPARE; 146 } 147 148 @Override 149 protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException { 150 super.serializeStateData(serializer); 151 152 MasterProcedureProtos.DeleteNamespaceStateData.Builder deleteNamespaceMsg = 153 MasterProcedureProtos.DeleteNamespaceStateData.newBuilder().setNamespaceName(namespaceName); 154 if (this.nsDescriptor != null) { 155 deleteNamespaceMsg 156 .setNamespaceDescriptor(ProtobufUtil.toProtoNamespaceDescriptor(this.nsDescriptor)); 157 } 158 serializer.serialize(deleteNamespaceMsg.build()); 159 } 160 161 @Override 162 protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException { 163 super.deserializeStateData(serializer); 164 165 MasterProcedureProtos.DeleteNamespaceStateData deleteNamespaceMsg = 166 serializer.deserialize(MasterProcedureProtos.DeleteNamespaceStateData.class); 167 namespaceName = deleteNamespaceMsg.getNamespaceName(); 168 if (deleteNamespaceMsg.hasNamespaceDescriptor()) { 169 nsDescriptor = 170 ProtobufUtil.toNamespaceDescriptor(deleteNamespaceMsg.getNamespaceDescriptor()); 171 } 172 } 173 174 @Override 175 public TableOperationType getTableOperationType() { 176 return TableOperationType.EDIT; 177 } 178 179 @Override 180 protected String getNamespaceName() { 181 return namespaceName; 182 } 183 184 /** 185 * Action before any real action of deleting namespace. 186 * @param env MasterProcedureEnv 187 */ 188 private boolean prepareDelete(final MasterProcedureEnv env) throws IOException { 189 if (getTableNamespaceManager(env).doesNamespaceExist(namespaceName) == false) { 190 setFailure("master-delete-namespace", new NamespaceNotFoundException(namespaceName)); 191 return false; 192 } 193 if (NamespaceDescriptor.RESERVED_NAMESPACES.contains(namespaceName)) { 194 setFailure("master-delete-namespace", 195 new ConstraintException("Reserved namespace " + namespaceName + " cannot be removed.")); 196 return false; 197 } 198 199 int tableCount = 0; 200 try { 201 tableCount = env.getMasterServices().listTableDescriptorsByNamespace(namespaceName).size(); 202 } catch (FileNotFoundException fnfe) { 203 setFailure("master-delete-namespace", new NamespaceNotFoundException(namespaceName)); 204 return false; 205 } 206 if (tableCount > 0) { 207 setFailure("master-delete-namespace", 208 new ConstraintException("Only empty namespaces can be removed. Namespace " + namespaceName 209 + " has " + tableCount + " tables")); 210 return false; 211 } 212 213 // This is used for rollback 214 nsDescriptor = getTableNamespaceManager(env).get(namespaceName); 215 return true; 216 } 217 218 /** 219 * delete the row from the ns family in meta table. 220 * @param env MasterProcedureEnv 221 * @param namespaceName name of the namespace in string format 222 */ 223 private static void deleteNamespace(MasterProcedureEnv env, String namespaceName) 224 throws IOException { 225 getTableNamespaceManager(env).deleteNamespace(namespaceName); 226 } 227 228 /** 229 * Delete the namespace directories from the file system 230 * @param env MasterProcedureEnv 231 * @param namespaceName name of the namespace in string format 232 */ 233 private static void deleteDirectory(MasterProcedureEnv env, String namespaceName) 234 throws IOException { 235 MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem(); 236 FileSystem fs = mfs.getFileSystem(); 237 Path p = CommonFSUtils.getNamespaceDir(mfs.getRootDir(), namespaceName); 238 239 try { 240 for (FileStatus status : fs.listStatus(p)) { 241 if (!HConstants.HBASE_NON_TABLE_DIRS.contains(status.getPath().getName())) { 242 throw new IOException("Namespace directory contains table dir: " + status.getPath()); 243 } 244 } 245 if (!fs.delete(CommonFSUtils.getNamespaceDir(mfs.getRootDir(), namespaceName), true)) { 246 throw new IOException("Failed to remove namespace: " + namespaceName); 247 } 248 } catch (FileNotFoundException e) { 249 // File already deleted, continue 250 LOG.debug("deleteDirectory throws exception: " + e); 251 } 252 } 253 254 /** 255 * remove quota for the namespace 256 * @param env MasterProcedureEnv 257 * @param namespaceName name of the namespace in string format 258 **/ 259 private static void removeNamespaceQuota(final MasterProcedureEnv env, final String namespaceName) 260 throws IOException { 261 env.getMasterServices().getMasterQuotaManager().removeNamespaceQuota(namespaceName); 262 } 263}