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 */ 018 019package org.apache.hadoop.hbase.master.procedure; 020 021import java.io.FileNotFoundException; 022import java.io.IOException; 023import org.apache.hadoop.fs.FileStatus; 024import org.apache.hadoop.fs.FileSystem; 025import org.apache.hadoop.fs.Path; 026import org.apache.hadoop.hbase.HConstants; 027import org.apache.hadoop.hbase.NamespaceDescriptor; 028import org.apache.hadoop.hbase.NamespaceNotFoundException; 029import org.apache.hadoop.hbase.constraint.ConstraintException; 030import org.apache.hadoop.hbase.master.MasterFileSystem; 031import org.apache.hadoop.hbase.master.TableNamespaceManager; 032import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer; 033import org.apache.hadoop.hbase.util.CommonFSUtils; 034import org.apache.yetus.audience.InterfaceAudience; 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037 038import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 039import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos; 040import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.DeleteNamespaceState; 041 042/** 043 * The procedure to remove a namespace. 044 */ 045@InterfaceAudience.Private 046public class DeleteNamespaceProcedure 047 extends AbstractStateMachineNamespaceProcedure<DeleteNamespaceState> { 048 private static final Logger LOG = LoggerFactory.getLogger(DeleteNamespaceProcedure.class); 049 050 private NamespaceDescriptor nsDescriptor; 051 private String namespaceName; 052 private Boolean traceEnabled; 053 054 public DeleteNamespaceProcedure() { 055 this.nsDescriptor = null; 056 this.traceEnabled = null; 057 } 058 059 public DeleteNamespaceProcedure(final MasterProcedureEnv env, final String namespaceName) { 060 this(env, namespaceName, null); 061 } 062 063 public DeleteNamespaceProcedure(final MasterProcedureEnv env, final String namespaceName, 064 final ProcedurePrepareLatch latch) { 065 super(env, latch); 066 this.namespaceName = namespaceName; 067 this.nsDescriptor = null; 068 this.traceEnabled = null; 069 } 070 071 @Override 072 protected Flow executeFromState(final MasterProcedureEnv env, final DeleteNamespaceState state) 073 throws InterruptedException { 074 LOG.info(this.toString()); 075 try { 076 switch (state) { 077 case DELETE_NAMESPACE_PREPARE: 078 boolean present = prepareDelete(env); 079 releaseSyncLatch(); 080 if (!present) { 081 assert isFailed() : "Delete namespace should have an exception here"; 082 return Flow.NO_MORE_STATE; 083 } 084 setNextState(DeleteNamespaceState.DELETE_NAMESPACE_DELETE_FROM_NS_TABLE); 085 break; 086 case DELETE_NAMESPACE_DELETE_FROM_NS_TABLE: 087 deleteFromNSTable(env, namespaceName); 088 setNextState(DeleteNamespaceState.DELETE_NAMESPACE_REMOVE_FROM_ZK); 089 break; 090 case DELETE_NAMESPACE_REMOVE_FROM_ZK: 091 removeFromZKNamespaceManager(env, namespaceName); 092 setNextState(DeleteNamespaceState.DELETE_NAMESPACE_DELETE_DIRECTORIES); 093 break; 094 case DELETE_NAMESPACE_DELETE_DIRECTORIES: 095 deleteDirectory(env, namespaceName); 096 setNextState(DeleteNamespaceState.DELETE_NAMESPACE_REMOVE_NAMESPACE_QUOTA); 097 break; 098 case DELETE_NAMESPACE_REMOVE_NAMESPACE_QUOTA: 099 removeNamespaceQuota(env, namespaceName); 100 return Flow.NO_MORE_STATE; 101 default: 102 throw new UnsupportedOperationException(this + " unhandled state=" + state); 103 } 104 } catch (IOException e) { 105 if (isRollbackSupported(state)) { 106 setFailure("master-delete-namespace", e); 107 } else { 108 LOG.warn("Retriable error trying to delete namespace " + namespaceName + 109 " (in state=" + state + ")", e); 110 } 111 } 112 return Flow.HAS_MORE_STATE; 113 } 114 115 @Override 116 protected void rollbackState(final MasterProcedureEnv env, final DeleteNamespaceState state) 117 throws IOException { 118 if (state == DeleteNamespaceState.DELETE_NAMESPACE_PREPARE) { 119 // nothing to rollback, pre is just table-state checks. 120 // We can fail if the table does not exist or is not disabled. 121 // TODO: coprocessor rollback semantic is still undefined. 122 releaseSyncLatch(); 123 return; 124 } 125 126 // The procedure doesn't have a rollback. The execution will succeed, at some point. 127 throw new UnsupportedOperationException("unhandled state=" + state); 128 } 129 130 @Override 131 protected boolean isRollbackSupported(final DeleteNamespaceState state) { 132 switch (state) { 133 case DELETE_NAMESPACE_PREPARE: 134 return true; 135 default: 136 return false; 137 } 138 } 139 140 @Override 141 protected DeleteNamespaceState getState(final int stateId) { 142 return DeleteNamespaceState.valueOf(stateId); 143 } 144 145 @Override 146 protected int getStateId(final DeleteNamespaceState state) { 147 return state.getNumber(); 148 } 149 150 @Override 151 protected DeleteNamespaceState getInitialState() { 152 return DeleteNamespaceState.DELETE_NAMESPACE_PREPARE; 153 } 154 155 @Override 156 protected void serializeStateData(ProcedureStateSerializer serializer) 157 throws IOException { 158 super.serializeStateData(serializer); 159 160 MasterProcedureProtos.DeleteNamespaceStateData.Builder deleteNamespaceMsg = 161 MasterProcedureProtos.DeleteNamespaceStateData.newBuilder().setNamespaceName(namespaceName); 162 if (this.nsDescriptor != null) { 163 deleteNamespaceMsg.setNamespaceDescriptor( 164 ProtobufUtil.toProtoNamespaceDescriptor(this.nsDescriptor)); 165 } 166 serializer.serialize(deleteNamespaceMsg.build()); 167 } 168 169 @Override 170 protected void deserializeStateData(ProcedureStateSerializer serializer) 171 throws IOException { 172 super.deserializeStateData(serializer); 173 174 MasterProcedureProtos.DeleteNamespaceStateData deleteNamespaceMsg = 175 serializer.deserialize(MasterProcedureProtos.DeleteNamespaceStateData.class); 176 namespaceName = deleteNamespaceMsg.getNamespaceName(); 177 if (deleteNamespaceMsg.hasNamespaceDescriptor()) { 178 nsDescriptor = 179 ProtobufUtil.toNamespaceDescriptor(deleteNamespaceMsg.getNamespaceDescriptor()); 180 } 181 } 182 183 @Override 184 public TableOperationType getTableOperationType() { 185 return TableOperationType.EDIT; 186 } 187 188 @Override 189 protected String getNamespaceName() { 190 return namespaceName; 191 } 192 193 /** 194 * Action before any real action of deleting namespace. 195 * @param env MasterProcedureEnv 196 * @throws IOException 197 */ 198 private boolean prepareDelete(final MasterProcedureEnv env) throws IOException { 199 if (getTableNamespaceManager(env).doesNamespaceExist(namespaceName) == false) { 200 setFailure("master-delete-namespace", new NamespaceNotFoundException(namespaceName)); 201 return false; 202 } 203 if (NamespaceDescriptor.RESERVED_NAMESPACES.contains(namespaceName)) { 204 setFailure("master-delete-namespace", new ConstraintException( 205 "Reserved namespace "+ namespaceName +" cannot be removed.")); 206 return false; 207 } 208 209 int tableCount = 0; 210 try { 211 tableCount = env.getMasterServices().listTableDescriptorsByNamespace(namespaceName).size(); 212 } catch (FileNotFoundException fnfe) { 213 setFailure("master-delete-namespace", new NamespaceNotFoundException(namespaceName)); 214 return false; 215 } 216 if (tableCount > 0) { 217 setFailure("master-delete-namespace", new ConstraintException( 218 "Only empty namespaces can be removed. Namespace "+ namespaceName + " has " 219 + tableCount +" tables")); 220 return false; 221 } 222 223 // This is used for rollback 224 nsDescriptor = getTableNamespaceManager(env).get(namespaceName); 225 return true; 226 } 227 228 /** 229 * delete the row from namespace table 230 * @param env MasterProcedureEnv 231 * @param namespaceName name of the namespace in string format 232 * @throws IOException 233 */ 234 protected static void deleteFromNSTable( 235 final MasterProcedureEnv env, 236 final String namespaceName) throws IOException { 237 getTableNamespaceManager(env).removeFromNSTable(namespaceName); 238 } 239 240 /** 241 * undo the delete 242 * @param env MasterProcedureEnv 243 * @throws IOException 244 */ 245 private void undoDeleteFromNSTable(final MasterProcedureEnv env) { 246 try { 247 if (nsDescriptor != null) { 248 CreateNamespaceProcedure.insertIntoNSTable(env, nsDescriptor); 249 } 250 } catch (Exception e) { 251 // Ignore 252 LOG.debug("Rollback of deleteFromNSTable throws exception: " + e); 253 } 254 } 255 256 /** 257 * remove from ZooKeeper. 258 * @param env MasterProcedureEnv 259 * @param namespaceName name of the namespace in string format 260 * @throws IOException 261 */ 262 protected static void removeFromZKNamespaceManager( 263 final MasterProcedureEnv env, 264 final String namespaceName) throws IOException { 265 getTableNamespaceManager(env).removeFromZKNamespaceManager(namespaceName); 266 } 267 268 /** 269 * undo the remove from ZooKeeper 270 * @param env MasterProcedureEnv 271 * @throws IOException 272 */ 273 private void undoRemoveFromZKNamespaceManager(final MasterProcedureEnv env) { 274 try { 275 if (nsDescriptor != null) { 276 CreateNamespaceProcedure.updateZKNamespaceManager(env, nsDescriptor); 277 } 278 } catch (Exception e) { 279 // Ignore 280 LOG.debug("Rollback of removeFromZKNamespaceManager throws exception: " + e); 281 } 282 } 283 284 /** 285 * Delete the namespace directories from the file system 286 * @param env MasterProcedureEnv 287 * @param namespaceName name of the namespace in string format 288 * @throws IOException 289 */ 290 protected static void deleteDirectory( 291 final MasterProcedureEnv env, 292 final String namespaceName) throws IOException { 293 MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem(); 294 FileSystem fs = mfs.getFileSystem(); 295 Path p = CommonFSUtils.getNamespaceDir(mfs.getRootDir(), namespaceName); 296 297 try { 298 for(FileStatus status : fs.listStatus(p)) { 299 if (!HConstants.HBASE_NON_TABLE_DIRS.contains(status.getPath().getName())) { 300 throw new IOException("Namespace directory contains table dir: " + status.getPath()); 301 } 302 } 303 if (!fs.delete(CommonFSUtils.getNamespaceDir(mfs.getRootDir(), namespaceName), true)) { 304 throw new IOException("Failed to remove namespace: " + namespaceName); 305 } 306 } catch (FileNotFoundException e) { 307 // File already deleted, continue 308 LOG.debug("deleteDirectory throws exception: " + e); 309 } 310 } 311 312 /** 313 * undo delete directory 314 * @param env MasterProcedureEnv 315 * @throws IOException 316 */ 317 private void rollbackDeleteDirectory(final MasterProcedureEnv env) throws IOException { 318 try { 319 CreateNamespaceProcedure.createDirectory(env, nsDescriptor); 320 } catch (Exception e) { 321 // Ignore exception 322 LOG.debug("Rollback of deleteDirectory throws exception: " + e); 323 } 324 } 325 326 /** 327 * remove quota for the namespace 328 * @param env MasterProcedureEnv 329 * @param namespaceName name of the namespace in string format 330 * @throws IOException 331 **/ 332 protected static void removeNamespaceQuota( 333 final MasterProcedureEnv env, 334 final String namespaceName) throws IOException { 335 env.getMasterServices().getMasterQuotaManager().removeNamespaceQuota(namespaceName); 336 } 337 338 /** 339 * undo remove quota for the namespace 340 * @param env MasterProcedureEnv 341 * @throws IOException 342 **/ 343 private void rollbacRemoveNamespaceQuota(final MasterProcedureEnv env) throws IOException { 344 try { 345 CreateNamespaceProcedure.setNamespaceQuota(env, nsDescriptor); 346 } catch (Exception e) { 347 // Ignore exception 348 LOG.debug("Rollback of removeNamespaceQuota throws exception: " + e); 349 } 350 } 351 352 private static TableNamespaceManager getTableNamespaceManager(final MasterProcedureEnv env) { 353 return env.getMasterServices().getClusterSchema().getTableNamespaceManager(); 354 } 355 /** 356 * The procedure could be restarted from a different machine. If the variable is null, we need to 357 * retrieve it. 358 * @return traceEnabled 359 */ 360 private Boolean isTraceEnabled() { 361 if (traceEnabled == null) { 362 traceEnabled = LOG.isTraceEnabled(); 363 } 364 return traceEnabled; 365 } 366}